ultra_settings 0.0.1.rc1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+
5
+ module UltraSettings
6
+ # Utility functions for coercing values to other data types.
7
+ class Coerce
8
+ # rubocop:disable Lint/BooleanSymbol
9
+ FALSE_VALUES = Set.new([
10
+ false, 0,
11
+ "0", :"0",
12
+ "f", :f,
13
+ "F", :F,
14
+ "false", :false,
15
+ "FALSE", :FALSE,
16
+ "off", :off,
17
+ "OFF", :OFF
18
+ ]).freeze
19
+ # rubocop:enable Lint/BooleanSymbol
20
+
21
+ class << self
22
+ # Cast a value to a specific type.
23
+ #
24
+ # @param value [Object]
25
+ # @param type [Symbol]
26
+ # @return [Object]
27
+ def coerce_value(value, type)
28
+ return nil if value.nil? || value == ""
29
+
30
+ case type
31
+ when :integer
32
+ value.is_a?(Integer) ? value : value.to_s&.to_i
33
+ when :float
34
+ value.is_a?(Float) ? value : value.to_s&.to_f
35
+ when :boolean
36
+ Coerce.boolean(value)
37
+ when :datetime
38
+ Coerce.time(value)
39
+ when :array
40
+ Array(value).map(&:to_s)
41
+ when :symbol
42
+ value.to_s.to_sym
43
+ else
44
+ value.to_s
45
+ end
46
+ end
47
+
48
+ # Cast variations of booleans (i.e. "true", "false", 1, 0, etc.) to actual boolean objects.
49
+ #
50
+ # @param value [Object]
51
+ # @return [Boolean]
52
+ def boolean(value)
53
+ if value == false
54
+ false
55
+ elsif blank?(value)
56
+ nil
57
+ else
58
+ !FALSE_VALUES.include?(value)
59
+ end
60
+ end
61
+
62
+ # Cast a value to a Time object.
63
+ #
64
+ # @param value [Object]
65
+ # @return [Time]
66
+ def time(value)
67
+ value = nil if value.nil? || value.to_s.empty?
68
+ return nil if value.nil?
69
+ time = if value.is_a?(Numeric)
70
+ Time.at(value)
71
+ elsif value.respond_to?(:to_time)
72
+ value.to_time
73
+ else
74
+ Time.parse(value.to_s)
75
+ end
76
+ if time.respond_to?(:in_time_zone) && Time.respond_to?(:zone)
77
+ time = time.in_time_zone(Time.zone)
78
+ end
79
+ time
80
+ end
81
+
82
+ # @return [Boolean] true if the value is nil or empty.
83
+ def blank?(value)
84
+ return true if value.nil?
85
+ if value.respond_to?(:empty?)
86
+ value.empty?
87
+ else
88
+ value.to_s.empty?
89
+ end
90
+ end
91
+
92
+ # @return [Boolean] true if the value is not nil and not empty.
93
+ def present?(value)
94
+ !blank?(value)
95
+ end
96
+ end
97
+ end
98
+ end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "singleton"
4
-
5
3
  module UltraSettings
6
4
  class Configuration
7
5
  include Singleton
@@ -9,24 +7,33 @@ module UltraSettings
9
7
  ALLOWED_NAME_PATTERN = /\A[a-z_][a-zA-Z0-9_]*\z/
10
8
  ALLOWED_TYPES = [:string, :symbol, :integer, :float, :boolean, :datetime, :array].freeze
11
9
 
12
- class_attribute :environment_variables_disabled, instance_accessor: false, default: false
13
-
14
- class_attribute :runtime_settings_disabled, instance_accessor: false, default: false
15
-
16
- class_attribute :yaml_config_disabled, instance_accessor: false, default: false
17
-
18
- class_attribute :env_var_delimiter, instance_accessor: false, default: "_"
19
-
20
- class_attribute :setting_delimiter, instance_accessor: false, default: "."
21
-
22
- class_attribute :env_var_upcase, instance_accessor: false, default: true
23
-
24
- class_attribute :setting_upcase, instance_accessor: false, default: false
25
-
26
- class_attribute :yaml_config_directory, instance_accessor: false, default: "config"
27
-
28
10
  class << self
