configatron 3.2.0 → 4.5.1
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +2 -2
- data/History.txt +72 -0
- data/README.md +25 -24
- data/Rakefile +10 -4
- data/configatron.gemspec +1 -0
- data/lib/configatron.rb +22 -4
- data/lib/configatron/core.rb +6 -6
- data/lib/configatron/ext/kernel.rb +5 -0
- data/lib/configatron/integrations.rb +4 -0
- data/lib/configatron/integrations/minitest.rb +29 -0
- data/lib/configatron/{rails.rb → integrations/rails.rb} +1 -1
- data/lib/configatron/root_store.rb +121 -0
- data/lib/configatron/store.rb +104 -67
- data/lib/configatron/version.rb +1 -1
- data/lib/generators/configatron/install/templates/configatron/defaults.rb +2 -2
- data/lib/generators/configatron/install/templates/configatron/development.rb +2 -2
- data/lib/generators/configatron/install/templates/configatron/production.rb +2 -2
- data/lib/generators/configatron/install/templates/configatron/test.rb +2 -2
- data/lib/generators/configatron/install/templates/initializers/configatron.rb +1 -1
- data/test/_lib.rb +17 -0
- data/test/functional/_lib.rb +10 -0
- data/test/functional/_lib/scripts/core.rb +11 -0
- data/test/functional/configatron.rb +165 -0
- data/test/functional/delayed.rb +18 -0
- data/test/functional/loading.rb +10 -0
- data/test/functional/minitest.rb +18 -0
- data/test/unit/_lib.rb +10 -0
- data/test/unit/configatron/proc.rb +24 -0
- data/test/unit/configatron/root_store.rb +37 -0
- data/test/unit/configatron/store.rb +149 -0
- metadata +55 -28
- data/.ruby-version +0 -1
- data/lib/configatron/deep_clone.rb +0 -69
- data/lib/configatron/kernel.rb +0 -21
- data/test/configatron/kernel_test.rb +0 -24
- data/test/configatron/proc_test.rb +0 -27
- data/test/configatron/store_test.rb +0 -256
- data/test/configatron_test.rb +0 -5
- data/test/test_helper.rb +0 -15
data/lib/configatron/store.rb
CHANGED
@@ -1,68 +1,94 @@
|
|
1
1
|
require 'forwardable'
|
2
2
|
|
3
3
|
class Configatron
|
4
|
-
class Store
|
4
|
+
class Store < BasicObject
|
5
5
|
extend ::Forwardable
|
6
6
|
|
7
|
-
def initialize(
|
8
|
-
@
|
9
|
-
@
|
10
|
-
@attributes
|
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
|
14
|
-
@
|
28
|
+
def __cow
|
29
|
+
@cow
|
15
30
|
end
|
16
31
|
|
17
|
-
def
|
18
|
-
|
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 @
|
24
|
-
raise Configatron::UndefinedKeyError.new("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 @
|
33
|
-
raise Configatron::LockedError.new("
|
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
|
-
|
40
|
-
if
|
41
|
-
|
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
|
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
|
-
|
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
|
91
|
+
if ::Hash === value
|
66
92
|
self[key].configure_from_hash(value)
|
67
93
|
else
|
68
94
|
store(key, value)
|
@@ -70,45 +96,15 @@ class Configatron
|
|
70
96
|
end
|
71
97
|
end
|
72
98
|
|
73
|
-
def
|
74
|
-
|
75
|
-
yield
|
76
|
-
temp_end
|
77
|
-
end
|
78
|
-
|
79
|
-
def temp_start
|
80
|
-
@__temp = @attributes.deep_clone
|
81
|
-
end
|
82
|
-
|
83
|
-
def temp_end
|
84
|
-
@attributes = @__temp
|
85
|
-
end
|
86
|
-
|
87
|
-
def method_missing(name, *args, &block)
|
88
|
-
if block_given?
|
89
|
-
yield self[name]
|
90
|
-
else
|
91
|
-
name = name.to_s
|
92
|
-
if /(.+)=$/.match(name)
|
93
|
-
return store($1, args[0])
|
94
|
-
elsif /(.+)!/.match(name)
|
95
|
-
key = $1
|
96
|
-
if self.has_key?(key)
|
97
|
-
return self[key]
|
98
|
-
else
|
99
|
-
raise Configatron::UndefinedKeyError.new($1)
|
100
|
-
end
|
101
|
-
else
|
102
|
-
return self[name]
|
103
|
-
end
|
104
|
-
end
|
99
|
+
def to_s
|
100
|
+
@name
|
105
101
|
end
|
106
102
|
|
107
|
-
def inspect
|
103
|
+
def inspect
|
108
104
|
f_out = []
|
109
105
|
@attributes.each do |k, v|
|
110
|
-
if
|
111
|
-
v.inspect
|
106
|
+
if ::Configatron::Store === v
|
107
|
+
v.inspect.each_line do |line|
|
112
108
|
if line.match(/\n/)
|
113
109
|
line.each_line do |l|
|
114
110
|
l.strip!
|
@@ -120,22 +116,63 @@ class Configatron
|
|
120
116
|
end
|
121
117
|
end
|
122
118
|
else
|
123
|
-
f_out << "#{name}.#{k} = #{v.inspect}"
|
119
|
+
f_out << "#{@name}.#{k} = #{v.inspect}"
|
124
120
|
end
|
125
121
|
end
|
126
122
|
f_out.compact.sort.join("\n")
|
127
|
-
|
123
|
+
end
|
124
|
+
|
125
|
+
def method_missing(name, *args, &block)
|
126
|
+
do_lookup(name, *args, &block)
|
127
|
+
end
|
128
|
+
|
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
|
134
|
+
end
|
135
|
+
|
136
|
+
# So that puts works (it expects the object to respond to to_ary)
|
137
|
+
def to_ary
|
138
|
+
nil
|
139
|
+
end
|
140
|
+
|
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
|
151
|
+
yield self[name]
|
152
|
+
else
|
153
|
+
name = name.to_s
|
154
|
+
if name.end_with?('=')
|
155
|
+
return store(name[0..-2], args[0])
|
156
|
+
elsif name.end_with?('!')
|
157
|
+
key = name[0..-2]
|
158
|
+
if self.has_key?(key)
|
159
|
+
return self[key]
|
160
|
+
else
|
161
|
+
::Kernel.raise ::Configatron::UndefinedKeyError.new($1)
|
162
|
+
end
|
163
|
+
else
|
164
|
+
return self[name]
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
128
168
|
|
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?
|
data/lib/configatron/version.rb
CHANGED
@@ -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,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
|