configurable 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History +7 -0
- data/lib/configurable.rb +5 -9
- data/lib/configurable/class_methods.rb +199 -181
- data/lib/configurable/delegate.rb +7 -8
- data/lib/configurable/delegate_hash.rb +15 -20
- metadata +2 -2
data/History
CHANGED
data/lib/configurable.rb
CHANGED
@@ -111,18 +111,14 @@ require 'configurable/class_methods'
|
|
111
111
|
# alt.set_sym('two')
|
112
112
|
# alt.config[:sym] # => :two
|
113
113
|
#
|
114
|
-
# Idiosyncratically, true
|
115
|
-
#
|
114
|
+
# Idiosyncratically, true and false may also be provided as reader/writer
|
115
|
+
# values.
|
116
116
|
#
|
117
|
-
#
|
118
|
-
#
|
119
|
-
# false Sets the default reader/writer but does not define
|
117
|
+
# true:: Same as using the defaults, accessors are defined.
|
118
|
+
# false:: Sets the default reader/writer but does not define
|
120
119
|
# the accessors (think 'define reader/writer' => false).
|
121
120
|
#
|
122
|
-
#
|
123
|
-
# the accessors. In effect this will define a config
|
124
|
-
# that does not map to the instance, but will be
|
125
|
-
# present in instance.config
|
121
|
+
# Nil is not allowed as a value.
|
126
122
|
#
|
127
123
|
# ==== Non-reader/writer attributes
|
128
124
|
#
|
@@ -133,223 +133,224 @@ module Configurable
|
|
133
133
|
# end
|
134
134
|
# end
|
135
135
|
#
|
136
|
+
# === Attributes
|
137
|
+
#
|
138
|
+
# Several attributes may be specified to modify how a config is constructed.
|
139
|
+
# Attribute keys should be specified as symbols.
|
140
|
+
#
|
141
|
+
# Attribute:: Description
|
142
|
+
# reader:: The method used to read the configuration.
|
143
|
+
# (default: key)
|
144
|
+
# writer:: The method used to write the configuration
|
145
|
+
# (default: "#{key}=")
|
146
|
+
#
|
147
|
+
# Neither attribute may be set to nil, but they may be set to non-default
|
148
|
+
# values. In that case, config_attr will register the method names as
|
149
|
+
# provided, but it will not define the methods themselves. Specifying true
|
150
|
+
# uses and defines the default methods. Specifying false uses the default
|
151
|
+
# method name, but does not define the method itself.
|
152
|
+
#
|
153
|
+
# Any additional attributes are registered with the configuration.
|
136
154
|
def config_attr(key, value=nil, attributes={}, &block)
|
137
155
|
attributes = merge_attributes(block, attributes)
|
138
156
|
|
139
|
-
# define the
|
140
|
-
reader =
|
141
|
-
|
142
|
-
|
143
|
-
when true
|
144
|
-
reader = key
|
145
|
-
attr_reader(key)
|
146
|
-
public(key)
|
147
|
-
when false
|
148
|
-
reader = key
|
157
|
+
# define the reader
|
158
|
+
reader = define_attribute_method(:reader, attributes, key) do |attribute|
|
159
|
+
attr_reader(attribute)
|
160
|
+
public(attribute)
|
149
161
|
end
|
150
|
-
|
151
|
-
# define the
|
152
|
-
|
153
|
-
|
154
|
-
if block_given? && writer != true
|
162
|
+
|
163
|
+
# define the writer
|
164
|
+
if block_given? && attributes[:writer] != true
|
155
165
|
raise ArgumentError, "a block may not be specified without writer == true"
|
156
166
|
end
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
block_given? ? define_method(writer, &block) : attr_writer(key)
|
162
|
-
public writer
|
163
|
-
when false
|
164
|
-
writer = "#{key}="
|
167
|
+
|
168
|
+
writer = define_attribute_method(:writer, attributes, "#{key}=") do |attribute|
|
169
|
+
block_given? ? define_method(attribute, &block) : attr_writer(key)
|
170
|
+
public(attribute)
|
165
171
|
end
|
166
|
-
|
172
|
+
|
167
173
|
configurations[key] = Delegate.new(reader, writer, value, attributes)
|
168
174
|
end
|
169
|
-
|
170
|
-
# Adds
|
171
|
-
#
|
172
|
-
#
|
173
|
-
# the instance config
|
175
|
+
|
176
|
+
# Adds nested configurations to self. Nest creates a new configurable
|
177
|
+
# class using the block, and provides accessors to an instance of the
|
178
|
+
# new class. Everything is set up so you can access configs through
|
179
|
+
# the instance or through config.
|
174
180
|
#
|
175
181
|
# class A
|
176
182
|
# include Configurable
|
177
|
-
# config :key, 'value'
|
178
183
|
#
|
179
|
-
#
|
180
|
-
#
|
184
|
+
# config :key, 'one'
|
185
|
+
# nest :nest do
|
186
|
+
# config :key, 'two'
|
181
187
|
# end
|
182
188
|
# end
|
183
189
|
#
|
184
|
-
#
|
185
|
-
#
|
186
|
-
#
|
190
|
+
# a = A.new
|
191
|
+
# a.key # => 'one'
|
192
|
+
# a.config[:key] # => 'one'
|
187
193
|
#
|
188
|
-
#
|
189
|
-
#
|
190
|
-
#
|
191
|
-
#
|
194
|
+
# a.nest.key # => 'two'
|
195
|
+
# a.config[:nest][:key] # => 'two'
|
196
|
+
#
|
197
|
+
# a.nest.key = 'TWO'
|
198
|
+
# a.config[:nest][:key] # => 'TWO'
|
192
199
|
#
|
193
|
-
#
|
194
|
-
#
|
200
|
+
# a.config[:nest][:key] = 2
|
201
|
+
# a.nest.key # => 2
|
195
202
|
#
|
196
|
-
#
|
197
|
-
#
|
198
|
-
#
|
203
|
+
# a.config.to_hash # => {:key => 'one', :nest => {:key => 2}}
|
204
|
+
# a.nest.config.to_hash # => {:key => 2}
|
205
|
+
# a.nest.class # => A::Nest
|
199
206
|
#
|
200
|
-
#
|
207
|
+
# An existing configurable class may be provided instead of using the block
|
208
|
+
# to define a new configurable class. Recursive nesting is supported.
|
209
|
+
#
|
210
|
+
# class B
|
201
211
|
# include Configurable
|
202
|
-
# nest(:a, A) {|overrides| A.new(overrides) }
|
203
212
|
#
|
204
|
-
#
|
205
|
-
#
|
213
|
+
# config :key, 1, &c.integer
|
214
|
+
# nest :nest do
|
215
|
+
# config :key, 2, &c.integer
|
216
|
+
# nest :nest do
|
217
|
+
# config :key, 3, &c.integer
|
218
|
+
# end
|
206
219
|
# end
|
207
220
|
# end
|
208
221
|
#
|
209
|
-
#
|
210
|
-
#
|
211
|
-
#
|
212
|
-
#
|
213
|
-
# c.config[:a].to_hash # => {:key => 'one'}
|
214
|
-
#
|
215
|
-
# c.config[:a][:key] = 'two'
|
216
|
-
# c.a.key # => "two"
|
217
|
-
#
|
218
|
-
# c.config[:a] = {:key => 'three'}
|
219
|
-
# c.a.key # => "three"
|
220
|
-
#
|
221
|
-
# The initialize block executes in class context, much like config.
|
222
|
-
#
|
223
|
-
# # An equivalent class to illustrate class-context
|
224
|
-
# class EquivalentClass
|
225
|
-
# attr_reader :a, A
|
226
|
-
#
|
227
|
-
# INITIALIZE_BLOCK = lambda {|overrides| A.new(overrides) }
|
228
|
-
#
|
229
|
-
# def initialize(overrides={})
|
230
|
-
# @a = INITIALIZE_BLOCK.call(overrides[:a] || {})
|
231
|
-
# end
|
222
|
+
# class C
|
223
|
+
# include Configurable
|
224
|
+
# nest :a, A
|
225
|
+
# nest :b, B
|
232
226
|
# end
|
233
227
|
#
|
234
|
-
#
|
235
|
-
#
|
236
|
-
#
|
237
|
-
#
|
238
|
-
#
|
239
|
-
#
|
240
|
-
#
|
241
|
-
#
|
242
|
-
#
|
243
|
-
#
|
244
|
-
#
|
245
|
-
# :
|
246
|
-
# :
|
247
|
-
# :
|
248
|
-
# :
|
249
|
-
#
|
250
|
-
#
|
251
|
-
#
|
252
|
-
#
|
253
|
-
|
228
|
+
# c = C.new
|
229
|
+
# c.b.key = 7
|
230
|
+
# c.b.nest.key = "8"
|
231
|
+
# c.config[:b][:nest][:nest][:key] = "9"
|
232
|
+
#
|
233
|
+
# c.config.to_hash
|
234
|
+
# # => {
|
235
|
+
# # :a => {
|
236
|
+
# # :key => 'one',
|
237
|
+
# # :nest => {:key => 'two'}
|
238
|
+
# # },
|
239
|
+
# # :b => {
|
240
|
+
# # :key => 7,
|
241
|
+
# # :nest => {
|
242
|
+
# # :key => 8,
|
243
|
+
# # :nest => {:key => 9}
|
244
|
+
# # }
|
245
|
+
# # }}
|
246
|
+
#
|
247
|
+
# === Attributes
|
248
|
+
#
|
249
|
+
# Nest provides a number of attributes that can modify how a nest is
|
250
|
+
# constructed. Attribute keys should be specified as symbols.
|
251
|
+
#
|
252
|
+
# Attribute:: Description
|
253
|
+
# const_name:: Determines the constant name of the configurable
|
254
|
+
# class within the nesting class. May be nil.
|
255
|
+
# (default: key.to_s.capitalize)
|
256
|
+
# instance_reader:: The method accessing the nested instance. (default: key)
|
257
|
+
# instance_writer:: The method to set the nested instance. (default: "#{key}=")
|
258
|
+
# instance_initializer:: The method that initializes the instance.
|
259
|
+
# (default: "initialize_#{key}")
|
260
|
+
# reader:: The method used to read the instance configuration.
|
261
|
+
# (default: "#{key}_config_reader")
|
262
|
+
# writer:: The method used to initialize or reconfigure the
|
263
|
+
# instance. (default: "#{key}_config_writer")
|
264
|
+
#
|
265
|
+
# Except for const_name, these attributes are used to define methods
|
266
|
+
# required for nesting to work properly. None of the method attributes may
|
267
|
+
# be set to nil, but they may be set to non-default values. In that case,
|
268
|
+
# nest will register the method names as provided, but it will not define
|
269
|
+
# the methods themselves. The user must define methods with the following
|
270
|
+
# functionality:
|
271
|
+
#
|
272
|
+
# Attribute:: Function
|
273
|
+
# instance_reader:: Returns the instance of the configurable class
|
274
|
+
# instance_writer:: Inputs and sets the instance of the configurable class
|
275
|
+
# instance_initializer:: Receives the initial config and return an instance of
|
276
|
+
# configurable class
|
277
|
+
# reader:: Returns instance.config
|
278
|
+
# writer:: Reconfigures instance using the input overrides,
|
279
|
+
# or uses instance_initializer and instance_writer to
|
280
|
+
# initialize and set the instance.
|
281
|
+
#
|
282
|
+
# Methods can be public or otherwise. Specifying true uses and defines the
|
283
|
+
# default methods. Specifying false uses the default method name, but does
|
284
|
+
# not define the method itself.
|
285
|
+
#
|
286
|
+
# Any additional attributes are registered with the configuration.
|
287
|
+
def nest(key, configurable_class=nil, attributes={}, &block)
|
254
288
|
attributes = merge_attributes(block, attributes)
|
289
|
+
attributes = {
|
290
|
+
:instance_reader => true,
|
291
|
+
:instance_writer => true,
|
292
|
+
:initializer => true
|
293
|
+
}.merge(attributes)
|
255
294
|
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
instance_variable_set(instance_variable, yield(input))
|
260
|
-
end
|
295
|
+
# define the nested configurable
|
296
|
+
if configurable_class
|
297
|
+
raise "a block is not allowed when a configurable class is specified" if block_given?
|
261
298
|
else
|
262
|
-
|
299
|
+
configurable_class = Class.new { include Configurable }
|
300
|
+
configurable_class.class_eval(&block) if block_given?
|
263
301
|
end
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
# nest(:a, A) {|overrides| A.new(overrides) }
|
271
|
-
#
|
272
|
-
# def initialize(overrides={})
|
273
|
-
# initialize_config(overrides)
|
274
|
-
# end
|
275
|
-
# end
|
276
|
-
#
|
277
|
-
# # An equivalent class to illustrate instance-context
|
278
|
-
# class EquivalentClass
|
279
|
-
# attr_reader :a, A
|
280
|
-
#
|
281
|
-
# def a_initialize(overrides)
|
282
|
-
# A.new(overrides)
|
283
|
-
# end
|
284
|
-
#
|
285
|
-
# def initialize(overrides={})
|
286
|
-
# @a = send(:a_initialize, overrides[:a] || {})
|
287
|
-
# end
|
288
|
-
# end
|
289
|
-
#
|
290
|
-
def nest_attr(key, configurable_class, attributes={}, &block)
|
291
|
-
unless configurable_class.kind_of?(Configurable::ClassMethods)
|
292
|
-
raise ArgumentError, "not a Configurable class: #{configurable_class}"
|
302
|
+
|
303
|
+
# set the new constant
|
304
|
+
const_name = if attributes.has_key?(:const_name)
|
305
|
+
attributes.delete(:const_name)
|
306
|
+
else
|
307
|
+
key.to_s.capitalize
|
293
308
|
end
|
309
|
+
const_set(const_name, configurable_class) if const_name
|
294
310
|
|
295
|
-
|
311
|
+
# define instance reader
|
312
|
+
instance_reader = define_attribute_method(:instance_reader, attributes, key) do |attribute|
|
313
|
+
attr_reader(key)
|
314
|
+
public(key)
|
315
|
+
end
|
316
|
+
|
317
|
+
# define instance writer
|
318
|
+
instance_writer = define_attribute_method(:instance_writer, attributes, "#{key}=") do |attribute|
|
319
|
+
attr_writer(key)
|
320
|
+
public(attribute)
|
321
|
+
end
|
296
322
|
|
297
|
-
#
|
298
|
-
|
323
|
+
# define initializer
|
324
|
+
initializer = define_attribute_method(:initializer, attributes, "initialize_#{key}") do |attribute|
|
325
|
+
define_method(attribute) {|config| configurable_class.new.reconfigure(config) }
|
326
|
+
private(attribute)
|
327
|
+
end
|
299
328
|
|
300
|
-
#
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
writer = attributes.delete(:writer)
|
329
|
+
# define the reader
|
330
|
+
reader = define_attribute_method(:reader, attributes, "#{key}_config_reader") do |attribute|
|
331
|
+
define_method(attribute) { send(instance_reader).config }
|
332
|
+
private(attribute)
|
333
|
+
end
|
306
334
|
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
initializer = boolean_select(reader, "#{key}_initialize")
|
314
|
-
reader = boolean_select(reader, "#{key}_config_reader")
|
315
|
-
writer = boolean_select(writer, "#{key}_config_writer")
|
316
|
-
|
317
|
-
# the public accessor
|
318
|
-
attr_reader instance_reader
|
319
|
-
|
320
|
-
define_method(instance_writer) do |value|
|
321
|
-
instance_variable_set(instance_var, value)
|
322
|
-
end
|
323
|
-
public(instance_reader, instance_writer)
|
324
|
-
|
325
|
-
# the initializer
|
326
|
-
define_method(initializer, &block)
|
327
|
-
|
328
|
-
# the reader returns the config for the instance
|
329
|
-
define_method(reader) do
|
330
|
-
instance_variable_get(instance_var).config
|
331
|
-
end
|
332
|
-
|
333
|
-
# the writer initializes the instance if necessary,
|
334
|
-
# or reconfigures the instance if it already exists
|
335
|
-
define_method(writer) do |value|
|
336
|
-
if instance_variable_defined?(instance_var)
|
337
|
-
instance_variable_get(instance_var).reconfigure(value)
|
335
|
+
# define the writer
|
336
|
+
writer = define_attribute_method(:writer, attributes, "#{key}_config_writer") do |attribute|
|
337
|
+
define_method(attribute) do |value|
|
338
|
+
if instance = send(instance_reader)
|
339
|
+
instance.reconfigure(value)
|
338
340
|
else
|
339
|
-
|
341
|
+
send(instance_writer, send(initializer, value))
|
340
342
|
end
|
341
343
|
end
|
342
|
-
private(
|
343
|
-
else
|
344
|
-
reader = writer = nil
|
344
|
+
private(attribute)
|
345
345
|
end
|
346
346
|
|
347
|
-
|
348
|
-
|
349
|
-
|
347
|
+
# define the configuration
|
348
|
+
nested_config = DelegateHash.new(configurable_class.configurations)
|
349
|
+
configurations[key] = Delegate.new(reader, writer, nested_config, attributes)
|
350
|
+
|
350
351
|
check_infinite_nest(configurable_class.configurations)
|
351
|
-
end
|
352
|
-
|
352
|
+
end
|
353
|
+
|
353
354
|
# Alias for Validation
|
354
355
|
def c
|
355
356
|
Validation
|
@@ -357,13 +358,30 @@ module Configurable
|
|
357
358
|
|
358
359
|
private
|
359
360
|
|
360
|
-
# a helper to
|
361
|
-
#
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
361
|
+
# a helper to define methods that may be overridden in attributes.
|
362
|
+
# yields the default to the block if the default is supposed to
|
363
|
+
# be defined. returns the symbolized method name.
|
364
|
+
def define_attribute_method(name, attributes, default) # :nodoc:
|
365
|
+
attribute = attributes.delete(name)
|
366
|
+
|
367
|
+
case attribute
|
368
|
+
when true
|
369
|
+
# true means use the default and define the method
|
370
|
+
attribute = default
|
371
|
+
yield(attribute)
|
372
|
+
|
373
|
+
when false
|
374
|
+
# false means use the default, but let the user define the method
|
375
|
+
attribute = default
|
376
|
+
|
377
|
+
when nil
|
378
|
+
# nil is not allowed
|
379
|
+
raise "#{name.inspect} attribute cannot be nil"
|
366
380
|
end
|
381
|
+
# ... all other values specify what the method should be,
|
382
|
+
# and lets the user define the method.
|
383
|
+
|
384
|
+
attribute.to_sym
|
367
385
|
end
|
368
386
|
|
369
387
|
# a helper to initialize configurations for the first time,
|
@@ -27,8 +27,7 @@ module Configurable
|
|
27
27
|
# line, in a web form, or a desktop app).
|
28
28
|
attr_reader :attributes
|
29
29
|
|
30
|
-
# Initializes a new Delegate with the specified key
|
31
|
-
# and default value.
|
30
|
+
# Initializes a new Delegate with the specified key and default value.
|
32
31
|
def initialize(reader, writer="#{reader}=", default=nil, attributes={})
|
33
32
|
self.default = default
|
34
33
|
self.reader = reader
|
@@ -61,16 +60,16 @@ module Configurable
|
|
61
60
|
duplicate && @duplicable ? @default.dup : @default
|
62
61
|
end
|
63
62
|
|
64
|
-
# Sets the reader for self.
|
65
|
-
# but may also be set to nil.
|
63
|
+
# Sets the reader for self.
|
66
64
|
def reader=(value)
|
67
|
-
|
65
|
+
raise ArgumentError, "reader may not be nil" if value == nil
|
66
|
+
@reader = value.to_sym
|
68
67
|
end
|
69
68
|
|
70
|
-
# Sets the writer for self.
|
71
|
-
# but may also be set to nil.
|
69
|
+
# Sets the writer for self.
|
72
70
|
def writer=(value)
|
73
|
-
|
71
|
+
raise ArgumentError, "writer may not be nil" if value == nil
|
72
|
+
@writer = value.to_sym
|
74
73
|
end
|
75
74
|
|
76
75
|
# Returns true if the default value is a kind of DelegateHash.
|
@@ -58,8 +58,8 @@ module Configurable
|
|
58
58
|
end
|
59
59
|
|
60
60
|
# Binds self to the specified receiver. Delegate values are removed from
|
61
|
-
# store and sent to their writer
|
62
|
-
#
|
61
|
+
# store and sent to their writer on receiver. If the store has no value
|
62
|
+
# for a delegate key, the delegate default value will be used.
|
63
63
|
def bind(receiver)
|
64
64
|
raise ArgumentError, "receiver cannot be nil" if receiver == nil
|
65
65
|
|
@@ -89,15 +89,15 @@ module Configurable
|
|
89
89
|
self
|
90
90
|
end
|
91
91
|
|
92
|
-
# Retrieves the value corresponding to the key. When bound, delegates
|
93
|
-
#
|
94
|
-
# be returned. When unbound, if the store has no value
|
95
|
-
# delgate default value will be returned.
|
92
|
+
# Retrieves the value corresponding to the key. When bound, delegates pull
|
93
|
+
# values from the receiver using the delegate.reader method; otherwise the
|
94
|
+
# value in store will be returned. When unbound, if the store has no value
|
95
|
+
# for a delegate, the delgate default value will be returned.
|
96
96
|
def [](key)
|
97
97
|
return store[key] unless delegate = delegates[key]
|
98
98
|
|
99
99
|
case
|
100
|
-
when bound?
|
100
|
+
when bound?
|
101
101
|
receiver.send(delegate.reader)
|
102
102
|
when store.has_key?(key)
|
103
103
|
store[key]
|
@@ -106,17 +106,15 @@ module Configurable
|
|
106
106
|
end
|
107
107
|
end
|
108
108
|
|
109
|
-
# Stores a value for the key. When bound, delegates
|
110
|
-
#
|
109
|
+
# Stores a value for the key. When bound, delegates set the value in the
|
110
|
+
# receiver using the delegate.writer method; otherwise values are stored in
|
111
|
+
# store.
|
111
112
|
def []=(key, value)
|
112
113
|
if bound? && delegate = delegates[key]
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
end
|
114
|
+
receiver.send(delegate.writer, value)
|
115
|
+
else
|
116
|
+
store[key] = value
|
117
117
|
end
|
118
|
-
|
119
|
-
store[key] = value
|
120
118
|
end
|
121
119
|
|
122
120
|
# Returns the union of delegate and store keys.
|
@@ -181,8 +179,6 @@ module Configurable
|
|
181
179
|
# helper to map delegate values from source to the receiver
|
182
180
|
def map(source) # :nodoc:
|
183
181
|
delegates.each_pair do |key, delegate|
|
184
|
-
next unless writer = delegate.writer
|
185
|
-
|
186
182
|
# map the value; if no value is set in the source then use the
|
187
183
|
# delegate default. if map_default is false, then simply skip...
|
188
184
|
# this ensures each config is initialized to a value when bound
|
@@ -193,15 +189,14 @@ module Configurable
|
|
193
189
|
else next
|
194
190
|
end
|
195
191
|
|
196
|
-
receiver.send(writer, value)
|
192
|
+
receiver.send(delegate.writer, value)
|
197
193
|
end
|
198
194
|
end
|
199
195
|
|
200
196
|
# helper to unmap delegates from the receiver to a target hash
|
201
197
|
def unmap(target) # :nodoc:
|
202
198
|
delegates.each_pair do |key, delegate|
|
203
|
-
|
204
|
-
target[key] = receiver.send(reader)
|
199
|
+
target[key] = receiver.send(delegate.reader)
|
205
200
|
end
|
206
201
|
end
|
207
202
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: configurable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Simon Chiang
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-03-05 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|