darthjee-core_ext 1.7.3 → 1.7.4

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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +19 -8
  3. data/.gitignore +1 -0
  4. data/.rubocop.yml +7 -0
  5. data/.rubocop_todo.yml +7 -3
  6. data/Dockerfile +9 -0
  7. data/README.md +5 -6
  8. data/Rakefile +2 -0
  9. data/config/yardstick.rb +13 -0
  10. data/config/yardstick.yml +35 -0
  11. data/core_ext.gemspec +9 -7
  12. data/docker-compose.yml +3 -6
  13. data/lib/darthjee/core_ext/array.rb +2 -2
  14. data/lib/darthjee/core_ext/class.rb +90 -4
  15. data/lib/darthjee/core_ext/enumerable.rb +5 -1
  16. data/lib/darthjee/core_ext/hash.rb +30 -0
  17. data/lib/darthjee/core_ext/hash/cameliazable.rb +91 -0
  18. data/lib/darthjee/core_ext/hash/chain_fetcher.rb +10 -0
  19. data/lib/darthjee/core_ext/hash/key_changeable.rb +85 -53
  20. data/lib/darthjee/core_ext/hash/key_changer.rb +41 -31
  21. data/lib/darthjee/core_ext/hash/value_changer.rb +128 -11
  22. data/lib/darthjee/core_ext/version.rb +1 -1
  23. data/spec/integration/yard/{array → darthjee/core_ext/array}/hash_builder_spec.rb +3 -3
  24. data/spec/integration/yard/{array_spec.rb → darthjee/core_ext/array_spec.rb} +0 -0
  25. data/spec/integration/yard/darthjee/core_ext/class/default_value_spec.rb +143 -0
  26. data/spec/integration/yard/{date → darthjee/core_ext/date}/days_between_spec.rb +6 -6
  27. data/spec/integration/yard/{enumerable_spec.rb → darthjee/core_ext/enumerable_spec.rb} +0 -0
  28. data/spec/integration/yard/darthjee/core_ext/hash/cameliazable_spec.rb +34 -0
  29. data/spec/integration/yard/darthjee/core_ext/hash/chain_fetcher_spec.rb +51 -0
  30. data/spec/integration/yard/darthjee/core_ext/hash/key_changeable_spec.rb +48 -0
  31. data/spec/integration/yard/{hash → darthjee/core_ext/hash}/transformable_spec.rb +7 -3
  32. data/spec/integration/yard/darthjee/core_ext/hash/value_changer_spec.rb +66 -0
  33. data/spec/integration/yard/darthjee/core_ext/hash_spec.rb +42 -0
  34. data/spec/lib/array_spec.rb +27 -17
  35. data/spec/lib/class_spec.rb +108 -7
  36. data/spec/lib/darthjee/core_ext/hash/deep_hash_constructor_spec.rb +1 -1
  37. data/spec/lib/darthjee/core_ext/hash/key_changer_spec.rb +8 -8
  38. data/spec/lib/darthjee/core_ext/hash/keys_sorter_spec.rb +1 -0
  39. data/spec/lib/darthjee/core_ext/hash/to_hash_mapper_spec.rb +1 -0
  40. data/spec/lib/darthjee/core_ext/hash/value_changer_spec.rb +246 -0
  41. data/spec/lib/date_spec.rb +5 -4
  42. data/spec/lib/hash_spec.rb +45 -32
  43. data/spec/lib/math_spec.rb +1 -0
  44. data/spec/lib/numeric_spec.rb +39 -17
  45. data/spec/lib/object_spec.rb +7 -7
  46. data/spec/lib/symbol_spec.rb +2 -2
  47. data/spec/lib/time_spec.rb +5 -4
  48. data/spec/support/models/default_reader_model.rb +8 -0
  49. data/spec/support/models/default_value_model.rb +2 -0
  50. data/spec/support/models/dummy_iterator.rb +15 -0
  51. data/spec/support/models/dummy_transformer.rb +15 -0
  52. data/spec/support/shared_examples/array/array_random.rb +2 -1
  53. data/spec/support/shared_examples/clean.rb +20 -20
  54. data/spec/support/shared_examples/date.rb +18 -13
  55. data/spec/support/shared_examples/hash/chain_fetch.rb +4 -3
  56. data/spec/support/shared_examples/hash/chain_hash_keys_changer.rb +14 -7
  57. data/spec/support/shared_examples/hash/hash_keys_changer.rb +10 -4
  58. data/spec/support/shared_examples/hash/hash_transpose.rb +1 -1
  59. data/spec/support/shared_examples/hash/keys_appender.rb +14 -4
  60. data/spec/support/shared_examples/hash/keys_camelizer.rb +7 -7
  61. data/spec/support/shared_examples/hash/keys_sorter.rb +3 -3
  62. data/spec/support/shared_examples/hash/keys_underscorer.rb +2 -2
  63. data/spec/support/shared_examples/hash/map_to_hash.rb +14 -12
  64. data/spec/support/shared_examples/hash/remap.rb +4 -4
  65. data/spec/support/shared_examples/hash/value_changer.rb +40 -33
  66. metadata +70 -20
  67. data/spec/integration/yard/class/default_value_spec.rb +0 -58
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 75f22a94cfe27dfcc43f9209d7684ee7cef8546c
4
- data.tar.gz: 7077d45122b6498b960f459c5fda5c9965138902
3
+ metadata.gz: 103b4b6d1acc61970210009223f1dafb42ffe566
4
+ data.tar.gz: 6887e4ef498e5e8edaa14c604ac8b79b91f9e709
5
5
  SHA512:
