Class: APISmith::Smash

Inherits:
Hashie::Dash
  • Object
show all
Defined in:
lib/api_smith/smash.rb

Overview

Extends Hashie::Dash to suppress unknown keys when passing data, but is configurable to raises an UnknownKey exception when accessing keys in the Smash.

APISmith::Smash is a subclass of Hashie::Dash that adds several features making it suitable for use in writing api clients. Namely,

  • The ability to silence exceptions on unknown keys (vs. Raising NoMethodError)

  • The ability to define conversion of incoming data via transformers

  • The ability to define aliases for keys via the from parameter.

Examples:

a simple, structured object with the most common use cases.

class MyResponse < APISmith::Smash
  property :full_name, :from => :fullName
  property :value_percentage, :transformer => :to_f
  property :short_name
  property :created, :transformer => lambda { |v| Date.parse(v) }
end

response = MyResponse.new({
  :fullName         => "Bob Smith",
  :value_percentage => "10.5",
  :short_name       => 'Bob',
  :created          => '2010-12-28'
})

p response.short_name # => "Bob"
p response.full_name # => "Bob Smith"
p response.value_percentage # => 10.5
p response.created.class # => Date

Author:

  • Darcy Laycock

  • Steve Webb

Defined Under Namespace

Classes: UnknownKey

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.call(value) ⇒ Array<Smash>, Smash

Automates type conversion (including on Array and Hashes) to this type. Used so we can pass this class similarily to how we pass lambdas as an object, primarily for use as transformers.

Parameters:

  • the (Object)

    object to attempt to convert.

Returns:

  • (Array<Smash>, Smash)

    The converted object / array of objects if possible, otherwise nil.


138
139
140
141
142
143
144
145
146
# File 'lib/api_smith/smash.rb', line 138

def self.call(value)
  if value.is_a?(Array)
    value.map { |v| call v }.compact
  elsif value.is_a?(Hash)
    new value
  else
    nil
  end
end

.exception_on_unknown_key=(value) ⇒ Object

Sets whether or not Smash should raise NoMethodError on an unknown key. Sets it for the current class.

Parameters:

  • value (Boolean)

    true to throw exceptions.


71
72
73
# File 'lib/api_smith/smash.rb', line 71

def self.exception_on_unknown_key=(value)
  @exception_on_unknown_key = value
end

.exception_on_unknown_key?Boolean

Test if the object should raise a NoMethodError exception on unknown property accessors or whether it should be silenced.

Returns:

  • (Boolean)

    true if an exception will be raised when accessing an unknown key else, false.


63
64
65
# File 'lib/api_smith/smash.rb', line 63

def self.exception_on_unknown_key?
  defined?(@exception_on_unknown_key) && @exception_on_unknown_key
end

.inherited(klass) ⇒ Object

Hook to make it inherit instance variables correctly. Called once the Smash is inherited from in another object to maintain state.


93
94
95
96
97
98
# File 'lib/api_smith/smash.rb', line 93

def self.inherited(klass)
  super
  klass.instance_variable_set '@transformers',             transformers.dup
  klass.instance_variable_set '@key_mapping',              key_mapping.dup
  klass.instance_variable_set '@exception_on_unknown_key', exception_on_unknown_key?
end

.key_mappingObject

Returns a class-specific hash of incoming keys and their resultant property name, useful for mapping non-standard names (e.g. displayName) to their more ruby-like equivelant (e.g. display_name).

Returns:

  • The hash of key mappings.


54
55
56
# File 'lib/api_smith/smash.rb', line 54

def self.key_mapping
  (@key_mapping ||= {})
end

.property(property_name, options = {}) ⇒ Object

Create a new property (i.e., hash key) for this Object type. This method allows for converting property names and defining custom transformers for more complex types.

Parameters:

  • property_name (Symbol)

    The property name (duh).

  • options (Hash) (defaults to: {})

Options Hash (options):

  • :from (String, Array<String>)

    Also accept values for this property when using the key(s) specified in from.

  • :transformer (Block)

    Specify a class or block to use when transforming the data.


109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/api_smith/smash.rb', line 109

def self.property(property_name, options = {})
  super
  if options[:from]
    property_name = property_name.to_s
    Array(options[:from]).each do |k|
      key_mapping[k.to_s] = property_name
    end
  end
  if options[:transformer]
    transformer_for property_name, options[:transformer]
  end
end

.property?(key) ⇒ Boolean

Does this Smash class contain a specific property (key), or does it have a key mapping (via :from)

Parameters:

  • key (Symbol)

    the property to test for.

Returns:

  • (Boolean)

    true if this class contains the key; else, false.


127
128
129
# File 'lib/api_smith/smash.rb', line 127

def self.property?(key)
  super || key_mapping.has_key?(key.to_s)
end

.transformer_for(key, value = nil, &blk) ⇒ Object

Sets the transformer that is invoked when the given key is set.

Parameters:

  • key (Symbol)

    The key should this transformer operate on

  • value (#call) (defaults to: nil)

    If a block isn't given, used to transform via #call.

  • blk (Block)

    The block used to transform the key.

Raises:

  • (ArgumentError)

81
82
83
84
85
86
87
88
89
# File 'lib/api_smith/smash.rb', line 81

def self.transformer_for(key, value = nil, &blk)
  if blk.nil? && value
    blk = value.respond_to?(:call) ? value : value.to_sym.to_proc
  end
  raise ArgumentError, 'must provide a transformation' if blk.nil?
  transformers[key.to_s] = blk
  # For each subclass, set the transformer.
  Array(@subclasses).each { |klass| klass.transformer_for(key, value) }
end

.transformersObject

Returns a class-specific hash of transformers, containing the attribute name mapped to the transformer that responds to call.

Returns:

  • The hash of transformers.


46
47
48
# File 'lib/api_smith/smash.rb', line 46

def self.transformers
  (@transformers ||= {})
end

Instance Method Details

#[](property) ⇒ Object

Access the value responding to a key, normalising the key into a form we know (e.g. processing the from value to convert it to the actual property name).

Parameters:

  • property (Symbol)

    the key to check for.

Returns:

  • The value corresponding to property. nil if it does not exist.


154
155
156
157
158
# File 'lib/api_smith/smash.rb', line 154

def [](property)
  super transform_key(property)
rescue UnknownKey
  nil
end

#[]=(property, value) ⇒ Object

Sets the value for a given key. Transforms the key first (e.g. taking into account from values) and transforms the property using any transformers.

Parameters:

  • property (Symbol)

    the key to set.

  • value (String)

    the value to set.

Returns:

  • If the property exists value is returned; else, nil.


166
167
168
169
170
171
# File 'lib/api_smith/smash.rb', line 166

def []=(property, value)
  key = transform_key(property)
  super key, transform_property(key, value)
rescue UnknownKey
  nil
end