configatron 2.13.0 → 3.0.0.rc1

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.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +0 -0
  3. data/.rvmrc +0 -1
  4. data/.travis.yml +5 -2
  5. data/Gemfile +5 -1
  6. data/Gemfile.lock +30 -13
  7. data/Guardfile +24 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +122 -112
  10. data/Rakefile +6 -4
  11. data/V2-README.md +243 -0
  12. data/configatron.gemspec +4 -31
  13. data/lib/configatron/core.rb +3 -94
  14. data/lib/configatron/deep_clone.rb +69 -0
  15. data/lib/configatron/errors.rb +5 -9
  16. data/lib/configatron/rails.rb +8 -8
  17. data/lib/configatron/store.rb +66 -339
  18. data/lib/configatron/version.rb +1 -1
  19. data/lib/configatron.rb +8 -16
  20. data/lib/generators/configatron/install/install_generator.rb +0 -0
  21. data/lib/generators/configatron/install/templates/configatron/defaults.rb +0 -0
  22. data/lib/generators/configatron/install/templates/configatron/development.rb +0 -0
  23. data/lib/generators/configatron/install/templates/configatron/production.rb +0 -0
  24. data/lib/generators/configatron/install/templates/configatron/test.rb +0 -0
  25. data/lib/generators/configatron/install/templates/initializers/configatron.rb +0 -0
  26. data/test/configatron/store_test.rb +191 -0
  27. data/test/configatron_test.rb +5 -0
  28. data/test/test_helper.rb +14 -0
  29. metadata +27 -54
  30. data/LICENSE +0 -21
  31. data/lib/configatron/core_ext/class.rb +0 -25
  32. data/lib/configatron/core_ext/kernel.rb +0 -8
  33. data/lib/configatron/core_ext/object.rb +0 -13
  34. data/lib/configatron/core_ext/string.rb +0 -90
  35. data/lib/configatron/proc.rb +0 -104
  36. data/spec/configatron/proc_spec.rb +0 -67
  37. data/spec/configatron/rails_spec.rb +0 -66
  38. data/spec/lib/class_spec.rb +0 -46
  39. data/spec/lib/complex.yml +0 -13
  40. data/spec/lib/configatron_spec.rb +0 -630
  41. data/spec/lib/futurama.yml +0 -6
  42. data/spec/lib/lost.yml +0 -14
  43. data/spec/lib/math.yml +0 -2
  44. data/spec/lib/merge.yml +0 -14
  45. data/spec/spec_helper.rb +0 -4
  46. data/spec/support/rails.rb +0 -7
@@ -1,373 +1,100 @@
1
- class Configatron
2
- class Store
3
- if RUBY_VERSION.match(/^1\.9\.[^1]/)
4
- require 'syck'
5
- ::YAML::ENGINE.yamler = 'syck' unless RUBY_PLATFORM == 'java'
6
- end
7
-
8
- alias_method :send!, :send
9
-
10
- # Takes an optional Hash of parameters
11
- def initialize(options = {}, name = nil, parent = nil)
12
- @_name = name
13
- @_parent = parent
14
- @_store = {}
15
- configure_from_hash(options)
16
- @_protected = []
17
- @_locked = false
18
- end
19
-
20
- def [](key)
21
- method_missing(key.to_sym)
22
- end
23
-
24
- def []=(key, value)
25
- method_missing(:"#{key}=", value)
26
- end
27
-
28
- # Returns a Hash representing the configurations
29
- def to_hash
30
- h = Hash.new
31
- @_store.each { |k,v|
32
- # Descend the tree and hashify each node
33
- h[k] = v.is_a?(Store) ? v.to_hash : v
34
- }
35
- h
36
- end
1
+ require 'forwardable'
37
2
 
38
- def heirarchy
39
- path = [@_name]
40
- parent = @_parent
41
- until parent.nil?
42
- path << parent.instance_variable_get('@_name')
43
- parent = parent.instance_variable_get('@_parent')
44
- end
45
- path.compact!
46
- path.reverse!
47
- path.join('.')
48
- end
49
-
50
- def configatron_keys
51
- return @_store.keys.collect{|k| k.to_s}.sort
52
- end
3
+ class Configatron
4
+ class Store# < BasicObject
5
+ extend ::Forwardable
53
6
 
