configatron 3.2.0 → 4.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/.gitignore +1 -0
  2. data/.travis.yml +2 -2
  3. data/History.txt +72 -0
  4. data/README.md +25 -24
  5. data/Rakefile +10 -4
  6. data/configatron.gemspec +1 -0
  7. data/lib/configatron.rb +22 -4
  8. data/lib/configatron/core.rb +6 -6
  9. data/lib/configatron/ext/kernel.rb +5 -0
  10. data/lib/configatron/integrations.rb +4 -0
  11. data/lib/configatron/integrations/minitest.rb +29 -0
  12. data/lib/configatron/{rails.rb → integrations/rails.rb} +1 -1
  13. data/lib/configatron/root_store.rb +121 -0
  14. data/lib/configatron/store.rb +100 -63
  15. data/lib/configatron/version.rb +1 -1
  16. data/lib/generators/configatron/install/templates/configatron/defaults.rb +2 -2
  17. data/lib/generators/configatron/install/templates/configatron/development.rb +2 -2
  18. data/lib/generators/configatron/install/templates/configatron/production.rb +2 -2
  19. data/lib/generators/configatron/install/templates/configatron/test.rb +2 -2
  20. data/lib/generators/configatron/install/templates/initializers/configatron.rb +1 -1
  21. data/test/_lib.rb +17 -0
  22. data/test/functional/_lib.rb +10 -0
  23. data/test/functional/_lib/scripts/core.rb +11 -0
  24. data/test/functional/configatron.rb +165 -0
  25. data/test/functional/delayed.rb +18 -0
  26. data/test/functional/loading.rb +10 -0
  27. data/test/functional/minitest.rb +18 -0
  28. data/test/unit/_lib.rb +10 -0
  29. data/test/unit/configatron/proc.rb +24 -0
  30. data/test/unit/configatron/root_store.rb +37 -0
  31. data/test/unit/configatron/store.rb +149 -0
  32. metadata +73 -30
  33. checksums.yaml +0 -7
  34. data/.ruby-version +0 -1
  35. data/lib/configatron/deep_clone.rb +0 -69
  36. data/lib/configatron/kernel.rb +0 -21
  37. data/test/configatron/kernel_test.rb +0 -24
  38. data/test/configatron/proc_test.rb +0 -27
  39. data/test/configatron/store_test.rb +0 -256
  40. data/test/configatron_test.rb +0 -5
  41. data/test/test_helper.rb +0 -15
@@ -1,68 +1,94 @@
1
1
  require 'forwardable'
2
2
 
3
3
  class Configatron
4
- class Store# < BasicObject
4
+ class Store < BasicObject
5
5
  extend ::Forwardable
6
6
 
7
- def initialize(attributes = {})
8
- @__locked = false
9
- @attributes = attributes || {}
10
- @attributes.send(:extend, DeepClone)
7
+ def initialize(root_store, name='configatron', attributes={}, path=[])
8
+ @root_store = root_store
9
+ @name = name
10
+ @attributes = attributes
11
+
12
+ # We could derive @name from @path, though this would break
13
+ # backwards-compatibility.
14
+ @path = path
15
+
16
+ @cow = root_store.__cow
17
+ end
18
+
19
+ def clone
20
+ Store.new(
21
+ @root_store,
22
+ @name,
23
+ @attributes.clone,
24
+ @path
25
+ )
11
26
  end
12
27
 
13
- def lock!(value=true)
14
- @__locked = value
28
+ def __cow
29
+ @cow
15
30
  end
16
31
 
17
- def reset!
18
- @attributes.clear
32
+ def __cow_clone
33
+ # A temp has started since the last time this was written
34
+ if @root_store.__cow != @cow
35
+ self.clone
36
+ else
37
+ self
38
+ end
19
39
  end
20
40
 
21
41
  def [](key)
22
42
  val = fetch(key.to_sym) do
23
- if @__locked
24
- raise Configatron::UndefinedKeyError.new("Key Not Found: #{key}")
43
+ if @root_store.locked?
44
+ ::Kernel.raise ::Configatron::UndefinedKeyError.new("Key not found: #{key} (for locked #{self})")
25
45
  end
26
- ::Configatron::Store.new
46
+ ::Configatron::Store.new(@root_store, "#{@name}.#{key}", {}, @path + [key])
27
47
  end
28
48
  return val
29
49
  end
30
50
 
31
51
  def store(key, value)
