ruby-terraform 1.7.0.pre.7 → 1.7.0.pre.8

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: 40166cca660dc61e95b7b9983f757c4e3c148005226006167adebea38c5922bb
4
- data.tar.gz: 8140497c2e9c0a7acb5bce2967157a47b796bb2e0c716b70477cfac936a55fbd
3
+ metadata.gz: 29d107877da7868db24f108a46175dcc3f72b92baa17453cfd588784ecadbbe5
4
+ data.tar.gz: 4c3f6114288c1428313eb48c08a48d48ef1c824dbdcaf3c1f770c45a694687bd
5
5
  SHA512:
6
- metadata.gz: a6d0e5d37e4a84ed32d97e12af6af490cbfd9d08323cc811e58dcdd7595f6aed067c9f7348a2da1ce591e7c00556c305df3e4492a8644006be864b54f1d8f475
7
- data.tar.gz: ff0ba6c53a93af497e79ec00a75f25baa2667ed86009a475d976ecf15b949c8eded5fde7a4e69b5ae1b7f0c6126df979d945a6b7165cfe479498c2f584171668
6
+ metadata.gz: ef8aa4827093bdd287a3473ba0d61ce2812152812e3f60b8836b4aff306992ca77d5c619da0f591bf5f05c5f8b5a77181bb535e0f10a80adc924cbf8251264b9
7
+ data.tar.gz: 72f79d40b6d54ec9d5ad5998e953633b856778e1d3d565fddcac05ae8a7b0dd299de73a575624342c394f4e3bc39ac16bb2c492ae7717f3ced03bf9962dade25
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ruby-terraform (1.7.0.pre.7)
4
+ ruby-terraform (1.7.0.pre.8)
5
5
  immutable-struct (~> 2.4)
6
6
  lino (~> 3.0)
7
7
 
@@ -115,17 +115,17 @@ GEM
115
115
  diff-lcs (>= 1.2.0, < 2.0)
116
116
  rspec-support (~> 3.11.0)
117
117
  rspec-support (3.11.0)
118
- rubocop (1.31.2)
118
+ rubocop (1.32.0)
119
119
  json (~> 2.3)
120
120
  parallel (~> 1.10)
121
121
  parser (>= 3.1.0.0)
122
122
  rainbow (>= 2.2.2, < 4.0)
123
123
  regexp_parser (>= 1.8, < 3.0)
124
124
  rexml (>= 3.2.5, < 4.0)
125
- rubocop-ast (>= 1.18.0, < 2.0)
125
+ rubocop-ast (>= 1.19.1, < 2.0)
126
126
  ruby-progressbar (~> 1.7)
127
127
  unicode-display_width (>= 1.4.0, < 3.0)
128
- rubocop-ast (1.18.0)
128
+ rubocop-ast (1.19.1)
129
129
  parser (>= 3.1.1.0)
130
130
  rubocop-rake (0.6.0)
131
131
  rubocop (~> 1.0)
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative './values'
4
+ require_relative './path_set'
4
5
 
5
6
  module RubyTerraform
6
7
  module Models
@@ -8,6 +9,7 @@ module RubyTerraform
8
9
  module Objects
9
10
  class << self
10
11
  # rubocop:disable Style/RedundantAssignment
12
+ # rubocop:disable Metrics/MethodLength
11
13
  def box(object, unknown: nil, sensitive: nil)
12
14
  initial = boxed_empty_by_value(object)
13
15
  object = symbolise(object)
@@ -16,135 +18,112 @@ module RubyTerraform
16
18
 
17
19
  return Values.unknown(sensitive: sensitive) if unknown == true
18
20
 
21
+ unless object.is_a?(Hash) || object.is_a?(Array)
22
+ return Values.known(object, sensitive: sensitive)
23
+ end
24
+
19
25
  boxed_unknown =
20
26
  box_unknown(unknown, sensitive: sensitive, initial: initial)
27
+
21
28
  boxed_object =
22
29
  box_known(object, sensitive: sensitive, initial: boxed_unknown)
23
30
 
24
31
  boxed_object
25
32
  end
26
-
33
+ # rubocop:enable Metrics/MethodLength
27
34
  # rubocop:enable Style/RedundantAssignment
28
35
 
