gooddata 1.3.0-java → 1.3.1-java

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 (74) hide show
  1. checksums.yaml +4 -4
  2. data/.dockerignore +2 -0
  3. data/.document +0 -0
  4. data/.yardopts +0 -0
  5. data/CHANGELOG.md +48 -0
  6. data/CLI.md +0 -0
  7. data/CONTRIBUTING.md +19 -12
  8. data/Dockerfile +37 -0
  9. data/Guardfile +0 -0
  10. data/README.md +1 -1
  11. data/Rakefile +17 -1
  12. data/TODO.md +0 -0
  13. data/bin/run_brick.rb +31 -0
  14. data/dependency_decisions.yml +0 -0
  15. data/gooddata.gemspec +1 -0
  16. data/lib/gooddata/bricks/hello_world_brick.rb +21 -0
  17. data/lib/gooddata/bricks/middleware/gooddata_middleware.rb +12 -0
  18. data/lib/gooddata/bricks/middleware/logger_middleware.rb +12 -0
  19. data/lib/gooddata/bricks/pipeline.rb +12 -0
  20. data/lib/gooddata/exceptions/filter_maqlization.rb +0 -6
  21. data/lib/gooddata/extensions/class.rb +4 -0
  22. data/lib/gooddata/extensions/enumerable.rb +0 -3
  23. data/lib/gooddata/extensions/extensions.rb +0 -3
  24. data/lib/gooddata/extensions/false.rb +8 -16
  25. data/lib/gooddata/extensions/hash.rb +10 -41
  26. data/lib/gooddata/extensions/integer.rb +9 -3
  27. data/lib/gooddata/extensions/nil.rb +5 -13
  28. data/lib/gooddata/extensions/object.rb +0 -11
  29. data/lib/gooddata/extensions/string.rb +11 -5
  30. data/lib/gooddata/extensions/true.rb +8 -16
  31. data/lib/gooddata/helpers/global_helpers.rb +12 -0
  32. data/lib/gooddata/helpers/global_helpers_params.rb +5 -3
  33. data/lib/gooddata/lcm/actions/apply_custom_maql.rb +6 -0
  34. data/lib/gooddata/lcm/actions/base_action.rb +8 -2
  35. data/lib/gooddata/lcm/actions/collect_multiple_projects_column.rb +46 -0
  36. data/lib/gooddata/lcm/actions/collect_users_brick_users.rb +9 -2
  37. data/lib/gooddata/lcm/actions/execute_schedules.rb +0 -2
  38. data/lib/gooddata/lcm/actions/hello_world.rb +1 -1
  39. data/lib/gooddata/lcm/actions/synchronize_cas.rb +6 -0
  40. data/lib/gooddata/lcm/actions/synchronize_ldm.rb +6 -0
  41. data/lib/gooddata/lcm/actions/synchronize_user_filters.rb +70 -107
  42. data/lib/gooddata/lcm/actions/synchronize_users.rb +1 -13
  43. data/lib/gooddata/lcm/brick_logger.rb +26 -0
  44. data/lib/gooddata/lcm/lcm2.rb +46 -7
  45. data/lib/gooddata/lcm/types/base_type.rb +4 -0
  46. data/lib/gooddata/lcm/types/class/class.rb +2 -0
  47. data/lib/gooddata/lcm/types/complex/complex.rb +2 -0
  48. data/lib/gooddata/lcm/types/special/array.rb +2 -0
  49. data/lib/gooddata/mixins/is_folder.rb +0 -0
  50. data/lib/gooddata/mixins/to_json.rb +0 -0
  51. data/lib/gooddata/mixins/uri_getter.rb +0 -0
  52. data/lib/gooddata/models/blueprint/project_blueprint.rb +5 -5
  53. data/lib/gooddata/models/blueprint/to_manifest.rb +0 -2
  54. data/lib/gooddata/models/domain.rb +1 -0
  55. data/lib/gooddata/models/from_wire.rb +0 -2
  56. data/lib/gooddata/models/profile.rb +1 -1
  57. data/lib/gooddata/models/project.rb +5 -0
  58. data/lib/gooddata/models/user_filters/user_filter_builder.rb +49 -32
  59. data/lib/gooddata/models/user_group.rb +3 -0
  60. data/lib/gooddata/rest/README.md +0 -0
  61. data/lib/gooddata/rest/client.rb +4 -4
  62. data/lib/gooddata/rest/object.rb +2 -0
  63. data/lib/gooddata/version.rb +1 -1
  64. data/lib/templates/bricks/brick.rb.erb +0 -0
  65. data/lib/templates/bricks/main.rb.erb +0 -0
  66. data/lib/templates/project/Goodfile.erb +0 -0
  67. data/lib/templates/project/data/commits.csv +0 -0
  68. data/lib/templates/project/data/devs.csv +0 -0
  69. data/lib/templates/project/data/repos.csv +0 -0
  70. data/lib/templates/project/model/model.rb.erb +0 -0
  71. metadata +23 -5
  72. data/lib/gooddata/extensions/big_decimal.rb +0 -17
  73. data/lib/gooddata/extensions/numeric.rb +0 -15
  74. data/lib/gooddata/extensions/symbol.rb +0 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b48905b795b73128a61f84b07810a685a6805b79
