module OptionsModel::Concerns::Attributes::ClassMethods
Public Instance Methods
attribute(name, cast_type, default: nil, array: false)
click to toggle source
# File lib/options_model/concerns/attributes.rb, line 9 def attribute(name, cast_type, default: nil, array: false) check_not_finalized! name = name.to_sym check_name_validity! name ActiveModel::Type.lookup(cast_type) attribute_defaults[name] = default default_extractor = if default.respond_to?(:call) ".call" elsif default.duplicable? ".deep_dup" else "" end generated_attribute_methods.synchronize do generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1 def #{name} value = attributes[:#{name}] return value unless value.nil? attributes[:#{name}] = self.class.attribute_defaults[:#{name}]#{default_extractor} attributes[:#{name}] end STR if array generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1 def #{name}=(value) if value.respond_to?(:to_a) attributes[:#{name}] = value.to_a.map { |i| ActiveModel::Type.lookup(:#{cast_type}).cast(i) } elsif value.nil? attributes[:#{name}] = self.class.attribute_defaults[:#{name}]#{default_extractor} else raise ArgumentError, "`value` should respond to `to_a`, but got \#{value.class} -- \#{value.inspect}" end end STR else generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1 def #{name}=(value) attributes[:#{name}] = ActiveModel::Type.lookup(:#{cast_type}).cast(value) end STR generated_attribute_methods.send :alias_method, :"#{name}?", name if cast_type == :boolean end end attribute_names_for_inlining << name self end
attribute_defaults()
click to toggle source
# File lib/options_model/concerns/attributes.rb, line 132 def attribute_defaults @attribute_defaults ||= ActiveSupport::HashWithIndifferentAccess.new end
attribute_names()
click to toggle source
# File lib/options_model/concerns/attributes.rb, line 148 def attribute_names attribute_names_for_nesting + attribute_names_for_inlining end
attribute_names_for_inlining()
click to toggle source
# File lib/options_model/concerns/attributes.rb, line 144 def attribute_names_for_inlining @attribute_names_for_inlining ||= Set.new end
attribute_names_for_nesting()
click to toggle source
# File lib/options_model/concerns/attributes.rb, line 140 def attribute_names_for_nesting @attribute_names_for_nesting ||= Set.new end
embeds_one(name, class_name: nil, anonymous_class: nil)
click to toggle source
# File lib/options_model/concerns/attributes.rb, line 89 def embeds_one(name, class_name: nil, anonymous_class: nil) check_not_finalized! raise ArgumentError, "must provide at least one of `class_name` or `anonymous_class`" if class_name.blank? && anonymous_class.nil? name = name.to_sym check_name_validity! name nested_classes[name] = if class_name.present? class_name.constantize else anonymous_class end generated_attribute_methods.synchronize do generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1 def #{name} nested_attributes[:#{name}] ||= self.class.nested_classes[:#{name}].new(attributes[:#{name}]) end STR generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1 def #{name}=(value) klass = self.class.nested_classes[:#{name}] if value.respond_to?(:to_h) nested_attributes[:#{name}] = klass.new(value.to_h) elsif value.is_a? klass nested_attributes[:#{name}] = value elsif value.nil? nested_attributes[:#{name}] = klass.new else raise ArgumentError, "`value` should respond to `to_h` or \#{klass}, but got \#{value.class}" end end STR end attribute_names_for_nesting << name self end
enum_attribute(name, enum, default: nil, allow_nil: false)
click to toggle source
# File lib/options_model/concerns/attributes.rb, line 66 def enum_attribute(name, enum, default: nil, allow_nil: false) check_not_finalized! raise ArgumentError, "enum should be an Array and can't empty" unless enum.is_a?(Array) && enum.any? enum = enum.map(&:to_s) attribute name, :string, default: default pluralized_name = name.to_s.pluralize generated_class_methods.synchronize do generated_class_methods.module_eval <<-STR, __FILE__, __LINE__ + 1 def #{pluralized_name} %w(#{enum.join(' ')}).freeze end STR validates name, inclusion: { in: enum }, allow_nil: allow_nil end self end
finalize!(nested = true)
click to toggle source
# File lib/options_model/concerns/attributes.rb, line 156 def finalize!(nested = true) nested_classes.values.each(&:finalize!) if nested @finalized = true end
finalized?()
click to toggle source
# File lib/options_model/concerns/attributes.rb, line 152 def finalized? @finalized ||= false end
nested_classes()
click to toggle source
# File lib/options_model/concerns/attributes.rb, line 136 def nested_classes @nested_classes ||= ActiveSupport::HashWithIndifferentAccess.new end
Protected Instance Methods
check_name_validity!(symbolized_name)
click to toggle source
# File lib/options_model/concerns/attributes.rb, line 164 def check_name_validity!(symbolized_name) if dangerous_attribute_method?(symbolized_name) raise ArgumentError, "#{symbolized_name} is defined by #{OptionsModel::Base}. Check to make sure that you don't have an attribute or method with the same name." end if attribute_names_for_inlining.include?(symbolized_name) || attribute_names_for_nesting.include?(symbolized_name) raise ArgumentError, "duplicate define attribute `#{symbolized_name}`" end end
check_not_finalized!()
click to toggle source
# File lib/options_model/concerns/attributes.rb, line 174 def check_not_finalized! raise "can't modify finalized #{self}" if finalized? end
generated_attribute_methods()
click to toggle source
# File lib/options_model/concerns/attributes.rb, line 196 def generated_attribute_methods @generated_attribute_methods ||= Module.new do extend Mutex_m end.tap { |mod| include mod } end
generated_class_methods()
click to toggle source
# File lib/options_model/concerns/attributes.rb, line 202 def generated_class_methods @generated_class_methods ||= Module.new do extend Mutex_m end.tap { |mod| extend mod } end