Module: Dottie
- Defined in:
- lib/dottie.rb,
lib/dottie/freckle.rb,
lib/dottie/methods.rb,
lib/dottie/version.rb
Defined Under Namespace
Modules: Methods Classes: Freckle
Constant Summary collapse
- VERSION =
'0.0.1'
Class Method Summary collapse
-
.[](obj) ⇒ Object
Creates a new Dottie::Freckle from a standard Ruby Hash or Array.
-
.dottie_key?(key) ⇒ Boolean
Checks whether a key looks like a key Dottie understands.
-
.fetch(obj, key, default = :_fetch_default_) ⇒ Object
Mimics the behavior of Hash#fetch, raising an error if a key does not exist and no default value or block is provided.
-
.get(obj, key) ⇒ Object
Gets a value from an object.
-
.has_key?(obj, key) ⇒ Boolean
Checks whether a Hash or Array contains the last part of a Dottie-style key.
-
.key_parts(key) ⇒ Object
Parses a Dottie key into an Array of strings and integers.
-
.set(obj, key, value) ⇒ Object
Sets a value in an object, creating missing nodes (Hashes and Arrays) as needed.
Class Method Details
.[](obj) ⇒ Object
Creates a new Dottie::Freckle from a standard Ruby Hash or Array.
12 13 14 |
# File 'lib/dottie.rb', line 12 def self.[](obj) Dottie::Freckle.new(obj) end |
.dottie_key?(key) ⇒ Boolean
Checks whether a key looks like a key Dottie understands.
119 120 121 |
# File 'lib/dottie.rb', line 119 def self.dottie_key?(key) !!(key.is_a?(String) && key =~ /[.\[]/) || key.is_a?(Array) end |
.fetch(obj, key, default = :_fetch_default_) ⇒ Object
Mimics the behavior of Hash#fetch, raising an error if a key does not exist and no default value or block is provided.
104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/dottie.rb', line 104 def self.fetch(obj, key, default = :_fetch_default_) if Dottie.has_key?(obj, key) Dottie.get(obj, key) elsif block_given? yield(key) elsif default != :_fetch_default_ default else raise KeyError.new(%{key not found: "#{key}"}) end end |
.get(obj, key) ⇒ Object
Gets a value from an object. Does not assume the object has been extended with Dottie methods.
20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/dottie.rb', line 20 def self.get(obj, key) Dottie.key_parts(key).each do |k| obj = case obj when Hash, Array obj[k] else nil end end obj end |
.has_key?(obj, key) ⇒ Boolean
Checks whether a Hash or Array contains the last part of a Dottie-style key.
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/dottie.rb', line 77 def self.has_key?(obj, key) key_parts = Dottie.key_parts(key) key_parts.each_with_index do |k, i| # look for the key if this is the last key part if i == key_parts.size - 1 if obj.is_a?(Array) && k.is_a?(Integer) return obj.size > k elsif obj.is_a?(Hash) return obj.has_key?(k) else return false end else obj = case obj when Hash, Array obj[k] else return false end end end end |
.key_parts(key) ⇒ Object
Parses a Dottie key into an Array of strings and integers.
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/dottie.rb', line 126 def self.key_parts(key) if key.is_a?(String) parts = [] s = StringScanner.new(key) loop do if s.scan(/\./) next elsif (p = s.scan(/[^\[\].]+/)) parts << p elsif (p = s.scan(/\[-?\d+\]/)) parts << p.scan(/-?\d+/).first.to_i elsif (p = s.scan(/\[(first|last)\]/)) parts << (p[1..-2] == 'first' ? 0 : -1) elsif (p = s.scan(/\[.+?\]/)) parts << p[1..-2] # remove '[' and ']' else break end end parts elsif key.is_a?(Array) key else raise TypeError.new("expected String or Array but got #{key.class.name}") end end |
.set(obj, key, value) ⇒ Object
Sets a value in an object, creating missing nodes (Hashes and Arrays) as needed. Does not assume the object has been extended with Dottie methods.
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/dottie.rb', line 36 def self.set(obj, key, value) key_parts = Dottie.key_parts(key) key_parts.each_with_index do |k, i| # set the value if this is the last key part if i == key_parts.size - 1 case obj when Hash, Array obj[k] = value else raise TypeError.new("expected Hash or Array but got #{obj.class.name}") end # otherwise, walk down the tree, creating missing nodes along the way else obj = case obj when Hash, Array # look ahead at the next key to see if an array should be created if key_parts[i + 1].is_a?(Integer) obj[k] ||= [] else obj[k] ||= {} end when nil # look at the key to see if an array should be created case k when Integer obj[k] = [] else obj[k] = {} end else raise TypeError.new("expected Hash, Array, or nil but got #{obj.class.name}") end end end # return the value that was set value end |