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,11 +4,34 @@ module Darthjee
4
4
  module CoreExt
5
5
  module Hash
6
6
  # @api private
7
+ #
8
+ # @author Darthjee
7
9
  class KeyChanger
8
10
  def initialize(hash)
9
11
  @hash = hash
10
12
  end
11
13
 
14
+ # Changes keys based on map
15
+ #
16
+ # @param keys_map [::Hash] map of
17
+ # original => final key
18
+ #
19
+ # @return [::Hash] the given hash modified
20
+ #
21
+ # @example
22
+ # hash = { a: 1, 'b' => 2 }
23
+ # changer = Darthjee::CoreExt::Hash::KeyChanger.new(hash)
24
+ # remap_map = { a: 1, 'b' => 2 }
25
+ #
26
+ # changer.remap(remap_map)
27
+ #
28
+ # hash # changed to {
29
+ # # za: 1,
30
+ # # 'yb' => 2,
31
+ # # zb: nil
32
+ # # }
33
+ #
34
+ # @example (see Hash::KeyChangeable#remap_keys)
12
35
  def remap(keys_map)
13
36
  new_hash = {}
14
37
  keys_map.each do |o, n|
@@ -19,20 +42,27 @@ module Darthjee
19
42
 
20
43
  # Change the keys of the given hash returning the new hash
21
44
  #
22
- # @return New hash after keys tranformation
45
+ # @param [::TrueClass,::FalseClass]
46
+ # recursive flag defining
47
+ # the change to happen also on inner hashes
23
48
  #
24
- # @param [::Hash] options options for transformation
25
- # @option options [Boolean] recursive: flag defining
26
- # the change to happen also
27
- # on inner hashes (defaults to: true)
49
+ # @return [::Hash] Given hash after keys tranformation
28
50
  #
29
- # @example (see Hash#change_keys)
30
- def change_keys(options = {}, &block)
31
- options = {
32
- recursive: true
33
- }.merge!(options)
34
-
35
- if options[:recursive]
51
+ # @example (see Hash::KeyChangeable#change_keys)
52
+ #
53
+ # @example
54
+ # hash = { a: 1, 'b' => { c: 3 } }
55
+ # changer = Darthjee::CoreExt::Hash::KeyChanger.new(hash)
56
+ # changer.change_keys { |k| "key_#{k}" }
57
+ #
58
+ # hash # changed to {
59
+ # # 'key_a' => 1,
60
+ # # 'key_b' => {
61
+ # # 'key_c' => 3
62
+ # # }
63
+ # # }
64
+ def change_keys(recursive: true, &block)
65
+ if recursive
36
66
  hash.deep_transform_keys!(&block)
37
67
  else
38
68
  hash.transform_keys!(&block)
@@ -41,35 +71,81 @@ module Darthjee
41
71
 
42
72
  # Performs camelization of the keys of the hash
43
73
  #
44
- # @return [::Hash] the given hash with it's keys changed
45
- # @param [::Hash] options options
46
- # @option options [::Boolean] uppercase_first_letter: flag
74
+ # @param [::Hash] options
75
+ # @param [::TrueClass,::FalseClass]
76
+ # uppercase_first_letter flag
47
77
  # defining the type of CamelCase
78
+ # @option options [::TrueClass,::FalseClass]
79
+ # recursive (true) flag defining
80
+ # the change to happen also on inner hashes
48
81
  #
49
- # @example (see Hash#camelize_keys)
50
- def camelize_keys(options = {})
51
- options = {
52
- uppercase_first_letter: true
53
- }.merge!(options)
54
-
55
- type = options[:uppercase_first_letter] ? :upper : :lower
82
+ # @return [::Hash] the given hash with it's keys
83
+ # changed
84
+ #
85
+ # @see #change_keys
86
+ # @see Cameliazable#camelize_keys
87
+ #
88
+ # @example (see Cameliazable#camelize_keys)
89
+ #
90
+ # @example
91
+ # hash = { my_key: { inner_key: 10 } }
92
+ # changer = Darthjee::CoreExt::Hash::KeyChanger.new(hash)
93
+ # changer.camelize_keys
94
+ # hash # changed to { MyKey: { InnerKey: 10 } }
95
+ def camelize_keys(uppercase_first_letter: true, **options)
96
+ type = uppercase_first_letter ? :upper : :lower
56
97
 