6
- metadata.gz: df8e4a2c6c6a6b27611d9ba1d52785323239ec7df2a9239862a1116db897b4112a875d6d279f1a507c7906615247d88375198a9e921e33d9272c6e76d8e46d4c
7
- data.tar.gz: a71629faafba5302d768e80c556d5deec6249e9a09b14f1a3fd7457e97edf4d14bcd075ca89b89d0c9a2110e2dfcf6e6b86ea3540c43ad334c5e50de1da70b8b
6
+ metadata.gz: 21eed9f42d42a66a273413cdd784b32e3f1b07b9be39e6c34e8dba7162c442ad2cb4b96be93177a4d6b33e87e20365524b4e5d72bccbe27cbc22d1f0943c28db
7
+ data.tar.gz: 6de12c71400aa5f5cbd50b7614df418c50815ddf4f222cfe56b3cc852acbd2a745108e3f56394f065de4fb9c518fe02b0a77ddc02669f626b17f0c77b7b17054
@@ -2,13 +2,24 @@ version: 2
2
2
  jobs:
3
3
  build:
4
4
  docker:
5
- - image: circleci/ruby:2.4.1
5
+ - image: darthjee/circleci_ruby_240:0.1.0
6
6
  steps:
7
7
  - checkout
8
- - run: curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
9
- - run: chmod +x ./cc-test-reporter
10
- - run: ./cc-test-reporter before-build
11
- - run: bundle install
12
- - run: bundle exec rspec
13
- - run: rubocop
14
- - run: ./cc-test-reporter after-build --exit-code $?
8
+ - run:
9
+ name: Prepare Coverage Test Report
10
+ command: cc-test-reporter before-build
11
+ - run:
12
+ name: Bundle Install
13
+ command: bundle install
14
+ - run:
15
+ name: RSpec
16
+ command: bundle exec rspec
17
+ - run:
18
+ name: Rubocop
19
+ command: rubocop
20
+ - run:
21
+ name: Coverage Test Report
22
+ command: cc-test-reporter after-build --exit-code $?
23
+ - run:
24
+ name: Yardstick coverage check
25
+ command: bundle exec rake verify_measurements
data/.gitignore CHANGED
@@ -3,3 +3,4 @@ pkg
3
3
  Gemfile.lock
4
4
  doc
5
5
  .yardoc
6
+ measurement
@@ -1,3 +1,4 @@
1
+ require: rubocop-rspec
1
2
  inherit_from: .rubocop_todo.yml
2
3
 
3
4
  AllCops:
@@ -11,3 +12,9 @@ Metrics/BlockLength:
11
12
  Exclude:
12
13
  - 'spec/**/*_spec.rb'
13
14
  - 'spec/support/shared_*/**/*.rb'
