Module: Diffbot::CoercibleHash

Included in:
Frontpage, Item, Items::Product, Product
Defined in:
lib/diffbot/coercible_hash.rb

Overview

Public: Extend a hash with this mixin to make keys coercible to certain classes. These keys, when assigned to the hash, will be transformed into the specified classes.

The object you pass as coercion types should implement either a ‘coerce` or a `new` method.

You can define rules to coerce properties into classes or collections of classes. In the latter case, CoercibleHash will just map over whatever value is passed and attempt to coerce each item individually to the given class.

Examples

class Address < Struct.new(:street, :zipcode, :state)
  def self.coerce(address)
    new(address[:street], address[:zipcode], address[:state])
  end
end

class Person < Hash
  extend Diffbot::CoercibleHash

  coerce_property :address, Address
  coerce_property :children, collection: Person

  def name
    self["name"]
  end
end

person = Person.new(address: {
  street: "123 Example St.", zipcode: "12345", state: "XX"
})

person.address.street #=> "123 Example St."
# etc.

father = Person.new(name: "John", children: [
  { name: "Tim" }, { name: "Sarah" }
])

father.name                #=> "John"
father.children.first.name #=> "Tim"
father.children.last.name  #=> "Sarah"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#coercionsObject (readonly)

The coercion rules defined for this hash.



48
49
50
# File 'lib/diffbot/coercible_hash.rb', line 48

def coercions
  @coercions
end

Class Method Details

.extended(base) ⇒ Object

Adds a #[]= that checks for coercion on the property and delegates to super.



51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/diffbot/coercible_hash.rb', line 51

def self.extended(base)
  base.instance_variable_set("@coercions", {})
  base.class_eval do
    def []=(property, value)
      if self.class.coercions.key?(property.to_s)
        super property, self.class.coercions[property.to_s].(value)
      else
        super
      end
    end
  end
end

Instance Method Details

#coerce_property(property, options) ⇒ Object

Public: Coerce a property of this hash into a given type. We will try to call .coerce on the object you pass as the class, and if that fails, we will call .new.

property - The name of the property to coerce. class_or_options - Either a class to which coerce, or a hash with options:

* class:      The class to which coerce
* collection: Coerce the key into an array of members of
              this class.

Examples

class Person < Hash
  extend Diffbot::CoercibleHash

  coerce_property :address, Address

  coerce_property :children, collection: Person

  coerce_property :dob, class: Date
end


85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/diffbot/coercible_hash.rb', line 85

def coerce_property(property, options)
  unless options.is_a?(Hash)
    options = { class: options }
  end

  coercion_method = ->(obj) do
    if obj.respond_to?(:coerce)
      obj.method(:coerce)
    elsif obj.respond_to?(:new)
      obj.method(:new)
    else
      raise ArgumentError, "#{obj.inspect} does not implement neither .coerce nor .new"
    end
  end

  if options.has_key?(:collection)
    klass = options[:collection]
    coercion = ->(value) { value.map { |el| coercion_method[klass][el] } }
  elsif options.has_key?(:class)
    klass = options[:class]
    coercion = ->(value) { coercion_method[klass][value] }
  else
    raise ArgumentError, "You need to specify either :class or :collection"
  end

  coercions[property.to_s] = coercion
end