ood_core 0.17.4 → 0.18.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4c94c6fbf110564ec2cff9d885d2799566a70e1e98759f44fec00fde6eb0cdec
4
- data.tar.gz: 7ed0326c52582dbd8b15a272706e8aa58e36a2be0555a78c65037bb74517d0a6
3
+ metadata.gz: a110306c66de3f349e7a5569cedc3ea02dfedd5dfaa360b352f8689af113f98b
4
+ data.tar.gz: ae608343e63bb98e6383fea71af53943d7136bdf72d8bf46b653f0c19801fcec
5
5
  SHA512:
6
- metadata.gz: b6af308bf4acb767e6c3128ce753714ebcee4f33a17b5114a1196d73ec7df63be5d5007ad985c752329463e2533ed1bbfa8951426a2a035ef08ce9b3704b5984
7
- data.tar.gz: 76b07812da52479c3d5c834c51dcb6c5af328721436474197a77bd0423f5061361d333d233c235da7a699e31ac772104eb65902e74ee25afb4640c6e5adc4add
6
+ metadata.gz: 8a6b9928561a6dba1b84cbb2ac58d389b84e8317589648c483382c166c81982859fb74f68f76297a25319faed06712c6256abdf1c6a5e0175be939aa0392f283
7
+ data.tar.gz: 21396c77e39329f9d7b6112c7900dd7ffa51d695b137d15089c487799ed16e3f74aea1f1dfab9958e2928fb98f49db098f865906c53abf667a8ed64ceda5dc53
data/CHANGELOG.md CHANGED
@@ -7,6 +7,43 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.18.1] - 10-18-2021
11
+
12
+ ### Fixed
13
+
14
+ - Fixed kubernetes initialization in [331](https://github.com/OSC/ood_core/pull/331).
15
+
16
+ ## [0.18.0] - 10-18-2021
17
+
18
+ ### Fixed
19
+
20
+ - Fixed LHA crashing on strange bash output in [322](https://github.com/OSC/ood_core/pull/322).
21
+
22
+ ### Added
23
+
24
+ - All adapters now respond to #{adapter}? methods like slurm?, pbspro?, kubernetes? and so on
25
+ in [326](https://github.com/OSC/ood_core/pull/326).
26
+
27
+ ### Changed
28
+
29
+ - The kubernetes adapter now expects to set context statically in [324](https://github.com/OSC/ood_core/pull/324).
30
+ And can now accept context as a part of it's interface. It will now also always send --context when using OIDC
31
+ and that context defaults to the clustername in [327](https://github.com/OSC/ood_core/pull/327).
32
+ - Removed the activesupport dependency in [329](https://github.com/OSC/ood_core/pull/329).
33
+
34
+ ## [0.17.6] - 8-24-2021
35
+
36
+ ### Added
37
+
38
+ - kubernetes now allows for arbitrary labels to be set in [317](https://github.com/OSC/ood_core/pull/317).
39
+ - kubernetes now allows for limits and requests to be different in [318](https://github.com/OSC/ood_core/pull/318).
40
+
41
+ ## [0.17.5] - 8-20-2021
42
+
43
+ ### Fixed
44
+
45
+ - kubernetes jobs delete without waiting in [314](https://github.com/OSC/ood_core/pull/314).
46
+
10
47
  ## [0.17.4] - 7-29-2021
11
48
 
12
49
  Functionally the same as [0.17.3] but with some CI updates.
@@ -374,7 +411,11 @@ Functionally the same as [0.17.3] but with some CI updates.
374
411
  ### Added
375
412
  - Initial release!
376
413
 
377
- [Unreleased]: https://github.com/OSC/ood_core/compare/v0.17.4...HEAD
414
+ [Unreleased]: https://github.com/OSC/ood_core/compare/v0.18.1...HEAD
415
+ [0.18.1]: https://github.com/OSC/ood_core/compare/v0.18.0...v0.18.1
416
+ [0.18.0]: https://github.com/OSC/ood_core/compare/v0.17.8...v0.18.0
417
+ [0.17.6]: https://github.com/OSC/ood_core/compare/v0.17.5...v0.17.6
418
+ [0.17.5]: https://github.com/OSC/ood_core/compare/v0.17.4...v0.17.5
378
419
  [0.17.4]: https://github.com/OSC/ood_core/compare/v0.17.3...v0.17.4
379
420
  [0.17.3]: https://github.com/OSC/ood_core/compare/v0.17.2...v0.17.3
380
421
  [0.17.2]: https://github.com/OSC/ood_core/compare/v0.17.1...v0.17.2
data/RAILS-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2005-2021 David Heinemeier Hansson
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -63,6 +63,20 @@ module OodCore
63
63
  @errors = c.fetch(:errors, []) .to_a
64
64
  end
65
65
 
66
+
67
+ # programatically define some methods like slurm? or torque?
68
+ Dir.entries("#{__dir__}/job/adapters").select do |f|
69
+ File.file?("#{__dir__}/job/adapters/#{f}") && File.extname(f) == '.rb'
70
+ end.map do |f|
71
+ File.basename(f, '.rb')
72
+ end.reject do |f|
73
+ ['helper', 'drmaa' ].include?(f)
74
+ end.each do |adapter|
75
+ define_method(:"#{adapter}?") do
76
+ job_config.fetch(:adapter, nil) == adapter
77
+ end
78
+ end
79
+
66
80
  # Metadata that provides extra information about this cluster
67
81
  # @return [OpenStruct] the metadata
68
82
  def metadata
@@ -11,15 +11,15 @@ class OodCore::Job::Adapters::Kubernetes::Batch
11
11
  class Error < StandardError; end
12
12
  class NotFoundError < StandardError; end
13
13
 
14
- attr_reader :config_file, :bin, :cluster, :mounts
15
- attr_reader :all_namespaces, :using_context, :helper
14
+ attr_reader :config_file, :bin, :cluster, :context, :mounts
15
+ attr_reader :all_namespaces, :helper
16
16
  attr_reader :username_prefix, :namespace_prefix
17
17
  attr_reader :auto_supplemental_groups
18
18
 
19
19
  def initialize(options = {})
20
20
  options = options.to_h.symbolize_keys
21
21
 
22
- @config_file = options.fetch(:config_file, default_config_file)
22
+ @config_file = options.fetch(:config_file, self.class.default_config_file)
23
23
  @bin = options.fetch(:bin, '/usr/bin/kubectl')
24
24
  @cluster = options.fetch(:cluster, 'open-ondemand')
25
25
  @mounts = options.fetch(:mounts, []).map { |m| m.to_h.symbolize_keys }
@@ -28,15 +28,10 @@ class OodCore::Job::Adapters::Kubernetes::Batch
28
28
  @namespace_prefix = options.fetch(:namespace_prefix, '')
29
29
  @auto_supplemental_groups = options.fetch(:auto_supplemental_groups, false)
30
30
 
31
- @using_context = false
32
- @helper = OodCore::Job::Adapters::Kubernetes::Helper.new
31
+ tmp_ctx = options.fetch(:context, nil)
32
+ @context = tmp_ctx.nil? && oidc_auth?(options.fetch(:auth, {}).symbolize_keys) ? @cluster : tmp_ctx
33
33
 
34
- begin
35
- make_kubectl_config(options)
36
- rescue
37
- # FIXME could use a log here
38
- # means you couldn't 'kubectl set config'
39
- end
34
+ @helper = OodCore::Job::Adapters::Kubernetes::Helper.new
40
35
  end
41
36
 
42
37
  def resource_file(resource_type = 'pod')
@@ -61,10 +56,10 @@ class OodCore::Job::Adapters::Kubernetes::Batch
61
56
  end
62
57
 
63
58
  def info_all(attrs: nil)
64
- cmd = if all_namespaces
65
- "#{base_cmd} get pods -o json --all-namespaces"
59
+ cmd = if @all_namespaces
60
+ "#{base_cmd} -o json get pods --all-namespaces"
66
61
  else
67
- "#{namespaced_cmd} get pods -o json"
62
+ "#{namespaced_cmd} -o json get pods"
68
63
  end
69
64
 
70
65
  output = call(cmd)
@@ -117,6 +112,32 @@ class OodCore::Job::Adapters::Kubernetes::Batch
117
112
  safe_call("delete", "configmap", configmap_name(id))
118
113
  end
119
114
 
115
+ class << self
116
+ def default_config_file
117
+ (ENV['KUBECONFIG'] || "#{Dir.home}/.kube/config")
118
+ end
119
+
120
+ def default_auth
121
+ {
122
+ type: 'managed'
123
+ }.symbolize_keys
124
+ end
125
+
126
+ def default_server
127
+ {
128
+ endpoint: 'https://localhost:8080',
129
+ cert_authority_file: nil
130
+ }.symbolize_keys
131
+ end
132
+
133
+ def configure_kube!(config)
134
+ k = self.new(config)
135
+ # TODO: probably shouldn't be using send here
136
+ k.send(:set_cluster, config.fetch(:server, default_server).to_h.symbolize_keys)
137
+ k.send(:configure_auth, config.fetch(:auth, default_auth).to_h.symbolize_keys)
138
+ end
139
+ end
140
+
120
141
  private
121
142
 
122
143
  def safe_call(verb, resource, id)
@@ -125,7 +146,7 @@ class OodCore::Job::Adapters::Kubernetes::Batch
125
146
  when "get"
126
147
  call_json_output('get', resource, id)
127
148
  when "delete"
128
- call("#{namespaced_cmd} delete #{resource} #{id}")
149
+ call("#{namespaced_cmd} delete #{resource} #{id} --wait=false")
129
150
  end
130
151
  rescue NotFoundError
131
152
  {}
@@ -227,6 +248,7 @@ class OodCore::Job::Adapters::Kubernetes::Batch
227
248
  # and id=my-pod-id
228
249
  def call_json_output(verb, resource, id, stdin: nil)
229
250
  cmd = "#{formatted_ns_cmd} #{verb} #{resource} #{id}"
251
+
230
252
  data = call(cmd, stdin: stdin)
231
253
  data = data.empty? ? '{}' : data
232
254
  json_data = JSON.parse(data, symbolize_names: true)
@@ -250,27 +272,6 @@ class OodCore::Job::Adapters::Kubernetes::Batch
250
272
  "#{namespace_prefix}#{username}"
251
273
  end
252
274
 
253
- def context
254
- cluster
255
- end
256
-
257
- def default_config_file
258
- (ENV['KUBECONFIG'] || "#{Dir.home}/.kube/config")
259
- end
260
-
261
- def default_auth
262
- {
263
- type: 'managaged'
264
- }.symbolize_keys
265
- end
266
-
267
- def default_server
268
- {
269
- endpoint: 'https://localhost:8080',
270
- cert_authority_file: nil
271
- }.symbolize_keys
272
- end
273
-
274
275
  def formatted_ns_cmd
275
276
  "#{namespaced_cmd} -o json"
276
277
  end
@@ -281,7 +282,7 @@ class OodCore::Job::Adapters::Kubernetes::Batch
281
282
 
282
283
  def base_cmd
283
284
  base = "#{bin} --kubeconfig=#{config_file}"
284
- base << " --context=#{context}" if using_context
285
+ base << " --context=#{context}" if context?
285
286
  base
286
287
  end
287
288
 
@@ -309,28 +310,30 @@ class OodCore::Job::Adapters::Kubernetes::Batch
309
310
  nil
310
311
  end
311
312
 
312
- def make_kubectl_config(config)
313
- set_cluster(config.fetch(:server, default_server).to_h.symbolize_keys)
314
- configure_auth(config.fetch(:auth, default_auth).to_h.symbolize_keys)
315
- end
316
-
317
313
  def configure_auth(auth)
318
- type = auth.fetch(:type)
319
- return if managed?(type)
320
-
321
- case type
322
- when 'gke'
314
+ if managed_auth?(auth)
315
+ return
316
+ elsif gke_auth?(auth)
323
317
  set_gke_config(auth)
324
- when 'oidc'
325
- set_context
318
+ elsif oidc_auth?(auth)
319
+ set_context if context?
326
320
  end
327
321
  end
328
322
 
329
- def use_context
330
- @using_context = true
323
+ def context?
324
+ !@context.nil?
325
+ end
326
+
327
+ def gke_auth?(auth = {})
328
+ auth.fetch(:type, nil) == 'gke'
329
+ end
330
+
331
+ def oidc_auth?(auth = {})
332
+ auth.fetch(:type, nil) == 'oidc'
331
333
  end
332
334
 
333
- def managed?(type)
335
+ def managed_auth?(auth = {})
336
+ type = auth.fetch(:type, nil)
334
337
  if type.nil?
335
338
  true # maybe should be false?
336
339
  else
@@ -359,23 +362,24 @@ class OodCore::Job::Adapters::Kubernetes::Batch
359
362
  # gke cluster name can probably can differ from what ood calls the cluster
360
363
  cmd = "gcloud container clusters get-credentials #{locale} #{cluster}"
361
364
  env = { 'KUBECONFIG' => config_file }
362
- call(cmd, env)
365
+ call(cmd, env: env)
363
366
  end
364
367
 
365
368
  def set_context
366
- cmd = "#{base_cmd} config set-context #{cluster}"
369
+ # can't really use base_cmd, bc it may use --context flag
370
+ cmd = "#{bin} --kubeconfig=#{config_file} config set-context #{context}"
367
371
  cmd << " --cluster=#{cluster} --namespace=#{namespace}"
368
372
  cmd << " --user=#{k8s_username}"
369
373
 
370
374
  call(cmd)
371
- use_context
372
375
  end
373
376
 
374
377
  def set_cluster(config)
375
378
  server = config.fetch(:endpoint)
376
379
  cert = config.fetch(:cert_authority_file, nil)
377
380
 
378
- cmd = "#{base_cmd} config set-cluster #{cluster}"
381
+ # shouldn't use context here either
382
+ cmd = "#{bin} --kubeconfig=#{config_file} config set-cluster #{cluster}"
379
383
  cmd << " --server=#{server}"
380
384
  cmd << " --certificate-authority=#{cert}" unless cert.nil?
381
385
 
@@ -4,7 +4,9 @@ class OodCore::Job::Adapters::Kubernetes::Helper
4
4
  require_relative 'k8s_job_info'
5
5
  require 'resolv'
6
6
  require 'base64'
7
- require 'active_support/core_ext/hash'
7
+ require 'ood_core/refinements/hash_extensions'
8
+
9
+ using OodCore::Refinements::HashExtensions
8
10
 
9
11
  class K8sDataError < StandardError; end
10
12
 
@@ -49,14 +51,17 @@ class OodCore::Job::Adapters::Kubernetes::Helper
49
51
  command: parse_command(container[:command]),
50
52
  port: container[:port],
51
53
  env: default_env.merge(env),
52
- memory: container[:memory],
53
- cpu: container[:cpu],
54
+ memory_limit: container[:memory_limit] || container[:memory],
55
+ memory_request: container[:memory_request] || container[:memory],
56
+ cpu_limit: container[:cpu_limit] || container[:cpu],
57
+ cpu_request: container[:cpu_request] || container[:cpu],
54
58
  working_dir: container[:working_dir],
55
59
  restart_policy: container[:restart_policy],
56
60
  image_pull_policy: container[:image_pull_policy],
57
61
  image_pull_secret: container[:image_pull_secret],
58
62
  supplemental_groups: container[:supplemental_groups],
59
63
  startup_probe: container[:startup_probe],
64
+ labels: container[:labels],
60
65
  )
61
66
  end
62
67
 
@@ -55,14 +55,16 @@ module OodCore::Job::Adapters::Kubernetes::Resources
55
55
  end
56
56
 
57
57
  class Container
58
- attr_accessor :name, :image, :command, :port, :env, :memory, :cpu, :working_dir,
58
+ attr_accessor :name, :image, :command, :port, :env, :working_dir,
59
+ :memory_limit, :memory_request, :cpu_limit, :cpu_request,
59
60
  :restart_policy, :image_pull_policy, :image_pull_secret, :supplemental_groups,
60
- :startup_probe
61
+ :startup_probe, :labels
61
62
 
62
63
  def initialize(
63
- name, image, command: [], port: nil, env: {}, memory: "4Gi", cpu: "1",
64
+ name, image, command: [], port: nil, env: {},
65
+ memory_limit: nil, memory_request: nil, cpu_limit: nil, cpu_request: nil,
64
66
  working_dir: "", restart_policy: "Never", image_pull_policy: nil, image_pull_secret: nil, supplemental_groups: [],
65
- startup_probe: {}
67
+ startup_probe: {}, labels: {}
66
68
  )
67
69
  raise ArgumentError, "containers need valid names and images" unless name && image
68
70
 
@@ -71,14 +73,17 @@ module OodCore::Job::Adapters::Kubernetes::Resources
71
73
  @command = command.nil? ? [] : command
72
74
  @port = port&.to_i
73
75
  @env = env.nil? ? {} : env
74
- @memory = memory.nil? ? "4Gi" : memory
75
- @cpu = cpu.nil? ? "1" : cpu
76
+ @memory_limit = memory_limit.nil? ? "4Gi" : memory_limit
77
+ @memory_request = memory_request.nil? ? "4Gi" : memory_request
78
+ @cpu_limit = cpu_limit.nil? ? "1" : cpu_limit
79
+ @cpu_request = cpu_request.nil? ? "1" : cpu_request
76
80
  @working_dir = working_dir.nil? ? "" : working_dir
77
81
  @restart_policy = restart_policy.nil? ? "Never" : restart_policy
78
82
  @image_pull_policy = image_pull_policy.nil? ? "IfNotPresent" : image_pull_policy
79
83
  @image_pull_secret = image_pull_secret
80
84
  @supplemental_groups = supplemental_groups.nil? ? [] : supplemental_groups
81
85
  @startup_probe = TCPProbe.new(@port, startup_probe)
86
+ @labels = labels.nil? ? {} : labels
82
87
  end
83
88
 
84
89
  def ==(other)
@@ -87,14 +92,17 @@ module OodCore::Job::Adapters::Kubernetes::Resources
87
92
  command == other.command &&
88
93
  port == other.port &&
89
94
  env == other.env &&
90
- memory == other.memory &&
91
- cpu == other.cpu &&
95
+ memory_limit == other.memory_limit &&
96
+ memory_request == other.memory_request &&
97
+ cpu_limit == other.cpu_limit &&
98
+ cpu_request == other.cpu_request &&
92
99
  working_dir == other.working_dir &&
93
100
  restart_policy == other.restart_policy &&
94
101
  image_pull_policy == other.image_pull_policy &&
95
102
  image_pull_secret == other.image_pull_secret &&
96
103
  supplemental_groups == other.supplemental_groups &&
97
- startup_probe.to_h == other.startup_probe.to_h
104
+ startup_probe.to_h == other.startup_probe.to_h &&
105
+ labels.to_h == other.labels.to_h
98
106
  end
99
107
  end
100
108
 
@@ -10,6 +10,9 @@ metadata:
10
10
  <%- if !script.accounting_id.nil? && script.accounting_id != "" -%>
11
11
  account: <%= script.accounting_id %>
12
12
  <%- end -%>
13
+ <%- spec.container.labels.each_pair do |key, value| -%>
14
+ <%= key %>: "<%= value %>"
15
+ <%- end -%>
13
16
  annotations:
14
17
  <%- unless script.wall_time.nil? -%>
15
18
  pod.kubernetes.io/lifetime: <%= helper.seconds_to_duration(script.wall_time) %>
@@ -88,14 +91,14 @@ spec:
88
91
  <%- end # configmap mounts? and all_mounts not empty -%>
89
92
  resources:
90
93
  limits:
91
- memory: "<%= spec.container.memory %>"
92
- cpu: "<%= spec.container.cpu %>"
94
+ memory: "<%= spec.container.memory_limit %>"
95
+ cpu: "<%= spec.container.cpu_limit %>"
93
96
  <%- unless script.gpus_per_node.nil? -%>
94
97
  <%= gpu_type %>: <%= script.gpus_per_node %>
95
98
  <%- end -%>
96
99
  requests:
97
- memory: "<%= spec.container.memory %>"
98
- cpu: "<%= spec.container.cpu %>"
100
+ memory: "<%= spec.container.memory_request %>"
101
+ cpu: "<%= spec.container.cpu_request %>"
99
102
  <%- unless script.gpus_per_node.nil? -%>
100
103
  <%= gpu_type %>: <%= script.gpus_per_node %>
101
104
  <%- end -%>
@@ -61,7 +61,7 @@ class OodCore::Job::Adapters::LinuxHost::Launcher
61
61
 
62
62
  session_name = unique_session_name
63
63
  output = call(*cmd, stdin: wrapped_script(script, session_name))
64
- hostname = output.strip
64
+ hostname = parse_hostname(output)
65
65
 
66
66
  "#{session_name}@#{hostname}"
67
67
  end
@@ -242,22 +242,20 @@ class OodCore::Job::Adapters::LinuxHost::Launcher
242
242
  def list_remote_tmux_session(destination_host)
243
243
  # Note that the tmux variable substitution looks like Ruby string sub,
244
244
  # these must either be single quoted strings or Ruby-string escaped as well
245
- format_str = Shellwords.escape(
246
- ['#{session_name}', '#{session_created}', '#{pane_pid}'].join(UNIT_SEPARATOR)
247
- )
245
+ format_str = Shellwords.escape(['#{session_name}', '#{session_created}', '#{pane_pid}'].join(UNIT_SEPARATOR))
248
246
  keys = [:session_name, :session_created, :session_pid]
249
247
  cmd = ssh_cmd(destination_host, ['tmux', 'list-panes', '-aF', format_str])
250
-
251
- call(*cmd).split(
252
- "\n"
253
- ).map do |line|
248
+
249
+ call(*cmd).split("\n").map do |line|
254
250
  Hash[keys.zip(line.split(UNIT_SEPARATOR))].tap do |session_hash|
255
251
  session_hash[:destination_host] = destination_host
256
252
  session_hash[:id] = "#{session_hash[:session_name]}@#{destination_host}"
257
253
  end
258
- end.select{
259
- |session_hash| session_hash[:session_name].start_with?(session_name_label)
260
- }
254
+ end.select do |session_hash|
255
+ session_hash.compact.length >= 5 &&
256
+ !session_hash[:session_name].nil? &&
257
+ session_hash[:session_name].start_with?(session_name_label)
258
+ end
261
259
  rescue Error => e
262
260
  interpret_and_raise(e)
263
261
  []
@@ -287,4 +285,10 @@ class OodCore::Job::Adapters::LinuxHost::Launcher
287
285
  raise error
288
286
  end
289
287
  end
288
+
289
+ def parse_hostname(output)
290
+ output.split($/).map do |line|
291
+ line.match(/^(([\w+]|[a-zA-Z0-9][\w*-]*\.))*$/)
292
+ end.compact.last.to_s
293
+ end
290
294
  end
@@ -13,6 +13,7 @@ if [ -z "$hostname" ]; then
13
13
  exit 1
14
14
  fi
15
15
 
16
+ echo ""
16
17
  echo $hostname
17
18
 
18
19
  # Put the script into a temp file on localhost
@@ -2,6 +2,8 @@ module OodCore
2
2
  # Namespace for Ruby refinements
3
3
  module Refinements
4
4
  # This module provides refinements for manipulating the Ruby {Hash} class.
5
+ # Some elements have been taken from Rails (https://github.com/rails/rails)
6
+ # and it's LICENSE has been added as RAILS-LICENSE in the root directory of this project.
5
7
  module HashExtensions
6
8
  refine Hash do
7
9
  # Symbolize the keys in a {Hash}
@@ -28,6 +30,37 @@ module OodCore
28
30
  def compact
29
31
  self.select { |_, value| !value.nil? }
30
32
  end
33
+
34
+ # Returns a new hash with +self+ and +other_hash+ merged recursively.
35
+ #
36
+ # h1 = { a: true, b: { c: [1, 2, 3] } }
37
+ # h2 = { a: false, b: { x: [3, 4, 5] } }
38
+ #
39
+ # h1.deep_merge(h2) # => { a: false, b: { c: [1, 2, 3], x: [3, 4, 5] } }
40
+ #
41
+ # Like with Hash#merge in the standard library, a block can be provided
42
+ # to merge values:
43
+ #
44
+ # h1 = { a: 100, b: 200, c: { c1: 100 } }
45
+ # h2 = { b: 250, c: { c1: 200 } }
46
+ # h1.deep_merge(h2) { |key, this_val, other_val| this_val + other_val }
47
+ # # => { a: 100, b: 450, c: { c1: 300 } }
48
+ def deep_merge(other_hash, &block)
49
+ dup.deep_merge!(other_hash, &block)
50
+ end
51
+
52
+ # Same as +deep_merge+, but modifies +self+.
53
+ def deep_merge!(other_hash, &block)
54
+ merge!(other_hash) do |key, this_val, other_val|
55
+ if this_val.is_a?(Hash) && other_val.is_a?(Hash)
56
+ this_val.deep_merge(other_val, &block)
57
+ elsif block_given?
58
+ block.call(key, this_val, other_val)
59
+ else
60
+ other_val
61
+ end
62
+ end
63
+ end
31
64
  end
32
65
  end
33
66
  end
@@ -1,4 +1,4 @@
1
1
  module OodCore
2
2
  # The current version of {OodCore}
3
- VERSION = "0.17.4"
3
+ VERSION = "0.18.1"
4
4
  end
data/ood_core.gemspec CHANGED
@@ -25,7 +25,6 @@ Gem::Specification.new do |spec|
25
25
  spec.add_runtime_dependency "ood_support", "~> 0.0.2"
26
26
  spec.add_runtime_dependency "ffi", "~> 1.9", ">= 1.9.6"
27
27
  spec.add_development_dependency "bundler", "~> 2.1"
28
- spec.add_runtime_dependency "activesupport", ">= 5.2", "< 6.0"
29
28
  spec.add_development_dependency "rake", "~> 13.0.1"
30
29
  spec.add_development_dependency "rspec", "~> 3.0"
31
30
  spec.add_development_dependency "pry", "~> 0.10"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ood_core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.17.4
4
+ version: 0.18.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Franz
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2021-07-29 00:00:00.000000000 Z
13
+ date: 2021-10-18 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: ood_support
@@ -60,26 +60,6 @@ dependencies:
60
60
  - - "~>"
61
61
  - !ruby/object:Gem::Version
62
62
  version: '2.1'
63
- - !ruby/object:Gem::Dependency
64
- name: activesupport
65
- requirement: !ruby/object:Gem::Requirement
66
- requirements:
67
- - - ">="
68
- - !ruby/object:Gem::Version
69
- version: '5.2'
70
- - - "<"
71
- - !ruby/object:Gem::Version
72
- version: '6.0'
73
- type: :runtime
74
- prerelease: false
75
- version_requirements: !ruby/object:Gem::Requirement
76
- requirements:
77
- - - ">="
78
- - !ruby/object:Gem::Version
79
- version: '5.2'
80
- - - "<"
81
- - !ruby/object:Gem::Version
82
- version: '6.0'
83
63
  - !ruby/object:Gem::Dependency
84
64
  name: rake
85
65
  requirement: !ruby/object:Gem::Requirement
@@ -165,6 +145,7 @@ files:
165
145
  - CHANGELOG.md
166
146
  - Gemfile
167
147
  - LICENSE.txt
148
+ - RAILS-LICENSE
168
149
  - README.md
169
150
  - Rakefile
170
151
  - bin/console