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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +2 -2
  4. data/History.txt +72 -0
  5. data/README.md +25 -24
  6. data/Rakefile +10 -4
  7. data/configatron.gemspec +1 -0
  8. data/lib/configatron.rb +22 -4
  9. data/lib/configatron/core.rb +6 -6
  10. data/lib/configatron/ext/kernel.rb +5 -0
  11. data/lib/configatron/integrations.rb +4 -0
  12. data/lib/configatron/integrations/minitest.rb +29 -0
  13. data/lib/configatron/{rails.rb → integrations/rails.rb} +1 -1
  14. data/lib/configatron/root_store.rb +121 -0
  15. data/lib/configatron/store.rb +104 -67
  16. data/lib/configatron/version.rb +1 -1
  17. data/lib/generators/configatron/install/templates/configatron/defaults.rb +2 -2
  18. data/lib/generators/configatron/install/templates/configatron/development.rb +2 -2
  19. data/lib/generators/configatron/install/templates/configatron/production.rb +2 -2
  20. data/lib/generators/configatron/install/templates/configatron/test.rb +2 -2
  21. data/lib/generators/configatron/install/templates/initializers/configatron.rb +1 -1
  22. data/test/_lib.rb +17 -0
  23. data/test/functional/_lib.rb +10 -0
  24. data/test/functional/_lib/scripts/core.rb +11 -0
  25. data/test/functional/configatron.rb +165 -0
  26. data/test/functional/delayed.rb +18 -0
  27. data/test/functional/loading.rb +10 -0
  28. data/test/functional/minitest.rb +18 -0
  29. data/test/unit/_lib.rb +10 -0
  30. data/test/unit/configatron/proc.rb +24 -0
  31. data/test/unit/configatron/root_store.rb +37 -0
  32. data/test/unit/configatron/store.rb +149 -0
  33. metadata +55 -28
  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,45 +96,15 @@ class Configatron
70
96
  end
71
97
  end
72
98
 
73
- def temp(&block)
74
- temp_start
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(name = 'configatron')
103
+ def inspect
108
104
  f_out = []
109
105
  @attributes.each do |k, v|
110
- if v.is_a?(Configatron::Store)
111
- v.inspect("#{name}.#{k}").each_line do |line|
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
- end
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?
@@ -1,3 +1,3 @@
1
1
  class Configatron
2
- VERSION = "3.2.0"
2
+ VERSION = "4.5.1"
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