module Gorillib::Model::ClassMethods
Attributes
Public Instance Methods
# File lib/gorillib/model/schema_magic.rb, line 57 def assemble_positionals positionals = fields.values.keep_if{|fld| fld.position? }.sort_by!{|fld| fld.position } return [] if positionals.empty? if (positionals.map(&:position) != (0..positionals.length-1).to_a) then raise ConflictingPositionError, "field positions #{positionals.map(&:position).join(",")} for #{positionals.map(&:name).join(",")} aren't in strict minimal order" ; end positionals.map!(&:name) end
turn model constructor args (‘*positional_args, {attrs}`) into a combined attrs hash. positional_args are mapped to the set of attribute names in order – by default, the class’ field names.
Notes:
-
Positional args always clobber elements of the attribute hash.
-
Nil positional args are treated as present-and-nil (this might change).
-
Raises an error if positional args
@param [Array] args list of attributes, in order, to map.
@return [Hash] a combined, reconciled hash of attributes to set
# File lib/gorillib/model/schema_magic.rb, line 77 def attrs_hash_from_args(args) attrs = args.extract_options! if args.present? ArgumentError.check_arity!(args, 0..positionals.length){ "extracting args #{args} for #{self}" } positionals_to_map = positionals[0..(args.length-1)] attrs = attrs.merge(Hash[positionals_to_map.zip(args)]) end attrs end
# File lib/gorillib/model/schema_magic.rb, line 31 def collection(field_name, collection_type, options={}) options[:item_type] = options[:of] if options.has_key?(:of) field(field_name, collection_type, { field_type: ::Gorillib::Model::SimpleCollectionField}.merge(options)) end
Defines a new field
For each field that is defined, a getter and setter will be added as an instance method to the model. An Field
instance will be added to result of the fields class method.
@example
field :height, Integer
@param [Symbol] field_name The field name. Must start with ‘[A-Za-z_]` and subsequently contain only `[A-Za-z0-9_]` @param [Class] type The field’s type (required) @option options [String] doc Documentation string for the field (optional) @option options [Proc, Object] default Default value, or proc that instance can evaluate to find default value
@return Gorillib::Model::Field
# File lib/gorillib/model/schema_magic.rb, line 21 def field(field_name, type, options={}) options = options.symbolize_keys field_type = options.delete(:field_type){ ::Gorillib::Model::Field } fld = field_type.new(self, field_name, type, options) @_own_fields[fld.name] = fld _reset_descendant_fields fld.send(:inscribe_methods, self) fld end
@return [Array<Symbol>] The attribute names
# File lib/gorillib/model/schema_magic.rb, line 49 def field_names @_field_names ||= fields.keys end
@return [{Symbol => Gorillib::Model::Field
}]
# File lib/gorillib/model/schema_magic.rb, line 38 def fields return @_fields if defined?(@_fields) @_fields = ancestors.reverse.inject({}){|acc, ancestor| acc.merge!(ancestor.try(:_own_fields) || {}) } end
# File lib/gorillib/model/serialization.rb, line 31 def from_tuple(*vals) receive Hash[field_names[0..vals.length-1].zip(vals)] end
@return [true, false] true if the field is defined on this class
# File lib/gorillib/model/schema_magic.rb, line 44 def has_field?(field_name) fields.has_key?(field_name) end
@return Class
name and its attributes
@example Inspect the model’s definition.
Person.inspect #=> Person[first_name, last_name]
# File lib/gorillib/model/base.rb, line 256 def inspect "#{self.name || 'anon'}[#{ field_names.join(",") }]" end
# File lib/gorillib/model/base.rb, line 259 def inspect_compact() self.name || inspect ; end
A ‘native` object does not need any transformation; it is accepted directly. By default, an object is native if it `is_a?` this class
@param obj [Object] the object that will be received @return [true, false] true if the item does not need conversion
# File lib/gorillib/model/base.rb, line 248 def native?(obj) obj.is_a?(self) end
# File lib/gorillib/model/schema_magic.rb, line 53 def positionals @_positionals ||= assemble_positionals end
Receive external data, type-converting and creating contained models as necessary
@return [Gorillib::Model] the new object
# File lib/gorillib/model/base.rb, line 232 def receive(attrs={}, &block) return nil if attrs.nil? return attrs if native?(attrs) # Gorillib::Model::Validate.hashlike!(attrs){ "attributes for #{self.inspect}" } klass = attrs.has_key?(:_type) ? Gorillib::Factory(attrs[:_type]) : self warn "factory #{klass} is not a type of #{self} as specified in #{attrs}" unless klass <= self # klass.new(attrs, &block) end
A readable handle for this field
# File lib/gorillib/model/base.rb, line 224 def typename @typename ||= Gorillib::Inflector.underscore(self.name||'anon').gsub(%r{/}, '.') end
Protected Instance Methods
Ensure that classes inherit all their parents’ fields, even if fields are added after the child class is defined.
# File lib/gorillib/model/schema_magic.rb, line 93 def _reset_descendant_fields ObjectSpace.each_object(::Class) do |klass| klass.__send__(:remove_instance_variable, '@_fields') if (klass <= self) && klass.instance_variable_defined?('@_fields') klass.__send__(:remove_instance_variable, '@_field_names') if (klass <= self) && klass.instance_variable_defined?('@_field_names') klass.__send__(:remove_instance_variable, '@_positionals') if (klass <= self) && klass.instance_variable_defined?('@_positionals') end end
define the reader method ‘#foo` for a field named `:foo`
# File lib/gorillib/model/schema_magic.rb, line 102 def define_attribute_reader(field_name, field_type, visibility) define_meta_module_method(field_name, visibility) do begin read_attribute(field_name) rescue StandardError => err ; err.polish("#{self.class}.#{field_name}") rescue nil ; raise ; end end end
# File lib/gorillib/model/schema_magic.rb, line 125 def define_attribute_receiver(field_name, field_type, visibility) # @param [Object] val the raw value to type-convert and adopt # @return [Object] the attribute's new value define_meta_module_method("receive_#{field_name}", visibility) do |val| begin val = field_type.receive(val) write_attribute(field_name, val) rescue StandardError => err ; err.polish("#{self.class}.#{field_name} type #{type} on #{val}") rescue nil ; raise ; end end end
define the present method ‘#foo?` for a field named `:foo`
# File lib/gorillib/model/schema_magic.rb, line 118 def define_attribute_tester(field_name, field_type, visibility) field = fields[field_name] define_meta_module_method("#{field_name}?", visibility) do attribute_set?(field_name) || field.has_default? end end
define the writer method ‘#foo=` for a field named `:foo`
# File lib/gorillib/model/schema_magic.rb, line 111 def define_attribute_writer(field_name, field_type, visibility) define_meta_module_method("#{field_name}=", visibility) do |val| write_attribute(field_name, val) end end
Collection
receiver –
# File lib/gorillib/model/schema_magic.rb, line 139 def define_collection_receiver(field) collection_field_name = field.name; collection_type = field.type # @param [Array[Object],Hash[Object]] the collection to merge # @return [Gorillib::Collection] the updated collection define_meta_module_method("receive_#{collection_field_name}", true) do |coll, &block| begin if collection_type.native?(coll) write_attribute(collection_field_name, coll) else read_attribute(collection_field_name).receive!(coll, &block) end rescue StandardError => err ; err.polish("#{self.class} #{collection_field_name} collection on #{args}'") rescue nil ; raise ; end end end
# File lib/gorillib/model/schema_magic.rb, line 154 def inherited(base) base.instance_eval do self.meta_module @_own_fields ||= {} end super end