54
- # Checks whether or not a parameter exists
55
- #
56
- # Examples:
57
- # configatron.i.am.alive = 'alive!'
58
- # configatron.i.am.exists?(:alive) # => true
59
- # configatron.i.am.exists?(:dead) # => false
60
- def exists?(name)
61
- @_store.has_key?(name.to_sym) || @_store.has_key?(name.to_s)
7
+ def initialize(attributes = {})
8
+ @__locked = false
9
+ @attributes = attributes || {}
10
+ @attributes.send(:extend, DeepClone)
62
11
  end
63
12
 
64
- # respond_to to respond_to
65
- def respond_to?(name)
66
- exists?(name) || super
13
+ def lock!
14
+ @__locked = true
67
15
  end
68
16
 
69
- def inspect
70
- name = _store_name
71
- f_out = []
72
- @_store.each do |k, v|
73
- if v.is_a?(Configatron::Store)
74
- v.inspect.each_line do |line|
75
- if line.match(/\n/)
76
- line.each_line do |l|
77
- l.strip!
78
- f_out << l
79
- end
80
- else
81
- line.strip!
82
- f_out << line
83
- end
84
- end
85
- else
86
- f_out << "#{name}.#{k} = #{v.inspect}"
17
+ def [](key)
18
+ fetch(key.to_sym) do
19
+ if @__locked
20
+ raise Configatron::UndefinedKeyError.new("Key Not Found: #{key}")
87
21
  end
22
+ ::Configatron::Store.new
88
23
  end
89
- f_out.compact.sort.join("\n")
90
24
  end
91
25
 
92
- # Allows for the configuration of the system via a Hash
93
- def configure_from_hash(options)
94
- parse_options(options)
95
- end
96
-
97
- # Allows for the configuration of the system from a YAML file.
98
- # Takes the path to the YAML file. Also takes an optional parameter,
99
- # <tt>:hash</tt>, that indicates a specific hash that should be
100
- # loaded from the file.
101
- def configure_from_yaml(path, opts = {})
102
- Configatron.log.warn "DEPRECATED! (configure_from_yaml) Please stop using YAML and use Ruby instead. This method will be removed in 3.1."
103
- begin
104
- yml = ::Yamler.load(path)
105
- yml = yml[opts[:hash]] unless opts[:hash].nil?
106
- configure_from_hash(yml)
107
- rescue Errno::ENOENT => e
108
- puts e.message
26
+ def store(key, value)
27
+ if @__locked
28
+ raise Configatron::LockedError.new("Locked! Can not set key #{key}!")
109
29
  end
30
+ @attributes[key.to_sym] = value
110
31
  end
111
32
 
112
- # Returns true if there are no configuration parameters
113
- def nil?
114
- return @_store.empty?
115
- end
116
-
117
- def blank?
118
- value = retrieve(@_name)
119
- value.respond_to?(:empty?) ? value.empty? : !value
120
- end
121
-
122
- # Retrieves a certain parameter and if that parameter
123
- # doesn't exist it will return the default_value specified.
124
- def retrieve(name, default_value = nil)
125
- val = method_missing(name.to_sym)
126
- return val.is_a?(Configatron::Store) ? default_value : val
127
- end
128
-
129
- # Removes a parameter. In the case of a nested parameter
130
- # it will remove all below it.
131
- def remove(name)
132
- @_store.delete(name.to_sym)
133
- end
134
-
135
- # Sets a 'default' value. If there is already a value specified
136
- # it won't set the value.
137
- def set_default(name, default_value)
138
- unless @_store[name.to_sym]
139
- # @_store[name.to_sym] = parse_options(default_value)
140
- self.send("#{name}=", default_value)
141
- end
142
- end
143
-
144
- def method_missing(sym, *args) # :nodoc:
145
- if sym.to_s.match(/(.+)=$/)
146
- name = sym.to_s.gsub("=", '').to_sym
147
- raise Configatron::ProtectedParameter.new(name) if @_protected.include?(name) || methods_include?(name)
148
- raise Configatron::LockedNamespace.new(@_name) if @_locked && !@_store.has_key?(name)
149
- @_store[name] = parse_options(*args)
150
- elsif sym.to_s.match(/(.+)\?/)
151
- object = _store_lookup($1.to_sym)
152
- return !_object_blank?(object)
153
- elsif block_given?
154
- yield self.send(sym)
155
- elsif @_store.has_key?(sym)
156
- val = _store_lookup(sym)
157
- if val.is_a?(Configatron::Proc)
158
- res = val.execute
159
- if val.finalize?
160
- @_store[sym] = res
161
- end
162
- return res
33
+ def fetch(key, default_value = nil, &block)
34
+ val = @attributes[key.to_sym]
35
+ if val.nil?
36
+ if block_given?
37
+ val = block.call
38
+ elsif default_value
39
+ val = default_value
163
40
  end