29
- def define(name, type: :string, default: nil, default_if: nil, static: false, setting: nil, env_var: nil, yaml_key: nil)
11
+ # Define a field on the configuration. This will create a getter method for the field.
12
+ # The field value will be read from the environment, runtime settings, or a YAML file
13
+ # and coerced to the specified type. Empty strings will be converted to nil.
14
+ #
15
+ # @param name [Symbol, String] The name of the field.
16
+ # @param type [Symbol] The type of the field. Valid types are :string, :symbol, :integer,
17
+ # :float, :boolean, :datetime, and :array. The default type is :string. The :array type
18
+ # will return an array of strings.
19
+ # @param description [String] A description of the field.
20
+ # @param default [Object] The default value of the field.
21
+ # @param default_if [Proc, Symbol] A proc that returns true if the default value should be used.
22
+ # By default, the default value will be used if the field evaluates to nil. You can also set
23
+ # this to a symbol with the name of an instance method to call.
24
+ # @param static [Boolean] If true, the field value should never be changed. This is useful for
25
+ # fields that are used at startup to set static values in the application. Static field cannot
26
+ # be read from runtime settings.
27
+ # @param runtime_setting [String, Symbol] The name of the runtime setting to use for the field.
28
+ # By default this will be the underscored name of the class plus a dot plus the field name
29
+ # (i.e. MyServiceConfiguration#foo becomes "my_service.foo").
30
+ # @param env_var [String, Symbol] The name of the environment variable to use for the field.
31
+ # By default this will be the underscored name of the class plus an underscore plus the field name
32
+ # all in uppercase (i.e. MyServiceConfiguration#foo becomes "MY_SERVICE_FOO").
33
+ # @param yaml_key [String, Symbol] The name of the YAML key to use for the field. By default
34
+ # this is the name of the field.
35
+ # @return [void]
36
+ def field(name, type: :string, description: nil, default: nil, default_if: nil, static: nil, runtime_setting: nil, env_var: nil, yaml_key: nil)
30
37
  name = name.to_s
31
38
  type = type.to_sym
32
39
  static = !!static
@@ -39,27 +46,25 @@ module UltraSettings
39
46
  raise ArgumentError.new("Invalid type: #{type.inspect}")
40
47
  end
41
48
 
42
- unless default_if.nil? || default_if.is_a?(Proc)
43
- raise ArgumentError.new("default_if must be a Proc")
49
+ unless default_if.nil? || default_if.is_a?(Proc) || default_if.is_a?(Symbol)
50
+ raise ArgumentError.new("default_if must be a Proc or Symbol")
44
51
  end
45
52
 
46
53
  defined_fields[name] = Field.new(
47
54
  name: name,
48
55
  type: type,
56
+ description: description,
49
57
  default: default,
50
58
  default_if: default_if,
51
- env_var: env_var,
52
- setting_name: setting,
53
- yaml_key: yaml_key,
54
- env_var_prefix: env_var_prefix,
55
- env_var_upcase: env_var_upcase,
56
- setting_prefix: setting_prefix,
57
- setting_upcase: setting_upcase
59
+ env_var: construct_env_var(name, env_var),
60
+ runtime_setting: (static ? nil : construct_runtime_setting(name, runtime_setting)),
61
+ yaml_key: construct_yaml_key(name, yaml_key),
62
+ static: static
58
63
  )
59
64
 
60
65
  class_eval <<-RUBY, __FILE__, __LINE__ + 1 # rubocop:disable Security/Eval
61
66
  def #{name}
