motion_support 0.0.2 → 0.0.3

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.
@@ -0,0 +1,2 @@
1
+ require 'bubble-wrap/loader'
2
+ BubbleWrap.require('motion/core_ext/hash/keys.rb')
@@ -0,0 +1,4 @@
1
+ require 'bubble-wrap/loader'
2
+ Dir["#{File.dirname(__FILE__)}/hash/*.rb"].sort.each do |path|
3
+ require path
4
+ end
@@ -0,0 +1,4 @@
1
+ require 'bubble-wrap/loader'
2
+ BubbleWrap.require('motion/core_ext/object/*.rb') do
3
+ file('motion/core_ext/object/deep_dup.rb').depends_on('motion/core_ext/object/duplicable.rb')
4
+ end
@@ -0,0 +1,2 @@
1
+ require 'bubble-wrap/loader'
2
+ BubbleWrap.require('motion/core_ext/object/duplicable.rb')
@@ -0,0 +1,5 @@
1
+ require 'bubble-wrap/loader'
2
+ Dir["#{File.dirname(__FILE__)}/object/*.rb"].sort.each do |path|
3
+ require path
4
+ end
5
+
@@ -1,3 +1,3 @@
1
1
  module MotionSupport
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -0,0 +1,139 @@
1
+ class Hash
2
+ # Return a new hash with all keys converted using the block operation.
3
+ #
4
+ # hash = { name: 'Rob', age: '28' }
5
+ #
6
+ # hash.transform_keys{ |key| key.to_s.upcase }
7
+ # # => { "NAME" => "Rob", "AGE" => "28" }
8
+ def transform_keys
9
+ result = {}
10
+ each_key do |key|
11
+ result[yield(key)] = self[key]
12
+ end
13
+ result
14
+ end
15
+
16
+ # Destructively convert all keys using the block operations.
17
+ # Same as transform_keys but modifies +self+
18
+ def transform_keys!
19
+ keys.each do |key|
20
+ self[yield(key)] = delete(key)
21
+ end
22
+ self
23
+ end
24
+
25
+ # Return a new hash with all keys converted to strings.
26
+ #
27
+ # hash = { name: 'Rob', age: '28' }
28
+ #
29
+ # hash.stringify_keys
30
+ # #=> { "name" => "Rob", "age" => "28" }
31
+ def stringify_keys
32
+ transform_keys{ |key| key.to_s }
33
+ end
34
+
35
+ # Destructively convert all keys to strings. Same as
36
+ # +stringify_keys+, but modifies +self+.
37
+ def stringify_keys!
38
+ transform_keys!{ |key| key.to_s }
39
+ end
40
+
41
+ # Return a new hash with all keys converted to symbols, as long as
42
+ # they respond to +to_sym+.
43
+ #
44
+ # hash = { 'name' => 'Rob', 'age' => '28' }
45
+ #
46
+ # hash.symbolize_keys
47
+ # #=> { name: "Rob", age: "28" }
48
+ def symbolize_keys
49
+ transform_keys{ |key| key.to_sym rescue key }
50
+ end
51
+ alias_method :to_options, :symbolize_keys
52
+
53
+ # Destructively convert all keys to symbols, as long as they respond
54
+ # to +to_sym+. Same as +symbolize_keys+, but modifies +self+.
55
+ def symbolize_keys!
56
+ transform_keys!{ |key| key.to_sym rescue key }
57
+ end
58
+ alias_method :to_options!, :symbolize_keys!
59
+
60
+ # Validate all keys in a hash match *valid keys, raising ArgumentError on a mismatch.
61
+ # Note that keys are NOT treated indifferently, meaning if you use strings for keys but assert symbols
62
+ # as keys, this will fail.
63
+ #
64
+ # { :name => 'Rob', :years => '28' }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key: years"
65
+ # { :name => 'Rob', :age => '28' }.assert_valid_keys('name', 'age') # => raises "ArgumentError: Unknown key: name"
66
+ # { :name => 'Rob', :age => '28' }.assert_valid_keys(:name, :age) # => passes, raises nothing
67
+ def assert_valid_keys(*valid_keys)
68
+ valid_keys.flatten!
69
+ each_key do |k|
70
+ raise ArgumentError.new("Unknown key: #{k}") unless valid_keys.include?(k)
71
+ end
72
+ end
73
+
74
+ # Return a new hash with all keys converted by the block operation.
75
+ # This includes the keys from the root hash and from all
76
+ # nested hashes.
77
+ #
78
+ # hash = { person: { name: 'Rob', age: '28' } }
79
+ #
80
+ # hash.deep_transform_keys{ |key| key.to_s.upcase }
81
+ # # => { "PERSON" => { "NAME" => "Rob", "AGE" => "28" } }
82
+ def deep_transform_keys(&block)
83
+ result = {}
84
+ each do |key, value|
85
+ result[yield(key)] = value.is_a?(Hash) ? value.deep_transform_keys(&block) : value
86
+ end
87
+ result
88
+ end
89
+
90
+ # Destructively convert all keys by using the block operation.
91
+ # This includes the keys from the root hash and from all
92
+ # nested hashes.
93
+ def deep_transform_keys!(&block)
94
+ keys.each do |key|
95
+ value = delete(key)
96
+ self[yield(key)] = value.is_a?(Hash) ? value.deep_transform_keys!(&block) : value
97
+ end
98
+ self
99
+ end
100
+
101
+ # Return a new hash with all keys converted to strings.
102
+ # This includes the keys from the root hash and from all
103
+ # nested hashes.
104
+ #
105
+ # hash = { person: { name: 'Rob', age: '28' } }
106
+ #
107
+ # hash.deep_stringify_keys
108
+ # # => { "person" => { "name" => "Rob", "age" => "28" } }
109
+ def deep_stringify_keys
110
+ deep_transform_keys{ |key| key.to_s }
111
+ end
112
+
113
+ # Destructively convert all keys to strings.
114
+ # This includes the keys from the root hash and from all
115
+ # nested hashes.
116
+ def deep_stringify_keys!
117
+ deep_transform_keys!{ |key| key.to_s }
118
+ end
119
+
120
+ # Return a new hash with all keys converted to symbols, as long as
121
+ # they respond to +to_sym+. This includes the keys from the root hash
122
+ # and from all nested hashes.
123
+ #
124
+ # hash = { 'person' => { 'name' => 'Rob', 'age' => '28' } }
125
+ #
126
+ # hash.deep_symbolize_keys
127
+ # # => { person: { name: "Rob", age: "28" } }
128
+ def deep_symbolize_keys
129
+ deep_transform_keys{ |key| key.to_sym rescue key }
130
+ end
131
+
132
+ # Destructively convert all keys to symbols, as long as they respond
133
+ # to +to_sym+. This includes the keys from the root hash and from all
134
+ # nested hashes.
135
+ def deep_symbolize_keys!
136
+ deep_transform_keys!{ |key| key.to_sym rescue key }
137
+ end
138
+ end
139
+
@@ -0,0 +1,45 @@
1
+ class Object
2
+ # Returns a deep copy of object if it's duplicable. If it's
3
+ # not duplicable, returns +self+.
4
+ #
5
+ # object = Object.new
6
+ # dup = object.deep_dup
7
+ # dup.instance_variable_set(:@a, 1)
8
+ #
9
+ # object.instance_variable_defined?(:@a) #=> false
10
+ # dup.instance_variable_defined?(:@a) #=> true
11
+ def deep_dup
12
+ duplicable? ? dup : self
13
+ end
14
+ end
15
+
16
+ class Array
17
+ # Returns a deep copy of array.
18
+ #
19
+ # array = [1, [2, 3]]
20
+ # dup = array.deep_dup
21
+ # dup[1][2] = 4
22
+ #
23
+ # array[1][2] #=> nil
24
+ # dup[1][2] #=> 4
25
+ def deep_dup
26
+ map { |it| it.deep_dup }
27
+ end
28
+ end
29
+
30
+ class Hash
31
+ # Returns a deep copy of hash.
32
+ #
33
+ # hash = { a: { b: 'b' } }
34
+ # dup = hash.deep_dup
35
+ # dup[:a][:c] = 'c'
36
+ #
37
+ # hash[:a][:c] #=> nil
38
+ # dup[:a][:c] #=> "c"
39
+ def deep_dup
40
+ each_with_object(dup) do |(key, value), hash|
41
+ hash[key.deep_dup] = value.deep_dup
42
+ end
43
+ end
44
+ end
45
+
@@ -0,0 +1,95 @@
1
+ #--
2
+ # Most objects are cloneable, but not all. For example you can't dup +nil+:
3
+ #
4
+ # nil.dup # => TypeError: can't dup NilClass
5
+ #
6
+ # Classes may signal their instances are not duplicable removing +dup+/+clone+
7
+ # or raising exceptions from them. So, to dup an arbitrary object you normally
8
+ # use an optimistic approach and are ready to catch an exception, say:
9
+ #
10
+ # arbitrary_object.dup rescue object
11
+ #
12
+ # Rails dups objects in a few critical spots where they are not that arbitrary.
13
+ # That rescue is very expensive (like 40 times slower than a predicate), and it
14
+ # is often triggered.
15
+ #
16
+ # That's why we hardcode the following cases and check duplicable? instead of
17
+ # using that rescue idiom.
18
+ #++
19
+ class Object
20
+ # Can you safely dup this object?
21
+ #
22
+ # False for +nil+, +false+, +true+, symbol, and number objects;
23
+ # true otherwise.
24
+ def duplicable?
25
+ true
26
+ end
27
+ end
28
+
29
+ class NilClass
30
+ # +nil+ is not duplicable:
31
+ #
32
+ # nil.duplicable? # => false
33
+ # nil.dup # => TypeError: can't dup NilClass
34
+ #
35
+ def duplicable?
36
+ false
37
+ end
38
+ end
39
+
40
+ class FalseClass
41
+ # +false+ is not duplicable:
42
+ #
43
+ # false.duplicable? # => false
44
+ # false.dup # => TypeError: can't dup FalseClass
45
+ #
46
+ def duplicable?
47
+ false
48
+ end
49
+ end
50
+
51
+ class TrueClass
52
+ # +true+ is not duplicable:
53
+ #
54
+ # true.duplicable? # => false
55
+ # true.dup # => TypeError: can't dup TrueClass
56
+ #
57
+ def duplicable?
58
+ false
59
+ end
60
+ end
61
+
62
+ class Symbol
63
+ # Symbols are not duplicable:
64
+ #
65
+ # :my_symbol.duplicable? # => false
66
+ # :my_symbol.dup # => TypeError: can't dup Symbol
67
+ #
68
+ def duplicable?
69
+ false
70
+ end
71
+ end
72
+
73
+ class Numeric
74
+ # Numbers are not duplicable:
75
+ #
76
+ # 3.duplicable? # => false
77
+ # 3.dup # => TypeError: can't dup Fixnum
78
+ #
79
+ def duplicable?
80
+ false
81
+ end
82
+ end
83
+
84
+ class BigDecimal
85
+ begin
86
+ BigDecimal.new('4.56').dup
87
+
88
+ def duplicable?
89
+ true
90
+ end
91
+ rescue TypeError
92
+ # can't dup, so use superclass implementation
93
+ end
94
+ end
95
+
@@ -0,0 +1,205 @@
1
+ describe "Hash Keys" do
2
+ before do
3
+ @strings = { 'a' => 1, 'b' => 2 }
4
+ @nested_strings = { 'a' => { 'b' => { 'c' => 3 } } }
5
+ @symbols = { :a => 1, :b => 2 }
6
+ @nested_symbols = { :a => { :b => { :c => 3 } } }
7
+ @mixed = { :a => 1, 'b' => 2 }
8
+ @nested_mixed = { 'a' => { :b => { 'c' => 3 } } }
9
+ @fixnums = { 0 => 1, 1 => 2 }
10
+ @nested_fixnums = { 0 => { 1 => { 2 => 3} } }
11
+ @illegal_symbols = { [] => 3 }
12
+ @nested_illegal_symbols = { [] => { [] => 3} }
13
+ @upcase_strings = { 'A' => 1, 'B' => 2 }
14
+ @nested_upcase_strings = { 'A' => { 'B' => { 'C' => 3 } } }
15
+ end
16
+
17
+ it "methods" do
18
+ h = {}
19
+ h.respond_to?(:transform_keys).should == true
20
+ h.respond_to?(:transform_keys!).should == true
21
+ h.respond_to?(:deep_transform_keys).should == true
22
+ h.respond_to?(:deep_transform_keys!).should == true
23
+ h.respond_to?(:symbolize_keys).should == true
24
+ h.respond_to?(:symbolize_keys!).should == true
25
+ h.respond_to?(:deep_symbolize_keys).should == true
26
+ h.respond_to?(:deep_symbolize_keys!).should == true
27
+ h.respond_to?(:stringify_keys).should == true
28
+ h.respond_to?(:stringify_keys!).should == true
29
+ h.respond_to?(:deep_stringify_keys).should == true
30
+ h.respond_to?(:deep_stringify_keys!).should == true
31
+ h.respond_to?(:to_options).should == true
32
+ h.respond_to?(:to_options!).should == true
33
+ end
34
+
35
+ it "transform keys" do
36
+ @strings.transform_keys{ |key| key.to_s.upcase }.should == @upcase_strings
37
+ @symbols.transform_keys{ |key| key.to_s.upcase }.should == @upcase_strings
38
+ @mixed.transform_keys{ |key| key.to_s.upcase }.should == @upcase_strings
39
+ end
40
+
41
+ it "transform keys not mutates" do
42
+ transformed_hash = @mixed.dup
43
+ transformed_hash.transform_keys{ |key| key.to_s.upcase }
44
+ transformed_hash.should == @mixed
45
+ end
46
+
47
+ it "deep transform keys" do
48
+ @nested_symbols.deep_transform_keys{ |key| key.to_s.upcase }.should == @nested_upcase_strings
49
+ @nested_strings.deep_transform_keys{ |key| key.to_s.upcase }.should == @nested_upcase_strings
50
+ @nested_mixed.deep_transform_keys{ |key| key.to_s.upcase }.should == @nested_upcase_strings
51
+ end
52
+
53
+ it "deep transform keys not mutates" do
54
+ transformed_hash = @nested_mixed.deep_dup
55
+ transformed_hash.deep_transform_keys{ |key| key.to_s.upcase }
56
+ transformed_hash.should == @nested_mixed
57
+ end
58
+
59
+ it "transform keys!" do
60
+ @symbols.dup.transform_keys!{ |key| key.to_s.upcase }.should == @upcase_strings
61
+ @strings.dup.transform_keys!{ |key| key.to_s.upcase }.should == @upcase_strings
62
+ @mixed.dup.transform_keys!{ |key| key.to_s.upcase }.should == @upcase_strings
63
+ end
64
+
65
+ it "transform keys with bang mutates" do
66
+ transformed_hash = @mixed.dup
67
+ transformed_hash.transform_keys!{ |key| key.to_s.upcase }
68
+ transformed_hash.should == @upcase_strings
69
+ { :a => 1, "b" => 2 }.should.equal(@mixed)
70
+ end
71
+
72
+ it "deep transform keys!" do
73
+ @nested_symbols.deep_dup.deep_transform_keys!{ |key| key.to_s.upcase }.should == @nested_upcase_strings
74
+ @nested_strings.deep_dup.deep_transform_keys!{ |key| key.to_s.upcase }.should == @nested_upcase_strings
75
+ @nested_mixed.deep_dup.deep_transform_keys!{ |key| key.to_s.upcase }.should == @nested_upcase_strings
76
+ end
77
+
78
+ it "deep transform keys with bang mutates" do
79
+ transformed_hash = @nested_mixed.deep_dup
80
+ transformed_hash.deep_transform_keys!{ |key| key.to_s.upcase }
81
+ transformed_hash.should == @nested_upcase_strings
82
+ { 'a' => { :b => { 'c' => 3 } } }.should == @nested_mixed
83
+ end
84
+
85
+ it "symbolize keys" do
86
+ @symbols.symbolize_keys.should == @symbols
87
+ @strings.symbolize_keys.should == @symbols
88
+ @mixed.symbolize_keys.should == @symbols
89
+ end
90
+
91
+ it "symbolize keys not mutates" do
92
+ transformed_hash = @mixed.dup
93
+ transformed_hash.symbolize_keys
94
+ transformed_hash.should == @mixed
95
+ end
96
+
97
+ it "deep symbolize keys" do
98
+ @nested_symbols.deep_symbolize_keys.should == @nested_symbols
99
+ @nested_strings.deep_symbolize_keys.should == @nested_symbols
100
+ @nested_mixed.deep_symbolize_keys.should == @nested_symbols
101
+ end
102
+
103
+ it "deep symbolize keys not mutates" do
104
+ transformed_hash = @nested_mixed.deep_dup
105
+ transformed_hash.deep_symbolize_keys
106
+ transformed_hash.should == @nested_mixed
107
+ end
108
+
109
+ it "symbolize keys!" do
110
+ @symbols.dup.symbolize_keys!.should == @symbols
111
+ @strings.dup.symbolize_keys!.should == @symbols
112
+ @mixed.dup.symbolize_keys!.should == @symbols
113
+ end
114
+
115
+ it "symbolize keys with bang mutates" do
116
+ transformed_hash = @mixed.dup
117
+ transformed_hash.deep_symbolize_keys!
118
+ transformed_hash.should == @symbols
119
+ { :a => 1, "b" => 2 }.should.equal(@mixed)
120
+ end
121
+
122
+ it "deep symbolize keys!" do
123
+ @nested_symbols.deep_dup.deep_symbolize_keys!.should == @nested_symbols
124
+ @nested_strings.deep_dup.deep_symbolize_keys!.should == @nested_symbols
125
+ @nested_mixed.deep_dup.deep_symbolize_keys!.should == @nested_symbols
126
+ end
127
+
128
+ it "deep symbolize keys with bang mutates" do
129
+ transformed_hash = @nested_mixed.deep_dup
130
+ transformed_hash.deep_symbolize_keys!
131
+ transformed_hash.should == @nested_symbols
132
+ { 'a' => { :b => { 'c' => 3 } } }.should == @nested_mixed
133
+ end
134
+
135
+ it "symbolize keys preserves keys that cant be symbolized" do
136
+ @illegal_symbols.symbolize_keys.should == @illegal_symbols
137
+ @illegal_symbols.dup.symbolize_keys!.should == @illegal_symbols
138
+ end
139
+
140
+ it "deep symbolize keys preserves keys that cant be symbolized" do
141
+ @nested_illegal_symbols.deep_symbolize_keys.should == @nested_illegal_symbols
142
+ @nested_illegal_symbols.deep_dup.deep_symbolize_keys!.should == @nested_illegal_symbols
143
+ end
144
+
145
+ it "symbolize keys preserves fixnum keys" do
146
+ @fixnums.symbolize_keys.should == @fixnums
147
+ @fixnums.dup.symbolize_keys!.should == @fixnums
148
+ end
149
+
150
+ it "deep symbolize keys preserves fixnum keys" do
151
+ @nested_fixnums.deep_symbolize_keys.should == @nested_fixnums
152
+ @nested_fixnums.deep_dup.deep_symbolize_keys!.should == @nested_fixnums
153
+ end
154
+
155
+ it "stringify keys" do
156
+ @symbols.stringify_keys.should == @strings
157
+ @strings.stringify_keys.should == @strings
158
+ @mixed.stringify_keys.should == @strings
159
+ end
160
+
161
+ it "stringify keys not mutates" do
162
+ transformed_hash = @mixed.dup
163
+ transformed_hash.stringify_keys
164
+ transformed_hash.should == @mixed
165
+ end
166
+
167
+ it "deep stringify keys" do
168
+ @nested_symbols.deep_stringify_keys.should == @nested_strings
169
+ @nested_strings.deep_stringify_keys.should == @nested_strings
170
+ @nested_mixed.deep_stringify_keys.should == @nested_strings
171
+ end
172
+
173
+ it "deep stringify keys not mutates" do
174
+ transformed_hash = @nested_mixed.deep_dup
175
+ transformed_hash.deep_stringify_keys
176
+ transformed_hash.should == @nested_mixed
177
+ end
178
+
179
+ it "stringify keys!" do
180
+ @symbols.dup.stringify_keys!.should == @strings
181
+ @strings.dup.stringify_keys!.should == @strings
182
+ @mixed.dup.stringify_keys!.should == @strings
183
+ end
184
+
185
+ it "stringify keys with bang mutates" do
186
+ transformed_hash = @mixed.dup
187
+ transformed_hash.stringify_keys!
188
+ transformed_hash.should == @strings
189
+ { :a => 1, "b" => 2 }.should.equal(@mixed)
190
+ end
191
+
192
+ it "deep stringify keys!" do
193
+ @nested_symbols.deep_dup.deep_stringify_keys!.should == @nested_strings
194
+ @nested_strings.deep_dup.deep_stringify_keys!.should == @nested_strings
195
+ @nested_mixed.deep_dup.deep_stringify_keys!.should == @nested_strings
196
+ end
197
+
198
+ it "deep stringify keys with bang mutates" do
199
+ transformed_hash = @nested_mixed.deep_dup
200
+ transformed_hash.deep_stringify_keys!
201
+ transformed_hash.should == @nested_strings
202
+ { 'a' => { :b => { 'c' => 3 } } }.should == @nested_mixed
203
+ end
204
+
205
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: motion_support
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -62,6 +62,11 @@ files:
62
62
  - lib/motion_support/core_ext/array.rb
