darthjee-core_ext 1.7.4 → 2.0.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.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +7 -1
  3. data/.gitignore +2 -0
  4. data/.reek.yml +3 -0
  5. data/ARRAY_README.md +72 -15
  6. data/CLASS_README.md +154 -0
  7. data/DATE_README.md +19 -9
  8. data/Dockerfile +2 -5
  9. data/ENUMERABLE_README.md +154 -4
  10. data/HASH_README.md +276 -135
  11. data/MATH_README.md +14 -10
  12. data/OBJECT_README.md +18 -35
  13. data/README.md +8 -4
  14. data/Rakefile +1 -0
  15. data/SYMBOL_README.md +13 -18
  16. data/config/rubycritc.rb +12 -0
  17. data/config/yardstick.yml +45 -3
  18. data/core_ext.gemspec +10 -8
  19. data/docker-compose.yml +15 -7
  20. data/lib/darthjee/core_ext/array.rb +17 -12
  21. data/lib/darthjee/core_ext/array/hash_builder.rb +20 -4
  22. data/lib/darthjee/core_ext/class.rb +28 -10
  23. data/lib/darthjee/core_ext/date.rb +1 -0
  24. data/lib/darthjee/core_ext/enumerable.rb +67 -30
  25. data/lib/darthjee/core_ext/hash.rb +4 -2
  26. data/lib/darthjee/core_ext/hash/cameliazable.rb +39 -8
  27. data/lib/darthjee/core_ext/hash/chain_fetcher.rb +17 -1
  28. data/lib/darthjee/core_ext/hash/changeable.rb +88 -0
  29. data/lib/darthjee/core_ext/hash/deep_hash_constructor.rb +127 -63
  30. data/lib/darthjee/core_ext/hash/deep_hash_constructor/setter.rb +112 -0
  31. data/lib/darthjee/core_ext/hash/key_changeable.rb +126 -54
  32. data/lib/darthjee/core_ext/hash/key_changer.rb +140 -40
  33. data/lib/darthjee/core_ext/hash/keys_sorter.rb +42 -6
  34. data/lib/darthjee/core_ext/hash/squasher.rb +131 -18
  35. data/lib/darthjee/core_ext/hash/transformable.rb +133 -41
  36. data/lib/darthjee/core_ext/hash/transposeable.rb +37 -8
  37. data/lib/darthjee/core_ext/hash/value_changer.rb +76 -36
  38. data/lib/darthjee/core_ext/math.rb +37 -6
  39. data/lib/darthjee/core_ext/numeric.rb +10 -0
  40. data/lib/darthjee/core_ext/object.rb +29 -0
  41. data/lib/darthjee/core_ext/symbol.rb +22 -2
  42. data/lib/darthjee/core_ext/version.rb +1 -1
  43. data/scripts/check_readme.sh +6 -0
  44. data/scripts/rubycritic.sh +10 -0
  45. data/spec/integration/readme/array_spec.rb +96 -0
  46. data/spec/integration/readme/class_spec.rb +123 -0
  47. data/spec/integration/readme/date_spec.rb +20 -0
  48. data/spec/integration/readme/enumerable_spec.rb +87 -0
  49. data/spec/integration/readme/hash_spec.rb +391 -0
  50. data/spec/integration/readme/math_spec.rb +31 -0
  51. data/spec/integration/readme/object_spec.rb +49 -0
  52. data/spec/integration/readme/symbol_spec.rb +29 -0
  53. data/spec/integration/yard/darthjee/core_ext/enumerable_spec.rb +24 -4
  54. data/spec/integration/yard/darthjee/core_ext/hash/cameliazable_spec.rb +11 -0
  55. data/spec/integration/yard/darthjee/core_ext/hash/changeable_spec.rb +68 -0
  56. data/spec/integration/yard/darthjee/core_ext/hash/deep_hash_constructor/setter_spec.rb +34 -0
  57. data/spec/integration/yard/darthjee/core_ext/hash/deep_hash_constructor_spec.rb +65 -0
  58. data/spec/integration/yard/darthjee/core_ext/hash/key_changeable_spec.rb +8 -0
  59. data/spec/integration/yard/darthjee/core_ext/hash/key_changer_spec.rb +59 -0
  60. data/spec/integration/yard/darthjee/core_ext/hash/keys_sorter_spec.rb +25 -0
  61. data/spec/integration/yard/darthjee/core_ext/hash/squasher_spec.rb +60 -0
  62. data/spec/integration/yard/darthjee/core_ext/hash/transformable_spec.rb +66 -36
  63. data/spec/integration/yard/darthjee/core_ext/hash/transposeable_spec.rb +35 -0
  64. data/spec/integration/yard/darthjee/core_ext/hash_spec.rb +13 -2
  65. data/spec/integration/yard/darthjee/core_ext/math_spec.rb +28 -0
  66. data/spec/integration/yard/darthjee/core_ext/numeric_spec.rb +11 -0
  67. data/spec/integration/yard/darthjee/core_ext/object_spec.rb +47 -0
  68. data/spec/integration/yard/darthjee/core_ext/symbol_spec.rb +17 -0
  69. data/spec/lib/darthjee/core_ext/hash/deep_hash_constructor/setter_spec.rb +64 -0
  70. data/spec/lib/darthjee/core_ext/hash/deep_hash_constructor_spec.rb +16 -156
  71. data/spec/lib/darthjee/core_ext/hash/keys_sorter_spec.rb +5 -4
  72. data/spec/lib/darthjee/core_ext/hash/squasher_spec.rb +8 -2
  73. data/spec/lib/darthjee/core_ext/hash/value_changer_spec.rb +4 -12
  74. data/spec/lib/hash_spec.rb +92 -146
  75. data/spec/lib/object_spec.rb +32 -0
  76. data/spec/support/models/client.rb +22 -0
  77. data/spec/support/shared_examples/hash/deep_hash.rb +166 -0
  78. data/spec/support/shared_examples/hash/hash_squasher.rb +54 -9
  79. data/spec/support/shared_examples/hash/keys_sorter.rb +266 -6
  80. metadata +70 -8
  81. data/lib/darthjee/core_ext/hash/to_hash_mapper.rb +0 -25
  82. data/spec/lib/darthjee/core_ext/hash/to_hash_mapper_spec.rb +0 -11