32
- if @__locked
33
- raise Configatron::LockedError.new("Locked! Can not set key #{key}!")
52
+ if @root_store.locked?
53
+ ::Kernel.raise ::Configatron::LockedError.new("Cannot set key #{key} for locked #{self}")
54
+ end
55
+
56
+ key = key.to_sym
57
+ if @root_store.__cow != @cow
58
+ copy = @root_store.__cow_path(@path)
59
+ # Cow should now match, so this won't recurse. (Note this is
60
+ # not particularly thread-safe.)
61
+ copy.store(key, value)
62
+ else
63
+ @attributes[key] = value
34
64
  end
35
- @attributes[key.to_sym] = value
36
65
  end
37
66
 
38
67
  def fetch(key, default_value = nil, &block)
39
- val = @attributes[key.to_sym]
40
- if val.nil?
41
- if block_given?
68
+ key = key.to_sym
69
+ if key?(key)
70
+ val = @attributes[key]
71
+ else
72
+ if block
42
73
  val = block.call
43
74
  elsif default_value
44
75
  val = default_value
45
76
  end
46
77
  store(key, val)
47
78
  end
48
- if val.is_a?(Configatron::Proc)
79
+ if ::Configatron::Proc === val
49
80
  val = val.call
50
81
  end
51
82
  return val
52
83
  end
53
84
 
54
- def nil?
55
- @attributes.empty?
56
- end
57
-
58
85
  def key?(key)
59
- val = self[key.to_sym]
60
- !val.is_a?(Configatron::Store)
86
+ @attributes.key?(key.to_sym)
61
87
  end
62
88
 
63
89
  def configure_from_hash(hash)
64
90
  hash.each do |key, value|
65
- if value.is_a?(Hash)
91
+ if ::Hash === value
66
92
  self[key].configure_from_hash(value)
67
93
  else
68
94
  store(key, value)
@@ -70,22 +96,58 @@ class Configatron
70
96
  end
71
97
  end
72
98
 
73
- def temp(&block)
74
- temp_start
75
- yield
76
- temp_end
99
+ def to_s
100
+ @name
101
+ end
102
+
103
+ def inspect
104
+ f_out = []
105
+ @attributes.each do |k, v|
106
+ if ::Configatron::Store === v
107
+ v.inspect.each_line do |line|
108
+ if line.match(/\n/)
109
+ line.each_line do |l|
110
+ l.strip!
111
+ f_out << l
112
+ end
113
+ else
114
+ line.strip!
115
+ f_out << line
116
+ end
117
+ end
118
+ else
119
+ f_out << "#{@name}.#{k} = #{v.inspect}"
120
+ end
121
+ end
122
+ f_out.compact.sort.join("\n")
123
+ end
124
+
125
+ def method_missing(name, *args, &block)
126
+ do_lookup(name, *args, &block)
77
127
  end
78
128
 
79
- def temp_start
80
- @__temp = @attributes.deep_clone
129
+ def to_h
130
+ @attributes.each_with_object({}) do |(k, v), h|
131
+ v = v.call if ::Configatron::Proc === v
132
+ h[k] = Store === v ? v.to_h : v
133
+ end
81
134
  end
82
135
 
83
- def temp_end
84
- @attributes = @__temp
136
+ # So that puts works (it expects the object to respond to to_ary)
137
+ def to_ary
138
+ nil
85
139
  end
86
140
 
87
- def method_missing(name, *args, &block)
88
- if block_given?
141
+ # So that we keep backward-compatibility in case people are using nil? to check
142
+ # configatron settings:
143
+ def nil?
144
+ false
145
+ end
146
+
147
+ private
148
+
149
+ def do_lookup(name, *args, &block)
150
+ if block
89
151
  yield self[name]
90
152
  else
91
153
  name = name.to_s
@@ -96,7 +158,7 @@ class Configatron
96
158
  if self.has_key?(key)
97
159
  return self[key]
98
160
  else
99
- raise Configatron::UndefinedKeyError.new($1)
161
+ ::Kernel.raise ::Configatron::UndefinedKeyError.new($1)
100
162
  end
101
163
  else
102
164
  return self[name]
@@ -104,38 +166,13 @@ class Configatron
104
166
  end
105
167
  end
106
168
 