63
63
  - lib/motion_support/core_ext/array/extract_options.rb
64
64
  - lib/motion_support/core_ext/array/prepend_append.rb
65
+ - lib/motion_support/core_ext/hash.rb
66
+ - lib/motion_support/core_ext/hash/keys.rb
67
+ - lib/motion_support/core_ext/object.rb
68
+ - lib/motion_support/core_ext/object/deep_dup.rb
69
+ - lib/motion_support/core_ext/object/duplicable.rb
65
70
  - lib/motion_support/core_ext/string.rb
66
71
  - lib/motion_support/core_ext/string/inflections.rb
67
72
  - lib/motion_support/inflections.rb
@@ -72,6 +77,9 @@ files:
72
77
  - motion/concern.rb
73
78
  - motion/core_ext/array/extract_options.rb
74
79
  - motion/core_ext/array/prepend_append.rb
80
+ - motion/core_ext/hash/keys.rb
81
+ - motion/core_ext/object/deep_dup.rb
82
+ - motion/core_ext/object/duplicable.rb
75
83
  - motion/core_ext/string/inflections.rb
76
84
  - motion/inflections.rb
77
85
  - motion/inflector/inflections.rb
@@ -80,6 +88,7 @@ files:
80
88
  - spec/concern_spec.rb
81
89
  - spec/core_ext/array/extract_options_spec.rb
