module ORMivore::Entity::ClassMethods

Constants

ALLOWED_ATTRIBUTE_TYPES

Attributes

attributes_declaration[R]
optional_attributes_list[R]

Public Instance Methods

attributes_list() click to toggle source
# File lib/ormivore/entity.rb, line 10
def attributes_list
  attributes_declaration.keys
end
coerce(attrs) click to toggle source
# File lib/ormivore/entity.rb, line 55
def coerce(attrs)
  attrs.each do |name, type|
    attr_value = attrs[name]
    declared_type = attributes_declaration[name]
    if declared_type && !attr_value.is_a?(declared_type)
      attrs[name] = Kernel.public_send(declared_type.name.to_sym, attr_value)
    end
  end
rescue ArgumentError => e
  raise ORMivore::BadArgumentError.new(e)
end
construct(attrs, id) click to toggle source
# File lib/ormivore/entity.rb, line 14
def construct(attrs, id)
  id = coerce_id(id)

  coerced_attrs = attrs.symbolize_keys.tap { |h| coerce(h) }.freeze

  base_attributes = coerced_attrs
  dirty_attributes = {}.freeze

  validate_presence_of_proper_attributes(base_attributes, dirty_attributes)

  obj = allocate

  obj.instance_variable_set(:@id, id)
  obj.instance_variable_set(:@base_attributes, base_attributes)
  obj.instance_variable_set(:@dirty_attributes, dirty_attributes)

  # TODO how to do custom validation?
  # validate

  obj
end
validate_presence_of_proper_attributes(base, dirty) click to toggle source
# File lib/ormivore/entity.rb, line 36
def validate_presence_of_proper_attributes(base, dirty)
  # doing complicated way first because it is much more memory efficient
  # but it does not allow for good error messages, so if something is
  # wrong, need to proceed to inefficient validation that produces nice
  # messages
  missing = 0
  known_counts = attributes_list.each_with_object([0, 0]) { |attr, acc|
    acc[0] += 1 if base[attr]
    acc[1] += 1 if dirty[attr]
    missing +=1 unless optional_attributes_list.include?(attr) || base[attr] || dirty[attr]
  }

  if missing > 0 || [base.length, dirty.length] != known_counts
    expensive_validate_presence_of_proper_attributes(
      base.merge(dirty)
    )
  end
end

Private Instance Methods

attributes(declaration) click to toggle source
# File lib/ormivore/entity.rb, line 69
      def attributes(declaration)
        @attributes_declaration = declaration.symbolize_keys.freeze
        validate_attributes_declaration
        # @attributes_list = methods.map(&:to_sym)
        @optional_attributes_list ||= []

        attributes_list.map(&:to_s).each do |attr|
          module_eval(<<-EOS)
            def #{attr}
              @dirty_attributes[:#{attr}] || @base_attributes[:#{attr}]
            end
          EOS
          self::Builder.module_eval(<<-EOS)
            def #{attr}
              attributes[:#{attr}]
            end
            def #{attr}=(value)
              attributes[:#{attr}] = value
            end
          EOS
        end
      end
coerce_id(value) click to toggle source
# File lib/ormivore/entity.rb, line 118
def coerce_id(value)
  value ? Integer(value) : nil
rescue ArgumentError
  raise ORMivore::BadArgumentError, "Not a valid id: #{value.inspect}"
end
expensive_validate_presence_of_proper_attributes(attrs) click to toggle source

TODO figure out how to differenciate private methods that are part of ORMivore API from those that are NOT

# File lib/ormivore/entity.rb, line 100
def expensive_validate_presence_of_proper_attributes(attrs)
  attributes_list.each do |attr|
    unless attrs.delete(attr) || optional_attributes_list.include?(attr)
      raise BadAttributesError, "Missing attribute '#{attr}'"
    end
  end

  raise BadAttributesError, "Unknown attributes #{attrs.inspect}" unless attrs.empty?
end
optional(*methods) click to toggle source
# File lib/ormivore/entity.rb, line 92
def optional(*methods)
  @optional_attributes_list = methods.map(&:to_sym)
end
validate_attributes_declaration() click to toggle source
# File lib/ormivore/entity.rb, line 110
def validate_attributes_declaration
  attributes_declaration.each do |name, type|
    unless ALLOWED_ATTRIBUTE_TYPES.include?(type)
      raise ORMivore::BadArgumentError, "Invalid attribute type #{type.inspect}"
    end
  end
end