107
- def inspect(name = 'configatron')
108
- f_out = []
109
- @attributes.each do |k, v|
110
- if v.is_a?(Configatron::Store)
111
- v.inspect("#{name}.#{k}").each_line do |line|
112
- if line.match(/\n/)
113
- line.each_line do |l|
114
- l.strip!
115
- f_out << l
116
- end
117
- else
118
- line.strip!
119
- f_out << line
120
- end
121
- end
122
- else
123
- f_out << "#{name}.#{k} = #{v.inspect}"
124
- end
125
- end
126
- f_out.compact.sort.join("\n")
127
- end
128
-
129
169
  alias :[]= :store
130
- alias :blank? :nil?
131
170
  alias :has_key? :key?
171
+ alias :to_hash :to_h
132
172
 
133
173
  def_delegator :@attributes, :values
134
174
  def_delegator :@attributes, :keys
135
175
  def_delegator :@attributes, :each
136
- def_delegator :@attributes, :empty?
137
- def_delegator :@attributes, :to_h
138
- def_delegator :@attributes, :to_hash
139
176
  def_delegator :@attributes, :delete
140
177
  # def_delegator :@attributes, :fetch
141
178
  # def_delegator :@attributes, :has_key?
@@ -1,3 +1,3 @@
1
1
  class Configatron
2
- VERSION = "3.2.0"
2
+ VERSION = "4.5.0"
3
3
  end
@@ -3,5 +3,5 @@
3
3
  # Example:
4
4
  # configatron.emails.welcome.subject = 'Welcome!'
5
5
  # configatron.emails.sales_reciept.subject = 'Thanks for your order'
6
- #
7
- # configatron.file.storage = :s3
6
+ #
7
+ # configatron.file.storage = :s3
@@ -1,4 +1,4 @@
1
1
  # Override your default settings for the Development environment here.
2
- #
2
+ #
3
3
  # Example:
4
- # configatron.file.storage = :local
4
+ # configatron.file.storage = :local
@@ -1,4 +1,4 @@
1
1
  # Override your default settings for the Production environment here.
2
- #
2
+ #
3
3
  # Example:
4
- # configatron.file.storage = :s3
4
+ # configatron.file.storage = :s3
@@ -1,4 +1,4 @@
1
1
  # Override your default settings for the Test environment here.
2
- #
2
+ #
3
3
  # Example:
4
- # configatron.file.storage = :local
4
+ # configatron.file.storage = :local
@@ -1,2 +1,2 @@
1
1
  require 'configatron'
