configatron 2.13.0 → 3.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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