164
- return val
165
- else
166
- # This will error out if strict is enabled, and be a no-op
167
- # otherwise. The nice thing is the error message will be the
168
- # same as in the .method? case.
169
- _store_lookup(sym)
170
- store = Configatron::Store.new({}, sym, self)
171
- @_store[sym] = store
172
- return store
41
+ store(key, val)
173
42
  end
43
+ return val
174
44
  end
175
45
 
176
- def ==(other) # :nodoc:
177
- self.to_hash == other
178
- end
179
-
180
- # Prevents a parameter from being reassigned. If called on a 'namespace' then
181
- # all parameters below it will be protected as well.
182
- def protect(name)
183
- @_protected << name.to_sym
184
- end
185
-
186
- # Prevents all parameters from being reassigned.
187
- def protect_all!
188
- @_protected.clear
189
- @_store.keys.each do |k|
190
- val = self.send(k)
191
- val.protect_all! if val.class == Configatron::Store
192
- @_protected << k
193
- end
194
- end
195
-
196
- # Removes the protection of a parameter.
197
- def unprotect(name)
198
- @_protected.reject! { |e| e == name.to_sym }
199
- end
200
-
201
- def unprotect_all!
202
- @_protected.clear
203
- @_store.keys.each do |k|
204
- val = self.send(k)
205
- val.unprotect_all! if val.class == Configatron::Store
206
- end
207
- end
208
-
209
- # Prevents a namespace from having new parameters set. The lock is applied
210
- # recursively to any namespaces below it.
211
- def lock(name)
212
- namespace = @_store[name.to_sym]
213
- raise ArgumentError, "Namespace #{name.inspect} does not exist" if namespace.nil?
214
- namespace.lock!
46
+ def nil?
47
+ @attributes.empty?
215
48
  end
216
49
 
217
- def unlock(name)
218
- namespace = @_store[name.to_sym]
219
- raise ArgumentError, "Namespace #{name.inspect} does not exist" if namespace.nil?
220
- namespace.unlock!
50
+ def has_key?(key)
51
+ val = self[key]
52
+ !val.is_a?(Configatron::Store)
221
53
  end
222
54
 
223
- # = DeepClone
224
- #
225
- # == Version
226
- # 1.2006.05.23 (change of the first number means Big Change)
227
- #
228
- # == Description
229
- # Adds deep_clone method to an object which produces deep copy of it. It means
230
- # if you clone a Hash, every nested items and their nested items will be cloned.
231
- # Moreover deep_clone checks if the object is already cloned to prevent endless recursion.
232
- #
233
- # == Usage
234
- #
235
- # (see examples directory under the ruby gems root directory)
236
- #
237
- # require 'rubygems'
238
- # require 'deep_clone'
239
- #
240
- # include DeepClone
241
- #
242
- # obj = []
243
- # a = [ true, false, obj ]
244
- # b = a.deep_clone
245
- # obj.push( 'foo' )
246
- # p obj # >> [ 'foo' ]
247
- # p b[2] # >> []
248
- #
249
- # == Source
250
- # http://simplypowerful.1984.cz/goodlibs/1.2006.05.23
251
- #
252
- # == Author
253
- # jan molic (/mig/at_sign/1984/dot/cz/)
254
- #
255
- # == Licence
256
- # You can redistribute it and/or modify it under the same terms of Ruby's license;
257
- # either the dual license version in 2003, or any later version.
258
- #
259
- def deep_clone( obj=self, cloned={} )
260
- if cloned.has_key?( obj.object_id )
261
- return cloned[obj.object_id]
262
- else
263
- begin
264
- cl = obj.clone
265
- rescue Exception
266
- # unclonnable (TrueClass, Fixnum, ...)
267
- cloned[obj.object_id] = obj
268
- return obj
55
+ def configure_from_hash(hash)
56
+ hash.each do |key, value|
57
+ if value.is_a?(Hash)
58
+ self[key].configure_from_hash(value)
269
59
  else