29
- def paths(object, current = [], accumulator = [])
30
- normalised = normalise(object)
31
- if normalised.is_a?(Enumerable)
32
- normalised.inject(accumulator) do |a, e|
33
- paths(e[0], current + [e[1]], a)
34
- end
35
- else
36
- accumulator + [current]
37
- end
36
+ def paths(object)
37
+ PathSet.extract_from(object)
38
38
  end
39
39
 
40
- def known_values(paths, object: {}, sensitive: {})
41
- paths.map do |path|
42
- resolved = try_dig(object, path)
43
- resolved_sensitive = try_dig(sensitive, path) == true
40
+ def known_values(path_set, object: {}, sensitive: {})
41
+ path_set.paths.map do |path|
42
+ resolved = path.read(object)
43
+ resolved_sensitive = path.read(sensitive) == true
44
44
 
45
45
  Values.known(resolved, sensitive: resolved_sensitive)
46
46
  end
47
47
  end
48
48
 
49
- def unknown_values(paths, unknown: {}, sensitive: {})
50
- paths.map do |path|
51
- resolved = try_dig(unknown, path)
52
- resolved_sensitive = try_dig(sensitive, path) == true
49
+ def unknown_values(path_set, unknown: {}, sensitive: {})
50
+ path_set.paths.map do |path|
51
+ resolved = path.read(unknown)
52
+ resolved_sensitive = path.read(sensitive) == true
53
53
 
54
54
  resolved ? Values.unknown(sensitive: resolved_sensitive) : nil
55
55
  end
56
56
  end
57
57
 
58
+ def object(path_set, values,
59
+ sensitive: {},
60
+ initial: Values.empty_map,
61
+ filler: Values.omitted)
62
+ gaps = path_set.gaps
63
+ extra_values = gaps.paths.collect { |p| [p, filler] }
64
+
65
+ path_values = path_set.paths.zip(values) + extra_values
66
+ path_values = sort_by_path(path_values)
67
+
68
+ update_all(initial, path_values, sensitive)
69
+ end
70
+
58
71
  private
59
72
 
60
- # rubocop:disable Metrics/MethodLength
61
73
  def box_unknown(unknown, sensitive: {}, initial: Values.empty_map)
62
- unknown_paths = paths(unknown)
63
- if root_path(unknown_paths)
64
- return Values.unknown(sensitive: sensitive)
65
- end
66
-
74
+ path_set = paths(unknown)
67
75
  unknown_values = unknown_values(
68
- unknown_paths, unknown: unknown, sensitive: sensitive
76
+ path_set, unknown: unknown, sensitive: sensitive
69
77
  )
70
-
71
78
  object(
72
- unknown_paths, unknown_values,
73
- sensitive: sensitive, initial: initial
79
+ path_set, unknown_values, sensitive: sensitive, initial: initial
74
80
  )
75
81
  end
76
- # rubocop:enable Metrics/MethodLength
77
82
 
78
- # rubocop:disable Metrics/MethodLength
79
83
  def box_known(object, sensitive: {}, initial: Values.empty_map)
80
- object_paths = paths(object)
81
- if root_path(object_paths)
82
- return Values.known(object, sensitive: sensitive)
83
- end
84
-
84
+ path_set = paths(object)
85
85
  object_values = known_values(
86
- object_paths, object: object, sensitive: sensitive
86
+ path_set, object: object, sensitive: sensitive
87
87
  )
88
-
89
88
  object(
90
- object_paths, object_values,
91
- sensitive: sensitive, initial: initial
89
+ path_set, object_values, sensitive: sensitive, initial: initial
92
90
  )
93
91
  end
94
- # rubocop:enable Metrics/MethodLength
95
92
 
96
- def object(paths, values, sensitive: {}, initial: Values.empty_map)
97
- paths
98
- .zip(values)
99
- .each_with_object(initial) do |path_value, object|
93
+ def update_all(object, path_values, sensitive = {})
94
+ path_values.each_with_object(object) do |path_value, obj|
100
95
  path, value = path_value
101
- update_in(object, path, value, sensitive: sensitive)
96
+ update_in(obj, path, value, sensitive: sensitive)
102
97
  end
103
98
  end
104
99
 
105
100
  def update_in(object, path, value, sensitive: {})
