aws-flow 1.0.8 → 1.0.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +1 -1
- data/Rakefile +18 -31
- data/aws-flow.gemspec +1 -1
- data/lib/aws/decider.rb +1 -2
- data/lib/aws/decider/activity.rb +99 -53
- data/lib/aws/decider/activity_definition.rb +43 -7
- data/lib/aws/decider/async_decider.rb +56 -57
- data/lib/aws/decider/async_retrying_executor.rb +4 -5
- data/lib/aws/decider/data_converter.rb +2 -2
- data/lib/aws/decider/decider.rb +46 -41
- data/lib/aws/decider/decision_context.rb +2 -2
- data/lib/aws/decider/exceptions.rb +6 -6
- data/lib/aws/decider/executor.rb +15 -11
- data/lib/aws/decider/flow_defaults.rb +54 -22
- data/lib/aws/decider/generic_client.rb +7 -7
- data/lib/aws/decider/history_helper.rb +0 -0
- data/lib/aws/decider/implementation.rb +5 -5
- data/lib/aws/decider/options.rb +285 -155
- data/lib/aws/decider/state_machines.rb +10 -10
- data/lib/aws/decider/task_handler.rb +5 -5
- data/lib/aws/decider/task_poller.rb +152 -15
- data/lib/aws/decider/utilities.rb +14 -14
- data/lib/aws/decider/version.rb +1 -1
- data/lib/aws/decider/worker.rb +21 -20
- data/lib/aws/decider/workflow_client.rb +78 -31
- data/lib/aws/decider/workflow_clock.rb +1 -1
- data/lib/aws/decider/workflow_definition.rb +5 -5
- data/lib/aws/decider/workflow_definition_factory.rb +1 -1
- data/lib/aws/decider/workflow_enabled.rb +1 -1
- data/lib/aws/flow/async_backtrace.rb +19 -18
- data/lib/aws/flow/async_scope.rb +32 -16
- data/lib/aws/flow/begin_rescue_ensure.rb +61 -56
- data/lib/aws/flow/fiber.rb +14 -6
- data/lib/aws/flow/flow_utils.rb +9 -6
- data/lib/aws/flow/future.rb +43 -18
- data/lib/aws/flow/implementation.rb +34 -18
- data/lib/aws/flow/simple_dfa.rb +12 -4
- data/lib/aws/flow/tasks.rb +120 -86
- data/{test/aws → spec/aws/integration}/integration_spec.rb +0 -0
- data/{test/aws → spec/aws/unit}/async_backtrace_spec.rb +1 -0
- data/{test/aws → spec/aws/unit}/async_scope_spec.rb +0 -0
- data/{test/aws → spec/aws/unit}/begin_rescue_ensure_spec.rb +90 -2
- data/{test/aws → spec/aws/unit}/decider_spec.rb +41 -53
- data/{test/aws → spec/aws/unit}/external_task_spec.rb +0 -0
- data/{test/aws → spec/aws/unit}/factories.rb +0 -0
- data/{test/aws → spec/aws/unit}/fiber_condition_variable_spec.rb +0 -0
- data/{test/aws → spec/aws/unit}/fiber_spec.rb +0 -0
- data/{test/aws → spec/aws/unit}/flow_spec.rb +0 -0
- data/{test/aws → spec/aws/unit}/future_spec.rb +0 -0
- data/{test/aws → spec/aws/unit}/preinclude_tests.rb +0 -0
- data/{test/aws → spec/aws/unit}/rubyflow.rb +0 -0
- data/{test/aws → spec/aws/unit}/simple_dfa_spec.rb +0 -0
- data/{test/aws → spec}/spec_helper.rb +0 -0
- metadata +30 -30
@@ -16,7 +16,7 @@
|
|
16
16
|
module AWS
|
17
17
|
module Flow
|
18
18
|
|
19
|
-
#
|
19
|
+
# @api private
|
20
20
|
module DecisionStateMachineDFA
|
21
21
|
attr_accessor :transitions, :symbols, :states, :start_state
|
22
22
|
|
@@ -40,7 +40,7 @@ module AWS
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def get_transitions
|
43
|
-
# Turns out, you are your own ancestor
|
43
|
+
# Turns out, you are your own ancestor.
|
44
44
|
ancestors.slice(1..-1).map {|x| x.transitions if x.respond_to? :transitions}.compact.
|
45
45
|
inject({}) {|x, y| x.merge(y)}.merge(@transitions)
|
46
46
|
end
|
@@ -77,7 +77,7 @@ module AWS
|
|
77
77
|
end
|
78
78
|
end
|
79
79
|
|
80
|
-
#
|
80
|
+
# @api private
|
81
81
|
class CompleteWorkflowStateMachine
|
82
82
|
extend DecisionStateMachineDFA
|
83
83
|
attr_reader :id
|
@@ -86,10 +86,10 @@ module AWS
|
|
86
86
|
return if symbol == :handle_decision_task_started_event
|
87
87
|
raise "UnsupportedOperation"
|
88
88
|
end
|
89
|
-
# Creates a new CompleteWorkflowStateMachine
|
89
|
+
# Creates a new `CompleteWorkflowStateMachine`.
|
90
90
|
#
|
91
91
|
# @param id
|
92
|
-
# The decider
|
92
|
+
# The decider ID.
|
93
93
|
#
|
94
94
|
# @param attributes
|
95
95
|
#
|
@@ -110,7 +110,7 @@ module AWS
|
|
110
110
|
end
|
111
111
|
|
112
112
|
|
113
|
-
#
|
113
|
+
# @api private
|
114
114
|
class DecisionStateMachineBase
|
115
115
|
extend DecisionStateMachineDFA
|
116
116
|
attr_reader :id
|
@@ -152,7 +152,7 @@ module AWS
|
|
152
152
|
end
|
153
153
|
|
154
154
|
|
155
|
-
#
|
155
|
+
# @api private
|
156
156
|
class ActivityDecisionStateMachine < DecisionStateMachineBase
|
157
157
|
|
158
158
|
attr_reader :attributes
|
@@ -207,7 +207,7 @@ module AWS
|
|
207
207
|
end
|
208
208
|
|
209
209
|
|
210
|
-
#
|
210
|
+
# @api private
|
211
211
|
class TimerDecisionStateMachine < DecisionStateMachineBase
|
212
212
|
attr_accessor :cancelled
|
213
213
|
def initialize(decision_id, attributes)
|
@@ -257,7 +257,7 @@ module AWS
|
|
257
257
|
end
|
258
258
|
|
259
259
|
|
260
|
-
#
|
260
|
+
# @api private
|
261
261
|
class SignalDecisionStateMachine < DecisionStateMachineBase
|
262
262
|
def initialize(decision_id, attributes)
|
263
263
|
@attributes = attributes
|
@@ -305,7 +305,7 @@ module AWS
|
|
305
305
|
end
|
306
306
|
|
307
307
|
|
308
|
-
#
|
308
|
+
# @api private
|
309
309
|
class ChildWorkflowDecisionStateMachine < DecisionStateMachineBase
|
310
310
|
attr_accessor :run_id, :attributes
|
311
311
|
def initialize(decision_id, attributes)
|
@@ -17,11 +17,11 @@ module AWS
|
|
17
17
|
module Flow
|
18
18
|
|
19
19
|
|
20
|
-
# A decision task handler to work with a {WorkflowTaskPoller}. Create a DecisionTaskHandler and pass it to
|
21
|
-
# WorkflowTaskPoller on {WorkflowTaskPoller#initialize construction}.
|
20
|
+
# A decision task handler to work with a {WorkflowTaskPoller}. Create a `DecisionTaskHandler` and pass it to
|
21
|
+
# {WorkflowTaskPoller} on {WorkflowTaskPoller#initialize construction}.
|
22
22
|
class DecisionTaskHandler
|
23
23
|
|
24
|
-
# Creates a new DecisionTaskHandler
|
24
|
+
# Creates a new `DecisionTaskHandler`.
|
25
25
|
#
|
26
26
|
# @param workflow_definition_map
|
27
27
|
#
|
@@ -35,7 +35,7 @@ module AWS
|
|
35
35
|
end
|
36
36
|
|
37
37
|
|
38
|
-
# Handles a decision task
|
38
|
+
# Handles a decision task.
|
39
39
|
#
|
40
40
|
# @param decision_task_iterator
|
41
41
|
#
|
@@ -58,7 +58,7 @@ module AWS
|
|
58
58
|
#
|
59
59
|
# @param history_helper
|
60
60
|
#
|
61
|
-
# @return [AsyncDecider]
|
61
|
+
# @return [AsyncDecider] The created {AsyncDecider}.
|
62
62
|
#
|
63
63
|
def create_async_decider(history_helper)
|
64
64
|
task = history_helper.get_decision_task
|
@@ -21,7 +21,7 @@ module AWS
|
|
21
21
|
class WorkflowTaskPoller
|
22
22
|
|
23
23
|
|
24
|
-
# Creates a new WorkflowTaskPoller
|
24
|
+
# Creates a new `WorkflowTaskPoller`.
|
25
25
|
#
|
26
26
|
# @param service
|
27
27
|
# The Amazon SWF service object on which this task poller will operate.
|
@@ -72,15 +72,46 @@ module AWS
|
|
72
72
|
end
|
73
73
|
@service.respond_decision_task_completed(task_completed_request)
|
74
74
|
rescue AWS::SimpleWorkflow::Errors::UnknownResourceFault => e
|
75
|
-
@logger.
|
76
|
-
@logger.
|
75
|
+
@logger.error "Error in the poller, #{e}"
|
76
|
+
@logger.error "The error class in #{e.class}"
|
77
77
|
rescue Exception => e
|
78
|
-
@logger.
|
78
|
+
@logger.error "Error in the poller, #{e}"
|
79
79
|
end
|
80
80
|
end
|
81
81
|
end
|
82
82
|
|
83
|
+
# A poller for activity tasks.
|
84
|
+
#
|
83
85
|
class ActivityTaskPoller
|
86
|
+
|
87
|
+
# Initializes a new `ActivityTaskPoller`.
|
88
|
+
#
|
89
|
+
# @param service
|
90
|
+
# *Required*. The AWS::SimpleWorkflow instance to use.
|
91
|
+
#
|
92
|
+
# @param domain
|
93
|
+
# *Required*. The domain used by the workflow.
|
94
|
+
#
|
95
|
+
# @param task_list
|
96
|
+
# *Required*. The task list used to poll for activity tasks.
|
97
|
+
#
|
98
|
+
# @param activity_definition_map
|
99
|
+
# *Required*. The {ActivityDefinition} instance that implements the
|
100
|
+
# activity to run. This map is in the form:
|
101
|
+
#
|
102
|
+
# { :activity_type => 'activity_definition_name' }
|
103
|
+
#
|
104
|
+
# The named activity definition will be run when the {#execute} method
|
105
|
+
# is called.
|
106
|
+
#
|
107
|
+
# @param options
|
108
|
+
# *Optional*. Options to set for the activity poller. You can set the
|
109
|
+
# following options:
|
110
|
+
#
|
111
|
+
# * `logger` - The logger to use.
|
112
|
+
# * `max_workers` - The maximum number of workers that can be running at
|
113
|
+
# once. The default is 20.
|
114
|
+
#
|
84
115
|
def initialize(service, domain, task_list, activity_definition_map, executor, options=nil)
|
85
116
|
@service = service
|
86
117
|
@domain = domain
|
@@ -91,6 +122,13 @@ module AWS
|
|
91
122
|
@executor = executor
|
92
123
|
end
|
93
124
|
|
125
|
+
# Executes the specified activity task.
|
126
|
+
#
|
127
|
+
# @param task
|
128
|
+
# *Required*. The
|
129
|
+
# [AWS::SimpleWorkflow::ActivityTask](http://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SimpleWorkflow/ActivityTask.html)
|
130
|
+
# object to run.
|
131
|
+
#
|
94
132
|
def execute(task)
|
95
133
|
activity_type = task.activity_type
|
96
134
|
begin
|
@@ -106,13 +144,34 @@ module AWS
|
|
106
144
|
@service.respond_activity_task_completed(:task_token => task.task_token, :result => output)
|
107
145
|
end
|
108
146
|
rescue ActivityFailureException => e
|
109
|
-
@logger.
|
147
|
+
@logger.error "The activity failed, with original output of #{original_result} and dataconverted result of #{output}. aws-flow will now attempt to fail it."
|
110
148
|
respond_activity_task_failed_with_retry(task.task_token, e.message, e.details)
|
111
149
|
|
112
150
|
end
|
113
151
|
#TODO all the completion stuffs
|
114
152
|
end
|
115
153
|
|
154
|
+
# Responds to the decider that the activity task has failed, and attempts
|
155
|
+
# to retry the task.
|
156
|
+
#
|
157
|
+
# @note Retry behavior for this method is currently *not implemented*. For
|
158
|
+
# now, it simply wraps {#respond_activity_task_failed}.
|
159
|
+
#
|
160
|
+
# @param task_token
|
161
|
+
# *Required*. The task token from the {ActivityDefinition} object to
|
162
|
+
# retry. The task token is generated by the service and should be
|
163
|
+
# treated as an opaque value.
|
164
|
+
#
|
165
|
+
# @param reason
|
166
|
+
# *Required*. Description of the error that may assist in diagnostics.
|
167
|
+
# Although this value is *required*, you can set it to an empty string
|
168
|
+
# if you don't need this information.
|
169
|
+
#
|
170
|
+
# @param details
|
171
|
+
# *Required*. Detailed information about the failure. Although this
|
172
|
+
# value is *required*, you can set it to an empty string if you don't
|
173
|
+
# need this information.
|
174
|
+
#
|
116
175
|
def respond_activity_task_failed_with_retry(task_token, reason, details)
|
117
176
|
#TODO Set up this variable
|
118
177
|
if @failure_retrier.nil?
|
@@ -121,6 +180,20 @@ module AWS
|
|
121
180
|
end
|
122
181
|
end
|
123
182
|
|
183
|
+
# Responds to the decider that the activity task should be canceled, and
|
184
|
+
# attempts to retry the task.
|
185
|
+
#
|
186
|
+
# @note Retry behavior for this method is currently *not implemented*. For
|
187
|
+
# now, it simply wraps {#respond_activity_task_canceled}.
|
188
|
+
#
|
189
|
+
# @param task_token
|
190
|
+
# *Required*. The task token from the {ActivityDefinition} object to
|
191
|
+
# retry.
|
192
|
+
#
|
193
|
+
# @param message
|
194
|
+
# *Required*. A message that provides detail about why the activity task
|
195
|
+
# is cancelled.
|
196
|
+
#
|
124
197
|
def respond_activity_task_canceled_with_retry(task_token, message)
|
125
198
|
if @failure_retrier.nil?
|
126
199
|
respond_activity_task_canceled(task_token, message)
|
@@ -128,20 +201,68 @@ module AWS
|
|
128
201
|
#TODO Set up other stuff to do if we have it
|
129
202
|
end
|
130
203
|
|
204
|
+
# Responds to the decider that the activity task should be canceled. No
|
205
|
+
# retry is attempted.
|
206
|
+
#
|
207
|
+
# @param task_token
|
208
|
+
# *Required*. The task token from the {ActivityDefinition} object to
|
209
|
+
# retry.
|
210
|
+
#
|
211
|
+
# @param message
|
212
|
+
# *Required*. A message that provides detail about why the activity task
|
213
|
+
# is cancelled.
|
214
|
+
#
|
131
215
|
def respond_activity_task_canceled(task_token, message)
|
132
216
|
@service.respond_activity_task_canceled({:task_token => task_token, :details => message})
|
133
217
|
end
|
134
218
|
|
219
|
+
# Responds to the decider that the activity task has failed. No retry is
|
220
|
+
# attempted.
|
221
|
+
#
|
222
|
+
# @param task_token
|
223
|
+
# *Required*. The task token from the {ActivityDefinition} object to
|
224
|
+
# retry. The task token is generated by the service and should be
|
225
|
+
# treated as an opaque value.
|
226
|
+
#
|
227
|
+
# @param reason
|
228
|
+
# *Required*. Description of the error that may assist in diagnostics.
|
229
|
+
# Although this value is *required*, you can set it to an empty string
|
230
|
+
# if you don't need this information.
|
231
|
+
#
|
232
|
+
# @param details
|
233
|
+
# *Required*. Detailed information about the failure. Although this
|
234
|
+
# value is *required*, you can set it to an empty string if you don't
|
235
|
+
# need this information.
|
236
|
+
#
|
135
237
|
def respond_activity_task_failed(task_token, reason, details)
|
136
238
|
@logger.debug "The task token to be reported on is #{task_token}"
|
137
239
|
@service.respond_activity_task_failed(:task_token => task_token, :reason => reason.to_s, :details => details.to_s)
|
138
240
|
end
|
139
241
|
|
242
|
+
# Processes the specified activity task.
|
243
|
+
#
|
244
|
+
# @param task
|
245
|
+
# *Required*. The
|
246
|
+
# [AWS::SimpleWorkflow::ActivityTask](http://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SimpleWorkflow/ActivityTask.html)
|
247
|
+
# object to process.
|
248
|
+
#
|
140
249
|
def process_single_task(task)
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
250
|
+
|
251
|
+
# We are using the 'build' method to create a new ConnectionPool here to
|
252
|
+
# make sure that connection pools are not shared among forked processes.
|
253
|
+
# The default behavior of the ConnectionPool class is to cache a pool
|
254
|
+
# for a set of options created by the 'new' method and always use the
|
255
|
+
# same pool for the same set of options. This is undesirable when
|
256
|
+
# multiple processes want to use different connection pools with same
|
257
|
+
# options as is the case here.
|
258
|
+
# Since we can't change the pool of an already existing NetHttpHandler,
|
259
|
+
# we also create a new NetHttpHandler in order to use the new pool.
|
260
|
+
|
261
|
+
options = @service.config.to_h
|
262
|
+
options[:connection_pool] = AWS::Core::Http::ConnectionPool.build(options[:http_handler].pool.options)
|
263
|
+
options[:http_handler] = AWS::Core::Http::NetHttpHandler.new(options)
|
264
|
+
@service = AWS::SimpleWorkflow.new(options).client
|
265
|
+
|
145
266
|
begin
|
146
267
|
begin
|
147
268
|
execute(task)
|
@@ -157,13 +278,24 @@ module AWS
|
|
157
278
|
end
|
158
279
|
rescue Exception => e
|
159
280
|
semaphore_needs_release = true
|
160
|
-
@logger.
|
281
|
+
@logger.error "Got into the other error mode"
|
161
282
|
raise e
|
162
283
|
ensure
|
163
284
|
@poll_semaphore.release if semaphore_needs_release
|
164
285
|
end
|
165
286
|
end
|
166
287
|
|
288
|
+
# Polls the task list for a new activity task, and executes it if one is
|
289
|
+
# found.
|
290
|
+
#
|
291
|
+
# If `use_forking` is set to `true` and the maximum number of workers (as
|
292
|
+
# set in {#initialize}) are already executing, this method will block
|
293
|
+
# until the number of running workers is less than the maximum.
|
294
|
+
#
|
295
|
+
# @param use_forking
|
296
|
+
# *Optional*. Whether to use forking to execute the task. On Windows,
|
297
|
+
# you should set this to `false`.
|
298
|
+
#
|
167
299
|
def poll_and_process_single_task(use_forking = true)
|
168
300
|
@poll_semaphore ||= SuspendableSemaphore.new
|
169
301
|
@poll_semaphore.acquire
|
@@ -171,16 +303,17 @@ module AWS
|
|
171
303
|
@logger.debug "before the poll\n\n"
|
172
304
|
# This is to warm the lazily loaded clients in the @service, so we don't
|
173
305
|
# pay for their loading in every forked client
|
174
|
-
@service.config.to_h
|
175
306
|
begin
|
176
307
|
if use_forking
|
177
308
|
@executor.block_on_max_workers
|
178
309
|
end
|
179
310
|
task = @domain.activity_tasks.poll_for_single_task(@task_list)
|
180
|
-
|
181
|
-
|
311
|
+
if task
|
312
|
+
@logger.info "got a task, #{task.activity_type.name}"
|
313
|
+
@logger.info "The task token I got was: #{task.task_token}"
|
314
|
+
end
|
182
315
|
rescue Exception => e
|
183
|
-
@logger.
|
316
|
+
@logger.error "I have not been able to poll successfully, and am now bailing out, with error #{e}"
|
184
317
|
@poll_semaphore.release
|
185
318
|
return false
|
186
319
|
end
|
@@ -202,15 +335,19 @@ module AWS
|
|
202
335
|
end
|
203
336
|
end
|
204
337
|
|
338
|
+
# @api private
|
339
|
+
# @note This class is currently not implemented.
|
205
340
|
class SuspendableSemaphore
|
206
341
|
|
342
|
+
# @note This method is not implemented.
|
207
343
|
def initialize
|
208
|
-
|
209
344
|
end
|
210
345
|
|
346
|
+
# @note This method is not implemented.
|
211
347
|
def acquire
|
212
348
|
end
|
213
349
|
|
350
|
+
# @note This method is not implemented.
|
214
351
|
def release
|
215
352
|
end
|
216
353
|
end
|
@@ -17,9 +17,9 @@ require 'tmpdir'
|
|
17
17
|
|
18
18
|
module AWS
|
19
19
|
module Flow
|
20
|
-
# Utilities for the AWS Flow Framework for Ruby
|
20
|
+
# Utilities for the AWS Flow Framework for Ruby.
|
21
21
|
module Utilities
|
22
|
-
#
|
22
|
+
# @api private
|
23
23
|
class LogFactory
|
24
24
|
def self.make_logger(klass, name)
|
25
25
|
logname = "#{Dir.tmpdir}/#{klass.class.to_s}_#{name}"
|
@@ -31,7 +31,7 @@ module AWS
|
|
31
31
|
end
|
32
32
|
|
33
33
|
|
34
|
-
#
|
34
|
+
# @api private
|
35
35
|
def self.drill_on_future(future)
|
36
36
|
while (future.respond_to? :is_flow_future?) && future.is_flow_future?
|
37
37
|
future = future.get
|
@@ -39,7 +39,7 @@ module AWS
|
|
39
39
|
future
|
40
40
|
end
|
41
41
|
|
42
|
-
#
|
42
|
+
# @api private
|
43
43
|
def self.merge_all_options(*args)
|
44
44
|
args.compact!
|
45
45
|
youngest = args.last
|
@@ -49,7 +49,7 @@ module AWS
|
|
49
49
|
end
|
50
50
|
|
51
51
|
|
52
|
-
#
|
52
|
+
# @api private
|
53
53
|
def self.interpret_block_for_options(option_class, block, use_defaults = false)
|
54
54
|
|
55
55
|
return option_class.new({}, use_defaults) if block.nil?
|
@@ -87,7 +87,7 @@ module AWS
|
|
87
87
|
@return_value = Future.new
|
88
88
|
end
|
89
89
|
|
90
|
-
#
|
90
|
+
# Determines whether the object is a flow future. The contract is that
|
91
91
|
# flow futures must have a #get method.
|
92
92
|
def is_flow_future?
|
93
93
|
true
|
@@ -102,7 +102,7 @@ module AWS
|
|
102
102
|
end
|
103
103
|
end
|
104
104
|
|
105
|
-
#
|
105
|
+
# @api private
|
106
106
|
def self.is_external
|
107
107
|
if (defined? Fiber).nil?
|
108
108
|
return true
|
@@ -112,9 +112,9 @@ module AWS
|
|
112
112
|
return true
|
113
113
|
end
|
114
114
|
|
115
|
-
#
|
115
|
+
# @api private
|
116
116
|
module SelfMethods
|
117
|
-
#
|
117
|
+
# @api private
|
118
118
|
def handle_event(event, options)
|
119
119
|
id = options[:id_lambda].call(event) if options[:id_lambda]
|
120
120
|
id = event.attributes
|
@@ -139,11 +139,11 @@ module AWS
|
|
139
139
|
end
|
140
140
|
end
|
141
141
|
|
142
|
-
#
|
142
|
+
# @api private
|
143
143
|
module UpwardLookups
|
144
144
|
attr_accessor :precursors
|
145
145
|
|
146
|
-
#
|
146
|
+
# @api private
|
147
147
|
def held_properties
|
148
148
|
precursors = self.ancestors.dup
|
149
149
|
precursors.delete(self)
|
@@ -152,7 +152,7 @@ module AWS
|
|
152
152
|
result.flatten
|
153
153
|
end
|
154
154
|
|
155
|
-
#
|
155
|
+
# @api private
|
156
156
|
def property(name, methods_to_prepare = [lambda(&:to_s)])
|
157
157
|
@held_properties ||= []
|
158
158
|
@held_properties << name
|
@@ -170,12 +170,12 @@ module AWS
|
|
170
170
|
end
|
171
171
|
end
|
172
172
|
|
173
|
-
#
|
173
|
+
# @api private
|
174
174
|
def properties(*args)
|
175
175
|
args.each { |arg| property(arg) }
|
176
176
|
end
|
177
177
|
|
178
|
-
#
|
178
|
+
# @api private
|
179
179
|
module InstanceMethods
|
180
180
|
attr_accessor :precursors
|
181
181
|
def look_upwards(variable)
|