configatron 4.4.1 → 4.5.0
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.
- data/History.txt +4 -0
- data/lib/configatron.rb +0 -1
- data/lib/configatron/root_store.rb +38 -2
- data/lib/configatron/store.rb +40 -12
- data/lib/configatron/version.rb +1 -1
- data/test/functional/configatron.rb +18 -0
- data/test/unit/configatron/store.rb +2 -2
- metadata +4 -5
- data/lib/configatron/deep_clone.rb +0 -81
data/History.txt
CHANGED
data/lib/configatron.rb
CHANGED
@@ -15,11 +15,41 @@ class Configatron::RootStore < BasicObject
|
|
15
15
|
public :new
|
16
16
|
end
|
17
17
|
|
18
|
+
@@cow = 0
|
19
|
+
|
18
20
|
def initialize
|
19
21
|
@locked = false
|
22
|
+
@cow = nil
|
20
23
|
reset!
|
21
24
|
end
|
22
25
|
|
26
|
+
def __cow
|
27
|
+
@cow
|
28
|
+
end
|
29
|
+
|
30
|
+
def __cow_path(path)
|
31
|
+
start = @store.__cow_clone
|
32
|
+
|
33
|
+
node = start
|
34
|
+
branch = path.map do |key|
|
35
|
+
node = node[key]
|
36
|
+
node.__cow_clone
|
37
|
+
end
|
38
|
+
nodes = [start] + branch
|
39
|
+
|
40
|
+
# [node1, node2, node3] with
|
41
|
+
# [node2, node3, node4] and
|
42
|
+
# ['key1', 'key2, 'key3']
|
43
|
+
nodes[0...-1].zip(nodes[1..-1], path) do |parent, child, key|
|
44
|
+
# These are all cow_clones, so won't trigger a further cow
|
45
|
+
# modification.
|
46
|
+
parent[key] = child
|
47
|
+
end
|
48
|
+
|
49
|
+
@store = nodes.first
|
50
|
+
nodes.last
|
51
|
+
end
|
52
|
+
|
23
53
|
def method_missing(name, *args, &block)
|
24
54
|
store.__send__(name, *args, &block)
|
25
55
|
end
|
@@ -39,13 +69,19 @@ class Configatron::RootStore < BasicObject
|
|
39
69
|
end
|
40
70
|
|
41
71
|
def temp_start
|
42
|
-
@temp = ::Configatron::DeepClone.deep_clone(@store)
|
43
72
|
@temp_locked = @locked
|
73
|
+
@temp_cow = @cow
|
74
|
+
|
75
|
+
# Just need to have a unique Copy-on-Write generation ID
|
76
|
+
@cow = @@cow += 1
|
77
|
+
@temp = @store
|
44
78
|
end
|
45
79
|
|
46
80
|
def temp_end
|
47
|
-
@store = @temp
|
48
81
|
@locked = @temp_locked
|
82
|
+
@cow = @temp_cow
|
83
|
+
|
84
|
+
@store = @temp
|
49
85
|
end
|
50
86
|
|
51
87
|
def locked?
|
data/lib/configatron/store.rb
CHANGED
@@ -4,10 +4,38 @@ class Configatron
|
|
4
4
|
class Store < BasicObject
|
5
5
|
extend ::Forwardable
|
6
6
|
|
7
|
-
def initialize(root_store, name='configatron', attributes={})
|
7
|
+
def initialize(root_store, name='configatron', attributes={}, path=[])
|
8
8
|
@root_store = root_store
|
9
9
|
@name = name
|
10
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
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
def __cow
|
29
|
+
@cow
|
30
|
+
end
|
31
|
+
|
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
|
11
39
|
end
|
12
40
|
|
13
41
|
def [](key)
|
@@ -15,7 +43,7 @@ class Configatron
|
|
15
43
|
if @root_store.locked?
|
16
44
|
::Kernel.raise ::Configatron::UndefinedKeyError.new("Key not found: #{key} (for locked #{self})")
|
17
45
|
end
|
18
|
-
::Configatron::Store.new(@root_store, "#{@name}.#{key}")
|
46
|
+
::Configatron::Store.new(@root_store, "#{@name}.#{key}", {}, @path + [key])
|
19
47
|
end
|
20
48
|
return val
|
21
49
|
end
|
@@ -24,7 +52,16 @@ class Configatron
|
|
24
52
|
if @root_store.locked?
|
25
53
|
::Kernel.raise ::Configatron::LockedError.new("Cannot set key #{key} for locked #{self}")
|
26
54
|
end
|
27
|
-
|
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
|
64
|
+
end
|
28
65
|
end
|
29
66
|
|
30
67
|
def fetch(key, default_value = nil, &block)
|
@@ -89,15 +126,6 @@ class Configatron
|
|
89
126
|
do_lookup(name, *args, &block)
|
90
127
|
end
|
91
128
|
|
92
|
-
# Needed for deep_clone to actually clone this object
|
93
|
-
def clone(cloned={})
|
94
|
-
root_store = DeepClone.deep_clone(@root_store, cloned)
|
95
|
-
name = DeepClone.deep_clone(@name, cloned)
|
96
|
-
attributes = DeepClone.deep_clone(@attributes, cloned)
|
97
|
-
|
98
|
-
Store.new(root_store, name, attributes)
|
99
|
-
end
|
100
|
-
|
101
129
|
def to_h
|
102
130
|
@attributes.each_with_object({}) do |(k, v), h|
|
103
131
|
v = v.call if ::Configatron::Proc === v
|
data/lib/configatron/version.rb
CHANGED
@@ -99,6 +99,24 @@ class Critic::Functional::ConfigatronTest < Critic::Functional::Test
|
|
99
99
|
@kernel.unknown = 'known'
|
100
100
|
end
|
101
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
|
102
120
|
end
|
103
121
|
|
104
122
|
describe 'name' do
|
@@ -66,7 +66,7 @@ class Critic::Unit::StoreTest < Critic::Unit::Test
|
|
66
66
|
|
67
67
|
it "returns true if the key is a Configatron::Store" do
|
68
68
|
assert_equal(false, @store.key?(:bar))
|
69
|
-
@store.bar = Configatron::Store.new(Configatron::RootStore)
|
69
|
+
@store.bar = Configatron::Store.new(Configatron::RootStore.new)
|
70
70
|
assert_equal(true, @store.key?(:bar))
|
71
71
|
end
|
72
72
|
end
|
@@ -80,7 +80,7 @@ class Critic::Unit::StoreTest < Critic::Unit::Test
|
|
80
80
|
|
81
81
|
it "returns true if the key is a Configatron::Store" do
|
82
82
|
assert_equal(false, @store.has_key?(:bar))
|
83
|
-
@store.bar = Configatron::Store.new(Configatron::RootStore)
|
83
|
+
@store.bar = Configatron::Store.new(Configatron::RootStore.new)
|
84
84
|
assert_equal(true, @store.has_key?(:bar))
|
85
85
|
end
|
86
86
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: configatron
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.5.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-12-
|
12
|
+
date: 2014-12-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -94,7 +94,6 @@ files:
|
|
94
94
|
- configatron.gemspec
|
95
95
|
- lib/configatron.rb
|
96
96
|
- lib/configatron/core.rb
|
97
|
-
- lib/configatron/deep_clone.rb
|
98
97
|
- lib/configatron/delayed.rb
|
99
98
|
- lib/configatron/dynamic.rb
|
100
99
|
- lib/configatron/errors.rb
|
@@ -138,7 +137,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
138
137
|
version: '0'
|
139
138
|
segments:
|
140
139
|
- 0
|
141
|
-
hash:
|
140
|
+
hash: -3612198204560107212
|
142
141
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
143
142
|
none: false
|
144
143
|
requirements:
|
@@ -147,7 +146,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
147
146
|
version: '0'
|
148
147
|
segments:
|
149
148
|
- 0
|
150
|
-
hash:
|
149
|
+
hash: -3612198204560107212
|
151
150
|
requirements: []
|
152
151
|
rubyforge_project:
|
153
152
|
rubygems_version: 1.8.25
|
@@ -1,81 +0,0 @@
|
|
1
|
-
module Configatron::DeepClone
|
2
|
-
# = DeepClone
|
3
|
-
#
|
4
|
-
# == Version
|
5
|
-
# 1.2006.05.23.configatron.1 (change of the first number means Big Change)
|
6
|
-
#
|
7
|
-
# == Description
|
8
|
-
# Adds deep_clone method to an object which produces deep copy of it. It means
|
9
|
-
# if you clone a Hash, every nested items and their nested items will be cloned.
|
10
|
-
# Moreover deep_clone checks if the object is already cloned to prevent endless recursion.
|
11
|
-
#
|
12
|
-
# == Usage
|
13
|
-
#
|
14
|
-
# (see examples directory under the ruby gems root directory)
|
15
|
-
#
|
16
|
-
# require 'rubygems'
|
17
|
-
# require 'deep_clone'
|
18
|
-
#
|
19
|
-
# include DeepClone
|
20
|
-
#
|
21
|
-
# obj = []
|
22
|
-
# a = [ true, false, obj ]
|
23
|
-
# b = a.deep_clone
|
24
|
-
# obj.push( 'foo' )
|
25
|
-
# p obj # >> [ 'foo' ]
|
26
|
-
# p b[2] # >> []
|
27
|
-
#
|
28
|
-
# == Source
|
29
|
-
# http://simplypowerful.1984.cz/goodlibs/1.2006.05.23
|
30
|
-
#
|
31
|
-
# == Author
|
32
|
-
# jan molic (/mig/at_sign/1984/dot/cz/)
|
33
|
-
#
|
34
|
-
# == Licence
|
35
|
-
# You can redistribute it and/or modify it under the same terms of Ruby's license;
|
36
|
-
# either the dual license version in 2003, or any later version.
|
37
|
-
def self.deep_clone( obj=self, cloned={} )
|
38
|
-
if Configatron::RootStore === obj
|
39
|
-
# We never actually want to have multiple copies of our
|
40
|
-
# Configatron::RootStore -- when making a temp, we just stick
|
41
|
-
# the state-to-revert-to into an ivar.
|
42
|
-
return obj
|
43
|
-
elsif Configatron::Store === obj
|
44
|
-
# Need to special-case this since it's a BasicObject, meaning it
|
45
|
-
# doesn't respond to all the usual ivar magic methods
|
46
|
-
cl = obj.clone(cloned)
|
47
|
-
cloned[obj.__id__] = cl
|
48
|
-
cloned[cl.__id__] = cl
|
49
|
-
return cl
|
50
|
-
elsif cloned.key?( obj.__id__ )
|
51
|
-
return cloned[obj.__id__]
|
52
|
-
else
|
53
|
-
begin
|
54
|
-
cl = obj.clone
|
55
|
-
rescue Exception
|
56
|
-
# unclonnable (TrueClass, Fixnum, ...)
|
57
|
-
cloned[obj.__id__] = obj
|
58
|
-
return obj
|
59
|
-
else
|
60
|
-
cloned[obj.__id__] = cl
|
61
|
-
cloned[cl.__id__] = cl
|
62
|
-
case
|
63
|
-
when Hash === cl
|
64
|
-
cl.clone.each do |k,v|
|
65
|
-
cl[k] = deep_clone( v, cloned )
|
66
|
-
end
|
67
|
-
when Array === cl
|
68
|
-
cl.collect! do |v|
|
69
|
-
deep_clone( v, cloned )
|
70
|
-
end
|
71
|
-
end
|
72
|
-
cl.instance_variables.each do |var|
|
73
|
-
v = cl.instance_eval( var.to_s )
|
74
|
-
v_cl = deep_clone( v, cloned )
|
75
|
-
cl.instance_eval( "#{var} = v_cl" )
|
76
|
-
end
|
77
|
-
return cl
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|