57
- change_keys(options) do |k|
58
- k.camelize(type)
98
+ change_keys(options) do |key|
99
+ key.camelize(type)
59
100
  end
60
101
  end
61
102
 
103
+ # Changes keys by performing underscore transformation
104
+ #
105
+ # @param [::hash] options
106
+ # @option options [::TrueClass,::FalseClass]
107
+ # recursive (true) flag defining
108
+ # the change to happen also on inner hashes
109
+ #
110
+ # @return [::Hash]
111
+ #
112
+ # @example (see Cameliazable#underscore_keys)
113
+ #
114
+ # @example
115
+ # hash = { myKey: { InnerKey: 10 } }
116
+ # changer = Darthjee::CoreExt::Hash::KeyChanger.new(hash)
117
+ # changer.underscore_keys
118
+ #
119
+ # hash # changed to { my_key: { inner_key: 10 } }
62
120
  def underscore_keys(options = {})
63
121
  change_keys(options, &:underscore)
64
122
  end
65
123
 
66
- def change_text(options = {})
67
- options = {
68
- type: :keep
69
- }.merge!(options)
70
-
71
- change_keys(options) do |key|
72
- cast_new_key yield(key), key.class, options
124
+ # Change keys considering them to be strings
125
+ #
126
+ # @param options [::Hash]
127
+ # @param type [::Symbol] type that key will be case
128
+ # - keep: Cast the key back to the same type it was
129
+ # - string cast the key to {String}
130
+ # - symbol cast the key to {Symbol}
131
+ #
132
+ # @option options [::TrueClass,::FalseClass]
133
+ # recursive (true) flag defining
134
+ # the change to happen also on inner hashes
135
+ #
136
+ # @yield (key) key transformation block
137
+ #
138
+ # @return [::Hash] the given hash with changed keys
139
+ #
140
+ # @example
141
+ # hash = { key: { inner_key: 10 } }
142
+ # changer = Darthjee::CoreExt::Hash::KeyChanger.new(hash)
143
+ # changer.change_text { |key| key.to_s.upcase }
144
+ #
145
+ # hash # changed to { KEY: { INNER_KEY: 10 } }
146
+ def change_text(type: :keep, **options)
147
+ change_keys(**options) do |key|
148
+ cast_new_key yield(key), key.class, type
73
149
  end
74
150
  end
75
151
 
@@ -77,8 +153,22 @@ module Darthjee
77
153
 
78
154
  attr_reader :hash
79
155
 
80
- def cast_new_key(key, old_clazz, options)
81
- case class_cast(old_clazz, options)
156
+ # @api private
157
+ # @private
158
+ #
159
+ # Cast key to correct type (String or Symbol)
160
+ #
161
+ # @param key [::String] key to be cast
162
+ # (after transformation)
163
+ # @param old_clazz [::Class] original class of the key
164
+ # @param type [::Symbol] option of type
165
+ # - keep: Cast the key back to the same type it was
166
+ # - string cast the key to {String}
167
+ # - symbol cast the key to {Symbol}
168
+ #
169
+ # @return [::String,::Symbol]
170
+ def cast_new_key(key, old_clazz, type)
171
+ case class_cast(old_clazz, type)
82
172
  when :symbol then
83
173
  key.to_sym
84
174
  when :string then
@@ -86,13 +176,23 @@ module Darthjee
86
176
  end
87
177
  end
88
178
 
89
- def keep_class?(options)
90
- options[:type] == :keep
91
- end
92
-
93
- def class_cast(old_clazz, options)
94
- klass = keep_class?(options) && old_clazz.to_s.downcase.to_sym
95
- klass || options[:type]
179
+ # @api private
180
+ # @private
181
+ #
182
+ # Returns the type of the cast to be applied
183
+ #
184
+ # @param old_clazz [::Class] original class of a key
185
+ # @param type [:symbol] option of castying
186
+ # - keep: Cast the key back to the same type it was
187
+ # - string cast the key to {String}
188
+ # - symbol cast the key to {Symbol}
189
+ #
190
+ # @see #cast_new_key
191
+ #
192
+ # @return [::Symbol]
193
+ def class_cast(old_clazz, type)
194
+ return type unless type == :keep
195
+ old_clazz.to_s.downcase.to_sym
96
196
  end
