rcarver/orel

View on GitHub
lib/orel/relation/namer.rb

Summary

Maintainability
A
0 mins
Test Coverage
require 'digest/md5'

module Orel
  module Relation
    class Namer

      # The maximum string length of a mysql key.
      MYSQL_MAX_KEY_LENGTH = 64.freeze

      # Internal: Get a namer for a class. The options on the name
      # are determined by introspecting the class hierarchy.
      #
      # klass        - Class to name.
      # orel_options - Orel::Options that configure the naming.
      #
      # Returns an Orel::Relation::Namer.
      def self.for_class(klass, orel_options)
        options = {
          :prefix => orel_options.prefix,
          :suffix => orel_options.suffix,
          :pluralize => orel_options.pluralize
        }
        name = klass.name.split("::").last.underscore
        Namer.new(name, options)
      end

      # Internal: Initialize a new namer.
      #
      # name    - String singular name.
      # options - Hash of options. All are required:
      #           :pluralize - Boolean true to pluralize `name` where appropriate.
      #           :prefix    - String to prefix to all names.
      #           :suffix    - String to append to all names.
      #
      def initialize(name, options)
        @name = name
        @options = options
        @pluralize = options[:pluralize]
        @prefix = options[:prefix]
        @suffix = options[:suffix]
      end

      # Internal: Get a new instance of Namer that creates names
      # for a child heading.
      #
      # Returns an Orel::Relation::Namer.
      def for_child(name)
        Namer.new([@name, name].join("_"), @options.merge(:pluralize => false))
      end

      # Internal: The name of the heading.
      #
      # Returns a Symbol.
      def heading_name
        if @pluralize
          ix(@name.pluralize).to_sym
        else
          ix(@name).to_sym
        end
      end

      # Internal: Transform an attribute name so it can be used
      # on the other side of a relationship.
      #
      # attribute_name - Symbol name of the attribute.
      #
      # Examples
      #
      #     namer = Namer.new("user")
      #     namer.foreign_attribute_name(:id)
      #     # => :user_id
      #
      #     namer = Namer.new("user")
      #     namer.foreign_attribute_name(:name)
      #     # => :name
      #
      # Returns a Symbol.
      def foreign_attribute_name(attribute_name)
        if attribute_name == :id
          [@name, attribute_name].join('_').to_sym
        else
          attribute_name
        end
      end

      # Internal: Transform a set of attribute names into the name of
      # a unique key constraint. The resulting name aims to be somewhat
      # visibly descriptive while also unique within an entire database.
      #
      # attribute_names - Array of Symbol names of the attributes in the key.
      #
      # Examples
      #
      #     namer = Namer.new("user")
      #     namer.unique_key_name([:first_name, :last_name])
      #     # => :u_fn_ln_[MD5("first_name::last_name")]
      #
      # Returns a Symbol.
      def unique_key_name(attribute_names)
        parts = [
          shorten(heading_name),
          attribute_names.map { |a| shorten(a) },
          Digest::MD5.hexdigest(attribute_names.join('::'))
        ]
        parts.flatten.join('_')[0, MYSQL_MAX_KEY_LENGTH].to_sym
      end

      def foreign_key_constraint_name(heading_name, attribute_names)
        parts = [
          shorten(self.heading_name),
          shorten(heading_name),
          attribute_names.map { |a| shorten(a) },
          Digest::MD5.hexdigest(attribute_names.join('::'))
        ]
        parts.flatten.join('_')[0, MYSQL_MAX_KEY_LENGTH].to_sym
      end

    protected

      def ix(name)
        [@prefix, name, @suffix].compact.join
      end

      def shorten(name)
        name.to_s.split('_').map { |part| part[0, 1] }.join
      end

    end
  end
end