@@ -4,8 +4,7 @@ module Darthjee
4
4
  module CoreExt
5
5
  module Hash
6
6
  module Transformable
7
- # Merges both hashes not adding keys that don't
8
- # exist in the original hash
7
+ # Merge only common keys
9
8
  #
10
9
  # @param [::Hash] other other hash to be merged
11
10
  #
@@ -15,14 +14,16 @@ module Darthjee
15
14
  # hash = { a: 1, b: 2, c: 3 }
16
15
  # other = { b: 4, 'c' => 5, e: 6 }
17
16
  #
18
- # hash.exclusive_merge(other) # returns { a: 1, b: 4, c: 3 }
19
- # hash # returns { a: 1, b: 2, c: 3 }
17
+ # hash.exclusive_merge(other) # returns {
18
+ # # a: 1,
19
+ # # b: 4,
20
+ # # c: 3
21
+ # # }
20
22
  def exclusive_merge(other)
21
23
  dup.exclusive_merge!(other)
22
24
  end
23
25
 
24
- # Merges both hashes not adding keys that don't
25
- # exist in the original hash
26
+ # Merge only common keys
26
27
  #
27
28
  # @param [::Hash] other other hash to be merged
28
29
  #
@@ -32,17 +33,23 @@ module Darthjee
32
33
  # hash = { a: 1, b: 2, c: 3 }
33
34
  # other = { b: 4, 'c' => 5, e: 6 }
34
35
  #
35
- # hash.exclusive_merge!(other) # returns { a: 1, b: 4, c: 3 }
36
- # hash # returns { a: 1, b: 4, c: 3 }
36
+ # hash.exclusive_merge!(other) # changes hash to {
37
+ # # a: 1,
38
+ # # b: 4,
39
+ # # c: 3
40
+ # # }
37
41
  def exclusive_merge!(other)
38
42
  merge!(other.slice(*keys))
39
43
  end
40
44
 
45
+ # Map returning a hash keeping the original keys
46
+ #
41
47
  # Run map block where each pair key, value is mapped
42
48
  # to a new value to be assigned in the same key on the
43
49
  # returned hash
44
50
  #
45
- # @return new Hash made with the pairs key => mapped_value
51
+ # @return [::Hash] new Hash made with the pairs
52
+ # key => mapped_value
46
53
  #
47
54
  # @yield (key, value) block returning the new value
48
55
  #
@@ -55,22 +62,33 @@ module Darthjee
55
62
  # "#{key}->#{value.size}"
56
63
  # end
57
64
  #
58
- # new_hash # returns { a: 'a->4', b: 'b->7', c: 'c->1' }
59
- def map_to_hash(&block)
60
- Hash::ToHashMapper.new(self).map(&block)
65
+ # new_hash # returns {
66
+ # # a: 'a->4',
67
+ # # b: 'b->7',
68
+ # # c: 'c->1'
69
+ # # }
70
+ def map_to_hash
71
+ map do |key, value|
72
+ [key, yield(key, value)]
73
+ end.to_h
61
74
  end