270
- cloned[obj.object_id] = cl
271
- cloned[cl.object_id] = cl
272
- if cl.is_a?( Hash )
273
- cl.clone.each { |k,v|
274
- cl[k] = deep_clone( v, cloned )
275
- }
276
- elsif cl.is_a?( Array )
277
- cl.collect! { |v|
278
- deep_clone( v, cloned )
279
- }
280
- end
281
- cl.instance_variables.each do |var|
282
- v = cl.instance_eval( var.to_s )
283
- v_cl = deep_clone( v, cloned )
284
- cl.instance_eval( "#{var} = v_cl" )
285
- end
286
- return cl
60
+ store(key, value)
287
61
  end
288
62
  end
289
63
  end
290
64
 
291
- protected
292
- def lock!
293
- @_locked = true
294
- @_store.values.each { |store| store.lock! if store.is_a?(Configatron::Store) }
295
- end
296
-
297
- def unlock!
298
- @_locked = false
299
- @_store.values.each { |store| store.unlock! if store.is_a?(Configatron::Store) }
65
+ def temp(&block)
66
+ @__temp = @attributes.deep_clone
67
+ yield
68
+ @attributes = @__temp
300
69
  end
301
70
 
302
- private
303
- def methods_include?(name)
304
- self.methods.include?(RUBY_VERSION > '1.9.0' ? name.to_sym : name.to_s)
305
- end
306
-
307
- def is_syck?(obj)
308
- if defined?(SYCK_CONSTANT)
309
- Configatron.log.warn "DEPRECATED! (SYCK) Syck support has been removed from Configatron in Ruby 2.x. This feature will be removed entirely in Configatron 3.0. Please be advised."
310
- return obj.is_a?(SYCK_CONSTANT)
311
- end
312
- return false
313
- end
314
-
315
- def parse_options(options)
316
- if options.is_a?(Hash)
317
- options.each do |k,v|
318
- if v.is_a?(Hash)
319
- if v.keys.length == 1 && is_syck?(v.keys.first)
320
- self.method_missing("#{k}=", v.values.first.flatten)
321
- else
322
- self.method_missing(k.to_sym).configure_from_hash(v)
323
- end
324
- else
325
- self.method_missing("#{k}=", v)
326
- end
327
- end
71
+ def method_missing(name, *args, &block)
72
+ if block_given?
73
+ yield self[name]
328
74
  else
329
- return options
330
- end
331
- end
332
-
333
- def _store_name
334
- path = [@_name]
335
- parent = @_parent
336
- until parent.nil?
337
- path << parent.instance_variable_get('@_name')
338
- parent = parent.instance_variable_get('@_parent')
339
- end
340
- path << 'configatron'
341
- path.compact!
342
- path.reverse!
343
- path.join('.')
344
- end
345
-
346
- # Give it this awkward name to hopefully avoid config keys people
347
- # are using.
348
- def _store_lookup(sym)
349
- begin
350
- @_store.fetch(sym)
351
- rescue IndexError => e
352
- raise e.class.new("#{e.message} (for #{_store_name})") if Configatron.strict
353
- nil
75
+ name = name.to_s
76
+ if /(.+)=$/.match(name)
77
+ return store($1, args[0])
78
+ else
79
+ return self[name]
80
+ end
354
81
  end
355
82
  end
356
83
 
357
- def _object_blank?(object) # ported ActiveSupport method
358
- object.respond_to?(:empty?) ? object.empty? : !object
359
- end
360
-
361
- begin
362
- undef :test # :nodoc:
363
- rescue Exception => e
364
- end
84
+ alias :[]= :store
85
+ alias :blank? :nil?
365
86
 
