gorillib 0.0.8 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.textile +6 -0
- data/README.textile +34 -11
- data/VERSION +1 -1
- data/gorillib.gemspec +63 -5
- data/lib/gorillib/enumerable/sum.rb +2 -2
- data/lib/gorillib/hash/compact.rb +2 -29
- data/lib/gorillib/hash/deep_compact.rb +2 -12
- data/lib/gorillib/hash/deep_dup.rb +4 -0
- data/lib/gorillib/hash/deep_merge.rb +2 -14
- data/lib/gorillib/hash/indifferent_access.rb +207 -0
- data/lib/gorillib/hash/keys.rb +2 -40
- data/lib/gorillib/hash/reverse_merge.rb +2 -24
- data/lib/gorillib/hash/slice.rb +2 -51
- data/lib/gorillib/hash/tree_merge.rb +4 -0
- data/lib/gorillib/hashlike.rb +824 -0
- data/lib/gorillib/hashlike/compact.rb +60 -0
- data/lib/gorillib/hashlike/deep_compact.rb +18 -0
- data/lib/gorillib/hashlike/deep_dup.rb +15 -0
- data/lib/gorillib/hashlike/deep_merge.rb +20 -0
- data/lib/gorillib/hashlike/hashlike_via_accessors.rb +169 -0
- data/lib/gorillib/hashlike/keys.rb +59 -0
- data/lib/gorillib/hashlike/reverse_merge.rb +31 -0
- data/lib/gorillib/hashlike/slice.rb +67 -0
- data/lib/gorillib/hashlike/tree_merge.rb +76 -0
- data/lib/gorillib/metaprogramming/mattr_accessor.rb +1 -1
- data/lib/gorillib/receiver.rb +315 -0
- data/lib/gorillib/receiver/active_model_shim.rb +19 -0
- data/lib/gorillib/receiver/acts_as_hash.rb +191 -0
- data/lib/gorillib/receiver/acts_as_loadable.rb +42 -0
- data/lib/gorillib/receiver/tree_diff.rb +74 -0
- data/lib/gorillib/receiver/validations.rb +30 -0
- data/lib/gorillib/struct/acts_as_hash.rb +108 -0
- data/lib/gorillib/struct/hashlike_iteration.rb +0 -0
- data/notes/fancy_hashes_and_receivers.textile +120 -0
- data/notes/hash_rdocs.textile +97 -0
- data/spec/hash/deep_merge_spec.rb +0 -2
- data/spec/hash/indifferent_access_spec.rb +391 -0
- data/spec/hash/slice_spec.rb +35 -12
- data/spec/hashlike/behave_same_as_hash_spec.rb +105 -0
- data/spec/hashlike/hashlike_behavior_spec.rb +824 -0
- data/spec/hashlike/hashlike_via_accessors_fuzzing_spec.rb +37 -0
- data/spec/hashlike/hashlike_via_accessors_spec.rb +262 -0
- data/spec/hashlike_spec.rb +302 -0
- data/spec/metaprogramming/aliasing_spec.rb +3 -0
- data/spec/metaprogramming/cattr_accessor_spec.rb +2 -0
- data/spec/metaprogramming/class_attribute_spec.rb +2 -0
- data/spec/metaprogramming/delegation_spec.rb +2 -0
- data/spec/metaprogramming/mattr_accessor_spec.rb +2 -0
- data/spec/metaprogramming/singleton_class_spec.rb +3 -0
- data/spec/receiver/acts_as_hash_spec.rb +286 -0
- data/spec/receiver_spec.rb +478 -0
- data/spec/spec_helper.rb +11 -6
- data/spec/string/truncate_spec.rb +1 -0
- data/spec/struct/acts_as_hash_fuzz_spec.rb +67 -0
- data/spec/struct/acts_as_hash_spec.rb +426 -0
- data/spec/support/hashlike_fuzzing_helper.rb +127 -0
- data/spec/support/hashlike_helper.rb +75 -0
- data/spec/support/hashlike_struct_helper.rb +37 -0
- data/spec/support/hashlike_via_delegation.rb +30 -0
- data/spec/support/matchers/be_array_eql.rb +12 -0
- data/spec/support/matchers/be_hash_eql.rb +14 -0
- data/spec/support/matchers/enumerate_method.rb +10 -0
- data/spec/support/matchers/evaluate_to_true.rb +5 -0
- metadata +62 -4
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'gorillib/object/blank'
|
2
|
+
module Gorillib
|
3
|
+
module Hashlike
|
4
|
+
module Compact
|
5
|
+
|
6
|
+
# returns a compact!ed copy (contains no key/value pairs having nil? values)
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# hsh = { :a => 100, :b => nil, :c => false, :d => "" }
|
10
|
+
# hsh.compact # => { :a => 100, :c => false, :d => "" }
|
11
|
+
# hsh # => { :a => 100, :b => nil, :c => false, :d => "" }
|
12
|
+
#
|
13
|
+
# @return [Hashlike]
|
14
|
+
#
|
15
|
+
def compact
|
16
|
+
reject{|key,val| val.nil? }
|
17
|
+
end
|
18
|
+
|
19
|
+
# Removes all key/value pairs having nil? values
|
20
|
+
#
|
21
|
+
# @example
|
22
|
+
# hsh = { :a => 100, :b => nil, :c => false, :d => "" }
|
23
|
+
# hsh.compact # => { :a => 100, :c => false, :d => "" }
|
24
|
+
# hsh # => { :a => 100, :c => false, :d => "" }
|
25
|
+
#
|
26
|
+
# @return [Hashlike]
|
27
|
+
#
|
28
|
+
def compact!
|
29
|
+
delete_if{|key,val| val.nil? }
|
30
|
+
end
|
31
|
+
|
32
|
+
# returns a compact!ed copy (contains no key/value pairs having blank? values)
|
33
|
+
#
|
34
|
+
# @example
|
35
|
+
# hsh = { :a => 100, :b => nil, :c => false, :d => "" }
|
36
|
+
# hsh.compact # => { :a => 100 }
|
37
|
+
# hsh # => { :a => 100, :b => nil, :c => false, :d => "" }
|
38
|
+
#
|
39
|
+
# @return [Hashlike]
|
40
|
+
#
|
41
|
+
def compact_blank
|
42
|
+
reject{|key,val| val.blank? }
|
43
|
+
end
|
44
|
+
|
45
|
+
# Removes all key/value pairs having blank? values
|
46
|
+
#
|
47
|
+
# @example
|
48
|
+
# hsh = { :a => 100, :b => nil, :c => false, :d => "" }
|
49
|
+
# hsh.compact # => { :a => 100 }
|
50
|
+
# hsh # => { :a => 100 }
|
51
|
+
#
|
52
|
+
# @return [Hashlike]
|
53
|
+
#
|
54
|
+
def compact_blank!
|
55
|
+
delete_if{|key,val| val.blank? }
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'gorillib/object/blank'
|
2
|
+
module Gorillib
|
3
|
+
module Hashlike
|
4
|
+
module DeepCompact
|
5
|
+
|
6
|
+
#
|
7
|
+
# deep_compact! removes all keys with 'blank?' values in the hash, in place, recursively
|
8
|
+
#
|
9
|
+
def deep_compact!
|
10
|
+
self.each do |key, val|
|
11
|
+
val.deep_compact! if val.respond_to?(:deep_compact!)
|
12
|
+
self.delete(key) if val.blank?
|
13
|
+
end
|
14
|
+
self
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Gorillib
|
2
|
+
module Hashlike
|
3
|
+
module DeepDup
|
4
|
+
# Returns a deep copy of hash.
|
5
|
+
def deep_dup
|
6
|
+
duplicate = self.dup
|
7
|
+
duplicate.each_pair do |k,v|
|
8
|
+
tv = duplicate[k]
|
9
|
+
duplicate[k] = tv.is_a?(Hash) && v.is_a?(Hash) ? tv.deep_dup : v
|
10
|
+
end
|
11
|
+
duplicate
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Gorillib
|
2
|
+
module Hashlike
|
3
|
+
module DeepMerge
|
4
|
+
# Returns a new hash with +self+ and +other_hash+ merged recursively.
|
5
|
+
def deep_merge(other_hash)
|
6
|
+
dup.deep_merge!(other_hash)
|
7
|
+
end unless method_defined?(:deep_merge)
|
8
|
+
|
9
|
+
# Returns a new hash with +self+ and +other_hash+ merged recursively.
|
10
|
+
# Modifies the receiver in place.
|
11
|
+
def deep_merge!(other_hash)
|
12
|
+
other_hash.each_pair do |k,v|
|
13
|
+
tv = self[k]
|
14
|
+
self[k] = tv.is_a?(Hash) && v.is_a?(Hash) ? tv.deep_merge(v) : v
|
15
|
+
end
|
16
|
+
self
|
17
|
+
end unless method_defined?(:deep_merge!)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
require 'enumerator'
|
2
|
+
module Gorillib
|
3
|
+
module Hashlike
|
4
|
+
#
|
5
|
+
# Makes a Receiver thingie behave mostly like a hash.
|
6
|
+
#
|
7
|
+
# By default, the hashlike methods iterate over the receiver attributes:
|
8
|
+
# instance #keys delegates to self.class.keys which calls
|
9
|
+
# receiver_attr_names. If you want to filter our add to the keys list, you
|
10
|
+
# can just override the class-level keys method (and call super, or not):
|
11
|
+
#
|
12
|
+
# def self.keys
|
13
|
+
# super + [:firstname, :lastname] - [:fullname]
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# in addition to the below, by including Enumerable, this also adds
|
17
|
+
#
|
18
|
+
# :each_cons, :each_entry, :each_slice, :each_with_index, :each_with_object,
|
19
|
+
# :map, :collect, :collect_concat, :entries, :to_a, :flat_map, :inject, :reduce,
|
20
|
+
# :group_by, :chunk, :cycle, :partition, :reverse_each, :slice_before, :drop,
|
21
|
+
# :drop_while, :take, :take_while, :detect, :find, :find_all, :find_index, :grep,
|
22
|
+
# :all?, :any?, :none?, :one?, :first, :count, :zip :max, :max_by, :min, :min_by,
|
23
|
+
# :minmax, :minmax_by, :sort, :sort_by
|
24
|
+
#
|
25
|
+
# As opposed to hash, does *not* define
|
26
|
+
#
|
27
|
+
# default, default=, default_proc, default_proc=, shift, flatten, compare_by_identity
|
28
|
+
# compare_by_identity? rehash
|
29
|
+
#
|
30
|
+
module HashlikeViaAccessors
|
31
|
+
|
32
|
+
# Hashlike#[]
|
33
|
+
#
|
34
|
+
# Element Reference -- Retrieves the value stored for +key+.
|
35
|
+
#
|
36
|
+
# In a normal hash, a default value can be set; none is provided here.
|
37
|
+
#
|
38
|
+
# Delegates to self.send(key)
|
39
|
+
#
|
40
|
+
# @example
|
41
|
+
# hsh = { :a => 100, :b => 200 }
|
42
|
+
# hsh[:a] # => 100
|
43
|
+
# hsh[:c] # => nil
|
44
|
+
#
|
45
|
+
# @param key [Object] key to retrieve
|
46
|
+
# @return [Object] the value stored for key, nil if missing
|
47
|
+
#
|
48
|
+
def [](key)
|
49
|
+
key = convert_key(key)
|
50
|
+
self.send(key)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Hashlike#[]=
|
54
|
+
# Hashlike#store
|
55
|
+
#
|
56
|
+
# Element Assignment -- Associates the value given by +val+ with the key
|
57
|
+
# given by +key+.
|
58
|
+
#
|
59
|
+
# key should not have its value changed while it is in use as a key. In a
|
60
|
+
# normal hash, a String passed as a key will be duplicated and frozen. No such
|
61
|
+
# guarantee is provided here
|
62
|
+
#
|
63
|
+
# Delegates to self.send("key=", val)
|
64
|
+
#
|
65
|
+
# @example
|
66
|
+
# hsh = { :a => 100, :b => 200 }
|
67
|
+
# hsh[:a] = 9
|
68
|
+
# hsh[:c] = 4
|
69
|
+
# hsh # => { :a => 9, :b => 200, :c => 4 }
|
70
|
+
#
|
71
|
+
# hsh[key] = val -> val
|
72
|
+
# hsh.store(key, val) -> val
|
73
|
+
#
|
74
|
+
# @param key [Object] key to associate
|
75
|
+
# @param val [Object] value to associate it with
|
76
|
+
# @return [Object]
|
77
|
+
#
|
78
|
+
def []=(key, val)
|
79
|
+
key = convert_key(key)
|
80
|
+
self.send("#{key}=", val)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Hashlike#delete
|
84
|
+
#
|
85
|
+
# Deletes and returns the value from +hsh+ whose key is equal to +key+. If the
|
86
|
+
# optional code block is given and the key is not found, pass in the key and
|
87
|
+
# return the result of +block+.
|
88
|
+
#
|
89
|
+
# In a normal hash, a default value can be set; none is provided here.
|
90
|
+
#
|
91
|
+
# @example
|
92
|
+
# hsh = { :a => 100, :b => 200 }
|
93
|
+
# hsh.delete(:a) # => 100
|
94
|
+
# hsh.delete(:z) # => nil
|
95
|
+
# hsh.delete(:z){|el| "#{el} not found" } # => "z not found"
|
96
|
+
#
|
97
|
+
# @overload hsh.delete(key) -> val
|
98
|
+
# @param key [Object] key to remove
|
99
|
+
# @return [Object, Nil] the removed object, nil if missing
|
100
|
+
#
|
101
|
+
# @overload hsh.delete(key){|key| block } -> val
|
102
|
+
# @param key [Object] key to remove
|
103
|
+
# @yield [Object] called (with key) if key is missing
|
104
|
+
# @yieldparam key
|
105
|
+
# @return [Object, Nil] the removed object, or if missing, the return value
|
106
|
+
# of the block
|
107
|
+
#
|
108
|
+
def delete(key, &block)
|
109
|
+
key = convert_key(key)
|
110
|
+
if has_key?(key)
|
111
|
+
val = self[key]
|
112
|
+
self.send(:remove_instance_variable, "@#{key}")
|
113
|
+
val
|
114
|
+
elsif block_given?
|
115
|
+
block.call(key)
|
116
|
+
else
|
117
|
+
nil
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# # Hashlike#==
|
122
|
+
# #
|
123
|
+
# # Equality -- Two hashes are equal if they contain the same number of keys,
|
124
|
+
# # and the value corresponding to each key in the first hash is equal (using
|
125
|
+
# # <tt>==</tt>) to the value for the same key in the second. If +obj+ is not a
|
126
|
+
# # Hashlike, attempt to convert it using +to_hash+ and return <tt>obj ==
|
127
|
+
# # hsh</tt>.
|
128
|
+
# #
|
129
|
+
# # Does not take a default value comparion into account.
|
130
|
+
# #
|
131
|
+
# # @example
|
132
|
+
# # h1 = { :a => 1, :c => 2 }
|
133
|
+
# # h2 = { 7 => 35, :c => 2, :a => 1 }
|
134
|
+
# # h3 = { :a => 1, :c => 2, 7 => 35 }
|
135
|
+
# # h4 = { :a => 1, :d => 2, :f => 35 }
|
136
|
+
# # h1 == h2 # => false
|
137
|
+
# # h2 == h3 # => true
|
138
|
+
# # h3 == h4 # => false
|
139
|
+
# #
|
140
|
+
# def ==(other_hash)
|
141
|
+
# (length == other_hash.length) &&
|
142
|
+
# all?{|k,v| v == other_hash[k] }
|
143
|
+
# end
|
144
|
+
|
145
|
+
# Hashlike#keys
|
146
|
+
#
|
147
|
+
# Returns a new array populated with the keys from this hashlike.
|
148
|
+
#
|
149
|
+
# @see Hashlike#values.
|
150
|
+
#
|
151
|
+
# @example
|
152
|
+
# hsh = { :a => 100, :b => 200, :c => 300, :d => 400 }
|
153
|
+
# hsh.keys # => [:a, :b, :c, :d]
|
154
|
+
#
|
155
|
+
# @return [Array] list of keys
|
156
|
+
#
|
157
|
+
def keys
|
158
|
+
instance_variables.map{|s| convert_key(s[1..-1]) }
|
159
|
+
end
|
160
|
+
|
161
|
+
protected
|
162
|
+
def convert_key(key)
|
163
|
+
raise ArgumentError, "Keys for #{self.class} must be symbols, strings or respond to #to_sym" unless key.respond_to?(:to_sym)
|
164
|
+
key.to_sym
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Gorillib
|
2
|
+
module Hashlike
|
3
|
+
module Keys
|
4
|
+
# Return a new hash with all keys converted to strings.
|
5
|
+
def stringify_keys
|
6
|
+
dup.stringify_keys!
|
7
|
+
end unless method_defined?(:stringify_keys)
|
8
|
+
|
9
|
+
# Destructively convert all keys to strings.
|
10
|
+
def stringify_keys!
|
11
|
+
keys.each do |key|
|
12
|
+
self[key.to_s] = delete(key)
|
13
|
+
end
|
14
|
+
self
|
15
|
+
end unless method_defined?(:stringify_keys!)
|
16
|
+
|
17
|
+
# Return a new hash with all keys converted to symbols, as long as
|
18
|
+
# they respond to +to_sym+.
|
19
|
+
def symbolize_keys
|
20
|
+
dup.symbolize_keys!
|
21
|
+
end unless method_defined?(:symbolize_keys)
|
22
|
+
|
23
|
+
# Destructively convert all keys to symbols, as long as they respond
|
24
|
+
# to +to_sym+.
|
25
|
+
def symbolize_keys!
|
26
|
+
keys.each do |key|
|
27
|
+
self[(key.to_sym rescue key) || key] = delete(key)
|
28
|
+
end
|
29
|
+
self
|
30
|
+
end unless method_defined?(:symbolize_keys!)
|
31
|
+
|
32
|
+
# Validate all keys in a hash match *valid keys, raising ArgumentError on a mismatch.
|
33
|
+
# Note that keys are NOT treated indifferently, meaning if you use strings for keys but assert symbols
|
34
|
+
# as keys, this will fail.
|
35
|
+
#
|
36
|
+
# ==== Examples
|
37
|
+
# { :name => "Rob", :years => "28" }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key(s): years"
|
38
|
+
# { :name => "Rob", :age => "28" }.assert_valid_keys("name", "age") # => raises "ArgumentError: Unknown key(s): name, age"
|
39
|
+
# { :name => "Rob", :age => "28" }.assert_valid_keys(:name, :age) # => passes, raises nothing
|
40
|
+
def assert_valid_keys(*valid_keys)
|
41
|
+
unknown_keys = keys - [valid_keys].flatten
|
42
|
+
raise(ArgumentError, "Unknown key(s): #{unknown_keys.join(", ")}") unless unknown_keys.empty?
|
43
|
+
end unless method_defined?(:assert_valid_keys)
|
44
|
+
end
|
45
|
+
# # @return [Hash] the object as a Hash with symbolized keys.
|
46
|
+
# def symbolize_keys() to_hash ; end
|
47
|
+
# # @return [Hash] the object as a Hash with string keys.
|
48
|
+
# def stringify_keys() to_hash.stringify_keys ; end
|
49
|
+
#
|
50
|
+
# # Used to provide the same interface as Hash.
|
51
|
+
# # @return This object unchanged.
|
52
|
+
# def symbolize_keys!; self end
|
53
|
+
#
|
54
|
+
# # Used to provide the same interface as Hash.
|
55
|
+
# # @return This object unchanged.
|
56
|
+
# def stringify_keys!; self end
|
57
|
+
#
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Gorillib
|
2
|
+
module Hashlike
|
3
|
+
module ReverseMerge
|
4
|
+
# Allows for reverse merging two hashes where the keys in the calling hash take precedence over those
|
5
|
+
# in the <tt>other_hash</tt>. This is particularly useful for initializing an option hash with default values:
|
6
|
+
#
|
7
|
+
# def setup(options = {})
|
8
|
+
# options.reverse_merge! :size => 25, :velocity => 10
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
# Using <tt>merge</tt>, the above example would look as follows:
|
12
|
+
#
|
13
|
+
# def setup(options = {})
|
14
|
+
# { :size => 25, :velocity => 10 }.merge(options)
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# The default <tt>:size</tt> and <tt>:velocity</tt> are only set if the +options+ hash passed in doesn't already
|
18
|
+
# have the respective key.
|
19
|
+
def reverse_merge(other_hash)
|
20
|
+
other_hash.merge(self)
|
21
|
+
end unless method_defined?(:reverse_merge)
|
22
|
+
|
23
|
+
# Performs the opposite of <tt>merge</tt>, with the keys and values from the first hash taking precedence over the second.
|
24
|
+
# Modifies the receiver in place.
|
25
|
+
def reverse_merge!(other_hash)
|
26
|
+
merge!( other_hash ){|k,o,n| o }
|
27
|
+
end unless method_defined?(:reverse_merge!)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Gorillib
|
2
|
+
module Hashlike
|
3
|
+
module Slice
|
4
|
+
# Slice a hash to include only the given allowed_keys. This is useful for
|
5
|
+
# limiting an options hash to valid keys before passing to a method:
|
6
|
+
#
|
7
|
+
# def search(criteria = {})
|
8
|
+
# assert_valid_keys(:mass, :velocity, :time)
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
# search(options.slice(:mass, :velocity, :time))
|
12
|
+
#
|
13
|
+
# If you have an array of keys you want to limit to, you should splat them:
|
14
|
+
#
|
15
|
+
# valid_keys = [:mass, :velocity, :time]
|
16
|
+
# search(options.slice(*valid_keys))
|
17
|
+
def slice(*allowed_keys)
|
18
|
+
allowed_keys = allowed_keys.map!{|key| convert_key(key) } if respond_to?(:convert_key)
|
19
|
+
hash = self.class.new
|
20
|
+
allowed_keys.each{|k| hash[k] = self[k] if has_key?(k) }
|
21
|
+
hash
|
22
|
+
end unless method_defined?(:slice)
|
23
|
+
|
24
|
+
# Replaces the hash with only the given allowed_keys.
|
25
|
+
# Returns a hash containing the removed key/value pairs
|
26
|
+
# @example
|
27
|
+
# hsh = {:a => 1, :b => 2, :c => 3, :d => 4}
|
28
|
+
# hsh.slice!(:a, :b)
|
29
|
+
# # => {:c => 3, :d =>4}
|
30
|
+
# hsh
|
31
|
+
# # => {:a => 1, :b => 2}
|
32
|
+
def slice!(*allowed_keys)
|
33
|
+
allowed_keys = allowed_keys.map!{|key| convert_key(key) } if respond_to?(:convert_key)
|
34
|
+
omit = slice(*self.keys - allowed_keys)
|
35
|
+
hash = slice(*allowed_keys)
|
36
|
+
replace(hash)
|
37
|
+
omit
|
38
|
+
end unless method_defined?(:slice!)
|
39
|
+
|
40
|
+
# This also works, and doesn't require #replace method, but is uglier and
|
41
|
+
# wasn't written by Railsians. I'm not sure that slice! is interesting if
|
42
|
+
# you're a duck-typed Hash but not is_a?(Hash), so we'll just leave it at the
|
43
|
+
# active_record implementation.
|
44
|
+
#
|
45
|
+
# def slice!(*allowed_keys)
|
46
|
+
# allowed_keys = allowed_keys.map!{|key| convert_key(key) } if respond_to?(:convert_key)
|
47
|
+
# omit_keys = self.keys - allowed_keys
|
48
|
+
# omit = slice(*omit_keys)
|
49
|
+
# omit_keys.each{|k| delete(k) }
|
50
|
+
# omit
|
51
|
+
# end
|
52
|
+
|
53
|
+
# Removes the given allowed_keys from the hash
|
54
|
+
# Returns a hash containing the removed key/value pairs
|
55
|
+
#
|
56
|
+
# @example
|
57
|
+
# hsh = {:a => 1, :b => 2, :c => 3, :d => 4}
|
58
|
+
# hsh.extract!(:a, :b)
|
59
|
+
# # => {:a => 1, :b => 2}
|
60
|
+
# hsh
|
61
|
+
# # => {:c => 3, :d =>4}
|
62
|
+
def extract!(*allowed_keys)
|
63
|
+
slice!(*self.keys - allowed_keys)
|
64
|
+
end unless method_defined?(:extract!)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|