62
- __get_value__(#{name.inspect}, #{static.inspect})
67
+ __get_value__(#{name.inspect})
63
68
  end
64
69
  RUBY
65
70
 
@@ -68,10 +73,41 @@ module UltraSettings
68
73
  end
69
74
  end
70
75
 
76
+ # List of the defined fields for the configuration.
77
+ #
78
+ # @return [Array<UltraSettings::Field>]
79
+ def fields
80
+ defined_fields.values
81
+ end
82
+
83
+ # Check if the field is defined on the configuration.
84
+ #
85
+ # @param name [Symbol, String] The name of the field.
86
+ # @return [Boolean]
87
+ def include?(name)
88
+ name = name.to_s
89
+ return true if defined_fields.include?(name)
90
+
91
+ if superclass <= Configuration
92
+ superclass.include?(name)
93
+ else
94
+ false
95
+ end
96
+ end
97
+
98
+ # Override the default environment variable prefix. By default this wil be
99
+ # the underscored name of the class plus an underscore
100
+ # (i.e. MyServiceConfiguration has a prefix of "MY_SERVICE_").
101
+ #
102
+ # @param value [String]
103
+ # @return [void]
71
104
  def env_var_prefix=(value)
72
105
  @env_var_prefix = value&.to_s
73
106
  end
74
107
 
108
+ # Get the environment variable prefix.
109
+ #
110
+ # @return [String]
75
111
  def env_var_prefix
76
112
  unless defined?(@env_var_prefix)
77
113
  @env_var_prefix = default_env_var_prefix
@@ -79,89 +115,310 @@ module UltraSettings
79
115
  @env_var_prefix
80
116
  end
81
117
 
82
- def setting_prefix=(value)
83
- @setting_prefix = value&.to_s
118
+ # Override the default runtime setting prefix. By default this wil be
119
+ # the underscored name of the class plus a dot (i.e. MyServiceConfiguration
120
+ # has a prefix of "my_service.").
121
+ #
122
+ # @param value [String]
123
+ # @return [void]
124
+ def runtime_setting_prefix=(value)
125
+ @runtime_setting_prefix = value&.to_s
84
126
  end
85
127
 
86
- def setting_prefix
87
- unless defined?(@setting_prefix)
88
- @setting_prefix = default_setting_prefix
128
+ # Get the runtime setting prefix.
129
+ #
130
+ # @return [String]
131
+ def runtime_setting_prefix
132
+ unless defined?(@runtime_setting_prefix)
133
+ @runtime_setting_prefix = default_runtime_setting_prefix
89
134
  end
90
- @setting_prefix
135
+ @runtime_setting_prefix
91
136
  end
92
137
 
138
+ # Override the default YAML config path. By default this will be the
139
+ # file matching the underscored name of the class in the configuration
140
+ # directory (i.e. MyServiceConfiguration has a default config path of
141
+ # "my_service.yml").
142
+ #
143
+ # @param value [String, Pathname]
144
+ # @return [void]
93
145
  def configuration_file=(value)
94
146
  value = Pathname.new(value) if value.is_a?(String)
95
- value = Rails.root + value if value && !value.absolute?
96
147
  @configuration_file = value
97
148
  end
98
149
 
150
+ # Get the YAML file path.
151
+ #
152
+ # @return [Pathname, nil]
99
153
  def configuration_file
100
154
  unless defined?(@configuration_file)
101
155
  @configuration_file = default_configuration_file
102
156
  end
103
- @configuration_file
157
+ return nil? unless @configuration_file
158
+
159
+ path = @configuration_file
160
+ if path.relative? && yaml_config_path
161
+ path = yaml_config_path.join(path)
162
+ end
163
+ path.expand_path
164
+ end
165
+
166
+ # Set to true to disable loading configuration from environment variables.
167
+ #
168
+ # @param value [Boolean]
169
+ # @return [void]
170
+ def environment_variables_disabled=(value)
171
+ set_inheritable_class_attribute(:@environment_variables_disabled, !!value)
172
+ end
173
+
174
+ # Check if loading configuration from environment variables is disabled.
175
+ #
176
+ # @return [Boolean]
177
+ def environment_variables_disabled?
178
+ get_inheritable_class_attribute(:@environment_variables_disabled, false)
179
+ end
180
+
181
+ # Set to true to disable loading configuration from runtime settings.
182
+ #
183
+ # @param value [Boolean]
184
+ # @return [void]
185
+ def runtime_settings_disabled=(value)
186
+ set_inheritable_class_attribute(:@runtime_settings_disabled, !!value)
187
+ end
188
+
189
+ # Check if loading configuration from runtime settings is disabled.
190
+ #
191
+ # @return [Boolean]
192
+ def runtime_settings_disabled?
193
+ get_inheritable_class_attribute(:@runtime_settings_disabled, false)
194
+ end
195
+
196
+ # Set to true to disable loading configuration from YAML files.
197
+ #
198
+ # @param value [Boolean]
199
+ # @return [void]
200
+ def yaml_config_disabled=(value)
201
+ set_inheritable_class_attribute(:@yaml_config_disabled, !!value)
202
+ end
203
+
204
+ # Check if loading configuration from YAML files is disabled.
205
+ #
206
+ # @return [Boolean]
207
+ def yaml_config_disabled?
208
+ get_inheritable_class_attribute(:@yaml_config_disabled, false)
209
+ end
210
+
211
+ # Set the environment variable delimiter used to construct the environment
212
+ # variable name for a field. By default this is an underscore.
213
+ #
214
+ # @param value [String]
215
+ def env_var_delimiter=(value)
216
+ set_inheritable_class_attribute(:@env_var_delimiter, value.to_s)
217
+ end
218
+
219
+ # Get the environment variable delimiter.
220
+ #
221
+ # @return [String]
222
+ def env_var_delimiter
223
+ get_inheritable_class_attribute(:@env_var_delimiter, "_")
224
+ end
225
+
226
+ # Set the runtime setting delimiter used to construct the runtime setting
227
+ # name for a field. By default this is a dot.
228
+ #
229
+ # @param value [String]
230
+ # @return [void]
231
+ def runtime_setting_delimiter=(value)
232
+ set_inheritable_class_attribute(:@runtime_setting_delimiter, value.to_s)
233
+ end
234
+
235
+ # Get the runtime setting delimiter.
236
+ #
237
+ # @return [String]
238
+ def runtime_setting_delimiter
239
+ get_inheritable_class_attribute(:@runtime_setting_delimiter, ".")
104
240
  end
105
241
 
242
+ # Set to true to upcase the environment variable name for a field. This
243
+ # is true by default.
244
+ #
245
+ # @param value [Boolean]
246
+ # @return [void]
247
+ def env_var_upcase=(value)
248
+ set_inheritable_class_attribute(:@env_var_upcase, !!value)
249
+ end
250
+
251
+ # Check if the environment variable name for a field should be upcased.
252
+ #
253
+ # @return [Boolean]
254
+ def env_var_upcase?
255
+ get_inheritable_class_attribute(:@env_var_upcase, true)
256
+ end
257
+
258
+ # Set to true to upcase the runtime setting name for a field. This
259
+ # is false by default.
260
+ #
261
+ # @param value [Boolean]
262
+ # @return [void]
263
+ def runtime_setting_upcase=(value)
264
+ set_inheritable_class_attribute(:@runtime_setting_upcase, !!value)
265
+ end
266
+
267
+ # Check if the runtime setting name for a field should be upcased.
268
+ #
269
+ # @return [Boolean]
270
+ def runtime_setting_upcase?
271
+ get_inheritable_class_attribute(:@runtime_setting_upcase, false)
272
+ end
273
+
274
+ # Set the directory where YAML files will be loaded from. By default this
275
+ # is the current working directory.
276
+ #
277
+ # @param value [String, Pathname]
278
+ # @return [void]
279
+ def yaml_config_path=(value)
280
+ value = Pathname.new(value) if value.is_a?(String)
281
+ value = value.expand_path if value&.relative?
282
+ set_inheritable_class_attribute(:@yaml_config_path, value)
283
+ end
284
+
285
+ # Get the directory where YAML files will be loaded from.
286
+ #
287
+ # @return [Pathname, nil]
288
+ def yaml_config_path
289
+ get_inheritable_class_attribute(:@yaml_config_path, nil)
290
+ end
291
+
292
+ # Set the environment namespace used in YAML file name. By default this
293
+ # is "development". Settings from the specific environment hash in the YAML
294
+ # file will be merged with base settings specified in the "shared" hash.
295
+ #
296
+ # @param value [String]
297
+ # @return [void]
298
+ def yaml_config_env=(value)
299
+ set_inheritable_class_attribute(:@yaml_config_env, value)
300
+ end
301
+
302
+ # Get the environment namespace used in YAML file name.
303
+ #
304
+ # @return [String]
305
+ def yaml_config_env
306
+ get_inheritable_class_attribute(:@yaml_config_env, "development")
307
+ end
308
+
309
+ # Override field values within a block.
310
+ #
311
+ # @param values [Hash<Symbol, Object>]] List of fields with the values they
312
+ # should return within the block.
313
+ # @return [Object] The value returned by the block.
314
+ def override!(values, &block)
315
+ instance.override!(values, &block)
316
+ end
317
+
318
+ # Load the YAML file for this configuration and return the values for the
319
+ # current environment.
320
+ #
321
+ # @return [Hash]
106
322
  def load_yaml_config
107
323
  return nil unless configuration_file
108
- return nil unless configuration_file.exist?
324
+ return nil unless configuration_file.exist? && configuration_file.file?
109
325
 
110
- Rails.application.config_for(configuration_file)
326
+ YamlConfig.new(configuration_file, yaml_config_env).to_h
111
327
  end
112
328
 
113
329
  private
114
330
 
115
331
  def defined_fields
116
332
  unless defined?(@defined_fields)
117
- @defined_fields = {}
118
- if superclass < Configuration
119
- superclass.send(:defined_fields).each do |name, field|
120
- @defined_fields[name] = Field.new(
121
- name: field.name,
122
- type: field.type,
123
- default: field.default,
124
- default_if: field.default_if,
125
- env_var: field.env_var,
126
- setting_name: field.setting_name,
127
- yaml_key: field.yaml_key,
128
- env_var_prefix: env_var_prefix,
129
- env_var_upcase: env_var_upcase,
130
- setting_prefix: setting_prefix,
131
- setting_upcase: setting_upcase
132
- )
133
- end
333
+ fields = {}
334
+ if superclass <= Configuration
335
+ fields = superclass.send(:defined_fields).dup
134
336
  end
337
+ @defined_fields = fields
135
338
  end
136
339
  @defined_fields
137
340
  end
138
341
 
139
342
  def root_name
140
- name.sub(/Configuration\z/, "")
343
+ name.sub(/Configuration\z/, "").split("::").collect do |part|
344
+ part.gsub(/([A-Z])(?=[A-Z][a-z])|([a-z\d])(?=[A-Z])/) { ($1 || $2) << "_" }.downcase
345
+ end.join("/")
346
+ end
347
+
348
+ def set_inheritable_class_attribute(name, value)
349
+ instance_variable_set(name, value)
350
+ end
351
+
352
+ def get_inheritable_class_attribute(name, default = nil)
353
+ if instance_variable_defined?(name)
354
+ instance_variable_get(name)
355
+ elsif self != Configuration
356
+ superclass.send(:get_inheritable_class_attribute, name, default)
357
+ else
358
+ default
359
+ end
141
360
  end
142
361
 
143
362
  def default_configuration_file
144
- path = Pathname.new(yaml_config_directory)
145
- path = Rails.root + path if defined?(Rails) && !path.absolute?
146
- path.join(*"#{root_name.underscore}.yml".split("/"))
363
+ path = Pathname.new(yaml_config_path)
364
+ path.join(*"#{root_name}.yml".split("/"))
147
365
  end
148
366
 
149
367
  def default_env_var_prefix
150
- prefix = root_name.underscore.gsub("/", env_var_delimiter) + env_var_delimiter
151
- prefix = prefix.upcase if env_var_upcase
368
+ prefix = root_name.gsub("/", env_var_delimiter) + env_var_delimiter
369
+ prefix = prefix.upcase if env_var_upcase?
152
370
  prefix
153
371
  end
154
372
 
155
- def default_setting_prefix
156
- prefix = root_name.underscore.gsub("/", setting_delimiter) + setting_delimiter
157
- prefix = prefix.upcase if setting_upcase
373
+ def default_runtime_setting_prefix
374
+ prefix = root_name.gsub("/", runtime_setting_delimiter) + runtime_setting_delimiter
375
+ prefix = prefix.upcase if runtime_setting_upcase?
158
376
  prefix
159
377
  end
378
+
379
+ def construct_env_var(name, env_var)
380
+ return nil if env_var == false
381
+ return nil if environment_variables_disabled? && env_var.nil?
382
+
383
+ env_var = nil if env_var == true
384
+
385
+ if env_var.nil?
386
+ env_var = "#{env_var_prefix}#{name}"
387
+ env_var = env_var.upcase if env_var_upcase?
388
+ end
389
+
390
+ env_var
391
+ end
392
+
393
+ def construct_runtime_setting(name, runtime_setting)
394
+ return nil if runtime_setting == false
395
+ return nil if runtime_settings_disabled? && runtime_setting.nil?
396
+
397
+ runtime_setting = nil if runtime_setting == true
398
+
399
+ if runtime_setting.nil?
400
+ runtime_setting = "#{runtime_setting_prefix}#{name}"
401
+ runtime_setting = runtime_setting.upcase if runtime_setting_upcase?
402
+ end
403
+
404
+ runtime_setting
405
+ end
406
+
407
+ def construct_yaml_key(name, yaml_key)
408
+ return nil if yaml_key == false
409
+ return nil if yaml_config_disabled? && yaml_key.nil?
410
+
411
+ yaml_key = nil if yaml_key == true
412
+ yaml_key = name if yaml_key.nil?
413
+
414
+ yaml_key
415
+ end
160
416
  end
161
417
 
162
418
  def initialize
163
419
  @mutex = Mutex.new
164
420
  @memoized_values = {}
421
+ @override_values = {}
165
422
  end
166
423
 
167
424
  def [](name)
@@ -169,30 +426,60 @@ module UltraSettings
169
426
  end
170
427
 
171
428
  def include?(name)
172
- self.class.send(:defined_fields).include?(name.to_s)
429
+ self.class.include?(name.to_s)
173
430
  end
174
431
 
175
- private
432
+ def override!(values, &block)
433
+ save_val = @override_values[Thread.current.object_id]
176
434
 
177
- def __get_value__(name, static)
178
- if static && @memoized_values.include?(name)
179
- return @memoized_values[name]
435
+ temp_values = (save_val || {}).dup
436
+ values.each do |key, value|
437
+ temp_values[key.to_s] = value
438
+ end
439
+
440
+ begin
441
+ @mutex.synchronize do
442
+ @override_values[Thread.current.object_id] = temp_values
443
+ end
444
+ yield
445
+ ensure
446
+ @mutex.synchronize do
447
+ @override_values[Thread.current.object_id] = save_val
448
+ end
180
449
  end
450
+ end
181
451
 
452
+ def __source__(name)
453
+ field = self.class.send(:defined_fields)[name]
454
+ source = field.source(env: ENV, settings: UltraSettings.__runtime_settings__, yaml_config: __yaml_config__)
455
+ source || :default
456
+ end
457
+
458
+ private
459
+
460
+ def __get_value__(name)
182
461
  field = self.class.send(:defined_fields)[name]
183
462
  return nil unless field
184
463
 
185
- if !Rails.application.initialized? && !static
186
- raise UltraSettings::NonStaticValueError.new("Cannot access non-static field #{name} during initialization")
464
+ if field.static? && @memoized_values.include?(name)
465
+ return @memoized_values[name]
187
466
  end
188
467
 
189
- env = ENV unless self.class.environment_variables_disabled?
190
- settings = __runtime_settings__ unless static || self.class.runtime_settings_disabled?
191
- yaml_config = __yaml_config__ unless self.class.yaml_config_disabled?
468
+ if @override_values[Thread.current.object_id]&.include?(name)
469
+ value = field.coerce(@override_values[Thread.current.object_id][name])
470
+ else
471
+ env = ENV if field.env_var
472
+ settings = UltraSettings.__runtime_settings__ if field.runtime_setting
473
+ yaml_config = __yaml_config__ if field.yaml_key
192
474
 
193
- value = field.value(yaml_config: yaml_config, env: env, settings: settings)
475
+ value = field.value(yaml_config: yaml_config, env: env, settings: settings)
476
+ end
477
+
478
+ if __use_default?(value, field.default_if)
479
+ value = field.default
480
+ end
194
481
 
195
- if static
482
+ if field.static?
196
483
  @mutex.synchronize do
197
484
  if @memoized_values.include?(name)
198
485
  value = @memoized_values[name]
@@ -205,8 +492,20 @@ module UltraSettings
205
492
  value
206
493
  end
207
494
 
208
- def __runtime_settings__
209
- SuperSettings
495
+ def __use_default?(value, default_if)
496
+ return true if value.nil?
497
+
498
+ if default_if.is_a?(Proc)
499
+ default_if.call(value)
500
+ elsif default_if.is_a?(Symbol)
501
+ begin
502
+ send(default_if, value)
503
+ rescue NoMethodError
504
+ raise NoMethodError, "default_if method `#{default_if}' not defined for #{self.class.name}"
505
+ end
506
+ else
507
+ false
508
+ end
210
509
  end
211
510
 
212
511
  def __yaml_config__