cb_hashie 2.0.0.beta

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,124 @@
1
+ module Hashie
2
+ module Extensions
3
+ # MethodReader allows you to access keys of the hash
4
+ # via method calls. This gives you an OStruct like way
5
+ # to access your hash's keys. It will recognize keys
6
+ # either as strings or symbols.
7
+ #
8
+ # Note that while nil keys will be returned as nil,
9
+ # undefined keys will raise NoMethodErrors. Also note that
10
+ # #respond_to? has been patched to appropriately recognize
11
+ # key methods.
12
+ #
13
+ # @example
14
+ # class User < Hash
15
+ # include Hashie::Extensions::MethodReader
16
+ # end
17
+ #
18
+ # user = User.new
19
+ # user['first_name'] = 'Michael'
20
+ # user.first_name # => 'Michael'
21
+ #
22
+ # user[:last_name] = 'Bleigh'
23
+ # user.last_name # => 'Bleigh'
24
+ #
25
+ # user[:birthday] = nil
26
+ # user.birthday # => nil
27
+ #
28
+ # user.not_declared # => NoMethodError
29
+ module MethodReader
30
+ def respond_to?(name)
31
+ return true if key?(name.to_s) || key?(name.to_sym)
32
+ super
33
+ end
34
+
35
+ def method_missing(name, *args)
36
+ return self[name.to_s] if key?(name.to_s)
37
+ return self[name.to_sym] if key?(name.to_sym)
38
+ super
39
+ end
40
+ end
41
+
42
+ # MethodWriter gives you #key_name= shortcuts for
43
+ # writing to your hash. Keys are written as strings,
44
+ # override #convert_key if you would like to have symbols
45
+ # or something else.
46
+ #
47
+ # Note that MethodWriter also overrides #respond_to such
48
+ # that any #method_name= will respond appropriately as true.
49
+ #
50
+ # @example
51
+ # class MyHash < Hash
52
+ # include Hashie::Extensions::MethodWriter
53
+ # end
54
+ #
55
+ # h = MyHash.new
56
+ # h.awesome = 'sauce'
57
+ # h['awesome'] # => 'sauce'
58
+ #
59
+ module MethodWriter
60
+ def respond_to?(name)
61
+ return true if name.to_s =~ /=$/
62
+ super
63
+ end
64
+
65
+ def method_missing(name, *args)
66
+ if args.size == 1 && name.to_s =~ /(.*)=$/
67
+ return self[convert_key($1)] = args.first
68
+ end
69
+
70
+ super
71
+ end
72
+
73
+ def convert_key(key)
74
+ key.to_s
75
+ end
76
+ end
77
+
78
+ # MethodQuery gives you the ability to check for the truthiness
79
+ # of a key via method calls. Note that it will return false if
80
+ # the key is set to a non-truthful value, not if the key isn't
81
+ # set at all. Use #key? for checking if a key has been set.
82
+ #
83
+ # MethodQuery will check against both string and symbol names
84
+ # of the method for existing keys. It also patches #respond_to
85
+ # to appropriately detect the query methods.
86
+ #
87
+ # @example
88
+ # class MyHash < Hash
89
+ # include Hashie::Extensions::MethodQuery
90
+ # end
91
+ #
92
+ # h = MyHash.new
93
+ # h['abc'] = 123
94
+ # h.abc? # => true
95
+ # h['def'] = nil
96
+ # h.def? # => false
97
+ # h.hji? # => NoMethodError
98
+ module MethodQuery
99
+ def respond_to?(name)
100
+ return true if name.to_s =~ /(.*)\?$/ && (key?($1) || key?($1.to_sym))
101
+ super
102
+ end
103
+
104
+ def method_missing(name, *args)
105
+ if args.empty? && name.to_s =~ /(.*)\?$/ && (key?($1) || key?($1.to_sym))
106
+ return self[$1] || self[$1.to_sym]
107
+ end
108
+
109
+ super
110
+ end
111
+ end
112
+
113
+ # A macro module that will automatically include MethodReader,
114
+ # MethodWriter, and MethodQuery, giving you the ability to read,
115
+ # write, and query keys in a hash using method call shortcuts.
116
+ module MethodAccess
117
+ def self.included(base)
118
+ [MethodReader, MethodWriter, MethodQuery].each do |mod|
119
+ base.send :include, mod
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,47 @@
1
+ module Hashie
2
+ module Extensions
3
+ # The Structure extension provides facilities for declaring
4
+ # properties that a Hash can have. This provides for the
5
+ # creation of structures that still behave like hashes but
6
+ # do not allow setting non-allowed keys.
7
+ #
8
+ # @example
9
+ # class RestrictedHash < Hash
10
+ # include Hashie::Extensions::MergeInitializer
11
+ # include Hashie::Extensions::Structure
12
+ #
13
+ # key :first
14
+ # key :second, :default => 'foo'
15
+ # end
16
+ #
17
+ # h = RestrictedHash.new(:first => 1)
18
+ # h[:first] # => 1
19
+ # h[:second] # => 'foo'
20
+ # h[:third] # => ArgumentError
21
+ #
22
+ module Structure
23
+ def self.included(base)
24
+ base.extend ClassMethods
25
+ base.class_eval do
26
+ @permitted_keys = superclass.permitted_keys if superclass.respond_to?(:permitted_keys)
27
+ end
28
+ end
29
+
30
+ module ClassMethods
31
+ def key(key, options = {})
32
+ (@permitted_keys ||= []) << key
33
+
34
+ if options[:default]
35
+ (@default_values ||= {})[key] = options.delete(:default)
36
+ end
37
+
38
+ permitted_keys
39
+ end
40
+
41
+ def permitted_keys
42
+ @permitted_keys
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,31 @@
1
+ require 'hashie/hash_extensions'
2
+
3
+ module Hashie
4
+ # A Hashie Hash is simply a Hash that has convenience
5
+ # functions baked in such as stringify_keys that may
6
+ # not be available in all libraries.
7
+ class Hash < ::Hash
8
+ include Hashie::HashExtensions
9
+
10
+ # Converts a mash back to a hash (with stringified keys)
11
+ def to_hash
12
+ out = {}
13
+ keys.each do |k|
14
+ if self[k].is_a?(Array)
15
+ out[k] ||= []
16
+ self[k].each do |array_object|
17
+ out[k] << (Hashie::Hash === array_object ? array_object.to_hash : array_object)
18
+ end
19
+ else
20
+ out[k] = Hashie::Hash === self[k] ? self[k].to_hash : self[k]
21
+ end
22
+ end
23
+ out
24
+ end
25
+
26
+ # The C geneartor for the json gem doesn't like mashies
27
+ def to_json(*args)
28
+ to_hash.to_json(*args)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,49 @@
1
+ module Hashie
2
+ module HashExtensions
3
+ def self.included(base)
4
+ # Don't tread on existing extensions of Hash by
5
+ # adding methods that are likely to exist.
6
+ %w(stringify_keys stringify_keys!).each do |hashie_method|
7
+ base.send :alias_method, hashie_method, "hashie_#{hashie_method}" unless base.instance_methods.include?(hashie_method)
8
+ end
9
+ end
10
+
11
+ # Destructively convert all of the keys of a Hash
12
+ # to their string representations.
13
+ def hashie_stringify_keys!
14
+ self.keys.each do |k|
15
+ unless String === k
16
+ self[k.to_s] = self.delete(k)
17
+ end
18
+ end
19
+ self
20
+ end
21
+
22
+ # Convert all of the keys of a Hash
23
+ # to their string representations.
24
+ def hashie_stringify_keys
25
+ self.dup.stringify_keys!
26
+ end
27
+
28
+ # Convert this hash into a Mash
29
+ def to_mash
30
+ ::Hashie::Mash.new(self)
31
+ end
32
+ end
33
+
34
+ module PrettyInspect
35
+ def self.included(base)
36
+ base.send :alias_method, :hash_inspect, :inspect
37
+ base.send :alias_method, :inspect, :hashie_inspect
38
+ end
39
+
40
+ def hashie_inspect
41
+ ret = "#<#{self.class.to_s}"
42
+ stringify_keys.keys.sort.each do |key|
43
+ ret << " #{key}=#{self[key].inspect}"
44
+ end
45
+ ret << ">"
46
+ ret
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,216 @@
1
+ require 'hashie/hash'
2
+
3
+ module Hashie
4
+ # Mash allows you to create pseudo-objects that have method-like
5
+ # accessors for hash keys. This is useful for such implementations
6
+ # as an API-accessing library that wants to fake robust objects
7
+ # without the overhead of actually doing so. Think of it as OpenStruct
8
+ # with some additional goodies.
9
+ #
10
+ # A Mash will look at the methods you pass it and perform operations
11
+ # based on the following rules:
12
+ #
13
+ # * No punctuation: Returns the value of the hash for that key, or nil if none exists.
14
+ # * Assignment (<tt>=</tt>): Sets the attribute of the given method name.
15
+ # * Existence (<tt>?</tt>): Returns true or false depending on whether that key has been set.
16
+ # * Bang (<tt>!</tt>): Forces the existence of this key, used for deep Mashes. Think of it as "touch" for mashes.
17
+ # * Under Bang (<tt>_</tt>): Like Bang, but returns a new Mash rather than creating a key. Used to test existance in deep Mashes.
18
+ #
19
+ # == Basic Example
20
+ #
21
+ # mash = Mash.new
22
+ # mash.name? # => false
23
+ # mash.name = "Bob"
24
+ # mash.name # => "Bob"
25
+ # mash.name? # => true
26
+ #
27
+ # == Hash Conversion Example
28
+ #
29
+ # hash = {:a => {:b => 23, :d => {:e => "abc"}}, :f => [{:g => 44, :h => 29}, 12]}
30
+ # mash = Mash.new(hash)
31
+ # mash.a.b # => 23
32
+ # mash.a.d.e # => "abc"
33
+ # mash.f.first.g # => 44
34
+ # mash.f.last # => 12
35
+ #
36
+ # == Bang Example
37
+ #
38
+ # mash = Mash.new
39
+ # mash.author # => nil
40
+ # mash.author! # => <Mash>
41
+ #
42
+ # mash = Mash.new
43
+ # mash.author!.name = "Michael Bleigh"
44
+ # mash.author # => <Mash name="Michael Bleigh">
45
+ #
46
+ # == Under Bang Example
47
+ #
48
+ # mash = Mash.new
49
+ # mash.author # => nil
50
+ # mash.author_ # => <Mash>
51
+ # mash.author_.name # => nil
52
+ #
53
+ # mash = Mash.new
54
+ # mash.author_.name = "Michael Bleigh" (assigned to temp object)
55
+ # mash.author # => <Mash>
56
+ #
57
+ class Mash < Hashie::Hash
58
+ include Hashie::PrettyInspect
59
+ alias_method :to_s, :inspect
60
+
61
+ # If you pass in an existing hash, it will
62
+ # convert it to a Mash including recursively
63
+ # descending into arrays and hashes, converting
64
+ # them as well.
65
+ def initialize(source_hash = nil, default = nil, &blk)
66
+ deep_update(source_hash) if source_hash
67
+ default ? super(default) : super(&blk)
68
+ end
69
+
70
+ class << self; alias [] new; end
71
+
72
+ def id #:nodoc:
73
+ key?("id") ? self["id"] : super
74
+ end
75
+
76
+ def type #:nodoc:
77
+ key?("type") ? self["type"] : super
78
+ end
79
+
80
+ alias_method :regular_reader, :[]
81
+ alias_method :regular_writer, :[]=
82
+
83
+ # Retrieves an attribute set in the Mash. Will convert
84
+ # any key passed in to a string before retrieving.
85
+ def [](key)
86
+ value = regular_reader(convert_key(key))
87
+ yield value if block_given?
88
+ value
89
+ end
90
+
91
+ # Sets an attribute in the Mash. Key will be converted to
92
+ # a string before it is set, and Hashes will be converted
93
+ # into Mashes for nesting purposes.
94
+ def []=(key,value) #:nodoc:
95
+ regular_writer(convert_key(key), convert_value(value))
96
+ end
97
+
98
+ # This is the bang method reader, it will return a new Mash
99
+ # if there isn't a value already assigned to the key requested.
100
+ def initializing_reader(key)
101
+ ck = convert_key(key)
102
+ regular_writer(ck, self.class.new) unless key?(ck)
103
+ regular_reader(ck)
104
+ end
105
+
106
+ # This is the under bang method reader, it will return a temporary new Mash
107
+ # if there isn't a value already assigned to the key requested.
108
+ def underbang_reader(key)
109
+ ck = convert_key(key)
110
+ if key?(ck)
111
+ regular_reader(ck)
112
+ else
113
+ self.class.new
114
+ end
115
+ end
116
+
117
+ def delete(key)
118
+ super(convert_key(key))
119
+ end
120
+
121
+ alias_method :regular_dup, :dup
122
+ # Duplicates the current mash as a new mash.
123
+ def dup
124
+ self.class.new(self, self.default)
125
+ end
126
+
127
+ def key?(key)
128
+ super(convert_key(key))
129
+ end
130
+ alias_method :has_key?, :key?
131
+ alias_method :include?, :key?
132
+ alias_method :member?, :key?
133
+
134
+ # Performs a deep_update on a duplicate of the
135
+ # current mash.
136
+ def deep_merge(other_hash)
137
+ dup.deep_update(other_hash)
138
+ end
139
+ alias_method :merge, :deep_merge
140
+
141
+ # Recursively merges this mash with the passed
142
+ # in hash, merging each hash in the hierarchy.
143
+ def deep_update(other_hash)
144
+ other_hash.each_pair do |k,v|
145
+ key = convert_key(k)
146
+ if regular_reader(key).is_a?(Mash) and v.is_a?(::Hash)
147
+ regular_reader(key).deep_update(v)
148
+ else
149
+ regular_writer(key, convert_value(v, true))
150
+ end
151
+ end
152
+ self
153
+ end
154
+ alias_method :deep_merge!, :deep_update
155
+ alias_method :update, :deep_update
156
+ alias_method :merge!, :update
157
+
158
+ # Performs a shallow_update on a duplicate of the current mash
159
+ def shallow_merge(other_hash)
160
+ dup.shallow_update(other_hash)
161
+ end
162
+
163
+ # Merges (non-recursively) the hash from the argument,
164
+ # changing the receiving hash
165
+ def shallow_update(other_hash)
166
+ other_hash.each_pair do |k,v|
167
+ regular_writer(convert_key(k), convert_value(v, true))
168
+ end
169
+ self
170
+ end
171
+
172
+ # Will return true if the Mash has had a key
173
+ # set in addition to normal respond_to? functionality.
174
+ def respond_to?(method_name, include_private=false)
175
+ return true if key?(method_name)
176
+ super
177
+ end
178
+
179
+ def method_missing(method_name, *args, &blk)
180
+ return self.[](method_name, &blk) if key?(method_name)
181
+ match = method_name.to_s.match(/(.*?)([?=!_]?)$/)
182
+ case match[2]
183
+ when "="
184
+ self[match[1]] = args.first
185
+ when "?"
186
+ !!self[match[1]]
187
+ when "!"
188
+ initializing_reader(match[1])
189
+ when "_"
190
+ underbang_reader(match[1])
191
+ else
192
+ default(method_name, *args, &blk)
193
+ end
194
+ end
195
+
196
+ protected
197
+
198
+ def convert_key(key) #:nodoc:
199
+ key.to_s
200
+ end
201
+
202
+ def convert_value(val, duping=false) #:nodoc:
203
+ case val
204
+ when self.class
205
+ val.dup
206
+ when ::Hash
207
+ val = val.dup if duping
208
+ self.class.new(val)
209
+ when Array
210
+ val.collect{ |e| convert_value(e) }
211
+ else
212
+ val
213
+ end
214
+ end
215
+ end
216
+ end