module Uuids module Base # @api hide # Adds getter and setter for the model attribute referred by uuid. # # @example # BelongsTo.add City, :state, State # city = City.new # city.respond_to? :state # => true # city.respond_to? :state= # => true class BelongsTo < Struct.new(:klass, :name, :model) # @api hide # @!attribute klass # The model to add the attribute to. # @return [ActiveRecord::Base] The model class. # @api hide # @!attribute name # The name of the attribute to be added. # @return [String, Symbol] The name. # @api hide # @!attribute model # The model to be referred by uuid. # @return [ActiveRecord::Base] The model class to be referred to. # @api hide # @!attribute [r] class_name # The name of the {#klass} constant. # @return [String] The {#klass} name. def class_name @class_name ||= klass.name end # @api hide # @!attribute [r] model_name # The name of the {#model} constant. # @return [String] The {#model} name. def model_name @model_name ||= model.name end # @api hide # @!attribute [r] model_name # The name of the {#klass} db table column to store reference to the # attribute by uuid. # @return [String] The name of the column. def column_name @column_name ||= "#{ name }_uuid" end # @api hide # Constructs the object and adds the attribute. # @example (see Uuids::Base::BelongsTo) # @params [ActiveRecord::Base] klass The model to add attribute to. # Should have a corresponding column for reference by uuid. # @params [String, Symbol] name The name of the attribute to be added. # @params [ActiveRecord::Base] model The model to be referred by uuid. # Should include the Uuids::Base module. # @raise (see Uuids::Base::BelongsTo#add) def self.add(*args) new(*args).add end # @api hide # Adds the attribute's getter and setter. # @raise [TypeError] if the {#model} doesn't include # the Uuids::Base module. # @raise [NotImplementedError] if the {#klass} doesn't have the # correspondint {#column_name} column. def add check_model check_column add_getter add_setter end private def check_model return if model.ancestors.include? Uuids::Base fail TypeError .new "#{ model_name } class doesn't include the Uuids::Base module" end def check_column return if klass.attribute_names.include? column_name fail NotImplementedError .new "#{ class_name }##{ column_name } attribute isn't implemented" end def add_getter klass.class_eval getter end def add_setter klass.class_eval setter end def getter "def #{ name }; uuid = read_attribute :#{ column_name }; return @#{ name } = nil unless uuid; if Array(@#{ name }.try(:uuids)).map(&:value).include? uuid; @#{ name }; else; @#{ name } = #{ model_name }.by_uuid(uuid).includes(:uuids).first; end; end" end def setter "def #{ name }=(value); fail TypeError unless value.nil? || value.is_a?(#{ model_name }); write_attribute :#{ column_name }, value.try(:uuid); #{ name }; end" end end end end