366
- if RUBY_PLATFORM == 'java'
367
- SYCK_CONSTANT = YAML::Yecht::MergeKey
368
- elsif RUBY_VERSION.match(/^1\.9/)
369
- SYCK_CONSTANT = Syck::MergeKey
370
- end
87
+ def_delegator :@attributes, :values
88
+ def_delegator :@attributes, :keys
89
+ def_delegator :@attributes, :each
90
+ def_delegator :@attributes, :empty?
91
+ def_delegator :@attributes, :inspect
92
+ def_delegator :@attributes, :to_h
93
+ def_delegator :@attributes, :to_hash
94
+ def_delegator :@attributes, :delete
95
+ # def_delegator :@attributes, :fetch
96
+ # def_delegator :@attributes, :has_key?
97
+ # def_delegator :$stdout, :puts
371
98
 
372
- end # Store
373
- end # Configatron
99
+ end
100
+ end
@@ -1,3 +1,3 @@
1
1
  class Configatron
2
- VERSION = "2.13.0"
2
+ VERSION = "3.0.0.rc1"
3
3
  end
data/lib/configatron.rb CHANGED
@@ -1,17 +1,9 @@
1
- # You can require 'configure/core' directly to avoid loading
2
- # configatron's monkey patches (and you can then set
3
- # `Configatron.disable_monkey_patchs = true` to enforce this). If you
4
- # do so, no `configatron` top-level method will be defined for
5
- # you. You can access the configatron object by Configatron.instance.
1
+ require "configatron/version"
2
+ require "configatron/core"
3
+ require "configatron/rails"
6
4
 