62
75
 
63
- # Squash the hash so that it becomes a single level hash
64
- # merging the keys of outter and inner hashes
76
+ # Squash the hash returning a single level hash
77
+ #
78
+ # The squashing happens by merging the keys of
79
+ # outter and inner hashes
65
80
  #
66
81
  # This operation is the oposite of {#to_deep_hash}
67
82
  #
68
- # @return [::Hash] A one level hash
83
+ # @param joiner [::String] String to be used when
84
+ # joining keys
85
+ #
86
+ # @return [::Hash] A new hash
69
87
  #
70
88
  # @see Squash::Builder
71
89
  # @see #to_deep_hash
72
90
  #
73
- # @example a name hash
91
+ # @example Simple Usage
74
92
  # hash = { name: { first: 'John', last: 'Doe' } }
75
93
  #
76
94
  # hash.squash # returns {
@@ -80,21 +98,63 @@ module Darthjee
80
98
  #
81
99
  # @example Reverting a #to_deep_hash call
82
100
  # person_data = {
83
- # 'person.name' => 'John',
84
- # 'person.age' => 23
101
+ # person: [{
102
+ # name: %w[John Wick],
103
+ # age: 22
104
+ # }, {
105
+ # name: %w[John Constantine],
106
+ # age: 25
107
+ # }]
85
108
  # }
86
109
  # person = person_data.to_deep_hash
87
110
  #
88
111
  # person.squash # returns {
89
- # # 'person.name' => 'John',
90
- # # 'person.age' => 23
112
+ # # 'person' => [{
113
+ # # 'name' => %w[John Wick],
114
+ # # 'age' => 22
115
+ # # }, {
116
+ # # 'name' => %w[John Constantine],
117
+ # # 'age' => 25
118
+ # # }]
91
119
  # # }
92
- def squash
93
- Hash::Squasher.squash(self)
120
+ #
121
+ # @example Giving a custom joiner
122
+ # hash = {
123
+ # links: {
124
+ # home: '/',
125
+ # products: '/products'
126
+ # }
127
+ # }
128
+ #
129
+ # hash.squash('> ') # returns {
130
+ # # 'links> home' => '/',
131
+ # # 'links> products' => '/products'
132
+ # # }
133
+ def squash(joiner = '.')
134
+ Hash::Squasher.new(joiner).squash(deep_dup)
135
+ end
136
+
137
+ # Squash the hash so that it becomes a single level hash
138
+ #
139
+ # The squashing happens by merging the keys of
140
+ # outter and inner hashes
141
+ #
142
+ # This operation is the oposite of {#to_deep_hash!}
143
+ #
144
+ # @param joiner [::String] String to be used when
145
+ # joining keys
146
+ #
147
+ # @return [::Hash] A new hash
148
+ #
149
+ # @see Squash::Builder
150
+ # @see #to_deep_hash!
151
+ #
152
+ # @example (see #squash)
153
+ def squash!(joiner = '.')
154
+ Hash::Squasher.new(joiner).squash(self)
94
155
  end
95
156
 
96
- # Creates a new hash of multiple levels from a one level
97
- # hash
157
+ # Creates a new hash of multiple levels
98
158
  #
99
159
  # this operation is the oposite from {#squash}
100
160
  #
@@ -103,32 +163,64 @@ module Darthjee
103
163
  # @see Hash::DeepHashConstructor
104
164
  # @see #squash
105
165
  #
106
- # @example construction of name hash
107
- # { 'name_first' => 'John', 'name_last' => 'Doe' }
166
+ # @example With custom separator
167
+ # hash = {
168
+ # 'person[0]_name_first' => 'John',
169
+ # 'person[0]_name_last' => 'Doe',
170
+ # 'person[1]_name_first' => 'John',
171
+ # 'person[1]_name_last' => 'Wick'
172
+ # }
108
173
  #
109
- # hash.to_deep_hash # return {
110
- # # 'name' => {
111
- # # 'first' => 'John',
112
- # # 'last' => 'Doe'
113
- # # }
114
- # # }
115
- # @example Reverting squash
174
+ # hash.to_deep_hash('_') # return {
175
+ # # 'person' => [{
176
+ # # 'name' => {
177
+ # # 'first' => 'John',
178
+ # # 'last' => 'Doe'
179
+ # # }, {
180
+ # # 'name' => {
181
+ # # 'first' => 'John',
182
+ # # 'last' => 'Wick'
183
+ # # }
184
+ # # }]
185
+ # # }
186
+ #
187
+ # @example Reverting the result of a squash
116
188
  # person = {
