sparkle_formation 2.0.2 → 2.1.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cbb4f29d2f9a0a89eab9ce978f32c653324714e5
4
- data.tar.gz: 8cd5b31636c808b98590f80f94611d1bad9b1095
3
+ metadata.gz: 7ed2f1e3bec855db611f4a8fcd7c3e3c509aa209
4
+ data.tar.gz: d6dc132ef582d52c55080c4a4e80d60fbb28c702
5
5
  SHA512:
6
- metadata.gz: 19eeadefa0b8119e8fe5dd9422950c5a18a707edad34ea4b7e867f322570ab1c5e12aea13e0a04f4cc1c1ac5640fe3030b89096a87fbd6b2ec247cca92ff7881
7
- data.tar.gz: c6a22c34f4f5de2cb35e039d538a7880205fbe3882c470e26c136713ebf6f5cbc4bbaa4789497230960dee1dc74d69bd7af83d380d3045477cb5f96791171e5b
6
+ metadata.gz: 09bd692653d5f7816a332fc2d7f772760bac7e13e34e8314518f99ec48604146a5c8bdf5ce3a321a4599307dcbf6b5673800979a9497c75e708722772882c4c0
7
+ data.tar.gz: e4958183360a572b03aff5ebeab2a9c2ea6db984f8db91bb146b0d26b59665290096a43a0d05e425ecdcfd6c096bb080205a25c3e44f0ce798f34632a9affe4e
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ # v2.1.0
2
+ * Add template inheritance support (#145)
3
+ * Add layer merging support for templates, components, and dynamics (#145)
4
+ * Update allowed Ruby constraint to require version greater than or equal to 2.1
5
+
1
6
  # v2.0.2
2
7
  * Provide useful return value from `#dynamic!` (#137)
3
8
  * Implement proper Azure resource generator (#141)
@@ -205,7 +205,7 @@ the `dynamic!` call:
205
205
  ~~~ruby
206
206
  SparkleFormation.new(:with_namespace) do
207
207
  dynamic!(:aws_opsworks_instance, :foobar,
208
- :resource_suffix_name => :instance
208
+ :resource_name_suffix => :instance
209
209
  )
210
210
  end
211
211
  ~~~
@@ -0,0 +1,159 @@
1
+ ---
2
+ title: "Inheritance and Merging"
3
+ category: "dsl"
4
+ weight: 8
5
+ anchors:
6
+ - title: "Template Inheritance"
7
+ url: "#template-inheritance"
8
+ - title: "Template Merging"
9
+ url: "#template-merging"
10
+ - title: "Component and Dynamic Merging"
11
+ url: "#component-and-dynamic-merging"
12
+ - title: "Registry Items"
13
+ url: "#registry-items"
14
+ ---
15
+
16
+ ## Inheritance and Merging
17
+
18
+ SparkleFormation includes functionality allowing SparklePacks to
19
+ interact with previous layers when a file is requested from the
20
+ collection. This functionality makes it easy to slightly modify
21
+ files provided within previously registered SparklePacks without
22
+ having to recreate the entire contents of the file. It also allows
23
+ for creation of new templates based on existing templates.
24
+
25
+ ### Template Inheritance
26
+
27
+ Template inheritance allows a new template to inherit the data structures
28
+ of an existing template. For example, lets start with a simple template:
29
+
30
+ ~~~ruby
31
+ SparkleFormation.new(:simple) do
32
+ simple_template true
33
+ end
34
+ ~~~
35
+
36
+ The dumped result of this template is:
37
+
38
+ ~~~json
39
+ {
40
+ "SimpleTemplate": true
41
+ }
42
+ ~~~
43
+
44
+ Suppose that we wanted create a new template that was composed of everything
45
+ described in the `simple` template, but had an extra addition. Instead of
46
+ re-creating the contents in a new file, or attempting to break out components
47
+ or dynamics that may not be ideal, we can inherit the template instead:
48
+
49
+ ~~~ruby
50
+ SparkleFormation.new(:advanced, :inherit => :simple) do
51
+ advanced.item 'added'
52
+ end
53
+ ~~~
54
+
55
+ When this template is dumped, it will inherit the `simple` template and
56
+ add on to the resultant data structure giving the result:
57
+
58
+ ~~~json
59
+ {
60
+ "SimpleTemplate": true,
61
+ "Advanced": {
62
+ "Item": "added"
63
+ }
64
+ }
65
+ ~~~
66
+
67
+ Inheritance is not restricted to templates at a common level. A new
68
+ template can inherit templates provided by SparklePacks.
69
+
70
+ ### Template Merging
71
+
72
+ Template merging is only applicable when SparklePacks are in use. Template
73
+ merging allows direct modification of a specific named template. This strategy
74
+ is extremely useful when packs may be layering functionality into a template,
75
+ or the root pack wants to make a specific adjustment without creating a new
76
+ template.
77
+
78
+ The default behavior of SparkleFormation when a SparklePack provides a template with
79
+ the same name as a previously loaded SparklePack, it will overwrite the original
80
+ template. In some cases, this may not be the desired effect. If a pack would like
81
+ to make a simple adjustment to the template, this would require duplicating the
82
+ contents of the original template and appending the customization. Merging allows
83
+ adding customization without recreation.
84
+
85
+ Lets assume we have a core SparklePack loaded and it provides us with the `simple`
86
+ template:
87
+
88
+ ~~~ruby
89
+ SparkleFormation.new(:simple) do
90
+ simple_template true
91
+ end
92
+ ~~~
93
+
94
+ Now in our root SparklePack we want to make an adjustment to the `simple` template
95
+ directly. We can do this by merging:
96
+
97
+ ~~~ruby
98
+ SparkleFormation.new(:simple, :layering => :merge) do
99
+ modifier 'spox'
100
+ end
101
+ ~~~
102
+
103
+ When we dump the `simple` template now we get:
104
+
105
+ ~~~json
106
+ {
107
+ "SimpleTemplate": true,
108
+ "Modifier": "spox"
109
+ }
110
+ ~~~
111
+
112
+ This sort of merging make two strategies possible:
113
+
114
+ 1. Modifying the final result of a template for a specific need
115
+ 2. Modifying at serveral layers to allow SparklePacks to be additive
116
+
117
+ ### Component and Dynamic Merging
118
+
119
+ Merging behavior for components and dynamics follows the same pattern as templates.
120
+ Adding the `:layering => :merge` option will cause the item to be merged instead
121
+ of overwritten.
122
+
123
+ For components:
124
+
125
+ ~~~ruby
126
+ SparkleFormation.component(:name, :layering => :merge)
127
+ ~~~
128
+
129
+ and dynamics:
130
+
131
+ ~~~ruby
132
+ SparkleFormation.dynamic(:name, :layering => :merge)
133
+ ~~~
134
+
135
+ #### Dynamic return value
136
+
137
+ The return value of a dynamic cannot be inferred. This is due to the freeform nature
138
+ of dynamics leaving the resulting return value up to the author of the dynamic. When
139
+ merging dynamics it can be easy to return the wrong value. The library _could_ take
140
+ care of this and always return the context provided by the initial dynamic, but this
141
+ behavior is problematic: merges could remove the original context, or may want to
142
+ change the return context completely. Returning the proper value is left to the
143
+ author, but SparkleFormation makes it easy to return the previous context.
144
+
145
+ When the dynamics are merged, SparkleFormation will include an extra key in the
146
+ options `Hash` for the dynamic: `:previous_layer_result`. Now a dynamic can merge
147
+ in extra changes, and ensure the correct value is returned from the call:
148
+
149
+ ~~~ruby
150
+ SparkleFormation.dynamic(:name, :layering => :merge) do |name, args={}|
151
+ new.things.added 'here'
152
+ args[:previous_layer_result]
153
+ end
154
+ ~~~
155
+
156
+ ### Registry Items
157
+
158
+ Registry items are exempt from the merging behavior described above. Registry items
159
+ are always overwritten when redefined in higher level SparklePacks.
@@ -5,6 +5,9 @@ class SparkleFormation
5
5
  # SparkleFormation specific errors
6
6
  class Error < StandardError
7
7
 
8
+ # Inheritance error
9
+ class CircularInheritance < Error; end
10
+
8
11
  # File not found error
9
12
  class NotFound < KeyError
10
13
  attr_reader :name
@@ -110,11 +110,14 @@ class SparkleFormation
110
110
  ).last
111
111
  end
112
112
 
113
- def component(name, &block)
113
+ def component(name, args={}, &block)
114
114
  part_data[:component].push(
115
115
  ::Smash.new(
116
116
  :name => name,
117
117
  :block => block,
118
+ :args => Smash[
119
+ args.map(&:to_a)
120
+ ],
118
121
  :type => :component
119
122
  )
120
123
  ).last
@@ -6,6 +6,8 @@ class SparkleFormation
6
6
  # leak on long running processes with long lasting collections
7
7
  class SparkleCollection < Sparkle
8
8
 
9
+ autoload :Rainbow, 'sparkle_formation/sparkle_collection/rainbow'
10
+
9
11
  # Create a new collection of sparkles
10
12
  #
11
13
  # @return [self]
@@ -68,7 +70,10 @@ class SparkleFormation
68
70
  memoize("components_#{checksum}") do
69
71
  Smash.new.tap do |hsh|
70
72
  sparkles.each do |sprkl|
71
- hsh.merge!(sprkl.components)
73
+ sprkl.components.each_pair do |c_name, c_value|
74
+ hsh[c_name] ||= Rainbow.new(c_name, :component)
75
+ hsh[c_name].add_layer(c_value)
76
+ end
72
77
  end
73
78
  end
74
79
  end
@@ -79,7 +84,10 @@ class SparkleFormation
79
84
  memoize("dynamics_#{checksum}") do
80
85
  Smash.new.tap do |hsh|
81
86
  sparkles.each do |sprkl|
82
- hsh.merge!(sprkl.dynamics)
87
+ sprkl.dynamics.each_pair do |c_name, c_value|
88
+ hsh[c_name] ||= Rainbow.new(c_name, :dynamic)
89
+ hsh[c_name].add_layer(c_value)
90
+ end
83
91
  end
84
92
  end
85
93
  end
@@ -101,7 +109,10 @@ class SparkleFormation
101
109
  memoize("templates_#{checksum}") do
102
110
  Smash.new.tap do |hsh|
103
111
  sparkles.each do |sprkl|
104
- hsh.merge!(sprkl.templates)
112
+ sprkl.templates.each_pair do |c_name, c_value|
113
+ hsh[c_name] ||= Rainbow.new(c_name, :template)
114
+ hsh[c_name].add_layer(c_value)
115
+ end
105
116
  end
106
117
  end
107
118
  end
@@ -113,20 +124,21 @@ class SparkleFormation
113
124
  # @param name [String, Symbol] name of item
114
125
  # @return [Smash] requested item
115
126
  # @raises [NameError, Error::NotFound]
116
- def get(*args)
127
+ def get(type, name)
128
+ type_name = Sparkle::TYPES[type.to_s]
129
+ unless(type_name)
130
+ raise ArgumentError.new "Unknown file type requested from collection `#{type}`"
131
+ end
117
132
  result = nil
118
133
  error = nil
119
- sparkles.each do |sprkl|
120
- begin
121
- result = sprkl.get(*args)
122
- rescue Error::NotFound => error
123
- end
124
- end
125
- if(result)
126
- result
127
- else
128
- raise error
134
+ result = send(type_name)[name]
135
+ unless(result)
136
+ error_klass = Error::NotFound.const_get(
137
+ Bogo::Utility.camel(type)
138
+ )
139
+ raise error_klass.new(:name => name)
129
140
  end
141
+ result
130
142
  end
131
143
 
132
144
  protected
@@ -0,0 +1,92 @@
1
+ require 'sparkle_formation'
2
+ require 'forwardable'
3
+
4
+ class SparkleFormation
5
+
6
+ class SparkleCollection
7
+
8
+ # Contains a layered number of a specific item defined via
9
+ # a Sparkle. Items higher in the spectrum (greater layer index)
10
+ # have higher precedence than those below. This can be used for
11
+ # properly generating the end result based on merging or knockout rules.
12
+ class Rainbow
13
+
14
+ # Valid types for a rainbow
15
+ VALID_TYPES = [
16
+ :template,
17
+ :component,
18
+ :dynamic
19
+ ].freeze
20
+
21
+ extend Forwardable
22
+ def_delegators :top, *(Smash.public_instance_methods - Object.public_instance_methods)
23
+
24
+ # @return [String]
25
+ attr_reader :name
26
+ # @return [Symbol]
27
+ attr_reader :type
28
+ # @return [Array<Hash>]
29
+ attr_reader :spectrum
30
+
31
+ # Create a new rainbow
32
+ #
33
+ # @param name [String, Symbol] name of item
34
+ # @param type [String, Symbol] type of item
35
+ # @return [self]
36
+ def initialize(name, type)
37
+ unless(VALID_TYPES.include?(type.to_sym))
38
+ raise ArgumentError.new "Invalid type provdied for Rainbow instance `#{type}`"
39
+ end
40
+ @name = name.to_s
41
+ @type = type.to_sym
42
+ @spectrum = []
43
+ end
44
+
45
+ # Add a new layer to the top of the spectrum
46
+ #
47
+ # @param item [Hash]
48
+ # @return [self]
49
+ def add_layer(item)
50
+ unless(item.is_a?(Hash))
51
+ raise TypeError.new "Expecting type `Hash` but received type `#{item.class}`"
52
+ end
53
+ spectrum << item.to_smash
54
+ self
55
+ end
56
+
57
+ # Fetch item defined at given layer
58
+ #
59
+ # @param idx [Integer]
60
+ # @return [Hash]
61
+ def layer_at(idx)
62
+ if(idx <= spectrum.size)
63
+ spectrum.at(idx)
64
+ else
65
+ raise KeyError.new "Invalid layer requested for #{type} - #{name} (index: #{idx})"
66
+ end
67
+ end
68
+
69
+ # Generates a list of items to be processed in
70
+ # order to achieve the correct result based on
71
+ # expected merging behavior
72
+ #
73
+ # @return [Array<Hash>]
74
+ def monochrome
75
+ Array.new.tap do |result|
76
+ spectrum.each do |item|
77
+ unless(item.get(:args, :layering).to_s == 'merge')
78
+ result.clear
79
+ end
80
+ result << item
81
+ end
82
+ end
83
+ end
84
+
85
+ # @return [Hash]
86
+ def top
87
+ spectrum.last || {}
88
+ end
89
+ end
90
+
91
+ end
92
+ end
@@ -94,11 +94,14 @@ class SparkleFormation
94
94
  # @return [Hashish, SparkleStruct]
95
95
  def compile(path, *args)
96
96
  opts = args.detect{|i| i.is_a?(Hash) } || {}
97
- if(spath = (opts.delete(:sparkle_path) || SparkleFormation.sparkle_path))
98
- container = Sparkle.new(:root => spath)
99
- path = container.get(:template, path)[:path]
97
+ unless(path.is_a?(String) && File.file?(path.to_s))
98
+ if(spath = (opts.delete(:sparkle_path) || SparkleFormation.sparkle_path))
99
+ container = Sparkle.new(:root => spath)
100
+ path = container.get(:template, path)[:path]
101
+ end
100
102
  end
101
103
  formation = instance_eval(IO.read(path), path, 1)
104
+ formation.template_path = path
102
105
  if(args.delete(:sparkle))
103
106
  formation
104
107
  else
@@ -213,7 +216,17 @@ class SparkleFormation
213
216
  begin
214
217
  dyn = struct._self.sparkle.get(:dynamic, dynamic_name)
215
218
  raise dyn if dyn.is_a?(Exception)
216
- result = struct.instance_exec(*args, &dyn[:block])
219
+ dyn.monochrome.each do |dynamic_item|
220
+ if(result)
221
+ opts = args.detect{|i| i.is_a?(Hash)}
222
+ if(opts)
223
+ opts[:previous_layer_result] = result
224
+ else
225
+ args.push(:previous_layer_result => result)
226
+ end
227
+ end
228
+ result = struct.instance_exec(*args, &dynamic_item[:block])
229
+ end
217
230
  if(block_given?)
218
231
  result.instance_exec(&block)
219
232
  end
@@ -364,6 +377,10 @@ class SparkleFormation
364
377
  attr_reader :provider
365
378
  # @return [Class] Provider resources
366
379
  attr_reader :provider_resources
380
+ # @return [String] local path to template
381
+ attr_accessor :template_path
382
+ # @return [Array<String>] black listed templates
383
+ attr_reader :blacklisted_templates
367
384
 
368
385
  # Create new instance
369
386
  #
@@ -379,19 +396,23 @@ class SparkleFormation
379
396
  def initialize(name, options={}, &block)
380
397
  @name = name.to_sym
381
398
  @component_paths = []
382
- @sparkle = SparkleCollection.new
383
- @sparkle.set_root(
384
- Sparkle.new(
385
- Smash.new.tap{|h|
386
- s_path = options.fetch(:sparkle_path,
387
- self.class.custom_paths[:sparkle_path]
388
- )
389
- if(s_path)
390
- h[:root] = s_path
391
- end
392
- }
399
+ if(options[:sparkle_collection])
400
+ @sparkle = options[:sparkle_collection]
401
+ else
402
+ @sparkle = SparkleCollection.new
403
+ @sparkle.set_root(
404
+ Sparkle.new(
405
+ Smash.new.tap{|h|
406
+ s_path = options.fetch(:sparkle_path,
407
+ self.class.custom_paths[:sparkle_path]
408
+ )
409
+ if(s_path)
410
+ h[:root] = s_path
411
+ end
412
+ }
413
+ )
393
414
  )
394
- )
415
+ end
395
416
  self.provider = options.fetch(:provider, @parent ? @parent.provider : :aws)
396
417
  if(provider == :aws || !options[:disable_aws_builtins])
397
418
  require 'sparkle_formation/aws'
@@ -405,16 +426,105 @@ class SparkleFormation
405
426
  stack_resource_type,
406
427
  *options.fetch(:stack_resource_types, [])
407
428
  ].compact.uniq
429
+ @blacklisted_templates = [name]
408
430
  @components = Smash.new
409
431
  @load_order = []
410
432
  @overrides = []
411
433
  @parent = options[:parent]
434
+ @seed = Smash.new(
435
+ :inherit => options[:inherit],
436
+ :layering => options[:layering]
437
+ )
412
438
  if(block)
413
439
  load_block(block)
414
440
  end
415
441
  @compiled = nil
416
442
  end
417
443
 
444
+ # @return [Array<Proc>]
445
+ def raw_overrides
446
+ @overrides
447
+ end
448
+
449
+ # Update underlying data structures based on inherit
450
+ # or layering behavior if defined for this template
451
+ #
452
+ # @param options [Hash]
453
+ # @return [self]
454
+ def seed_self
455
+ memoize(:seed) do
456
+ options = @seed
457
+ if(options[:inherit] && options[:layering].to_s == 'merge')
458
+ raise ArgumentError.new 'Cannot merge and inherit!'
459
+ end
460
+ if(options[:inherit])
461
+ inherit_from(options[:inherit])
462
+ elsif(options[:layering].to_s == 'merge')
463
+ merge_previous!
464
+ end
465
+ true
466
+ end
467
+ self
468
+ end
469
+
470
+ # Merge previous layer template structure
471
+ #
472
+ # @return [self]
473
+ def merge_previous!
474
+ my_index = sparkle.get(:template, name).spectrum.find_index do |item|
475
+ item[:path] == template_path
476
+ end
477
+ template = self.class.compile(sparkle.get(:template, name).layer_at(my_index - 1)[:path], :sparkle)
478
+ extract_template_data(template)
479
+ end
480
+
481
+ # Inhert template structure
482
+ #
483
+ # @param template_name [String] name of template to inherit
484
+ # @return [self]
485
+ def inherit_from(template_name)
486
+ if(blacklisted_templates.map(&:to_s).include?(template_name.to_s))
487
+ raise Error::CircularInheritance.new "Circular inheritance detected between templates `#{template_name}` and `#{name}`" # rubocop:disable Metrics/LineLength
488
+ end
489
+ template = self.class.compile(sparkle.get(:template, template_name)[:path], :sparkle)
490
+ template.blacklisted_templates.replace(
491
+ (template.blacklisted_templates + blacklisted_templates).map(&:to_s).uniq
492
+ )
493
+ extract_template_data(template)
494
+ end
495
+
496
+ # Extract information from given template and integrate with current instance
497
+ #
498
+ # @param template [SparkleFormation]
499
+ # @return [self]
500
+ def extract_template_data(template)
501
+ if(provider != template.provider)
502
+ raise TypeError.new "This template `#{name}` cannot inherit template `#{template.name}`! Provider mismatch: `#{provider}` != `#{template.provider}`" # rubocop:disable Metrics/LineLength
503
+ end
504
+ sparkle.size.times do |idx|
505
+ template.sparkle.add_sparkle(sparkle.sparkle_at(idx))
506
+ end
507
+ template.seed_self
508
+ blacklisted_templates.replace(
509
+ (blacklisted_templates + template.blacklisted_templates).map(&:to_s).uniq
510
+ )
511
+ @parameters = template.parameters
512
+ @overrides = template.raw_overrides + raw_overrides
513
+ new_components = template.components
514
+ new_load_order = template.load_order
515
+ load_order.each do |comp_key|
516
+ if(components[comp_key])
517
+ original_key = comp_key
518
+ comp_key = "#{comp_key}_#{Smash.new(:name => name, :path => template_path).checksum}_child"
519
+ new_components[comp_key] = components[original_key]
520
+ end
521
+ new_load_order << comp_key
522
+ end
523
+ @components = new_components
524
+ @load_order = new_load_order
525
+ self
526
+ end
527
+
418
528
  # @return [String] provider stack resource type
419
529
  def stack_resource_type
420
530
  DEFAULT_STACK_RESOURCE
@@ -536,11 +646,13 @@ class SparkleFormation
536
646
  args.each do |thing|
537
647
  key = File.basename(thing.to_s).sub('.rb', '')
538
648
  if(thing.is_a?(String))
649
+ # NOTE: This needs to be deprecated and removed
650
+ # TODO: deprecate
539
651
  components[key] = self.class.load_component(thing)
652
+ @load_order << key
540
653
  else
541
- components[key] = sparkle.get(:component, thing)[:block]
654
+ @load_order << thing
542
655
  end
543
- @load_order << key
544
656
  end
545
657
  self
546
658
  end
@@ -565,6 +677,9 @@ class SparkleFormation
565
677
  unmemoize(:compile)
566
678
  end
567
679
  memoize(:compile) do
680
+ # NOTE: this is where we inject inherit or layering
681
+ seed_self
682
+
568
683
  set_compile_time_parameters!
569
684
  if(provider && SparkleAttribute.const_defined?(camel(provider)))
570
685
  const = SparkleAttribute.const_get(camel(provider))
@@ -593,7 +708,13 @@ class SparkleFormation
593
708
  compiled.set_state!(compile_state)
594
709
  end
595
710
  @load_order.each do |key|
596
- self.class.build(compiled, &components[key])
711
+ if(components[key])
712
+ self.class.build(compiled, &components[key])
713
+ else
714
+ sparkle.get(:component, key).monochrome.each do |component_block|
715
+ self.class.build(compiled, &component_block[:block])
716
+ end
717
+ end
597
718
  end
598
719
  @overrides.each do |override|
599
720
  if(override[:args] && !override[:args].empty?)
@@ -1,5 +1,5 @@
1
1
  # Unicorns and rainbows
2
2
  class SparkleFormation
3
3
  # Current library version
4
- VERSION = Gem::Version.new('2.0.2')
4
+ VERSION = Gem::Version.new('2.1.0')
5
5
  end
@@ -10,7 +10,7 @@ Gem::Specification.new do |s|
10
10
  s.description = 'Ruby DSL for programmatic orchestration API template generation'
11
11
  s.license = 'Apache-2.0'
12
12
  s.require_path = 'lib'
13
- s.required_ruby_version = '>= 2.0'
13
+ s.required_ruby_version = '>= 2.1'
14
14
  s.add_runtime_dependency 'attribute_struct', '>= 0.3.0', '< 0.5'
15
15
  s.add_runtime_dependency 'multi_json'
16
16
  s.add_runtime_dependency 'bogo'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sparkle_formation
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.2
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Roberts
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-01-28 00:00:00.000000000 Z
11
+ date: 2016-02-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: attribute_struct
@@ -158,6 +158,7 @@ files:
158
158
  - docs/building-blocks.md
159
159
  - docs/compile-time-parameters.md
160
160
  - docs/helper-methods.md
161
+ - docs/inheritance-merging.md
161
162
  - docs/nested-stacks.md
162
163
  - docs/overview.md
163
164
  - docs/sparkle-packs.md
@@ -195,6 +196,7 @@ files:
195
196
  - lib/sparkle_formation/sparkle_attribute/heat.rb
196
197
  - lib/sparkle_formation/sparkle_attribute/rackspace.rb
197
198
  - lib/sparkle_formation/sparkle_collection.rb
199
+ - lib/sparkle_formation/sparkle_collection/rainbow.rb
198
200
  - lib/sparkle_formation/sparkle_formation.rb
199
201
  - lib/sparkle_formation/sparkle_struct.rb
200
202
  - lib/sparkle_formation/translation.rb
@@ -215,7 +217,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
215
217
  requirements:
216
218
  - - ">="
217
219
  - !ruby/object:Gem::Version
218
- version: '2.0'
220
+ version: '2.1'
219
221
  required_rubygems_version: !ruby/object:Gem::Requirement
220
222
  requirements:
221
223
  - - ">="