class Bronze::Entities::Attributes::Builder
Service class to define attributes on an entity.
Constants
- VALID_OPTIONS
Provides a list of the valid options for the attribute_options parameter for
Builder#build
.
Attributes
@return [Class] the entity class on which attributes will be defined.
Public Class Methods
Registers a transform as the default transform for attributes with the specified type or a subtype of the specified type.
This default is not retroactive - any attributes already defined will use their existing default transform, if any. If more than one registered transform has a matching type, the most recently defined transform will be used.
@param type [Class] The attribute type. When defining an attribute, if
the type of the attribute is this class or a subclass of this class and no :transform option is given, the transform for the attribute will be the transform passed to ::attribute_transform.
@param transform [Class, Bronze::Transforms::Transform] The transform to
use as the default. If this value is a transform instance, the default transform for matching attributes will be the given transform. Otherwise, will set the default transform to the result of ::instance (if defined) or ::new.
# File lib/bronze/entities/attributes/builder.rb, line 44 def attribute_transform(type, transform) (@attribute_transforms ||= {})[type] = transform end
@param entity_class
[Class] The entity class on which attributes will be
defined.
# File lib/bronze/entities/attributes/builder.rb, line 76 def initialize(entity_class) @entity_class = entity_class end
Private Class Methods
# File lib/bronze/entities/attributes/builder.rb, line 50 def transform_for_attribute(attribute_type) (@attribute_transforms ||= {}).reverse_each do |type, transform| return transform if attribute_type <= type end return super if superclass.respond_to?(:transform_for_attribute) end
Public Instance Methods
Defines an attribute on the entity class.
@example Defining an Attribute
class Book < Bronze::Entities::Entity; end book = Book.new book.title #=> NoMethodError: undefined method `title' builder = Bronze::Entities::Attributes::Builder.new(Book) builder.define_attribute :title, String book.title #=> nil book.title = 'Romance of the Three Kingdoms' book.title #=> 'Romance of the Three Kingdoms'
@param attribute_name [Symbol, String] The name of the attribute to
define.
@param attribute_type [Class] The type of the attribute to define. @param attribute_options [Hash] Additional options for building the
attribute.
@option attribute_options [Object, Proc] :default The default value for
the attribute. If the attribute value is nil or has not been set, the attribute will be set to the default. If the default is a Proc, the Proc will be called each time and the attribute set to the return value. Otherwise, the attribute will be set to the default value.
@option attribute_options [Boolean] :default_transform If a transform is
set, marks the transform as a default transform, which can be overriden when normalizing the attribute.
@option attribute_options [Boolean] :foreign_key Marks the attribute as a
foreign key. Will be set to true by association builders, and generally should not be set manually. Defaults to false.
@option attribute_options [Boolean] :read_only If true, the writer method
for the attribute will be set as private. Defaults to false.
@option attribute_options [Class, Bronze::Transform] :transform If set,
the attribute will be normalized using this transform. By default, certain attribute types will be transformed - BigDecimal, Date, DateTime, Symbol, and Time.
@return [Attributes::Metadata] The generated metadata for the
attribute.
@raise Builder::Error if the attribute name or attribute type is missing
or invalid.
# File lib/bronze/entities/attributes/builder.rb, line 131 def build(attribute_name, attribute_type, attribute_options = {}) validate_attribute_name(attribute_name) validate_attribute_opts(attribute_options) characterize( attribute_name, attribute_type, attribute_options ) .tap { |metadata| define_property_methods(metadata) } end
Private Instance Methods
# File lib/bronze/entities/attributes/builder.rb, line 145 def attributes_module @attributes_module ||= define_attributes_module end
# File lib/bronze/entities/attributes/builder.rb, line 149 def characterize(attribute_name, attribute_type, attribute_options) Bronze::Entities::Attributes::Metadata.new( attribute_name, attribute_type, normalize_options(attribute_options, type: attribute_type) ) end
# File lib/bronze/entities/attributes/builder.rb, line 157 def define_attributes_module mod = if entity_class.const_defined?(:Attributes, false) entity_class::Attributes else entity_class.const_set(:Attributes, Module.new) end entity_class.send(:include, mod) unless entity_class < mod mod end
# File lib/bronze/entities/attributes/builder.rb, line 170 def define_property_methods(metadata) define_reader(metadata) define_writer(metadata) end
# File lib/bronze/entities/attributes/builder.rb, line 175 def define_reader(metadata) attr_name = metadata.name attributes_module.send :define_method, metadata.reader_name, -> { get_attribute(attr_name) } end
# File lib/bronze/entities/attributes/builder.rb, line 183 def define_writer(metadata) attr_name = metadata.name attributes_module.send :define_method, metadata.writer_name, ->(value) { set_attribute(attr_name, value) } return unless metadata.read_only? attributes_module.send(:private, metadata.writer_name) end
# File lib/bronze/entities/attributes/builder.rb, line 195 def normalize_options(options, type:) options = options.each.with_object({}) do |(key, value), hsh| hsh[key.intern] = value end unless options.key?(:default_transform) options[:default_transform] = !options[:transform] end options[:transform] = normalize_transform(options[:transform], type: type) options end
# File lib/bronze/entities/attributes/builder.rb, line 210 def normalize_transform(transform, type:) transform ||= self.class.send(:transform_for_attribute, type) return nil if transform.nil? transform_instance(transform) end
# File lib/bronze/entities/attributes/builder.rb, line 218 def transform_instance(transform) return transform unless transform.is_a?(Class) return transform.instance if transform.respond_to?(:instance) transform.new end
# File lib/bronze/entities/attributes/builder.rb, line 226 def validate_attribute_name(attribute_name) unless attribute_name.is_a?(String) || attribute_name.is_a?(Symbol) message = 'expected attribute name to be a String or Symbol, but was ' \ "#{attribute_name.inspect}" raise ArgumentError, message, caller[1..-1] end return unless attribute_name.to_s.empty? raise ArgumentError, "attribute name can't be blank", caller[1..-1] end
# File lib/bronze/entities/attributes/builder.rb, line 239 def validate_attribute_opts(attribute_options) attribute_options.each do |key, _value| next if VALID_OPTIONS.include?(key.to_s) raise ArgumentError, "invalid attribute option #{key.inspect}", caller[1..-1] end end