Module: HashToObj

Defined in:
lib/hash-to-obj.rb,
lib/hash-to-obj/version.rb,
lib/hash-to-obj/default_module.rb

Overview

We extend this to objectify our hashes. It will generate some methods on the hash to access the various keys.

Define a constant HashToObj::SQUELCH_GLOBAL_WARNINGS to squelch warnings about objectify already being defined.

Defined Under Namespace

Modules: DefaultModule

Constant Summary collapse

DUCK_TYPE_API =

Anything that you want to objectify needs to respond to these methods like a Hash would. If it doesn’t at least respond_to? these methods, an error will be thrown when objectifying.

[:[], :[]=, :each_key].freeze
VERSION =

:category: Current version of hash-to-obj

'0.3.0'.freeze

Class Method Summary collapse

Class Method Details

.generate_accessors(hash, key, override_warnings = false) ⇒ Object

Generates the accessors for the passed key on the passed hash. Unless override_warnings is true-y, this will throw errors if we’re gonna screw anything up. If you pass something that responds to :puts in override_warnings then warnings will just be puts’d to that, and things will continue.



91
92
93
94
95
96
97
98
99
# File 'lib/hash-to-obj.rb', line 91

def self.generate_accessors(hash, key, override_warnings = false)
  handle_warnings(hash, key, override_warnings)

  valid_key = valid_key?(key)
  hash.define_singleton_method(valid_key) { self[key] }
  hash.define_singleton_method("#{valid_key}=") do |value|
    self[key] = value
  end
end

.handle_warnings(hash, key, override_warnings = false) ⇒ Object

This will output any warnings we want to output.



103
104
105
106
107
108
109
110
111
# File 'lib/hash-to-obj.rb', line 103

def self.handle_warnings(hash, key, override_warnings = false)
  if !override_warnings && should_warn?(hash, key) then
    if override_warnings.respond_to?(:puts) then
      override_warnings.puts(should_warn?(hash, key))
    else
      raise ArgumentError, should_warn?(hash, key)
    end
  end
end

.internal_objectify(hash, override_warnings = false, default_module = nil) ⇒ Object

Internal version of objectify. Uses seperated arguments instead of options hash.



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/hash-to-obj.rb', line 55

def self.internal_objectify(hash,
                            override_warnings = false,
                            default_module = nil)
  # Make sure it looks SOMEWHAT familiar.
  unless quacks?(hash) then
    raise(ArgumentError,
          "Cannot objectify something that doesn't look like a hash.")
  end

  # Now lets actually add those methods.
  hash.each_key { |key| generate_accessors(hash, key, override_warnings) }

  hash.extend(default_module) if default_module

  hash
end

.objectify(hash, options = {}) ⇒ Object

Throws an error if hash doesn’t have all messages defined in DUCK_TYPE_API. Generates the accessors for all keys on the passed hash.

Accepted options:

override_warnings

Unless override_warnings is truthy, this will throw errors if we’re gonna screw anything up. If you pass something that responds to :puts in override_warnings then warnings will just be puts’d to that, and things will continue.

default_module

If a default_module is specified, that will be included in the hash before adding our accessors, essentially allowing you to define a set of methods that should be present after objectifying regardless of the keys in the hash.



32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/hash-to-obj.rb', line 32

def self.objectify(hash, options = {})
  if options.is_a?(Hash) then
    internal_objectify(hash,
                       options[:override_warnings],
                       options[:default_module])
  else
    unless HashToObj.const_defined?(:SQUELCH_GLOBAL_WARNINGS) then
      puts "objectify(hash, override_warnings) is deprecated.\n"\
           '  please use objectify(hash, override_warnings: true) instead.'
    end
    # Looks like they're using 0.1.0 API.
    internal_objectify(hash, options)
  end
end

.quacks?(obj) ⇒ Boolean

Returns true if the passed object responds to all methods defined in DUCK_TYPE_API.

Returns:

  • (Boolean)


75
76
77
78
79
80
81
82
83
# File 'lib/hash-to-obj.rb', line 75

def self.quacks?(obj)
  DUCK_TYPE_API.each do |method_sym|
    unless obj.respond_to?(method_sym) then
      return false
    end
  end

  true
end

.should_warn?(hash, key) ⇒ Boolean

If there should be a warning, returns the warning message. Otherwise returns false.

Returns:

  • (Boolean)


125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/hash-to-obj.rb', line 125

def self.should_warn?(hash, key)
  valid_key = valid_key?(key)

  # Make sure its a valid key...
  return "#{key} is not a valid key." unless valid_key
  # And make sure they don't have this method...
  if hash.respond_to?(valid_key) then
    return "#{hash} already has a #{valid_key} method defined."
  end
  if hash.respond_to?("#{valid_key}=") then
    return "#{hash} already has a #{valid_key}= method defined."
  end

  false
end

.valid_key?(key) ⇒ Boolean

Returns a valid method name for the passed key if there is one, otherwise returns false.

Returns:

  • (Boolean)


116
117
118
119
120
# File 'lib/hash-to-obj.rb', line 116

def self.valid_key?(key)
  regex_match = key.to_s.match(/\A[a-z_][a-z0-9_]*\Z/)
  return false if regex_match.nil?
  return key.to_s
end