sparkle_formation 1.2.0 → 2.0.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 +4 -4
- data/CHANGELOG.md +9 -0
- data/bin/generate_sparkle_docs +21 -6
- data/docs/helper-methods.md +26 -49
- data/lib/sparkle_formation.rb +3 -0
- data/lib/sparkle_formation/error.rb +8 -0
- data/lib/sparkle_formation/function_struct.rb +123 -0
- data/lib/sparkle_formation/provider.rb +12 -0
- data/lib/sparkle_formation/provider/aws.rb +201 -0
- data/lib/sparkle_formation/provider/azure.rb +168 -0
- data/lib/sparkle_formation/provider/heat.rb +163 -0
- data/lib/sparkle_formation/resources.rb +27 -10
- data/lib/sparkle_formation/resources/azure.rb +42 -0
- data/lib/sparkle_formation/resources/azure_resources.json +353 -0
- data/lib/sparkle_formation/resources/heat.rb +39 -0
- data/lib/sparkle_formation/resources/heat_resources.json +4994 -0
- data/lib/sparkle_formation/resources/rackspace.rb +39 -0
- data/lib/sparkle_formation/resources/rackspace_resources.json +2561 -0
- data/lib/sparkle_formation/sparkle_attribute.rb +14 -19
- data/lib/sparkle_formation/sparkle_attribute/aws.rb +12 -12
- data/lib/sparkle_formation/sparkle_attribute/azure.rb +161 -0
- data/lib/sparkle_formation/sparkle_attribute/heat.rb +177 -0
- data/lib/sparkle_formation/sparkle_attribute/rackspace.rb +21 -0
- data/lib/sparkle_formation/sparkle_formation.rb +70 -154
- data/lib/sparkle_formation/sparkle_struct.rb +39 -2
- data/lib/sparkle_formation/version.rb +1 -1
- data/sparkle_formation.gemspec +7 -3
- metadata +63 -7
@@ -0,0 +1,168 @@
|
|
1
|
+
require 'sparkle_formation'
|
2
|
+
|
3
|
+
class SparkleFormation
|
4
|
+
module Provider
|
5
|
+
# Azure specific implementation
|
6
|
+
module Azure
|
7
|
+
|
8
|
+
# @return [String] Type string for Azure Resource Manager stack resource
|
9
|
+
def stack_resource_type
|
10
|
+
'Microsoft.Resources/deployments'
|
11
|
+
end
|
12
|
+
|
13
|
+
# Generate policy for stack
|
14
|
+
#
|
15
|
+
# @return [Hash]
|
16
|
+
def generate_policy
|
17
|
+
{}
|
18
|
+
end
|
19
|
+
|
20
|
+
# Apply deeply nested stacks. This is the new nesting approach and
|
21
|
+
# does not bubble parameters up to the root stack. Parameters are
|
22
|
+
# isolated to the stack resource itself and output mapping is
|
23
|
+
# automatically applied.
|
24
|
+
#
|
25
|
+
# @yieldparam stack [SparkleFormation] stack instance
|
26
|
+
# @yieldparam resource [AttributeStruct] the stack resource
|
27
|
+
# @yieldparam s_name [String] stack resource name
|
28
|
+
# @yieldreturn [Hash] key/values to be merged into resource properties
|
29
|
+
# @return [Hash] dumped stack
|
30
|
+
def apply_deep_nesting(*args, &block)
|
31
|
+
outputs = collect_outputs
|
32
|
+
nested_stacks(:with_resource).each do |stack, resource|
|
33
|
+
unless(stack.nested_stacks.empty?)
|
34
|
+
stack.apply_deep_nesting(*args)
|
35
|
+
end
|
36
|
+
stack.compile.parameters.keys!.each do |parameter_name|
|
37
|
+
if(output_name = output_matched?(parameter_name, outputs.keys))
|
38
|
+
next if outputs[output_name] == stack
|
39
|
+
stack_output = stack.make_output_available(output_name, outputs)
|
40
|
+
resource.properties.parameters._set(parameter_name).value stack_output
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
if(block_given?)
|
45
|
+
extract_templates(&block)
|
46
|
+
end
|
47
|
+
compile.dump!
|
48
|
+
end
|
49
|
+
|
50
|
+
# Apply shallow nesting. This style of nesting will bubble
|
51
|
+
# parameters up to the root stack. This type of nesting is the
|
52
|
+
# original and now deprecated, but remains for compat issues so any
|
53
|
+
# existing usage won't be automatically busted.
|
54
|
+
#
|
55
|
+
# @yieldparam resource_name [String] name of stack resource
|
56
|
+
# @yieldparam stack [SparkleFormation] nested stack
|
57
|
+
# @yieldreturn [String] Remote URL storage for template
|
58
|
+
# @return [Hash]
|
59
|
+
def apply_shallow_nesting(*args, &block)
|
60
|
+
parameters = compile.parameters
|
61
|
+
output_map = {}
|
62
|
+
nested_stacks(:with_resource, :with_name).each do |_stack, stack_resource, stack_name|
|
63
|
+
remap_nested_parameters(compile, parameters, stack_name, stack_resource, output_map)
|
64
|
+
end
|
65
|
+
extract_templates(&block)
|
66
|
+
if(args.include?(:bubble_outputs))
|
67
|
+
output_map.each do |o_name, o_val|
|
68
|
+
compile.outputs._set(o_name).value compile._stack_output(*o_val)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
compile.dump!
|
72
|
+
end
|
73
|
+
|
74
|
+
# Extract output to make available for stack parameter usage at the
|
75
|
+
# current depth
|
76
|
+
#
|
77
|
+
# @param output_name [String] name of output
|
78
|
+
# @param outputs [Hash] listing of outputs
|
79
|
+
# @reutrn [Hash] reference to output value (used for setting parameter)
|
80
|
+
def make_output_available(output_name, outputs)
|
81
|
+
bubble_path = outputs[output_name].root_path - root_path
|
82
|
+
drip_path = root_path - outputs[output_name].root_path
|
83
|
+
bubble_path.each_slice(2) do |base_sparkle, ref_sparkle|
|
84
|
+
next unless ref_sparkle
|
85
|
+
base_sparkle.compile.outputs._set(output_name)._set(
|
86
|
+
:value, base_sparkle.compile._stack_output(
|
87
|
+
ref_sparkle.name, output_name
|
88
|
+
)
|
89
|
+
)
|
90
|
+
end
|
91
|
+
if(bubble_path.empty?)
|
92
|
+
if(drip_path.size == 1)
|
93
|
+
parent = drip_path.first.parent
|
94
|
+
if(parent && !parent.compile.parameters._set(output_name).nil?)
|
95
|
+
return compile.parameter!(output_name)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
raise ArgumentError.new "Failed to detect available bubbling path for output `#{output_name}`. " <<
|
99
|
+
'This may be due to a circular dependency! ' <<
|
100
|
+
"(Output Path: #{outputs[output_name].root_path.map(&:name).join(' > ')} " <<
|
101
|
+
"Requester Path: #{root_path.map(&:name).join(' > ')})"
|
102
|
+
end
|
103
|
+
result = compile._stack_output(bubble_path.first.name, output_name)
|
104
|
+
if(drip_path.size > 1)
|
105
|
+
parent = drip_path.first.parent
|
106
|
+
drip_path.unshift(parent) if parent
|
107
|
+
drip_path.each_slice(2) do |base_sparkle, ref_sparkle|
|
108
|
+
next unless ref_sparkle
|
109
|
+
base_sparkle.compile.resources[ref_sparkle.name].properties.parameters.value._set(output_name, result)
|
110
|
+
ref_sparkle.compile.parameters._set(output_name).type 'string' # TODO: <<<<------ type check and prop
|
111
|
+
result = compile._parameter(output_name)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
result
|
115
|
+
end
|
116
|
+
|
117
|
+
# Extract parameters from nested stacks. Check for previous nested
|
118
|
+
# stack outputs that match parameter. If match, set parameter to use
|
119
|
+
# output. If no match, check container stack parameters for match.
|
120
|
+
# If match, set to use ref. If no match, add parameter to container
|
121
|
+
# stack parameters and set to use ref.
|
122
|
+
#
|
123
|
+
# @param template [Hash] template being processed
|
124
|
+
# @param parameters [Hash] top level parameter set being built
|
125
|
+
# @param stack_name [String] name of stack resource
|
126
|
+
# @param stack_resource [Hash] duplicate of stack resource contents
|
127
|
+
# @param output_map [Hash] mapping of output names to required stack output access
|
128
|
+
# @return [TrueClass]
|
129
|
+
# @note if parameter has includes `StackUnique` a new parameter will
|
130
|
+
# be added to container stack and it will not use outputs
|
131
|
+
def remap_nested_parameters(template, parameters, stack_name, stack_resource, output_map)
|
132
|
+
nested_template = stack_resource.properties.stack.compile
|
133
|
+
stack_parameters = nested_template.parameters
|
134
|
+
unless(stack_parameters.nil?)
|
135
|
+
stack_parameters._keys.each do |pname|
|
136
|
+
pval = stack_parameters[pname]
|
137
|
+
unless(pval.stack_unique.nil?)
|
138
|
+
check_name = [stack_name, pname].join
|
139
|
+
else
|
140
|
+
check_name = pname
|
141
|
+
end
|
142
|
+
if(!parameters._set(check_name).nil?)
|
143
|
+
template.resources._set(stack_name).properties.parameters._set(pname).value(
|
144
|
+
template._parameter(check_name)
|
145
|
+
)
|
146
|
+
elsif(output_map[check_name])
|
147
|
+
template.resources._set(stack_name).properties.parameters._set(pname).value(
|
148
|
+
template._stack_output(*output_map[check_name])
|
149
|
+
)
|
150
|
+
else
|
151
|
+
parameters._set(check_name, pval)
|
152
|
+
template.resources._set(stack_name).properties.parameters._set(pname).value(
|
153
|
+
template._parameter(check_name)
|
154
|
+
)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
unless(nested_template.outputs.nil?)
|
159
|
+
nested_template.outputs.keys!.each do |oname|
|
160
|
+
output_map[oname] = [stack_name, oname]
|
161
|
+
end
|
162
|
+
end
|
163
|
+
true
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
require 'sparkle_formation'
|
2
|
+
|
3
|
+
class SparkleFormation
|
4
|
+
module Provider
|
5
|
+
# Heat specific implementation
|
6
|
+
module Heat
|
7
|
+
|
8
|
+
# @return [String] Type string for OpenStack HEAT stack resource
|
9
|
+
def stack_resource_type
|
10
|
+
'OS::Heat::Stack'
|
11
|
+
end
|
12
|
+
|
13
|
+
# Generate policy for stack
|
14
|
+
#
|
15
|
+
# @return [Hash]
|
16
|
+
def generate_policy
|
17
|
+
{}
|
18
|
+
end
|
19
|
+
|
20
|
+
# Apply deeply nested stacks. This is the new nesting approach and
|
21
|
+
# does not bubble parameters up to the root stack. Parameters are
|
22
|
+
# isolated to the stack resource itself and output mapping is
|
23
|
+
# automatically applied.
|
24
|
+
#
|
25
|
+
# @yieldparam stack [SparkleFormation] stack instance
|
26
|
+
# @yieldparam resource [AttributeStruct] the stack resource
|
27
|
+
# @yieldparam s_name [String] stack resource name
|
28
|
+
# @yieldreturn [Hash] key/values to be merged into resource properties
|
29
|
+
# @return [Hash] dumped stack
|
30
|
+
def apply_deep_nesting(*args, &block)
|
31
|
+
outputs = collect_outputs
|
32
|
+
nested_stacks(:with_resource).each do |stack, resource|
|
33
|
+
unless(stack.nested_stacks.empty?)
|
34
|
+
stack.apply_deep_nesting(*args)
|
35
|
+
end
|
36
|
+
stack.compile.parameters.keys!.each do |parameter_name|
|
37
|
+
if(output_name = output_matched?(parameter_name, outputs.keys))
|
38
|
+
next if outputs[output_name] == stack
|
39
|
+
stack_output = stack.make_output_available(output_name, outputs)
|
40
|
+
resource.properties.parameters._set(parameter_name, stack_output)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
if(block_given?)
|
45
|
+
extract_templates(&block)
|
46
|
+
end
|
47
|
+
compile.dump!
|
48
|
+
end
|
49
|
+
|
50
|
+
# Apply shallow nesting. This style of nesting will bubble
|
51
|
+
# parameters up to the root stack. This type of nesting is the
|
52
|
+
# original and now deprecated, but remains for compat issues so any
|
53
|
+
# existing usage won't be automatically busted.
|
54
|
+
#
|
55
|
+
# @yieldparam resource_name [String] name of stack resource
|
56
|
+
# @yieldparam stack [SparkleFormation] nested stack
|
57
|
+
# @yieldreturn [String] Remote URL storage for template
|
58
|
+
# @return [Hash]
|
59
|
+
def apply_shallow_nesting(*args, &block)
|
60
|
+
parameters = compile.parameters
|
61
|
+
output_map = {}
|
62
|
+
nested_stacks(:with_resource, :with_name).each do |_stack, stack_resource, stack_name|
|
63
|
+
remap_nested_parameters(compile, parameters, stack_name, stack_resource, output_map)
|
64
|
+
end
|
65
|
+
extract_templates(&block)
|
66
|
+
if(args.include?(:bubble_outputs))
|
67
|
+
output_map.each do |o_name, o_val|
|
68
|
+
compile.outputs._set(o_name).value compile._stack_output(*o_val)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
compile.dump!
|
72
|
+
end
|
73
|
+
|
74
|
+
# Extract output to make available for stack parameter usage at the
|
75
|
+
# current depth
|
76
|
+
#
|
77
|
+
# @param output_name [String] name of output
|
78
|
+
# @param outputs [Hash] listing of outputs
|
79
|
+
# @reutrn [Hash] reference to output value (used for setting parameter)
|
80
|
+
def make_output_available(output_name, outputs)
|
81
|
+
bubble_path = outputs[output_name].root_path - root_path
|
82
|
+
drip_path = root_path - outputs[output_name].root_path
|
83
|
+
bubble_path.each_slice(2) do |base_sparkle, ref_sparkle|
|
84
|
+
next unless ref_sparkle
|
85
|
+
base_sparkle.compile.outputs._set(output_name)._set(
|
86
|
+
:value, base_sparkle.compile._stack_output(
|
87
|
+
ref_sparkle.name, output_name
|
88
|
+
)
|
89
|
+
)
|
90
|
+
end
|
91
|
+
if(bubble_path.empty?)
|
92
|
+
if(drip_path.size == 1)
|
93
|
+
parent = drip_path.first.parent
|
94
|
+
if(parent && !parent.compile.parameters._set(output_name).nil?)
|
95
|
+
return compile.parameter!(output_name)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
raise ArgumentError.new "Failed to detect available bubbling path for output `#{output_name}`. " <<
|
99
|
+
'This may be due to a circular dependency! ' <<
|
100
|
+
"(Output Path: #{outputs[output_name].root_path.map(&:name).join(' > ')} " <<
|
101
|
+
"Requester Path: #{root_path.map(&:name).join(' > ')})"
|
102
|
+
end
|
103
|
+
result = compile._stack_output(bubble_path.first.name, output_name)
|
104
|
+
if(drip_path.size > 1)
|
105
|
+
parent = drip_path.first.parent
|
106
|
+
drip_path.unshift(parent) if parent
|
107
|
+
drip_path.each_slice(2) do |base_sparkle, ref_sparkle|
|
108
|
+
next unless ref_sparkle
|
109
|
+
base_sparkle.compile.resources[ref_sparkle.name].properties.parameters._set(output_name, result)
|
110
|
+
ref_sparkle.compile.parameters._set(output_name).type 'string' # TODO: <<<<------ type check and prop
|
111
|
+
result = compile._parameter(output_name)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
result
|
115
|
+
end
|
116
|
+
|
117
|
+
# Extract parameters from nested stacks. Check for previous nested
|
118
|
+
# stack outputs that match parameter. If match, set parameter to use
|
119
|
+
# output. If no match, check container stack parameters for match.
|
120
|
+
# If match, set to use ref. If no match, add parameter to container
|
121
|
+
# stack parameters and set to use ref.
|
122
|
+
#
|
123
|
+
# @param template [Hash] template being processed
|
124
|
+
# @param parameters [Hash] top level parameter set being built
|
125
|
+
# @param stack_name [String] name of stack resource
|
126
|
+
# @param stack_resource [Hash] duplicate of stack resource contents
|
127
|
+
# @param output_map [Hash] mapping of output names to required stack output access
|
128
|
+
# @return [TrueClass]
|
129
|
+
# @note if parameter has includes `StackUnique` a new parameter will
|
130
|
+
# be added to container stack and it will not use outputs
|
131
|
+
def remap_nested_parameters(template, parameters, stack_name, stack_resource, output_map)
|
132
|
+
nested_template = stack_resource.properties.stack.compile
|
133
|
+
stack_parameters = nested_template.parameters
|
134
|
+
unless(stack_parameters.nil?)
|
135
|
+
stack_parameters._keys.each do |pname|
|
136
|
+
pval = stack_parameters[pname]
|
137
|
+
unless(pval.stack_unique.nil?)
|
138
|
+
check_name = [stack_name, pname].join
|
139
|
+
else
|
140
|
+
check_name = pname
|
141
|
+
end
|
142
|
+
if(!parameters._set(check_name).nil?)
|
143
|
+
template.resources._set(stack_name).properties.parameters._set(pname, template._parameter(check_name))
|
144
|
+
elsif(output_map[check_name])
|
145
|
+
template.resources._set(stack_name).properties.parameters._set(pname)
|
146
|
+
template._stack_output(*output_map[check_name])
|
147
|
+
else
|
148
|
+
parameters._set(check_name, pval)
|
149
|
+
template.resources._set(stack_name).properties.parameters._set(pname, template._parameter(check_name))
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
unless(nested_template.outputs.nil?)
|
154
|
+
nested_template.outputs.keys!.each do |oname|
|
155
|
+
output_map[oname] = [stack_name, oname]
|
156
|
+
end
|
157
|
+
end
|
158
|
+
true
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
@@ -5,12 +5,25 @@ class SparkleFormation
|
|
5
5
|
class Resources
|
6
6
|
|
7
7
|
autoload :Aws, 'sparkle_formation/resources/aws'
|
8
|
+
autoload :Azure, 'sparkle_formation/resources/azure'
|
9
|
+
autoload :Heat, 'sparkle_formation/resources/heat'
|
10
|
+
autoload :Rackspace, 'sparkle_formation/resources/rackspace'
|
11
|
+
|
12
|
+
# Characters to be removed from supplied key on matching
|
13
|
+
RESOURCE_TYPE_TR = '_'
|
14
|
+
# String to split for resource namespacing
|
15
|
+
RESOURCE_TYPE_NAMESPACE_SPLITTER = '::'
|
8
16
|
|
9
17
|
class << self
|
10
18
|
|
11
19
|
include SparkleFormation::Utils::AnimalStrings
|
12
20
|
# @!parse include SparkleFormation::Utils::AnimalStrings
|
13
21
|
|
22
|
+
# @return [String] base registry key
|
23
|
+
def base_key
|
24
|
+
Bogo::Utility.snake(self.name.split('::').last) # rubocop:disable Style/RedundantSelf
|
25
|
+
end
|
26
|
+
|
14
27
|
# Register resource
|
15
28
|
#
|
16
29
|
# @param type [String] Orchestration resource type
|
@@ -20,7 +33,8 @@ class SparkleFormation
|
|
20
33
|
unless(class_variable_defined?(:@@registry))
|
21
34
|
@@registry = AttributeStruct.hashish.new
|
22
35
|
end
|
23
|
-
@@registry[
|
36
|
+
@@registry[base_key] ||= AttributeStruct.hashish.new
|
37
|
+
@@registry[base_key][type] = hash
|
24
38
|
true
|
25
39
|
end
|
26
40
|
|
@@ -67,11 +81,13 @@ class SparkleFormation
|
|
67
81
|
# @return [String, NilClass]
|
68
82
|
def registry_key(key)
|
69
83
|
o_key = key
|
70
|
-
key = key.to_s.tr(
|
84
|
+
key = key.to_s.tr(self.const_get(:RESOURCE_TYPE_TR), '') # rubocop:disable Style/RedundantSelf
|
71
85
|
snake_parts = nil
|
72
|
-
result = @@registry.keys.detect do |ref|
|
86
|
+
result = @@registry[base_key].keys.detect do |ref|
|
73
87
|
ref = ref.downcase
|
74
|
-
snake_parts = ref.split(
|
88
|
+
snake_parts = ref.split(
|
89
|
+
self.const_get(:RESOURCE_TYPE_NAMESPACE_SPLITTER) # rubocop:disable Style/RedundantSelf
|
90
|
+
)
|
75
91
|
until(snake_parts.empty?)
|
76
92
|
break if snake_parts.join('') == key
|
77
93
|
snake_parts.shift
|
@@ -79,8 +95,10 @@ class SparkleFormation
|
|
79
95
|
!snake_parts.empty?
|
80
96
|
end
|
81
97
|
if(result)
|
82
|
-
collisions = @@registry.keys.find_all do |ref|
|
83
|
-
split_ref = ref.downcase.split(
|
98
|
+
collisions = @@registry[base_key].keys.find_all do |ref|
|
99
|
+
split_ref = ref.downcase.split(
|
100
|
+
self.const_get(:RESOURCE_TYPE_NAMESPACE_SPLITTER) # rubocop:disable Style/RedundantSelf
|
101
|
+
)
|
84
102
|
ref = split_ref.slice(split_ref.size - snake_parts.size, split_ref.size).join('')
|
85
103
|
key == ref
|
86
104
|
end
|
@@ -97,16 +115,15 @@ class SparkleFormation
|
|
97
115
|
# @param key [String, Symbol]
|
98
116
|
# @return [Hashish, NilClass]
|
99
117
|
def lookup(key)
|
100
|
-
@@registry[registry_key(key)]
|
118
|
+
@@registry[base_key][registry_key(key)]
|
101
119
|
end
|
102
120
|
|
103
121
|
# @return [Hashish] currently loaded AWS registry
|
104
122
|
def registry
|
105
|
-
|
106
|
-
@@registry
|
107
|
-
else
|
123
|
+
unless(class_variable_defined?(:@@registry))
|
108
124
|
@@registry = AttributeStruct.hashish.new
|
109
125
|
end
|
126
|
+
@@registry[base_key]
|
110
127
|
end
|
111
128
|
|
112
129
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'sparkle_formation'
|
2
|
+
|
3
|
+
class SparkleFormation
|
4
|
+
|
5
|
+
# Resources helper
|
6
|
+
class Resources
|
7
|
+
|
8
|
+
# Azure specific resources collection
|
9
|
+
class Azure < Resources
|
10
|
+
|
11
|
+
# String to split for resource namespacing
|
12
|
+
RESOURCE_TYPE_NAMESPACE_SPLITTER = '/'
|
13
|
+
|
14
|
+
class << self
|
15
|
+
|
16
|
+
include Bogo::Memoization
|
17
|
+
|
18
|
+
# Load the builtin AWS resources
|
19
|
+
#
|
20
|
+
# @return [TrueClass]
|
21
|
+
def load!
|
22
|
+
memoize(:azure_resources, :global) do
|
23
|
+
load(
|
24
|
+
File.join(
|
25
|
+
File.dirname(__FILE__),
|
26
|
+
'azure_resources.json'
|
27
|
+
)
|
28
|
+
)
|
29
|
+
true
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Auto load data when included
|
34
|
+
def included(_klass)
|
35
|
+
load!
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|