97
197
  end
98
198
  end
@@ -3,32 +3,68 @@
3
3
  module Darthjee
4
4
  module CoreExt
5
5
  module Hash
6
+ # @api private
7
+ #
8
+ # @author Darthjee
9
+ #
10
+ # Class responsible for sorting keys of a Hash
6
11
  class KeysSorter
12
+ # @param hash [::hash] hash to be sorted
13
+ # @param recursive [::TrueClass,::FalseClass]
14
+ # flag indicating to perform transformation
15
+ # recursively
7
16
  def initialize(hash, recursive: true)
8
17
  @hash = hash
9
18
  @recursive = recursive
10
19
  end
11
20
 
21
+ # Creates a new Hash sorting it's keys
22
+ #
23
+ # @return [::Hash] new hash
24
+ #
25
+ # @example (see KeyChangeable#sort_keys)
26
+ #
27
+ # @example Simple Usage
28
+ # hash = { key: 10, a_key: { z: 5, a: 10 } }
29
+ # sorter = Darthjee::CoreExt::Hash::KeysSorter.new(hash)
30
+ #
31
+ # sorter.sort # changes hash to {
32
+ # # a_key: { a: 10, z: 5 },
33
+ # # key: 10
34
+ # # }
12
35
  def sort
13
- {}.tap do |new_hash|
36
+ hash.tap do
14
37
  sorted_keys.each do |key|
15
- new_hash[key] = change_value(hash[key])
38
+ hash[key] = change_value(hash.delete(key))
16
39
  end
17
40
  end
18
41
  end
19
42
 
20
43
  private
21
44
 
45
+ attr_reader :hash, :recursive
46
+
47
+ # @api private
48
+ # @private
49
+ #
50
+ # Returns all keys sorted
51
+ #
52
+ # @return [::Array<::Object>]
22
53
  def sorted_keys
23
54
  hash.keys.sort
24
55
  end
25
56
 
57
+ # @api private
58
+ # @private
59
+ #
60
+ # Applies recursion when needed
61
+ #
62
+ # @return [::Object]
26
63
  def change_value(value)
27
- return value unless value.is_a?(Hash) && recursive
28
- value.sort_keys(recursive: true)
64
+ return value unless recursive
65
+ return value unless value.is_a?(Hash)
66
+ self.class.new(value).sort
29
67
  end
30
-
31
- attr_reader :hash, :recursive
32
68
  end
33
69
  end
34
70
  end
@@ -3,30 +3,143 @@
3
3
  module Darthjee
4
4
  module CoreExt
5
5
  module Hash
6
- module Squasher
7
- class Builder
8
- attr_reader :key, :value
6
+ # @api private
7
+ #
8
+ # @author Darthjee
9
+ #
10
+ # class responsible for squashing a hash
11
+ #
12
+ # @see Transformable#squash
13
+ # @see Transformable#to_deep_hash
14
+ #
15
+ # @example (see Transformable#squash)
16
+ # @example (see #squash)
17
+ class Squasher
18
+ attr_reader :joiner
9
19
 