106
- path.inject([[], path.drop(1)]) do |context, step|
107
- seen, remaining = context
108
- pointer = [seen, step, remaining]
109
-
110
- update_object_for_step(object, pointer, value, sensitive: sensitive)
111
- update_context_for_step(pointer)
101
+ path.traverse(object) do |obj, step|
102
+ update_object_for_step(
103
+ obj, step, value, sensitive: sensitive
104
+ )
112
105
  end
113
- object
114
106
  end
115
107
 
116
108
  # rubocop:disable Metrics/MethodLength
117
- def update_object_for_step(object, pointer, value, sensitive: {})
118
- seen, step, remaining = pointer
109
+ def update_object_for_step(object, step, value, sensitive: {})
110
+ parent = step.seen.read(object, default: object)
111
+ upcoming = step.remaining.first
119
112
 
120
- parent = try_dig(object, seen, default: object)
121
- upcoming = remaining.first
122
-
123
- resolved_sensitive = try_dig(sensitive, seen + [step]) == true
113
+ found_sensitive = step.seen.append(step.element).read(sensitive)
114
+ resolved_sensitive = found_sensitive == true
124
115
  resolved =
125
- if remaining.empty?
116
+ if step.remaining.empty?
126
117
  value
127
118
  else
128
119
  boxed_empty_by_key(upcoming, sensitive: resolved_sensitive)
129
120
  end
130
121
 
131
- parent[step] ||= resolved
132
- end
133
- # rubocop:enable Metrics/MethodLength
134
-
135
- def update_context_for_step(pointer)
136
- seen, step, remaining = pointer
137
- [seen + [step], remaining.drop(1)]
138
- end
122
+ parent[step.element] ||= resolved
139
123
 
140
- def try_dig(object, path, default: nil)
141
- return default if path.empty?
142
-
143
- result = object.dig(*path)
144
- result.nil? ? default : result
145
- rescue NoMethodError, TypeError
146
- default
124
+ object
147
125
  end
126
+ # rubocop:enable Metrics/MethodLength
148
127
 
149
128
  def boxed_empty_by_key(key, sensitive: false)
150
129
  if key.is_a?(Numeric)
@@ -169,22 +148,12 @@ module RubyTerraform
169
148
  end
170
149
  end
171
150
 
172
- def normalise(object)
173
- case object
174
- when Array then object.each_with_index.to_a
175
- when Hash
176
- object.to_a.map do |e|
177
- [e[1], e[0].to_sym]
178
- end
179
- else object
180
- end
181
- end
182
-
183
151
  def symbolise(object)
184
152
  case object
185
153
  when Hash
186
154
  object.to_h { |key, value| [key.to_sym, symbolise(value)] }
187
- else object
155
+ else
156
+ object
188
157
  end
189
158
  end
190
159
 
@@ -192,8 +161,8 @@ module RubyTerraform
192
161
  object ? symbolise(object) : native_empty_by_value(target)
193
162
  end
194
163
 
195
- def root_path(paths)
196
- paths.count == 1 && paths[0].empty?
164
+ def sort_by_path(path_values)
165
+ path_values.sort { |a, b| a[0] <=> b[0] }
197
166
  end
198
167
  end
199
168
  end