4
- data.tar.gz: 3cf38bc8e8d47ac4e30270ff04ec4792fab29f96
3
+ metadata.gz: 22c2a5fb970353f14e3a452d8e1c096b06bf0be1
4
+ data.tar.gz: 80fb1d4e828e36959649b2e08f85a6ab8433fe55
5
5
  SHA512:
6
- metadata.gz: 5adb48ac0a518affbe50f57e693dfc83c3f254caf543c7c32adc552ccef564b9882c207d541067b5682b89b498faa8a201cb91879c90c2fc0068e09099f2be1c
7
- data.tar.gz: 217f13bdd340f4190e614f5bb28be027ae98bd44f7cb9f70cdd6fbb8392bb09a175c897a7dedd705e090469bf78dcf1a2505912aea04e34deefd557a24fea980
6
+ metadata.gz: 962015ddb685114bcc1ef38232a9e3b1bf1ecf9b8bffc72dc100f244df2dfa81a8fe517dd9d74addf4cc90910a349970e06a14ddf478d3e98beb582be7db60b7
7
+ data.tar.gz: '0946bcf5017327cd839e8cf63a1ca269a53e23234e68b5ad7aa27d5d3139a33d58bec13a50338e892d48b4b738f7482b280179866000418fccb94648a505e3a6'
@@ -0,0 +1,2 @@
1
+ .git
2
+ .gooddata
data/.document CHANGED
File without changes
data/.yardopts CHANGED
File without changes
@@ -1,4 +1,52 @@
1
1
  # GoodData Ruby SDK Changelog