2
- Configatron::Rails.init
2
+ Configatron::Integrations::Rails.init
data/test/_lib.rb ADDED
@@ -0,0 +1,17 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ ENV['MT_NO_EXPECTATIONS'] = 'true'
5
+ require 'minitest/autorun'
6
+ require 'minitest/spec'
7
+ require 'mocha/setup'
8
+
9
+ require 'configatron'
10
+
11
+ module Critic
12
+ class Test < ::MiniTest::Spec
13
+ def setup
14
+ # Put any stubs here that you want to apply globally
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,10 @@
1
+ require File.expand_path('../../_lib', __FILE__)
2
+
3
+ module Critic::Functional
4
+ module Stubs
5
+ end
6
+
7
+ class Test < Critic::Test
8
+ include Stubs
9
+ end
10
+ end
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ if defined?(configatron)
4
+ raise "`configatron` method was defined at load-time!"
5
+ end
6
+
7
+ require 'configatron/core'
8
+
9
+ if defined?(configatron)
10
+ raise "Loaded configatron/core but `configatron` was defined!"
11
+ end
@@ -0,0 +1,165 @@
1
+ require_relative '_lib'
2
+
3
+ class Critic::Functional::ConfigatronTest < Critic::Functional::Test
4
+ before do
5
+ @kernel = Configatron::RootStore.new
6
+ end
7
+
8
+ describe 'temp' do
9
+ before do
10
+ @kernel.a = 'A'
11
+ @kernel.b = 'B'
12
+ end
13
+
14
+ it 'allows for temporary setting of values' do
15
+ assert_equal('A', @kernel.a)
16
+ assert_equal('B', @kernel.b)
17
+ @kernel.temp do
18
+ @kernel.a = 'AA'
19
+ @kernel.c = 'C'
20
+ assert_equal('AA', @kernel.a)
21
+ assert_equal('B', @kernel.b)
22
+ assert_equal('C', @kernel.c)
23
+ end
24
+ assert_equal('A', @kernel.a)
25
+ assert_equal('B', @kernel.b)
26
+ assert_equal(false, @kernel.key?(:c))
27
+ end
28
+
29
+ it 'nested' do
30
+ @kernel.foo.bar = 'original'
31
+ @kernel.temp do
32
+ @kernel.foo.bar = 'temp'
33
+ assert_equal('temp', @kernel.foo.bar)
34
+ end
35
+ assert_equal('original', @kernel.foo.bar)
36
+ end
37
+
38
+ it 'cleans up after an exception' do
39
+ @kernel.foo.bar = 'original'
40
+
41
+ assert_raises(RuntimeError) do
42
+ @kernel.temp do
43
+ @kernel.foo.bar = 'temp'
44
+ raise RuntimeError.new('error')
45
+ end
46
+ end
47
+
48
+ assert_equal('original', @kernel.foo.bar)
49
+ end
50
+
51
+ it 'restores locking state' do
52
+ @kernel.lock!
53
+ @kernel.temp do
54
+ @kernel.unlock!
55
+ end
56
+ assert(@kernel.locked?)
57
+ end
58
+
59
+ describe 'start/end' do
60
+ it 'allows for temporary setting of values' do
61
+ assert_equal('A', @kernel.a)
62
+ assert_equal('B', @kernel.b)
63
+ @kernel.temp_start
64
+ @kernel.a = 'AA'
65
+ @kernel.c = 'C'
66
+ assert_equal('AA', @kernel.a)
67
+ assert_equal('B', @kernel.b)
68
+ assert_equal('C', @kernel.c)
69
+ @kernel.temp_end
70
+ assert_equal('A', @kernel.a)
71
+ assert_equal('B', @kernel.b)
72
+ assert_equal(false, @kernel.key?(:c))
73
+ end
74
+ end
75
+ end
76
+
77
+ describe 'lock!' do
78
+ before do
79
+ @kernel.a.b.c.d = 'DD'
80
+ @kernel.lock!
81
+ end
82
+
83
+ it 'raises an error when accessing non-existing values' do
84
+ assert @kernel.a != nil
85
+ assert @kernel.a.b != nil
86
+ assert @kernel.a.b.c != nil
87
+ assert_equal('DD', @kernel.a.b.c.d)
88
+ assert_raises(Configatron::UndefinedKeyError) do
89
+ @kernel.unknown
90
+ end
91
+ end
92
+
93
+ it 'responds to nil? for backward compatibility' do
94
+ refute_nil @kernel.a
95
+ end
96
+
97
+ it 'raises an error when trying to set a non-existing key' do
98
+ assert_raises(Configatron::LockedError) do
99
+ @kernel.unknown = 'known'
100
+ end
101
+ end
102
+
103
+ it 'locks during the block argument' do
104
+ @kernel.unlock!
105
+
106
+ @kernel.lock! do
107
+ assert(@kernel.locked?)
108
+ end
109
+
110
+ assert(!@kernel.locked?)
111
+ end
112
+
113
+ it 'executes a block argument' do
114
+ a = 1
115
+ @kernel.lock! do
116
+ a = 2
117
+ end
118
+ assert_equal(2, a)
119
+ end
120
+ end
121
+
122
+ describe 'name' do
123
+ it 'assigns an appropriate nested name' do
124
+ name = @kernel.foo.bar.baz.to_s
125
+ assert_equal(name, 'configatron.foo.bar.baz')
126
+ end
127
+ end
128
+
129
+ describe 'puts' do
130
+ it 'does not cause an exception' do
131
+ puts @kernel
132
+ puts @kernel.hi
133
+ end
134
+ end
135
+
136
+ describe 'private methods on kernel' do
137
+ it 'can be accessed through method accessors' do
138
+ @kernel.catch = 'hi'
139
+ @kernel.foo.catch = 'hi'
140
+
141
+ assert_equal('hi', @kernel.catch)
142
+ assert_equal('hi', @kernel.foo.catch)
143
+ end
144
+ end
145
+
146
+ describe 'to_h and to_hash' do
147
+ before do
148
+ @kernel.a = 1
149
+ @kernel.b.c = Configatron::Delayed.new{ @kernel.a + 3 }
150
+ end
151
+
152
+ it 'returns a hash representation' do
153
+ expected_hash = { a: 1, b: { c: 4 } }
154
+ assert_equal(expected_hash, @kernel.to_h)
155
+ assert_equal(expected_hash, @kernel.to_hash)
156
+ end
157
+ end
158
+
159
+ describe 'nil value' do
160
+ it 'remembers a nil value' do
161
+ @kernel.a = nil
162
+ assert_equal(nil, @kernel.a)
163
+ end
164
+ end
165
+ end