7
- base = File.join(File.dirname(__FILE__), 'configatron')
8
- require File.join(base, 'core')
9
-
10
- if Configatron.disable_monkey_patching
11
- raise "Cannot require 'configatron' directly, since monkey patching has been disabled. Run `Configatron.disable_monkey_patching = false` to re-enable it, or always require 'configatron/core' to load Configatron."
12
- end
13
-
14
- require File.join(base, 'core_ext', 'kernel')
15
- require File.join(base, 'core_ext', 'object')
16
- require File.join(base, 'core_ext', 'string')
17
- require File.join(base, 'core_ext', 'class')
5
+ module Kernel
6
+ def configatron
7
+ @__configatron ||= Configatron::Store.new
8
+ end
9
+ end
@@ -0,0 +1,191 @@
1
+ require 'test_helper'
2
+
3
+ describe Configatron::Store do
4
+
5
+ let(:store) { Configatron::Store.new }
6
+
7
+ context "[]" do
8
+
9
+ let(:store) { Configatron::Store.new(foo: "bar") }
10
+
11
+ it "returns the value if there is one" do
12
+ store[:foo].must_equal "bar"
13
+ store["foo"].must_equal "bar"
14
+ end
15
+
16
+ it "returns a new Configatron::Store object if there is no value" do
17
+ store[:unknown].must_be_kind_of Configatron::Store
18
+ store["unknown"].must_be_kind_of Configatron::Store
19
+ end
20
+
21
+ end
22
+
23
+ context "[]=" do
24
+
25
+ it "sets the value" do
26
+ store[:foo] = "bar"
27
+ store[:foo].must_equal "bar"
28
+ store["foo"].must_equal "bar"
29
+
30
+ store[:baz] = "bazzy"
31
+ store[:baz].must_equal "bazzy"
32
+ store["baz"].must_equal "bazzy"
33
+ end
34
+
35
+ end
36
+
37
+ context "fetch" do
38
+
39
+ let(:store) { Configatron::Store.new(foo: "bar") }
40
+
41
+ it "returns the value" do
42
+ store.fetch(:foo).must_equal "bar"
43
+ store.fetch("foo").must_equal "bar"
44
+ end
45
+
46
+ it "sets and returns the value of the default_value if no value is found" do
47
+ store.fetch(:bar, "bar!!").must_equal "bar!!"
48
+ store.bar.must_equal "bar!!"
49
+ end
50
+
51
+ it "sets and returns the value of the block if no value is found" do
52
+ store.fetch(:bar) do
53
+ "bar!"
54
+ end.must_equal "bar!"
55
+ store.bar.must_equal "bar!"
56
+ end
57
+
58
+ end
59
+
60
+ context "nil?" do
61
+
62
+ it "returns true if there is no value set" do
63
+ store.foo.must_be_nil
64
+ store.foo = "bar"
65
+ store.foo.wont_be_nil
66
+ end
67
+
68
+ end
69
+
70
+ context "empty?" do
71
+
72
+ it "returns true if there is no value set" do
73
+ store.foo.must_be_empty
74
+ store.foo = "bar"
75
+ store.foo.wont_be_empty
76
+ end
77
+
78
+ end
79
+
80
+ context "has_key?" do
81
+
82
+ it "returns true if there is a key" do
83
+ store.has_key?(:foo).must_equal false
84
+ store.foo = "bar"
85
+ store.has_key?(:foo).must_equal true
86
+ end
87
+
88
+ it "returns false if the key is a Configatron::Store" do
89
+ store.has_key?(:foo).must_equal false
90
+ store.foo = Configatron::Store.new
91
+ store.has_key?(:foo).must_equal false
92
+ end
93
+
94
+ end
95
+
96
+ context "method_missing" do
97
+
98
+ let(:store) { Configatron::Store.new(foo: "bar") }
99
+
100
+ it "returns the value if there is one" do
101
+ store.foo.must_equal "bar"
102
+ end
103
+
104
+ it "returns a Configatron::Store if there is no value" do
105
+ store.bar.must_be_kind_of Configatron::Store
106
+ end
107
+
108
+ it "works with deeply nested values" do
109
+ store.a.b.c.d = "DD"
110
+ store.a.b.c.d.must_equal "DD"
111
+ end
112
+
113
+ end
114
+
115
+ context "lock!" do
116
+
117
+ before do
118
+ store.a.b.c.d = 'DD'
119
+ store.lock!
120
+ end
121
+
122
+ it "raises an error when accessing non-existing values" do
123
+ store.a.wont_be_nil
124
+ store.a.b.wont_be_nil
125
+ store.a.b.c.wont_be_nil
126
+ store.a.b.c.d.must_equal "DD"
127
+ proc {store.unknown}.must_raise(Configatron::UndefinedKeyError)
128
+ end
129
+
130
+ it "raises an error when trying to set a non-existing key" do
131
+ proc {store.unknown = "known"}.must_raise(Configatron::LockedError)
132
+ end
133
+
134
+ end
135
+
136
+ context "temp" do
137
+
138
+ before do
139
+ store.a = 'A'
140
+ store.b = 'B'
141
+ end
142
+
143
+
144
+ it "allows for temporary setting of values" do
145
+ store.a.must_equal 'A'
146
+ store.b.must_equal 'B'
147
+ store.temp do
148
+ store.a = 'AA'
149
+ store.c = 'C'
150
+ store.a.must_equal 'AA'
151
+ store.b.must_equal 'B'
152
+ store.c.must_equal 'C'
153
+ end
154
+ store.a.must_equal 'A'
155
+ store.b.must_equal 'B'
156
+ store.c.must_be_nil
157
+ end
158
+
159
+ end
160
+
161
+ context "configuring" do
162
+
163
+ context "configure_from_hash" do
164
+
165
+ it "allows setup from a hash" do
166
+ store.configure_from_hash(one: 1, a: {b: {c: {d: "DD"}}})
167
+ store.one.must_equal 1
168
+ store.a.b.c.d.must_equal "DD"
169
+ end
170
+
171
+ end
172
+
173
+ context "with a block" do
174
+
175
+ before do
176
+ store.a.b = 'B'
177
+ end
178
+
179
+ it "yields up the store to configure with" do
180
+ store.a do |a|
181
+ a.c = 'CC'
182
+ end
183
+ store.a.b.must_equal 'B'
184
+ store.a.c.must_equal 'CC'
185
+ end
186
+
187
+ end
188
+
189
+ end
190
+
191
+ end
@@ -0,0 +1,5 @@
1
+ require 'test_helper'
2
+
3
+ describe Configatron do
4
+
5
+ end
@@ -0,0 +1,14 @@
1
+ require 'bundler/setup'
2
+
3
+ require 'configatron' # and any other gems you need
4
+
5
+ require 'minitest/autorun'
6
+ require "minitest-colorize"
7
+
8
+ class MiniTest::Spec
9
+
10
+ class << self
11
+ alias :context :describe
12
+ end
13
+
14
+ end