2
+ ## 1.3.1
3
+ - FEATURE: TMA-1030 Raise jruby version used in K8s docker image (#1284)
4
+ - Update README.md
5
+ - TRIVIAL: Correct dockerfile maintainer
6
+ - TMA-1033: show reason of filter composition failure (#1282)
7
+ - TMA-483 && TMA-963 Paralel ufb bug final fix
8
+ - TMA-963 && TMA-483: UFB and UB performance (#1234)
9
+ - TMA-1002 fixed broken tests
10
+ - no vcr (#1277)
11
+ - TMA-1005: Automate rotating credentials
12
+ - TMA-925: Optimize polling intervals
13
+ - Add info about running tests to CONTRIBUTING.md (#1262)
14
+ - Fix rubocop issue
15
+ - Add empty lines between licenses and modules
16
+ - SETI-2180 Updated base image namespace
17
+ - FEATURE: TMA-1030 Dockerize LCM bricks
18
+ - FEATURE: TMA-1030 Write brick outputs to files
19
+ - FEATURE: TMA-1030 Add Hello World brick
20
+ - REFACTOR: TMA-1030 Non functional changes
21
+ - BUGFIX: TMA-1040 Add nil result if action fails
22
+ - TEST: TMA-1040 Add tests for "perform" method in LCM2 module
23
+ - Require ActiveSupport where it's needed
24
+ - Revert Array refinement to Enumerable opening
25
+ - Revert class to reopening
26
+ - Use duplicable? from ActiveSupport
27
+ - Remove object.blank? as ActiveSupport already do it
28
+ - Revert Object to reopening
29
+ - Increase the scope of monkey patchs
30
+ - Fixes tests in CI
31
+ - Patch all places that use '.to_b' with all extensions that implements it
32
+ - Isolate Symbol monkeypatch in SymbolExtensions module
33
+ - Code :lipstick: Insert license header in files where it was missing
34
+ - Isolate String monkeypatch in StringExtensions module
35
+ - Add TrueExtensions and FalseExtensions in missing places
36
+ - Isolate Object monkeypatch in ObjectExtensions module
37
+ - Isolate Numeric monkeypatch in NumericExtensions module
38
+ - Isolate BigDecimal monkeypatch in BigDecimalExtensions module
39
+ - Adds Extensions to Globalhelper, it's the only one calls `duplicable?`
40
+ - Isolate Nil monkeypatch in NilExtensions module
41
+ - Isolate Integer monkeypatch in IntegerExtensions module
42
+ - Isolate Hash monkeypatch in HashExtensions module
43
+ - Isolate True/False monkey patchs in respectives modules
44
+ - Is a good practice to explicit the error in rescue block
45
+ - Isolate Enumerable monkey patch in EnumerableExtensions module
46
+ - Isolate Class monkeypatch in ClassExtensions module
47
+ - TMA-927: handle uppercase email inputs
48
+ - TMA-648 tests not deleting ads instances fixed
49
+
2
50
  ## 1.3.0
3
51
  - Add changelog for 1.2.1
4
52
  - Automate bumping version (#1243)
data/CLI.md CHANGED
File without changes
@@ -1,5 +1,24 @@
1
1
  # Contributing
2
2
 
3
+ ## Tests
4
+ ### Unit tests
5
+ `bundle exec rake test:unit`
6
+ ### Integration tests
7
+ Currently only GoodData employees can run integration tests for security reasons.
8
+
9
+ `GD_SPEC_PASSWORD=*** bundle exec rake test:integration`
10
+
11
+ [Integration tests](spec/integration) can be run against different GoodData [environments](spec/environment) or with
12
+ [VCR](https://relishapp.com/vcr/vcr/docs).
13
+
14
+ #### VCR test setup
15
+ When adding new integration test, always set `:vcr` metadata.
16
+ ```ruby
17
+ describe 'New integration test', :vcr
18
+ ```
19
+ The VCR `record` mode can be set via `VCR_RECORD_MODE` environment variable. Set it to `all` to make a new recording.
20
+ Please check the recorded payloads for possible sensitive data before submitting to github.
21
+
3
22
  ## Static analysis
4
23
  We use [Pronto](https://github.com/prontolabs/pronto) to detect code smells using static analysis. Comments are automatically created on pull requests when code smells are found.
5
24
 
@@ -35,16 +54,4 @@ We use [Pronto](https://github.com/prontolabs/pronto) to detect code smells usin
35
54
  [license](/LICENSE).
36
55
  1. Use `GoodData.logger` for logging instead of `puts`.
37
56
 
38
- ## Integration tests
39
- [Integration tests](spec/integration) can be run against different GoodData [environments](spec/environment) or with
40
- [VCR](https://relishapp.com/vcr/vcr/docs).
41
-
42
- ### VCR test setup
43
- When adding new integration test, always set `:vcr` metadata.
44
- ```ruby
45
- describe 'New integration test', :vcr
46
- ```
47
- The VCR `record` mode can be set via `VCR_RECORD_MODE` environment variable. Set it to `all` to make a new recording.
48
- Please check the recorded payloads for possible sensitive data before submitting to github.
49
-
50
57
  _Based on [GitLab's contribution guide](https://github.com/gitlabhq/gitlabhq/blob/master/CONTRIBUTING.md)._
@@ -0,0 +1,37 @@
1
+ FROM harbor.intgdc.com/tools/gdc-java-8-jre:b057b53
2
+
3
+ MAINTAINER LCM <lcm@gooddata.com>
4
+
5
+ LABEL image_name="GDC LCM Bricks"
6
+ LABEL maintainer="LCM <lcm@gooddata.com>"
7
+ LABEL git_repostiory_url="https://github.com/gooddata/gooddata-ruby/"
8
+ LABEL parent_image="harbor.intgdc.com/tools/gdc-java-8-jre:b057b53"
9
+
10
+ # which is required by RVM
11
+ RUN yum install -y curl which \
12
+ && yum clean all \
13
+ && rm -rf /var/cache/yum
14
+
15
+ RUN gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
16
+ RUN curl -sSL https://get.rvm.io | bash -s stable --ruby=jruby-9.1.14
17
+
18
+ # Switch to directory with sources
19
+ WORKDIR /src
20
+
21
+ # login shell is required by rvm
22
+ RUN /bin/bash -l -c ". /usr/local/rvm/scripts/rvm && gem update --system \
23
+ && gem install bundler rake"
24
+
25
+ ARG SOURCE_COMMIT
26
+ ENV GOODDATA_RUBY_COMMIT=$SOURCE_COMMIT
27
+
28
+ ADD ./bin ./bin
29
+ ADD ./lib ./lib
30
+ ADD ./Gemfile .
31
+ ADD ./gooddata.gemspec .
32
+
33
+ RUN /bin/bash -l -c ". /usr/local/rvm/scripts/rvm && bundle install"
34
+
35
+ ENTRYPOINT ["/bin/bash", "-l", "-c"]
36
+
37
+ CMD [ ". /usr/local/rvm/scripts/rvm && bundle exec ./bin/run_brick.rb" ]
data/Guardfile CHANGED
File without changes
data/README.md CHANGED
@@ -78,4 +78,4 @@ For full contributor info see [contributors page](https://github.com/gooddata/go
78
78
 
79
79
  ## Copyright
80
80
 
81
- Copyright (c) 2010 - 2015 GoodData Corporation and Thomas Watson Steen. See LICENSE for details.
81
+ Copyright (c) 2010 - 2018 GoodData Corporation and Thomas Watson Steen. See [LICENSE](/LICENSE) for details.
data/Rakefile CHANGED
@@ -5,10 +5,11 @@ require 'rubygems'
5
5
  require 'bundler/setup'
6
6
  require 'bundler/cli'
7
7
  require 'bundler/gem_tasks'
8
-
8
+ require 'gooddata'
9
9
  require 'rake/testtask'
10
10
  require 'rspec/core/rake_task'
11
11
 
12
+ require 'yaml'
12
13
  require 'yard'
13
14
 
14
15
  require 'rubocop/rake_task'
@@ -224,3 +225,18 @@ namespace :gitflow do
224
225
  system(file_path) || fail('Initializing git-flow failed!')
225
226
  end
226
227
  end
228
+
229
+ namespace :password do
230
+ task :rotate, [:value, :encryption_key] do |_, args|
231
+ key = 'password'
232
+ value = args[:value]
233
+ encryption_key = args[:encryption_key]
234
+ encrypted_value = GoodData::Helpers.encrypt(value, encryption_key).strip
235
+ secrets_path = File.join(File.dirname(__FILE__), 'spec/environment/secrets.yaml')
236
+ secrets = YAML.load_file(secrets_path)
237
+ secrets.each_value do |env|
238
+ env[key].replace(encrypted_value) if env.key?(key)
239
+ end
240
+ File.write(secrets_path, secrets.to_yaml)
241
+ end
242
+ end
data/TODO.md CHANGED
File without changes
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'remote_syslog_logger'
4
+ require_relative '../lib/gooddata'
5
+
6
+ DEFAULT_BRICK = 'hello_world_brick'
7
+
8
+ brick_type = ENV['BRICK_TYPE'] || DEFAULT_BRICK
9
+
10
+ syslog_node = ENV['NODE_NAME']
11
+ log = RemoteSyslogLogger.new(syslog_node, 514, :program => brick_type)
12
+
13
+ log.info "action=#{brick_type}_execution status=start"
14
+
15
+ begin
16
+ brick_pipeline = GoodData::Bricks::Pipeline.send("#{brick_type}_pipeline")
17
+ params_json = ENV['BRICK_PARAMS_JSON']
18
+ params = params_json.nil? ? {} : JSON.parse(params_json)
19
+
20
+ params['gooddata_ruby_commit'] = ENV['GOODDATA_RUBY_COMMIT'] || '<unknown>'
21
+ params['log_directory'] = ENV['LOG_DIRECTORY'] || '/tmp/'
22
+
23
+ @brick_result = brick_pipeline.call(params)
24
+ log.info "action=#{brick_type}_execution status=finished"
25
+ rescue NoMethodError => e
26
+ log.info "action=#{brick_type}_execution status=error Invalid brick type '#{brick_type}', #{e.message}"
27
+ raise e
28
+ rescue => e
29
+ log.info "action=#{brick_type}_execution status=error #{e.message}"
30
+ raise e
31
+ end
File without changes
@@ -60,6 +60,7 @@ Gem::Specification.new do |s|
60
60
  s.add_dependency 'multi_json', '~> 1.12'
61
61
  s.add_dependency 'parseconfig', '~> 1.0'
62
62
  s.add_dependency 'pmap', '~> 1.1'
63
+ s.add_dependency 'remote_syslog_logger', '~> 1.0.3'
63
64
  s.add_dependency 'restforce', '~> 2.4'
64
65
  s.add_dependency 'rest-client', '~> 2.0'
65
66
  s.add_dependency 'rubyzip', '~> 1.2', '>= 1.2.1'
@@ -0,0 +1,21 @@
1
+ require_relative 'brick'
2
+
3
+ module GoodData
4
+ module Bricks
5
+ # Simple brick used for testing and debug purposes
6
+ class HelloWorldBrick < GoodData::Bricks::Brick
7
+ def version
8
+ '0.0.1'
9
+ end
10
+
11
+ # HelloWorld brick entry-point
12
+ #
13
+ # @param [Hash] params Parameters
14
+ # @option [String] 'message' text to be returned in result, if nill - nothing is returned
15
+ # :reek:UtilityFunction
16
+ def call(params)
17
+ GoodData::LCM2.perform('hello_world', params)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -6,6 +6,18 @@
6
6
 
7
7
  require_relative 'base_middleware'
8
8
 
9
+ require 'gooddata/extensions/true'
10
+ require 'gooddata/extensions/false'
11
+ require 'gooddata/extensions/integer'
12
+ require 'gooddata/extensions/string'
13
+ require 'gooddata/extensions/nil'
14
+
15
+ using TrueExtensions
16
+ using FalseExtensions
17
+ using IntegerExtensions
18
+ using StringExtensions
19
+ using NilExtensions
20
+
9
21
  module GoodData
10
22
  module Bricks
11
23
  # Connects to platform and enriches parameters with GoodData::Client
@@ -6,6 +6,18 @@
6
6
 
7
7
  require 'logger'
8
8
 
9
+ require 'gooddata/extensions/true'
10
+ require 'gooddata/extensions/false'
11
+ require 'gooddata/extensions/integer'
12
+ require 'gooddata/extensions/string'
13
+ require 'gooddata/extensions/nil'
14
+
15
+ using TrueExtensions
16
+ using FalseExtensions
17
+ using IntegerExtensions
18
+ using StringExtensions
19
+ using NilExtensions
20
+
9
21
  require_relative 'base_middleware'
10
22
 
11
23
  module GoodData
@@ -9,6 +9,7 @@ require_relative 'user_filters_brick'
9
9
  require_relative 'release_brick'
10
10
  require_relative 'provisioning_brick'
11
11
  require_relative 'rollout_brick'
12
+ require_relative 'hello_world_brick'
12
13
 
13
14
  module GoodData
14
15
  module Bricks
@@ -87,6 +88,17 @@ module GoodData
87
88
  RolloutBrick
88
89
  ])
89
90
  end
91
+
92
+ def self.hello_world_brick_pipeline
93
+ prepare(
94
+ [
95
+ DecodeParamsMiddleware,
96
+ LoggerMiddleware,
97
+ BenchMiddleware,
98
+ HelloWorldBrick
99
+ ]
100
+ )
101
+ end
90
102
  end
91
103
  end
92
104
  end
@@ -6,11 +6,5 @@
6
6
 
7
7
  module GoodData
8
8
  class FilterMaqlizationError < RuntimeError
9
- attr_accessor :errors
10
-
11
- def initialize(errs = [])
12
- super('Filter MAQLization failed')
13
- @errors = errs
14
- end
15
9
  end
16
10
  end
@@ -1,3 +1,7 @@
1
+ # Copyright (c) 2010-2017 GoodData Corporation. All rights reserved.
2
+ # This source code is licensed under the BSD-style license found in the
3
+ # LICENSE file in the root directory of this source tree.
4
+
1
5
  class Class
2
6
  def short_name
3
7
  name.split('::').last
@@ -1,9 +1,6 @@
1
- # encoding: UTF-8
2
- #
3
1
  # Copyright (c) 2010-2017 GoodData Corporation. All rights reserved.
4
2
  # This source code is licensed under the BSD-style license found in the
5
3
  # LICENSE file in the root directory of this source tree.
6
-
7
4
  module Enumerable
8
5
  def mapcat(initial = [], &block)
9
6
  reduce(initial) do |a, e|
@@ -1,9 +1,6 @@
1
- # encoding: UTF-8
2
- #
3
1
  # Copyright (c) 2010-2017 GoodData Corporation. All rights reserved.
4
2
  # This source code is licensed under the BSD-style license found in the
5
3
  # LICENSE file in the root directory of this source tree.
6
-
7
4
  base = Pathname(__FILE__).dirname.expand_path
8
5
  Dir.glob(base + '*.rb').each do |file|
9
6
  require_relative file
@@ -1,23 +1,15 @@
1
- # encoding: UTF-8
2
- #
3
1
  # Copyright (c) 2010-2017 GoodData Corporation. All rights reserved.
4
2
  # This source code is licensed under the BSD-style license found in the
5
3
  # LICENSE file in the root directory of this source tree.
6
4
 
7
- class FalseClass
8
- # +false+ is not duplicable:
9
- #
10
- # false.duplicable? # => false
11
- # false.dup # => TypeError: can't dup FalseClass
12
- def duplicable?
13
- false
14
- end
15
-
16
- def to_b
17
- false
18
- end
5
+ module FalseExtensions
6
+ refine FalseClass do
7
+ def to_b
8
+ false
9
+ end
19
10
 
20
- def to_i
21
- 0
11
+ def to_i
12
+ 0
13
+ end
22
14
  end
23
15
  end
@@ -1,48 +1,17 @@
1
- # encoding: UTF-8
2
- #
3
1
  # Copyright (c) 2010-2017 GoodData Corporation. All rights reserved.
4
2
  # This source code is licensed under the BSD-style license found in the
5
3
  # LICENSE file in the root directory of this source tree.
6
4
 
7
- class Hash
8
- # Return a hash that includes everything but the given keys. This is useful for
9
- # limiting a set of parameters to everything but a few known toggles:
10
- #
11
- # @person.update_attributes(params[:person].except(:admin))
12
- #
13
- # If the receiver responds to +convert_key+, the method is called on each of the
14
- # arguments. This allows +except+ to play nice with hashes with indifferent access
15
- # for instance:
16
- #
17
- # {:a => 1}.with_indifferent_access.except(:a) # => {}
18
- # {:a => 1}.with_indifferent_access.except("a") # => {}
19
- #
20
- def except(*keys)
21
- dup.except!(*keys)
22
- end
23
-
24
- # Replaces the hash without the given keys.
25
- def except!(*keys)
26
- keys.each { |key| delete(key) }
27
- self
28
- end
29
-
30
- def slice(*keys)
31
- keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true)
32
- keys.each_with_object(self.class.new) { |k, hash| hash[k] = self[k] if key?(k) }
33
- end
34
-
35
- def compact
36
- select { |_, value| !value.nil? }
37
- end
38
-
39
- def deep_merge(hash)
40
- hash = hash.to_hash
41
- merge(hash) do |_key, old_val, new_val|
42
- if old_val.is_a?(Hash) && new_val.is_a?(Hash)
43
- old_val.deep_merge(new_val)
44
- else
45
- new_val
5
+ module HashExtensions
6
+ refine Hash do
7
+ def deep_merge(hash)
8
+ hash = hash.to_hash
9
+ merge(hash) do |_key, old_val, new_val|
10
+ if old_val.is_a?(Hash) && new_val.is_a?(Hash)
11
+ old_val.deep_merge(new_val)
12
+ else
13
+ new_val
14
+ end
46
15
  end
47
16
  end
48
17
  end