motion_support 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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