117
- # 'person' => {
118
- # 'name' => 'John',
119
- # 'age' => 23
120
- # }
189
+ # person: [{
190
+ # name: ['John', 'Wick'],
191
+ # age: 23
192
+ # }, {
193
+ # name: %w[John Constantine],
194
+ # age: 25
195
+ # }]
121
196
  # }
122
197
  # person_data = person.squash
123
198
  #
124
199
  # person_data.to_deep_hash
125
200
  # # returns {
126
- # # 'person' => {
127
- # # 'name' => 'John',
128
- # # 'age' => 23
129
- # # }
201
+ # # 'person' => [{
202
+ # # 'name' => ['John', 'Wick'],
203
+ # # 'age' => 23
204
+ # # }, {
205
+ # # 'name' => %w[John Constantine],
206
+ # # 'age' => 25
207
+ # # }]
130
208
  # # }
131
209
  def to_deep_hash(separator = '.')
210
+ Hash::DeepHashConstructor.new(separator).deep_hash(deep_dup)
211
+ end
212
+
213
+ # Changes hash to be a multiple level hash
214
+ #
215
+ # this operation is the oposite from {#squash!}
216
+ #
217
+ # @return [::Hash] Self changed to be a multi-level hash
218
+ #
219
+ # @see Hash::DeepHashConstructor
220
+ # @see #squash
221
+ #
222
+ # @example (see #to_deep_hash)
223
+ def to_deep_hash!(separator = '.')
132
224
  Hash::DeepHashConstructor.new(separator).deep_hash(self)
133
225
  end
134
226
  end
@@ -3,20 +3,49 @@
3
3
  module Darthjee
4
4
  module CoreExt
5
5
  module Hash
6
+ # @author darthjee
7
+ #
8
+ # Collection of methods for transposing keys
9
+ # and values of hash
6
10
  module Transposeable
11
+ # Transpose matrix swapping keys by values
12
+ #
13
+ # @return [::Hash]
14
+ #
15
+ # @example
16
+ # hash = {
17
+ # key1: :value1,
18
+ # key2: :value2,
19
+ # }
20
+ #
21
+ # hash.transpose # returns {
22
+ # # value1: :key1,
23
+ # # value2: :key2
24
+ # # }
7
25
  def transpose!
8
- aux = dup
9
- keys.each { |k| delete(k) }
10
- aux.each do |k, v|
11
- self[v] = k
12
- end
13
- self
26
+ transposed = transpose
27
+ keys.each(&method(:delete))
28
+ merge!(transposed)
14
29
  end
15
30
 
31
+ # Transpose matrix swapping keys by values
32
+ #
33
+ # @return [::Hash]
34
+ #
35
+ # @example
36
+ # hash = {
37
+ # key1: :value1,
38
+ # key2: :value2,
39
+ # }
40
+ #
41
+ # hash.transpose # changes hash to {
42
+ # # value1: :key1,
43
+ # # value2: :key2
44
+ # # }
16
45
  def transpose
17
46
  {}.tap do |new_hash|
18
- each do |k, v|
19
- new_hash[v] = k
47
+ each do |key, value|
48
+ new_hash[value] = key
20
49
  end
21
50
  end
22
51
  end
@@ -3,26 +3,27 @@
3
3
  module Darthjee
4
4
  module CoreExt
5
5
  module Hash
6
+ # @api private
7
+ # @author darthjee
8
+ #
6
9
  # Class responsible for changing values on a hash
7
10
  #
8
- # @attribute [Boolean] recursive
11
+ # @attribute [::TrueClass,::FalseClass] recursive
9
12
  # flag telling to apply transformation recursively
10
- # @attribute [Boolean] skip_inner
13
+ # @attribute [::TrueClass,::FalseClass] skip_inner
11
14
  # flag telling to not apply change block call to inner hashes
12
15
  # @attribute [::Proc] block
13
16
  # block to be called when changing the values
14
17
  #
15
18
  # @example
16
- # (see initialize)
19
+ # (see #initialize)
17
20
  #
18
21
  # @example
19
22
  # (see #change)
20
23
  class ValueChanger
21
- attr_accessor :recursive, :skip_inner, :block
22
-
23
- # @param [Boolean] recursive
24
+ # @param [::TrueClass,::FalseClass] recursive
24
25
  # flag telling to apply transformation recursively