82
90
  - spec/core_ext/array/prepend_append_spec.rb
91
+ - spec/core_ext/hash/keys_spec.rb
83
92
  - spec/helpers/constantize_test_cases.rb
84
93
  - spec/helpers/dup.rb
85
94
  - spec/helpers/inflector_test_cases.rb
@@ -98,7 +107,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
98
107
  version: '0'
99
108
  segments:
100
109
  - 0
101
- hash: 2308112850745967264
110
+ hash: 1428780749232898209
102
111
  required_rubygems_version: !ruby/object:Gem::Requirement
103
112
  none: false
104
113
  requirements:
@@ -107,7 +116,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
107
116
  version: '0'
108
117
  segments:
109
118
  - 0
110
- hash: 2308112850745967264
119
+ hash: 1428780749232898209
111
120
  requirements: []
112
121
  rubyforge_project:
113
122
  rubygems_version: 1.8.23
@@ -118,6 +127,7 @@ test_files:
118
127
  - spec/concern_spec.rb
119
128
  - spec/core_ext/array/extract_options_spec.rb
120
129
  - spec/core_ext/array/prepend_append_spec.rb
130
+ - spec/core_ext/hash/keys_spec.rb
121
131
  - spec/helpers/constantize_test_cases.rb
122
132
  - spec/helpers/dup.rb
123
133
  - spec/helpers/inflector_test_cases.rb