15
+
16
+ RSpec/AlignLeftLetBrace:
17
+ Enabled: true
18
+
19
+ RSpec/NestedGroups:
20
+ Max: 5
@@ -1,12 +1,16 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2018-07-26 20:39:06 +0000 using RuboCop version 0.58.1.
3
+ # on 2019-02-18 18:10:37 +0000 using RuboCop version 0.58.1.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
7
7
  # versions of RuboCop, may require this file to be generated again.
8
8
 
9
- # Offense count: 16
9
+ # Offense count: 3
10
+ # Configuration parameters: AggregateFailuresByDefault.
11
+ RSpec/MultipleExpectations:
12
+ Max: 2
13
+
14
+ # Offense count: 24
10
15
  Style/Documentation:
11
16
  Enabled: false
12
-
@@ -0,0 +1,9 @@
1
+ FROM darthjee/ruby_240:0.2.2
2
+
3
+ USER app
4
+ COPY ./ /home/app/app/
5
+
6
+ RUN gem uninstall bundler
7
+ RUN gem install bundler -v '1.17.3'
8
+ RUN bundle install
9
+
data/README.md CHANGED
@@ -3,9 +3,8 @@ Darthjee/CoreExt
3
3
 
4
4
  ![core_ext](https://raw.githubusercontent.com/darthjee/core_ext/master/mech.jpg)
5
5
 
6
- [![Code Climate](https://codeclimate.com/github/darthjee/core_ext/badges/gpa.svg)](https://codeclimate.com/github/darthjee/core_ext)
7
- [![Test Coverage](https://codeclimate.com/github/darthjee/core_ext/badges/coverage.svg)](https://codeclimate.com/github/darthjee/core_ext/coverage)
8
- [![Issue Count](https://codeclimate.com/github/darthjee/core_ext/badges/issue_count.svg)](https://codeclimate.com/github/darthjee/core_ext)
6
+ [![Maintainability](https://api.codeclimate.com/v1/badges/23f472dcc717ed285737/maintainability)](https://codeclimate.com/github/darthjee/core_ext/maintainability)
7
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/23f472dcc717ed285737/test_coverage)](https://codeclimate.com/github/darthjee/core_ext/test_coverage)
9
8
 
10
9
  Yard Documentation
11
10
  -------------------
@@ -16,17 +15,17 @@ This project adds some new methods to the core ruby classes
16
15
 
17
16
  To use core-ext either intall directly
18
17
 
19
- ```console
18
+ ```shell
20
19
  gem install darthjee-core_ext
21
20
  ```
22
21
 
23
22
  or add it to Gemfile
24
23
 
25
- ```
24
+ ```shell
26
25
  gem 'darthjee-core_ext'
27
26
  ```
28
27
 
29
- ```console
28
+ ```shell
30
29
  bundle install darthjee-core_ext
31
30
  ```
32
31
 
data/Rakefile CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  require 'bundler/gem_tasks'
4
4
  require 'rspec/core/rake_task'
5
+ require 'yardstick/rake/measurement'
6
+ require './config/yardstick'
5
7
 
6
8
  RSpec::Core::RakeTask.new
7
9
 
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yardstick/rake/measurement'
4
+ require 'yardstick/rake/verify'
5
+ require 'yaml'
6
+
7
+ options = YAML.load_file('config/yardstick.yml')
8
+
9
+ Yardstick::Rake::Measurement.new(:yardstick_measure, options) do |measurement|
10
+ measurement.output = 'measurement/report.txt'
11
+ end
12
+
13
+ Yardstick::Rake::Verify.new(:verify_measurements, options)
@@ -0,0 +1,35 @@
1
+ threshold: 65.4
2
+ require_exact_threshold: false
3
+ rules:
4
+ ApiTag::Presence:
5
+ enabled: true
6
+ exclude: []
7
+ ApiTag::Inclusion:
8
+ enabled: true
9
+ exclude: []
10
+ ApiTag::ProtectedMethod:
11
+ enabled: true
12
+ exclude: []
13
+ ApiTag::PrivateMethod:
14
+ enabled: true
15
+ exclude: []
16
+ ExampleTag:
17
+ enabled: true
18
+ exclude: []
19
+ ReturnTag:
20
+ enabled: true
21
+ exclude:
22
+ - Darthjee::CoreExt::Hash::ChainFetcher
23
+ Summary::Presence:
24
+ enabled: true
25
+ exclude:
26
+ - Darthjee::CoreExt::Hash::ChainFetcher
27
+ Summary::Length:
28
+ enabled: true
29
+ exclude: []
30
+ Summary::Delimiter:
31
+ enabled: true
32
+ exclude: []
33
+ Summary::SingleLine:
34
+ enabled: true
35
+ exclude: []
@@ -20,11 +20,13 @@ Gem::Specification.new do |gem|
20
20
 
21
21
  gem.add_runtime_dependency 'activesupport', '>= 5.2.x'
22
22
 
23
- gem.add_development_dependency 'bundler'
24
- gem.add_development_dependency 'pry-nav', '~> 0.2.4'
25
- gem.add_development_dependency 'rake', '>= 12.3.1'
26
- gem.add_development_dependency 'rspec', '>= 3.8'
27
- gem.add_development_dependency 'rubocop', '0.58.1'
28
- gem.add_development_dependency 'simplecov', '~> 0.16.x'
29
- gem.add_development_dependency 'yard', '>= 0.9'
23
+ gem.add_development_dependency 'bundler', '~> 1.17.x'
24
+ gem.add_development_dependency 'pry-nav', '~> 0.2.4'
25
+ gem.add_development_dependency 'rake', '>= 12.3.1'
26
+ gem.add_development_dependency 'rspec', '>= 3.8'
27
+ gem.add_development_dependency 'rubocop', '0.58.1'
28
+ gem.add_development_dependency 'rubocop-rspec', '1.30.0'
29
+ gem.add_development_dependency 'simplecov', '~> 0.16.x'
30
+ gem.add_development_dependency 'yard', '>= 0.9.18'
31
+ gem.add_development_dependency 'yardstick', '>= 0.9.9'
30
32
  end
@@ -1,18 +1,15 @@
1
1
  version: '2'
2
2
  services:
3
3
  base: &base
4
- image: ruby:2.4.0
4
+ build:
5
+ context: ./
5
6
  working_dir: /home/app/core_ext
6
7
  volumes:
7
8
  - .:/home/app/core_ext
8
- - core_ext_gems_2_4_0:/usr/local/bundle
9
9
 
10
10
  #################### CONTAINERS ####################
11
11
 
12
12
  core_ext:
13
13
  <<: *base
14
14
  container_name: core_ext
15
- command: bash -c 'gem install bundler; bundle install; bundle exec rspec'
16
-
17
- volumes:
18
- core_ext_gems_2_4_0:
15
+ command: bash -c 'bundle exec rspec'
@@ -142,7 +142,7 @@ module Darthjee
142
142
  # array.random # might return 10, 20 or 30
143
143
  # array # returns unchanged [10, 20, 30]
144
144
  def random
145
- self[rand(size)]
145
+ self[Random.rand(size)]
146
146
  end
147
147
 
148
148
  # Reeturns a random element of the array removing it from the array
@@ -152,7 +152,7 @@ module Darthjee
152
152
  # array.random! # might return 10, 20 or 30 ... lets say 20
153
153
  # array # returns changed [20, 30]
154
154
  def random!
155
- slice!(rand(size))
155
+ slice!(Random.rand(size))
156
156
  end
157
157
  end
158
158
  end
@@ -48,6 +48,8 @@ module Darthjee
48
48
  # methods to be added
49
49
  # @param [::Object] value default value
50
50
  #
51
+ # @see default_value
52
+ #
51
53
  # @example Defining a default values
52
54
  # class MyClass
53
55
  # default_values :name, :nick_name, 'John'
@@ -57,8 +59,6 @@ module Darthjee
57
59
  # MyClass.new.nick_name # returns 'John'
58
60
  #
59
61
  # @example Comparing value across instances
60
- # # frozen_string_literal: false
61
- #
62
62
  # class MyClass
63
63
  # default_values :name, :nick_name, 'John'
64
64
  # end
@@ -70,8 +70,6 @@ module Darthjee
70
70
  # instance.name.equal?(other.name) # returns true
71
71
  #
72
72
  # @example Comparing value across methods
73
- # # frozen_string_literal: false
74
- #
75
73
  # class MyClass
76
74
  # default_values :name, :nick_name, 'John'
77
75
  # end
@@ -85,6 +83,94 @@ module Darthjee
85
83
  default_value(name, value)
86
84
  end
87
85
  end
86
+
87
+ # @!visibility public
88
+ #
89
+ # Creates a method that will act as reader, but will
90
+ # return a default value when the instance variable
91
+ # was never set
92
+ #
93
+ # @example Defining a default value
94
+ # class Person
95
+ # attr_writer :name
96
+ # default_reader :name, 'John Doe'
97
+ # end
98
+ #
99
+ # model = Person.new
100
+ #
101
+ # model.name # returns 'John Doe'
102
+ #
103
+ # @example Changing the instance value
104
+ #
105
+ # model = Person.new
106
+ # model.name # returns 'John Doe'
107
+ #
108
+ # model.name = 'Joe'
109
+ # model.name # returns 'Joe'
110
+ #
111
+ # model.name = nil
112
+ # model.name # returns nil
113
+ #
114
+ # @example Changing values accros instances
115
+ #
116
+ # model = Person.new
117
+ # model.name # returns 'John Doe'
118
+ #
119
+ # model.name = 'Bob'
120
+ # model.name # returns 'Bob'
121
+ # Person.new.name # returns 'John Doe'
122
+ def default_reader(name, value)
123
+ define_method(name) do
124
+ return value unless instance_variable_defined?("@#{name}")
125
+ instance_variable_get("@#{name}")
126
+ end
127
+ end
128
+
129
+ # @!visibility public
130
+ #
131
+ # Creates methods that will act as readers, but will
132
+ # return a default value when the instance variables
133
+ # ware never set
134
+ #
135
+ # @example Defining default values
136
+ # class Person
137
+ # attr_writer :cars, :houses
138
+ # default_reader :cars, :houses, 'none'
139
+ # end
140
+ #
141
+ # model = Person.new
142
+ #
143
+ # model.cars # returns 'none'
144
+ #
145
+ # @example Changing the instance value
146
+ #
147
+ # model = Person.new
148
+ # model.cars # returns 'none'
149
+ #
150
+ # model.cars = ['volvo']
151
+ # model.cars # returns ['volvo']
152
+ #
153
+ # model.cars = nil
154
+ # model.cars # returns nil
155
+ #
156
+ # @example Changing values accros instances
157
+ #
158
+ # model = Person.new
159
+ # model.cars # returns 'none'
160
+ #
161
+ # model.cars = ['volvo']
162
+ # model.cars # returns ['volvo']
163
+ # Person.new.cars # returns 'none'
164
+ #
165
+ # @example Comparing value across methods
166
+ # model.cars # returns 'none'
167
+ # model.cars.equal?('none') # returns false
168
+ # model.nick_name.equal?(model.houses) # returns true
169
+ def default_readers(*names, value)
170
+ names.each do |name|
171
+ default_reader(name, value)
172
+ end
173
+ end
88
174
  end
89
175
  end
90
176
  end
@@ -1,13 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Enumerable
4
+ # (see #clean!)
5
+ #
6
+ # This method does not change the original
7
+ # enumerable
4
8
  def clean
5
9
  deep_dup.clean!
6
10
  end
7
11
 
8
12
  # Removes any element that is nil or empty
9
13
  #
10
- # @returns [::Enumerable] the enumerable itself
14
+ # @return [::Enumerable] the enumerable itself
11
15
  #
12
16
  # @example cleaning a Hash
13
17
  # hash = {
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'darthjee/core_ext/hash/cameliazable'
3
4
  require 'darthjee/core_ext/hash/key_changeable'
4
5
  require 'darthjee/core_ext/hash/transposeable'
5
6
  require 'darthjee/core_ext/hash/transformable'
@@ -15,6 +16,7 @@ module Darthjee
15
16
  autoload :ValueChanger, "#{PATH}/hash/value_changer"
16
17
  autoload :ToHashMapper, "#{PATH}/hash/to_hash_mapper"
17
18
 
19
+ include Hash::Cameliazable
18
20
  include Hash::KeyChangeable
19
21
  include Hash::Transposeable
20
22
  include Hash::Transformable
@@ -23,6 +25,34 @@ module Darthjee
23
25
  # Fetching methods
24
26
  #########################################
25
27
 
28
+ # Crawls through the hash fetching a key value from inside it
29
+ #
30
+ # this is the equivalent of chaining several calls to fetch method
31
+ #
32
+ # ```
33
+ # hash.chain_fetch(:key1, :key2)
34
+ # hash.fetch(:key1).fetch(:key2)
35
+ # ```
36
+ #
37
+ # @param [::Array<::Object>] keys List of keys to be fetched
38
+ # @param [::Proc] block block to be called in case of key not found
39
+ # @yield (key_not_found, keys_missing) The result of the yield
40
+ # will be the returned value instead of raising KeyError
41
+ #
42
+ # @return Object value fetched
43
+ #
44
+ # @example
45
+ # hash = {
46
+ # a: {
47
+ # b: { c: 1, d: 2 }
48
+ # }
49
+ # }
50
+ #
51
+ # hash.chain_fetch(:a, :b, :c) # returns 1
52
+ # hash.chain_fetch(:a, :c, :d) # raises KeyError
53
+ # hash.chain_fetch(:a, :c, :d) { 10 } # returns 10
54
+ # hash.chain_fetch(:a, :c, :d) { |key, _| key } # returns :c
55
+ # hash.chain_fetch(:a, :c, :d) { |_, missing| missing } # returns [:d]
26
56
  def chain_fetch(*keys, &block)
27
57
  ::Hash::ChainFetcher.new(self, *keys, &block).fetch
28
58
  end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Darthjee
4
+ module CoreExt
5
+ module Hash
6
+ # Module holding methods responsible for camelizing
7
+ # keys of a hash
8
+ #
9
+ # @api public
10
+ module Cameliazable
11
+ # Change keys to CamelCase without changing the
12
+ # original hash
13
+ #
14
+ # @return [::Hash] new hash with changed keys
15
+ # @param [::Hash] options options of camelization
16
+ # @option options [::Boolean] uppercase_first_letter: flag
17
+ # defining the type of CamelCase
18
+ #
19
+ # @see Hash::KeyChanger#camelize_keys
20
+ #
21
+ # @example
22
+ # hash = { first_key: 1, 'second_key' => 2 }
23
+ # hash.camelize_keys # returns {
24
+ # # FirstKey: 1,
25
+ # # 'SecondKey' => 2
26
+ # # }
27
+ #
28
+ # @example
29
+ # hash = { first_key: 1, 'second_key' => 2 }
30
+ # options = { uppercase_first_letter: false }
31
+ # hash.camelize_keys(options) # returns {
32
+ # # firstKey: 1,
33
+ # # 'secondKey' => 2
34
+ # # }
35
+ #
36
+ def camelize_keys(options = {})
37
+ dup.camelize_keys!(options)
38
+ end
39
+
40
+ # Change keys to CamelCase changing the
41
+ # original hash
42
+ #
43
+ # @return [::Hash] new hash with changed keys
44
+ # @param [::Hash] options options of camelization
45
+ # @option options [::Boolean] uppercase_first_letter: flag
46
+ # defining the type of CamelCase
47
+ #
48
+ # @example (see #camelize_keys)
49
+ #
50
+ # @see #camelize_keys
51
+ def camelize_keys!(options = {})
52
+ Hash::KeyChanger.new(self).camelize_keys(options)
53
+ end
54
+
55
+ # Camelize all keys in the hash as `key.camelize(:lower)
56
+ #
57
+ # @return [::Hash] the resulting hash
58
+ #
59
+ # @example
60
+ # hash = { first_key: 1, 'second_key' => 2 }
61
+ # hash.lower_camelize_keys # {
62
+ # # firstKey: 1,
63
+ # # 'secondKey' => 2
64
+ # # }
65
+ #
66
+ def lower_camelize_keys(options = {})
67
+ dup.lower_camelize_keys!(options)
68
+ end
69
+
70
+ # Camelize all keys in the hash
71
+ #
72
+ # @return [::Hash] self after changing the keys
73
+ #
74
+ # @example (see #lower_camelize_keys)
75
+ def lower_camelize_keys!(options = {})
76
+ options = options.merge(uppercase_first_letter: false)
77
+
78
+ camelize_keys!(options)
79
+ end
80
+
81
+ def underscore_keys(options = {})
82
+ dup.underscore_keys!(options)
83
+ end
84
+
85
+ def underscore_keys!(options = {})
86
+ Hash::KeyChanger.new(self).underscore_keys(options)
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end