10
- def initialize(key, value)
11
- @value = value
12
- @key = key
20
+ # @param joiner [::String] string used to join keys
21
+ def initialize(joiner = '.')
22
+ @joiner = joiner
23
+ end
24
+
25
+ # Squash a hash creating a new hash
26
+ #
27
+ # Squash the hash so that it becomes a single level
28
+ # hash merging the keys of outter and inner hashes
29
+ #
30
+ # @param hash [::Hash] hash to be squashed
31
+ #
32
+ # @return [::Hash]
33
+ #
34
+ # @example Simple usage
35
+ # hash = {
36
+ # person: [{
37
+ # name: %w[John Wick],
38
+ # age: 22
39
+ # }, {
40
+ # name: %w[John Constantine],
41
+ # age: 25
42
+ # }]
43
+ # }
44
+ #
45
+ # squasher = Darthjee::CoreExt::Hash::Squasher.new
46
+ #
47
+ # squasher.squash(hash) # changes hash to {
48
+ # # 'person[0].name[0]' => 'John',
49
+ # # 'person[0].name[1]' => 'Wick',
50
+ # # 'person[0].age' => 22,
51
+ # # 'person[1].name[0]' => 'John',
52
+ # # 'person[1].name[1]' => 'Constantine',
53
+ # # 'person[1].age' => 25
54
+ # # }
55
+ #
56
+ # @example Custom joiner
57
+ # hash = {
58
+ # person: {
59
+ # name: 'John',
60
+ # age: 22
61
+ # }
62
+ # }
63
+ #
64
+ # squasher = Darthjee::CoreExt::Hash::Squasher.new('> ')
65
+ #
66
+ # squasher.squash(hash) # changes hash to {
67
+ # # 'person> name' => 'John',
68
+ # # 'person> age' => 22
69
+ # # }
70
+ def squash(hash)
71
+ hash.keys.each do |key|
72
+ next unless hash[key].is_any?(Hash, Array)
73
+
74
+ value = hash.delete(key)
75
+ add_value_to_hash(hash, key, value)
76
+ end
77
+ hash
78
+ end
79
+
80
+ private
81
+
82
+ # @private
83
+ #
84
+ # Perform squashing on array
85
+ #
86
+ # @param key [::String] key to be prepended on
87
+ # hash keys
88
+ # @param array [::Array] array to be squashed
89
+ #
90
+ # @return [::Hash] hash with indexed keys
91
+ def squash_array(key, array)
92
+ array.map.with_index.inject({}) do |hash, (element, index)|
93
+ new_key = "#{key}[#{index}]"
94
+ add_value_to_hash(hash, new_key, element)
13
95
  end
96
+ end
14
97
 
15
- def to_h
16
- if value.is_a? Hash
17
- value.squash.inject({}) do |hash, (k, v)|
18
- new_key = [key, k].join('.')
19
- hash.merge!(new_key => v)
20
- end
21
- else
22
- { key => value }
23
- end
98
+ # @private
99
+ #
100
+ # Add positioned values to a hash
101
+ #
102
+ # @param hash [::Hash] hash to receive the values
103
+ # @param key [::String] String to be prepended
104
+ #
105
+ # @overload add_value_to_hash(hash, key, sub_hash)
106
+ # @param sub_hash [::Hash] subhash to be squashed
107
+ # key prepended and merged into hash
108
+ #
109
+ # @overload add_value_to_hash(hash, key, array)
110
+ # @param array [::Array] array to be squashed into
111
+ # {::Hash} and merged into hash
112
+ #
113
+ # @overload add_value_to_hash(hash, key, object)
114
+ # @param object [::Object] object to be treated
115
+ # as value
116
+ #
117
+ # @return [::Hash]
118
+ def add_value_to_hash(hash, key, element)
119
+ case element
120
+ when Hash
121
+ value = squash(element)
122
+ hash.merge! prepend_to_keys(key, value)
123
+ when Array
124
+ hash.merge! squash_array(key, element)
125
+ else
126
+ hash.merge!(key => element)
24
127
  end
25
128
  end
26
129
 
27
- def self.squash(origin)
28
- origin.inject({}) do |hash, (key, value)|
29
- hash.merge!(Builder.new(key, value).to_h)
130
+ # @private
131
+ #
132
+ # Appends prefix to all keys of a hash
133
+ #
134
+ # @param prefix [::String] prefix to be prepended
135
+ # @param hash [::Hash] original hash to me changed
136
+ # (already squashed)
137
+ #
138
+ # @return [::Hash] new hash already squashed
139
+ def prepend_to_keys(prefix, hash)
140
+ hash.inject({}) do |subhash, (key, value)|
141
+ new_key = [prefix, key].join(joiner)
142
+ subhash.merge!(new_key => value)
30
143
  end
31
144
  end
32
145
  end