25
- # @param [Boolean] skip_inner
26
+ # @param [::TrueClass,::FalseClass] skip_inner
26
27
  # flag telling to not apply change block call to inner hashes
27
28
  # @param [::Proc] block
28
29
  # block to be called when changing the values
@@ -108,61 +109,100 @@ module Darthjee
108
109
  # # [{ e: 3 }]
109
110
  # # ]
110
111
  def change(object)
111
- if object.respond_to?(:change_values)
112
+ if object.is_a?(Hash)
112
113
  change_hash(object)
113
- elsif iterable?(object)
114
+ elsif object.is_a?(Array)
114
115
  change_array(object)
116
+ elsif iterable?(object)
117
+ change_iterator(object)
115
118
  else
116
- object
119
+ new_value(object)
117
120
  end
118
121
  end
119
122
 
120
123
  private
121
124
 
125
+ attr_reader :recursive, :skip_inner, :block
126
+
127
+ # @private
128
+ #
122
129
  # Apply change logic to hash object
123
130
  #
124
- # @private
125
- def change_hash(original_hash)
126
- original_hash.tap do |hash|
127
- original_hash.each do |key, value|
131
+ # @param hash [::Hash] hash to be changed
132
+ #
133
+ # @return [::Hash]
134
+ def change_hash(hash)
135
+ hash.tap do
136
+ hash.each do |key, value|
128
137
  hash[key] = new_value(value)
129
138
  end
130
139
  end
131
140
  end
132
141
 
142
+ # @private
143
+ #
133
144
  # Apply change logic to iterator
134
145
  #
135
- # @private
146
+ # @param array [::Array] array to be changed
147
+ #
148
+ # @return [::Array]
136
149
  def change_array(array)
137
- method = %w[map! map].find { |m| array.respond_to? m }
150
+ array.map!(&method(:change))
151
+ end
138
152
 
139
- array.public_send(method) do |value|
140
- if value.respond_to?(:change_values)
141
- value.change_values(options, &block)
142
- elsif iterable?(value)
143
- change_array(value)
144
- else
145
- new_value(value)
146
- end
147
- end
153
+ # @private
154
+ #
155
+ # Change value from iterator
156
+ #
157
+ # @param iterator [::Object] object responding to map
158
+ #
159
+ # @return [::Array]
160
+ def change_iterator(iterator)
161
+ iterator.map(&method(:change))
148
162
  end
149
163
 
150
- # check wehether block should be called over
151
- # value or not
164
+ # @private
165
+ #
166
+ # Check wehether block should be called over value
152
167
  #
153
168
  # when the block is not iterable (not Array or Hash)
154
169
  # or when skip_inner option is set to be false,
155
170
  # then block should be called
156
171
  #
157
- # @private
172
+ # @param value [::Object] value to be checked
173
+ #
174
+ # @return [::TrueClass,::FalseClass]
158
175
  def change_value?(value)
159
176
  !iterable?(value) || !skip_inner
160
177
  end
161
178
 
179
+ # @private
180
+ #
181
+ # Checks if a value is iterable
182
+ #
183
+ # @param value [::Object] object to be checked
184
+ #
185
+ # @return [::TrueClass,::FalseClass]
162
186
  def iterable?(value)
163
- value.respond_to?(:each)
187
+ value.respond_to?(:map)
164
188
  end
165
189
 
190
+ # @private
191
+ #
192
+ # Performs recursive transformation
193
+ #
194
+ # @overload new_value(hash)
195
+ # @param hash [::Hash] sub-hash to be processed recursively
196
+ # @return [::Hash]
197
+ #
198
+ # @overload new_value(array)
199
+ # @param array [::Array] array to be processed recursively
200
+ # @return [::Array]
201
+ #
202
+ # @overload new_value(value)
203
+ # @param value [::Object] value to be transformed
204
+ #
205
+ # @return [::Object]
166
206
  def new_value(value)
167
207
  value = block.call(value) if change_value?(value)
168
208
 
@@ -171,16 +211,16 @@ module Darthjee
171
211
  change(value)
172
212
  end
173
213
 
214
+ # @private
215
+ #
216
+ # Checks if recursion should be applied
217
+ #
218
+ # @param value [::Object]
219
+ #
220
+ # @return [::TrueClass,::FalseClass]
174
221
  def apply_recursion?(value)
175
222
  iterable?(value) && recursive
176
223
  end
177
-
178
- def options
179
- @options ||= {
180
- recursive: recursive,
181
- skip_inner: skip_inner
182
- }
183
- end
184
224
  end
185
225
  end
186
226
  end