cem_acpt 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +7 -0
- data/Gemfile.lock +36 -15
- data/README.md +8 -4
- data/cem_acpt.gemspec +10 -10
- data/exe/cem_acpt +29 -3
- data/lib/cem_acpt/context.rb +132 -39
- data/lib/cem_acpt/core_extensions.rb +9 -12
- data/lib/cem_acpt/logging.rb +177 -19
- data/lib/cem_acpt/platform/base/cmd.rb +8 -2
- data/lib/cem_acpt/platform/gcp/cmd.rb +162 -79
- data/lib/cem_acpt/platform/gcp/compute.rb +6 -1
- data/lib/cem_acpt/platform/gcp.rb +3 -3
- data/lib/cem_acpt/puppet_helpers.rb +1 -0
- data/lib/cem_acpt/rspec_utils.rb +242 -0
- data/lib/cem_acpt/shared_objects.rb +147 -26
- data/lib/cem_acpt/spec_helper_acceptance.rb +21 -13
- data/lib/cem_acpt/test_data.rb +3 -14
- data/lib/cem_acpt/test_runner/run_handler.rb +187 -0
- data/lib/cem_acpt/test_runner/runner.rb +228 -0
- data/lib/cem_acpt/test_runner/runner_result.rb +103 -0
- data/lib/cem_acpt/test_runner.rb +10 -0
- data/lib/cem_acpt/utils.rb +84 -12
- data/lib/cem_acpt/version.rb +1 -1
- data/lib/cem_acpt.rb +18 -11
- metadata +47 -44
- data/.travis.yml +0 -6
- data/lib/cem_acpt/runner.rb +0 -304
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a7ce3a10380b032387479fc85590e5a9ca59c2dc5cec33d71cb9d79eaf6938a7
|
4
|
+
data.tar.gz: d3d12c621ee070af7cab276e6887c45343519c51e9260a1c6e6fd0fc4ca64e80
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d394053e259fb6d502a44d219b8dd056e1777d61a67a1a3da34360288ee6b623eadb842381900fe1d56d4944f0b9823c10c59f136f6ac486a8c0c47722dbfddf
|
7
|
+
data.tar.gz: c73ede8616dc75ab9da65573f5ad3a77c4ec604089183ede90e2a97a9fa2f5699ae012ba157935c0f7bdec9e2744f996d154958b40fde9271c545900f884de77
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,33 +1,39 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
cem_acpt (0.
|
4
|
+
cem_acpt (0.2.0)
|
5
5
|
concurrent-ruby (~> 1.1.9)
|
6
6
|
deep_merge (~> 1.2.2)
|
7
|
-
net-
|
8
|
-
net-ssh (~> 6.1)
|
7
|
+
net-ssh (~> 7.0.0.beta1)
|
9
8
|
puppet-modulebuilder (> 0.0.1)
|
10
|
-
rake
|
11
|
-
rspec
|
12
|
-
serverspec
|
9
|
+
rake
|
10
|
+
rspec
|
11
|
+
serverspec-cem-acpt
|
13
12
|
|
14
13
|
GEM
|
15
14
|
remote: https://rubygems.org/
|
16
15
|
specs:
|
17
|
-
|
16
|
+
ast (2.4.2)
|
17
|
+
concurrent-ruby (1.1.10)
|
18
18
|
deep_merge (1.2.2)
|
19
19
|
diff-lcs (1.5.0)
|
20
20
|
minitar (0.9)
|
21
21
|
multi_json (1.15.0)
|
22
22
|
net-scp (3.0.0)
|
23
23
|
net-ssh (>= 2.6.5, < 7.0.0)
|
24
|
-
net-ssh (
|
24
|
+
net-ssh (7.0.0.beta1)
|
25
25
|
net-telnet (0.1.1)
|
26
|
-
|
26
|
+
parallel (1.22.1)
|
27
|
+
parser (3.1.2.0)
|
28
|
+
ast (~> 2.4.1)
|
29
|
+
pathspec (1.0.0)
|
27
30
|
puppet-modulebuilder (0.3.0)
|
28
31
|
minitar (~> 0.9)
|
29
32
|
pathspec (>= 0.2.1, < 2.0.0)
|
33
|
+
rainbow (3.1.1)
|
30
34
|
rake (12.3.3)
|
35
|
+
regexp_parser (2.5.0)
|
36
|
+
rexml (3.2.5)
|
31
37
|
rspec (3.11.0)
|
32
38
|
rspec-core (~> 3.11.0)
|
33
39
|
rspec-expectations (~> 3.11.0)
|
@@ -40,29 +46,44 @@ GEM
|
|
40
46
|
rspec-its (1.3.0)
|
41
47
|
rspec-core (>= 3.0.0)
|
42
48
|
rspec-expectations (>= 3.0.0)
|
43
|
-
rspec-mocks (3.11.
|
49
|
+
rspec-mocks (3.11.1)
|
44
50
|
diff-lcs (>= 1.2.0, < 2.0)
|
45
51
|
rspec-support (~> 3.11.0)
|
46
52
|
rspec-support (3.11.0)
|
47
|
-
|
53
|
+
rubocop (1.30.1)
|
54
|
+
parallel (~> 1.10)
|
55
|
+
parser (>= 3.1.0.0)
|
56
|
+
rainbow (>= 2.2.2, < 4.0)
|
57
|
+
regexp_parser (>= 1.8, < 3.0)
|
58
|
+
rexml (>= 3.2.5, < 4.0)
|
59
|
+
rubocop-ast (>= 1.18.0, < 2.0)
|
60
|
+
ruby-progressbar (~> 1.7)
|
61
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
62
|
+
rubocop-ast (1.18.0)
|
63
|
+
parser (>= 3.1.1.0)
|
64
|
+
ruby-progressbar (1.11.0)
|
65
|
+
serverspec-cem-acpt (2.42.0)
|
48
66
|
multi_json
|
49
67
|
rspec (~> 3.0)
|
50
68
|
rspec-its
|
51
|
-
specinfra (~> 2.
|
69
|
+
specinfra (~> 2.83.1)
|
52
70
|
sfl (2.3)
|
53
|
-
specinfra (2.
|
71
|
+
specinfra (2.83.2)
|
54
72
|
net-scp
|
55
73
|
net-ssh (>= 2.7)
|
56
74
|
net-telnet (= 0.1.1)
|
57
75
|
sfl
|
76
|
+
unicode-display_width (2.1.0)
|
58
77
|
|
59
78
|
PLATFORMS
|
60
|
-
|
79
|
+
universal-java-17
|
80
|
+
x86_64-darwin-19
|
61
81
|
|
62
82
|
DEPENDENCIES
|
63
83
|
cem_acpt!
|
64
84
|
rake (~> 12.0)
|
65
85
|
rspec (~> 3.0)
|
86
|
+
rubocop
|
66
87
|
|
67
88
|
BUNDLED WITH
|
68
|
-
2.
|
89
|
+
2.3.9
|
data/README.md
CHANGED
@@ -81,6 +81,14 @@ Much like `name_pattern_vars`, specifying the `image_name_builder` top-level key
|
|
81
81
|
|
82
82
|
## Installation
|
83
83
|
|
84
|
+
### Installing from RubyGems
|
85
|
+
|
86
|
+
To install the gem locally to use as a CLI tool:
|
87
|
+
|
88
|
+
`gem install cem_acpt --prerelease`
|
89
|
+
|
90
|
+
### Installing into a Puppet module
|
91
|
+
|
84
92
|
Add this line to your module's Gemfile (usually under `group :development`):
|
85
93
|
|
86
94
|
```ruby
|
@@ -127,10 +135,6 @@ CemAcpt can use a config file (default path is `./cem_acpt_config.yaml`) to conf
|
|
127
135
|
|
128
136
|
With CemAcpt, acceptance test names can have semantic meaning and influence how your acceptance test nodes are provisioned and how test data is created. The semantic meaning of the test names can be configured in the config file.
|
129
137
|
|
130
|
-
#### Test data
|
131
|
-
|
132
|
-
Test data can
|
133
|
-
|
134
138
|
## Development
|
135
139
|
|
136
140
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/cem_acpt.gemspec
CHANGED
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
|
|
11
11
|
spec.summary = 'CEM Acceptance Tests'
|
12
12
|
spec.description = 'Litmus-like library focusing on CEM Acceptance Tests'
|
13
13
|
spec.homepage = 'https://github.com/puppetlabs/cem_acpt'
|
14
|
-
spec.license = '
|
14
|
+
spec.license = 'proprietary'
|
15
15
|
spec.required_ruby_version = Gem::Requirement.new('>= 2.5.0')
|
16
16
|
|
17
17
|
spec.metadata['homepage_uri'] = spec.homepage
|
@@ -20,18 +20,18 @@ Gem::Specification.new do |spec|
|
|
20
20
|
|
21
21
|
# Specify which files should be added to the gem when it is released.
|
22
22
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
23
|
-
spec.files = Dir.chdir(File.expand_path(
|
23
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
24
24
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
25
25
|
end
|
26
26
|
spec.bindir = 'exe'
|
27
27
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
28
28
|
spec.require_paths = ['lib']
|
29
|
-
spec.
|
30
|
-
spec.
|
31
|
-
spec.
|
32
|
-
spec.
|
33
|
-
spec.
|
34
|
-
spec.
|
35
|
-
spec.
|
36
|
-
spec.
|
29
|
+
spec.add_runtime_dependency 'concurrent-ruby', '~> 1.1.9'
|
30
|
+
spec.add_runtime_dependency 'deep_merge', '~> 1.2.2'
|
31
|
+
spec.add_runtime_dependency 'net-ssh', '~> 7.0.0.beta1'
|
32
|
+
spec.add_runtime_dependency 'puppet-modulebuilder', '> 0.0.1'
|
33
|
+
spec.add_runtime_dependency 'rake'
|
34
|
+
spec.add_runtime_dependency 'rspec'
|
35
|
+
spec.add_runtime_dependency 'serverspec-cem-acpt'
|
36
|
+
spec.add_development_dependency 'rubocop'
|
37
37
|
end
|
data/exe/cem_acpt
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#!/usr/bin/env
|
1
|
+
#!/usr/bin/env jruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require 'deep_merge'
|
@@ -40,17 +40,43 @@ parser = OptionParser.new do |opts|
|
|
40
40
|
|
41
41
|
opts.on('-I', '--CI', 'Run in CI mode') do
|
42
42
|
options[:CI] = true
|
43
|
+
options[:log_format] = 'github_action'
|
43
44
|
end
|
44
45
|
|
45
46
|
opts.on('-E', '--no-destroy-nodes', 'Do not destroy nodes') do
|
46
47
|
options[:no_destroy_nodes] = true
|
47
48
|
end
|
49
|
+
|
50
|
+
opts.on('-q', '--quiet', 'Do not log to stdout') do
|
51
|
+
options[:quiet] = true
|
52
|
+
end
|
53
|
+
|
54
|
+
opts.on('-v', '--verbose', 'Enables verbose logging mode') do
|
55
|
+
options[:verbose] = true
|
56
|
+
end
|
57
|
+
|
58
|
+
opts.on('-V', '--version', 'Show the cem_acpt version') do
|
59
|
+
options[:show_version] = true
|
60
|
+
end
|
61
|
+
|
62
|
+
opts.on('-S', '--no-epehemeral-ssh-key', 'Do not generate an ephemeral SSH key for test suites') do
|
63
|
+
options[:no_ephemeral_ssh_key] = true
|
64
|
+
end
|
65
|
+
|
66
|
+
# NOT IMPLEMENTED
|
67
|
+
# opts.on('-R', '--reuse-nodes', 'Reuses test nodes if compatible node inventory exists') do
|
68
|
+
# options[:reuse_nodes] = true
|
69
|
+
# end
|
48
70
|
end
|
49
71
|
|
50
72
|
parser.parse!
|
73
|
+
if options[:show_version]
|
74
|
+
puts CemAcpt::VERSION
|
75
|
+
exit 0
|
76
|
+
end
|
51
77
|
options[:module_dir] = Dir.pwd unless options[:module_dir]
|
52
|
-
options[:platforms] = [
|
53
|
-
if
|
78
|
+
options[:platforms] = %w[gcp vmpooler] unless options[:platform]
|
79
|
+
if (options[:log_level] == 'debug' || options[:verbose]) && !options[:quiet]
|
54
80
|
puts '#################### RUNNING ACCEPTANCE TEST SUITE ####################'
|
55
81
|
puts "Using options from command line: #{options}"
|
56
82
|
puts '#######################################################################'
|
data/lib/cem_acpt/context.rb
CHANGED
@@ -1,60 +1,153 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require_relative 'platform'
|
4
|
+
require_relative 'utils'
|
5
|
+
require_relative 'shared_objects'
|
6
|
+
require_relative 'test_data'
|
7
|
+
require_relative 'test_runner/run_handler'
|
8
|
+
require_relative 'logging'
|
9
|
+
|
4
10
|
module CemAcpt
|
5
11
|
# Context provides the context in which the RunHandler creates and starts Runners.
|
6
12
|
module Context
|
7
|
-
|
8
|
-
require_relative 'shared_objects'
|
9
|
-
require_relative 'test_data'
|
10
|
-
require_relative 'logging'
|
13
|
+
class ContextError < StandardError; end
|
11
14
|
|
12
15
|
class << self
|
13
16
|
include CemAcpt::Logging
|
17
|
+
|
18
|
+
KEY_PATH = File.join([ENV['HOME'], '.ssh', 'acpt_test_key']).freeze
|
19
|
+
KH_PATH = File.join([ENV['HOME'], '.ssh', 'acpt_test_known_hosts']).freeze
|
20
|
+
|
21
|
+
def log(msg, level = :info)
|
22
|
+
real_msg = "CONTEXT: #{msg}"
|
23
|
+
logger.send(level, real_msg)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Builds the Puppet module package
|
27
|
+
# @param opts [Hash] config opts
|
28
|
+
# @return [String] The path to the Puppet module package
|
29
|
+
def build_module_package(opts = {})
|
30
|
+
module_dir = opts[:module_dir] || __dir__
|
31
|
+
pkg_path = CemAcpt::Utils::Puppet.build_module_package(module_dir)
|
32
|
+
log("Module package built at #{pkg_path}")
|
33
|
+
pkg_path
|
34
|
+
end
|
35
|
+
|
36
|
+
# Creates a SSH key and a SSH known hosts file for the acceptance test suite
|
37
|
+
def new_test_ssh_key
|
38
|
+
log('Creating ephemeral SSH key and known hosts file for acceptance test suites...')
|
39
|
+
@ssh_priv_key, @ssh_pub_key = CemAcpt::Utils::SSH.ephemeral_ssh_key
|
40
|
+
@ssh_known_hosts = CemAcpt::Utils::SSH.acpt_known_hosts
|
41
|
+
CemAcpt::Utils::SSH.set_ssh_file_permissions(@ssh_priv_key, @ssh_pub_key, @ssh_known_hosts)
|
42
|
+
log('Successfully created SSH files...')
|
43
|
+
log("SSH private key: #{@ssh_priv_key}", :debug)
|
44
|
+
log("SSH public key: #{@ssh_pub_key}", :debug)
|
45
|
+
log("SSH known hosts: #{@ssh_known_hosts}", :debug)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Deletes acceptance test suite SSH files
|
49
|
+
def clean_test_ssh_key
|
50
|
+
log('Deleting ephemeral ssh keys and acpt_known_hosts if they exist...')
|
51
|
+
[@ssh_priv_key, @ssh_pub_key, @ssh_known_hosts].map { |f| File.delete(f) if File.exist?(f) }
|
52
|
+
end
|
53
|
+
|
54
|
+
# Prints a period to the terminal every 5 seconds in a single line to keep the terminal
|
55
|
+
# alive. This is used when running in CI mode. Does nothing unless the option `:CI` is
|
56
|
+
# `true`, or the environment variables CI or GITHUB_ACTION are set to a truthy value.
|
57
|
+
# @param opts [Hash] config opts
|
58
|
+
def keep_terminal_alive(opts = {})
|
59
|
+
@keep_terminal_alive = if opts[:CI] || ENV['CI'] || ENV['GITHUB_ACTION']
|
60
|
+
CemAcpt::Utils::Terminal.keep_terminal_alive
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def clean_up_test_suite(opts)
|
65
|
+
@ctx.node_inventory.clear!
|
66
|
+
@ctx.node_inventory.clean_local_files
|
67
|
+
clean_test_ssh_key unless opts[:no_ephemeral_ssh_key]
|
68
|
+
@run_handler&.destroy_test_nodes
|
69
|
+
@keep_terminal_alive&.kill
|
70
|
+
end
|
14
71
|
end
|
15
72
|
|
16
|
-
# Creates a context for the RunHandler to create and start Runners.
|
73
|
+
# Creates a context (CemAcpt::Context::Ctx) object for the RunHandler to create and start Runners.
|
17
74
|
# Provides the following objects for the Runners: a config object,
|
18
75
|
# the test data hash, the node inventory, and the local port allocator.
|
19
76
|
# Additionally, it creates the platform-specific node objects for each
|
20
77
|
# test suite in the test data. It then calls the provided block with
|
21
78
|
# the context objects nodes, config, test_data, and node_inventory.
|
22
79
|
# @param config_opts [Hash] the config options
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
logger.debug("
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
logger.debug("Using platform: #{config.get('platform')}")
|
43
|
-
nodes = CemAcpt::Platform.use(config.get('platform'), config, test_data, local_port_allocator)
|
44
|
-
block.call(nodes, config, test_data, node_inventory)
|
45
|
-
elsif config.has?('platforms')
|
46
|
-
config.get('platforms').each do |platform|
|
47
|
-
logger.debug("Using platform: #{platform}")
|
48
|
-
nodes = CemAcpt::Platform.use(platform, config, test_data, local_port_allocator)
|
49
|
-
block.call(nodes, config, test_data, node_inventory)
|
50
|
-
end
|
51
|
-
else
|
52
|
-
raise CemAcpt::Error, 'No platform(s) specified'
|
80
|
+
def self.with(**opts)
|
81
|
+
@opts = opts
|
82
|
+
@start_time = Time.now
|
83
|
+
raise CemAcpt::Context::ContextError, 'CemAcpt::Context.with requires a block' unless block_given?
|
84
|
+
|
85
|
+
config_file = @opts[:config_file] || File.expand_path('./cem_acpt_config.yaml')
|
86
|
+
logger.info("Running acceptance test suite at #{@start_time}")
|
87
|
+
logger.debug("Config opts: #{@opts}")
|
88
|
+
logger.debug("Config file: #{config_file}")
|
89
|
+
logger.info("Using module directory: #{@opts[:module_dir]}")
|
90
|
+
keep_terminal_alive(@opts)
|
91
|
+
Dir.chdir(opts[:module_dir]) do
|
92
|
+
new_test_ssh_key unless @opts[:no_ephemeral_ssh_key]
|
93
|
+
pkg_path = build_module_package(@opts)
|
94
|
+
@ctx = CemAcpt::Context::Ctx.new(opts: @opts, config_file: config_file, module_package_path: pkg_path)
|
95
|
+
logger.debug("Created Ctx object #{@ctx}")
|
96
|
+
@run_handler = CemAcpt::TestRunner::RunHandler.new(@ctx)
|
97
|
+
logger.debug("Created RunHandler object #{@run_handler}")
|
98
|
+
yield @run_handler
|
53
99
|
end
|
100
|
+
@exit_code = @run_handler.exit_code
|
54
101
|
rescue StandardError => e
|
55
|
-
logger.fatal("
|
56
|
-
logger.
|
57
|
-
|
102
|
+
logger.fatal("Acceptance test suite encountered an error: #{e.message}")
|
103
|
+
logger.fatal(e.backtrace.join("\n"))
|
104
|
+
@exit_code = 1
|
105
|
+
ensure
|
106
|
+
clean_up_test_suite(@opts)
|
107
|
+
total_minutes = ((Time.now - @start_time) / 60).round
|
108
|
+
logger.info("Test suite finished in ~#{total_minutes} minutes")
|
109
|
+
@exit_code || 1
|
110
|
+
end
|
111
|
+
|
112
|
+
# Ctx holds the context objects for the RunHandler to create and start Runners.
|
113
|
+
class Ctx
|
114
|
+
attr_reader :config, :test_data, :module_package_path, :node_inventory, :local_port_allocator
|
115
|
+
|
116
|
+
def initialize(opts: nil, config_file: nil, module_package_path: nil)
|
117
|
+
@config = CemAcpt::Config.new.load(opts: opts, config_file: config_file)
|
118
|
+
@test_data = CemAcpt::TestData.acceptance_test_data(@config)
|
119
|
+
@module_package_path = module_package_path
|
120
|
+
@node_inventory = CemAcpt::NodeInventory.new
|
121
|
+
@local_port_allocator = CemAcpt::LocalPortAllocator.new
|
122
|
+
prep_environment
|
123
|
+
end
|
124
|
+
|
125
|
+
def nodes
|
126
|
+
return @nodes if defined?(@nodes)
|
127
|
+
|
128
|
+
raise CemAcpt::Error, 'No platform(s) specified' unless @config.has?('platform') || @config.has?('platforms')
|
129
|
+
|
130
|
+
@nodes = nodes_from_platforms
|
131
|
+
@nodes
|
132
|
+
end
|
133
|
+
|
134
|
+
private
|
135
|
+
|
136
|
+
def prep_environment
|
137
|
+
@node_inventory.clean_local_files
|
138
|
+
end
|
139
|
+
|
140
|
+
def nodes_from_platforms
|
141
|
+
nodes = {}
|
142
|
+
if @config.has?('platform')
|
143
|
+
nodes[@config.get('platform')] = CemAcpt::Platform.use(@config.get('platform'), @config, @test_data, @local_port_allocator)
|
144
|
+
elsif @config.has?('platforms')
|
145
|
+
config.get('platforms').each do |platform|
|
146
|
+
nodes[platform] = CemAcpt::Platform.use(platform, @config, @test_data, @local_port_allocator)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
nodes
|
150
|
+
end
|
58
151
|
end
|
59
152
|
end
|
60
153
|
end
|
@@ -45,14 +45,14 @@ module CemAcpt::CoreExtensions
|
|
45
45
|
# Holds deep_freeze extensions to OpenStruct
|
46
46
|
module OpenStruct
|
47
47
|
def deep_freeze
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
48
|
+
unless deep_frozen?
|
49
|
+
@table.reduce({}) do |h, (key, value)|
|
50
|
+
fkey = key.respond_to?(:deep_freeze) ? key.deep_freeze : key
|
51
|
+
fval = value.respond_to?(:deep_freeze) ? value.deep_freeze : value
|
52
|
+
h.merge(fkey => fval)
|
53
|
+
end.freeze
|
54
|
+
@deep_frozen = true
|
55
|
+
end
|
56
56
|
end
|
57
57
|
|
58
58
|
def deep_frozen?
|
@@ -90,10 +90,7 @@ module CemAcpt::CoreExtensions
|
|
90
90
|
# hash = {a: {b: {c: 1}}}
|
91
91
|
# hash.dot_dig('a.b.c') # => 1
|
92
92
|
def dot_dig(path)
|
93
|
-
|
94
|
-
return result unless result.nil?
|
95
|
-
|
96
|
-
dig(*path.split('.'))
|
93
|
+
dig(*path.split('.').map(&:to_sym)) || dig(*path.split('.'))
|
97
94
|
end
|
98
95
|
|
99
96
|
# Stores a value in a nested Hash using a dot-separated path
|