@@ -0,0 +1,160 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../value_equality'
4
+
5
+ module RubyTerraform
6
+ module Models
7
+ # rubocop:disable Metrics/ClassLength
8
+ class Path
9
+ class << self
10
+ def empty
11
+ new([])
12
+ end
13
+ end
14
+
15
+ extend Forwardable
16
+
17
+ include Comparable
18
+ include ValueEquality
19
+
20
+ def_delegators(:@elements, :first, :last, :length, :empty?)
21
+
22
+ attr_reader(:elements)
23
+
24
+ def initialize(elements)
25
+ @elements = elements.compact
26
+ end
27
+
28
+ def references_any_lists?
29
+ elements.any? { |e| e.is_a?(Numeric) }
30
+ end
31
+
32
+ def references_any_maps?
33
+ elements.any? { |e| e.is_a?(Symbol) }
34
+ end
35
+
36
+ def same_parent_collection?(other)
37
+ return true if self == other
38
+
39
+ left, right = diff(other)
40
+ left.length == 1 && right.length == 1
41
+ end
42
+
43
+ def list_indices
44
+ elements.each_with_index.inject([]) do |acc, element_index|
45
+ element, index = element_index
46
+ element.is_a?(Numeric) ? acc + [[index, element]] : acc
47
+ end
48
+ end
49
+
50
+ def to_location(index)
51
+ return self.class.new([]) if index.negative?
52
+
53
+ self.class.new(elements[0..index])
54
+ end
55
+
56
+ def before_location(index)
57
+ return self.class.new([]) if index.negative?
58
+
59
+ self.class.new(elements[0...index])
60
+ end
61
+
62
+ def append(element)
63
+ self.class.new(elements + [element])
64
+ end
65
+
66
+ def drop(count = 1)
67
+ self.class.new(elements.drop(count))
68
+ end
69
+
70
+ def diff(other)
71
+ left, right = match_lengths(elements, other.elements)
72
+ pairwise = left.zip(right)
73
+ difference = pairwise.drop_while { |e| e[0] == e[1] }
74
+ difference = difference.empty? ? [[], []] : difference.transpose
75
+ difference.map { |e| self.class.new(e) }
76
+ end
77
+
78
+ def traverse(initial, &block)
79
+ initial_context = initial_traversal_context(initial)
80
+ final_context = elements.inject(initial_context) do |context, element|
81
+ state = block.call(context[:state], context[:step])
82
+ next_traversal_context(state, context[:step], element)
83
+ end
84
+
85
+ final_context[:state]
86
+ end
87
+
88
+ def read(object, default: nil)
89
+ return default if empty?
90
+
91
+ result = object.dig(*elements)
92
+ result.nil? ? default : result
93
+ rescue NoMethodError, TypeError
94
+ default
95
+ end
96
+
97
+ def <=>(other)
98
+ return 0 if self == other
99
+
100
+ left, right = diff(other)
101
+ return -1 if left.empty?
102
+ return 1 if right.empty?
103
+
104
+ compare_numbers_before_symbols(left.first, right.first)
105
+ end
106
+
107
+ def state
108
+ [elements]
109
+ end
110
+
111
+ private
112
+
113
+ class TraversalStep
114
+ attr_reader(:seen, :element, :remaining)
115
+
116
+ def initialize(seen, element, remaining)
117
+ @seen = seen
118
+ @element = element
119
+ @remaining = remaining
120
+ end
121
+ end
122
+
123
+ def initial_traversal_context(state)
124
+ {
125
+ state: state,
126
+ step: TraversalStep.new(self.class.empty, first, drop(1))
127
+ }
128
+ end
129
+
130
+ def next_traversal_context(state, position, step)
131
+ {
132
+ state: state,
133
+ step: TraversalStep.new(position.seen.append(step),
134
+ position.remaining.first,
135
+ position.remaining.drop(1))
136
+ }
137
+ end
138
+
139
+ def compare_numbers_before_symbols(left, right)
140
+ return -1 if left.is_a?(Numeric) && right.is_a?(Symbol)
141
+ return 1 if left.is_a?(Symbol) && right.is_a?(Numeric)
142
+
143
+ left <=> right
144
+ end
145
+
146
+ def match_lengths(left, right)
147
+ max_length = [left.count, right.count].max
148
+ [
149
+ pad_to_length(left, max_length),
150
+ pad_to_length(right, max_length)
151
+ ]
152
+ end
153
+
154
+ def pad_to_length(array, target_length)
155
+ array.clone.fill(nil, array.count, target_length - array.count)
156
+ end
157
+ end
158
+ # rubocop:enable Metrics/ClassLength
159
+ end
160
+ end
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../value_equality'
4
+
5
+ module RubyTerraform
6
+ module Models
7
+ class PathSet
8
+ class << self
9
+ def empty
10
+ new([])
11
+ end
12
+
13
+ def extract_from(object)
14
+ empty.add_paths_from(object)
15
+ end
16
+ end
17
+
18
+ extend Forwardable
19
+
20
+ include ValueEquality
21
+
22
+ def_delegators(:@paths, :empty?)
23
+
24
+ attr_reader(:paths)
25
+
26
+ def initialize(paths)
27
+ @paths = paths
28
+ end
29
+
30
+ def add_paths_from(object)
31
+ self.class.new(paths + extract_paths_from(object))
32
+ end
33
+
34
+ def gaps
35
+ initial_context = { last: Path.new([]), complete: [] }
36
+ result = paths.sort.inject(initial_context) do |acc, path|
37
+ current_path = path
38
+ last_path = acc[:last]
39
+ missing_paths = determine_missing_paths(last_path, current_path)
40
+ updated_paths = acc[:complete] + missing_paths
41
+
42
+ { last: current_path, complete: updated_paths }
43
+ end
44
+
45
+ self.class.new(result[:complete])
46
+ end
47
+
48
+ def state
49
+ [paths]
50
+ end
51
+
52
+ private
53
+
54
+ def extract_paths_from(
55
+ object,
56
+ current = Path.new([]),
57
+ accumulator = []
58
+ )
59
+ normalised = normalise(object)
60
+ if normalised.is_a?(Enumerable)
61
+ normalised.inject(accumulator) do |a, e|
62
+ extract_paths_from(e[0], current.append(e[1]), a)
63
+ end
64
+ else
65
+ accumulator + [current]
66
+ end
67
+ end
68
+
69
+ def normalise(object)
70
+ case object
71
+ when Array then object.each_with_index.to_a
72
+ when Hash
73
+ object.to_a.map do |e|
74
+ [e[1], e[0].to_sym]
75
+ end
76
+ else
77
+ object
78
+ end
79
+ end
80
+
81
+ # rubocop:disable Metrics/MethodLength
82
+ def determine_missing_paths(last_path, current_path)
83
+ last_indices = resolve_last_indices(last_path, current_path)
84
+ current_indices = current_path.list_indices
85
+
86
+ current_indices.inject([]) do |acc, current_index|
87
+ current_location, current_element = current_index
88
+ last_index =
89
+ last_indices.find { |index| index[0] == current_location }
90
+ last_element = last_index[1]
91
+
92
+ next(acc) unless current_element.positive?
93
+
94
+ start_element = last_element.nil? ? 0 : last_element + 1
95
+ next(acc) if start_element == current_element
96
+
97
+ acc + create_missing_paths(
98
+ start_element, current_element, current_path, current_location
99
+ )
100
+ end
101
+ end
102
+ # rubocop:enable Metrics/MethodLength
103
+
104
+ # rubocop:disable Metrics/MethodLength
105
+ def resolve_last_indices(last_path, current_path)
106
+ last_indices = last_path.list_indices
107
+ current_indices = current_path.list_indices
108
+
109
+ current_indices.collect do |current_entry|
110
+ location, current_element = current_entry
111
+ last_entry = last_indices.find { |index| index[0] == location }
112
+ last_element = last_entry&.slice(1)
113
+ reset_entry = [location, nil]
114
+
115
+ next(reset_entry) unless last_element
116
+ next(reset_entry) if current_element < last_element
117
+
118
+ current_sub_path = current_path.to_location(location)
119
+ last_sub_path = last_path.to_location(location)
120
+
121
+ unless current_sub_path.same_parent_collection?(last_sub_path)
122
+ next(reset_entry)
123
+ end
124
+
125
+ last_entry
126
+ end
127
+ end
128
+ # rubocop:enable Metrics/MethodLength
129
+
130
+ def create_missing_paths(from, to, path, location)
131
+ (from...to).collect do |element|
132
+ path.before_location(location).append(element)
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RubyTerraform
4
- VERSION = '1.7.0.pre.7'
4
+ VERSION = '1.7.0.pre.8'
5
5
  end
@@ -601,7 +601,7 @@ module RubyTerraform
601
601
  # actions.
602
602
  #
603
603
  # You can optionally save the plan to a file, which you can then pass to
604
- # the {#apply} command to perform exactly the actions described in the plan.
604
+ # the {#read} command to perform exactly the actions described in the plan.
605
605
  #
606
606
  # @param parameters The parameters used to invoke the command
607
607
  # @option parameters [String] :plan The path to a directory containing
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-terraform
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.7.0.pre.7
4
+ version: 1.7.0.pre.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - InfraBlocks Maintainers
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-07-21 00:00:00.000000000 Z
11
+ date: 2022-07-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: immutable-struct
@@ -324,6 +324,8 @@ files:
324
324
  - lib/ruby_terraform/models/map.rb
325
325
  - lib/ruby_terraform/models/objects.rb
326
326
  - lib/ruby_terraform/models/omitted_value.rb
327
+ - lib/ruby_terraform/models/path.rb
328
+ - lib/ruby_terraform/models/path_set.rb
327
329
  - lib/ruby_terraform/models/plan.rb
328
330
  - lib/ruby_terraform/models/resource_change.rb
329
331
  - lib/ruby_terraform/models/unknown_value.rb