markbates-configatron 2.3.2.20090731133933
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/LICENSE +21 -0
- data/README +143 -0
- data/lib/configatron.rb +9 -0
- data/lib/configatron/configatron.rb +55 -0
- data/lib/configatron/core_ext/class.rb +25 -0
- data/lib/configatron/core_ext/kernel.rb +8 -0
- data/lib/configatron/core_ext/object.rb +9 -0
- data/lib/configatron/core_ext/string.rb +90 -0
- data/lib/configatron/errors.rb +13 -0
- data/lib/configatron/store.rb +283 -0
- metadata +73 -0
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2009 Mark Bates
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
=Configatron
|
2
|
+
|
3
|
+
Configatron makes configuring your applications and scripts incredibly easy. No longer is a there a need to use constants or global variables. Now you can use a simple and painless system to configure your life. And, because it's all Ruby, you can do any crazy thing you would like to!
|
4
|
+
|
5
|
+
==Installation
|
6
|
+
|
7
|
+
Installation of Configatron is easy, as it is just a RubyGem:
|
8
|
+
|
9
|
+
$ sudo gem install configatron
|
10
|
+
|
11
|
+
If you'd like to live on the bleedin' edge you can install the development version from GitHub:
|
12
|
+
|
13
|
+
$ sudo gem install markbates-configatron --source=http://gems.github.com
|
14
|
+
|
15
|
+
Once installed you just need to require it:
|
16
|
+
|
17
|
+
require 'configatron'
|
18
|
+
|
19
|
+
==Examples
|
20
|
+
|
21
|
+
===Simple
|
22
|
+
|
23
|
+
configatron.email = 'me@example.com'
|
24
|
+
configatron.database_url = "postgres://localhost/mack_framework_rocks"
|
25
|
+
|
26
|
+
Now, anywhere in your code you can do the following:
|
27
|
+
|
28
|
+
configatron.email # => "me@example.com"
|
29
|
+
configatron.database_url # => "postgres://localhost/mack_framework_rocks"
|
30
|
+
|
31
|
+
Viola! Simple as can be.
|
32
|
+
|
33
|
+
Now you're saying, what if I want to have a 'default' set of options, but then override them later, based on other information? Simple again. Let's use our above example. We've configured our <tt>database_url</tt> option to be <tt>postgres://localhost/mack_framework_rocks</tt>. The problem with that is that is our production database url, not our development url. Fair enough, all you have to do is redeclare it:
|
34
|
+
|
35
|
+
configatron.database_url = "postgres://localhost/mack_framework_rocks_development"
|
36
|
+
|
37
|
+
becomes:
|
38
|
+
|
39
|
+
configatron.email # => "me@example.com"
|
40
|
+
configatron.database_url # => "postgres://localhost/mack_framework_rocks_development"
|
41
|
+
|
42
|
+
Notice how our other configuration parameters haven't changed? Cool, eh?
|
43
|
+
|
44
|
+
===Hash/YAML
|
45
|
+
|
46
|
+
You can configure configatron from a hash as well:
|
47
|
+
|
48
|
+
configatron.configure_from_hash({:email => {:pop => {:address => 'pop.example.com', :port => 110}}, :smtp => {:address => 'smtp.example.com'}})
|
49
|
+
|
50
|
+
configatron.email.pop.address # => 'pop.example.com'
|
51
|
+
configatron.email.pop.port # => 110
|
52
|
+
# and so on...
|
53
|
+
|
54
|
+
Notice how they're all namespaced for your as well. The same holds true for YAML files:
|
55
|
+
|
56
|
+
configuration.configure_from_yaml('/path/to/file.yml')
|
57
|
+
|
58
|
+
===Namespaces
|
59
|
+
|
60
|
+
The question that should be on your lips is what I need to have namespaced configuration parameters. It's easy! Configatron allows you to create namespaces.
|
61
|
+
|
62
|
+
configatron.website_url = "http://www.mackframework.com"
|
63
|
+
configatron.email.pop.address = "pop.example.com"
|
64
|
+
configatron.email.pop.port = 110
|
65
|
+
configatron.email.smtp.address = "smtp.example.com"
|
66
|
+
configatron.email.smtp.port = 25
|
67
|
+
|
68
|
+
becomes:
|
69
|
+
|
70
|
+
configatron.email.pop.address # => "pop.example.com"
|
71
|
+
configatron.email.smtp.address # => "smtp.example.com"
|
72
|
+
configatron.website_url # => "http://www.mackframework.com"
|
73
|
+
|
74
|
+
Configatron allows you to nest namespaces to your hearts content! Just keep going, it's that easy.
|
75
|
+
|
76
|
+
Of course you can update a single parameter n levels deep as well:
|
77
|
+
|
78
|
+
configatron.email.pop.address = "pop2.example.com"
|
79
|
+
|
80
|
+
configatron.email.pop.address # => "pop2.example.com"
|
81
|
+
configatron.email.smtp.address # => "smtp.example.com"
|
82
|
+
|
83
|
+
===Misc.
|
84
|
+
|
85
|
+
Even if parameters haven't been set, you can still call them, but you'll get a <tt>Configatron::Store</tt> object back. The Configatron::Store class, however, will respond true to <tt>.nil?</tt> if there are no parameters configured on it.
|
86
|
+
|
87
|
+
configatron.i.dont.exist.nil? # => true
|
88
|
+
configatron.i.dont.exist # => Configatron::Store
|
89
|
+
|
90
|
+
If you want to get back an actual <tt>nil</tt> then you can use the <tt>retrieve</tt> method:
|
91
|
+
|
92
|
+
configatron.i.do.exist = [:some, :array]
|
93
|
+
configatron.i.dont.retrieve(:exist, nil) # => nil
|
94
|
+
configatron.i.do.retrieve(:exist, :foo) # => [:some, :array]
|
95
|
+
|
96
|
+
You can set 'default' values for parameters. If there is already a setting, it won't be replaced. This is useful if you've already done your 'configuration' and you call a library, that needs to have parameters set. The library can set its defaults, without worrying that it might have overridden your custom settings.
|
97
|
+
|
98
|
+
configatron.set_default(:name, 'Mark Bates')
|
99
|
+
configatron.name # => 'Mark Bates'
|
100
|
+
configatron.set_default(:name, 'Me')
|
101
|
+
configatron.name # => 'Mark Bates'
|
102
|
+
|
103
|
+
Sometimes in testing, or other situations, you want to temporarily change some settings. You can do this with the <tt>temp</tt> method:
|
104
|
+
|
105
|
+
configatron.one = 1
|
106
|
+
configatron.letters.a = 'A'
|
107
|
+
configatron.letters.b = 'B'
|
108
|
+
configatron.temp do
|
109
|
+
configatron.letters.b = 'bb'
|
110
|
+
configatron.letters.c = 'c'
|
111
|
+
configatron.one # => 1
|
112
|
+
configatron.letters.a # => 'A'
|
113
|
+
configatron.letters.b # => 'bb'
|
114
|
+
configatron.letters.c # => 'c'
|
115
|
+
end
|
116
|
+
configatron.one # => 1
|
117
|
+
configatron.letters.a # => 'A'
|
118
|
+
configatron.letters.b # => 'B'
|
119
|
+
configatron.letters.c # => nil
|
120
|
+
|
121
|
+
You can also pass in an optional Hash to the <tt>temp</tt>:
|
122
|
+
|
123
|
+
configatron.one = 1
|
124
|
+
configatron.letters.a = 'A'
|
125
|
+
configatron.letters.b = 'B'
|
126
|
+
configatron.temp(:letters => {:b => 'bb', :c => 'c'}) do
|
127
|
+
configatron.one == 1
|
128
|
+
configatron.letters.a # => 'A'
|
129
|
+
configatron.letters.b # => 'bb'
|
130
|
+
configatron.letters.c # => 'c'
|
131
|
+
end
|
132
|
+
configatron.one == 1
|
133
|
+
configatron.letters.a # => 'A'
|
134
|
+
configatron.letters.b # => 'B'
|
135
|
+
configatron.letters.c # => nil
|
136
|
+
|
137
|
+
Enjoy!
|
138
|
+
|
139
|
+
==Contact
|
140
|
+
|
141
|
+
Please mail bugs, suggestions and patches to "development@metabates.com":mailto:development@metabates.com
|
142
|
+
|
143
|
+
On the web at: "http://www.metabates.com":http://www.metabates.com
|
data/lib/configatron.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
base = File.join(File.dirname(__FILE__), 'configatron')
|
2
|
+
require 'yamler'
|
3
|
+
require File.join(base, 'configatron')
|
4
|
+
require File.join(base, 'store')
|
5
|
+
require File.join(base, 'errors')
|
6
|
+
require File.join(base, 'core_ext', 'kernel')
|
7
|
+
require File.join(base, 'core_ext', 'object')
|
8
|
+
require File.join(base, 'core_ext', 'string')
|
9
|
+
require File.join(base, 'core_ext', 'class')
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
class Configatron
|
4
|
+
include Singleton
|
5
|
+
|
6
|
+
alias_method :send!, :send
|
7
|
+
|
8
|
+
def initialize # :nodoc:
|
9
|
+
@_namespace = [:default]
|
10
|
+
reset!
|
11
|
+
end
|
12
|
+
|
13
|
+
# Forwards the method call onto the 'namespaced' Configatron::Store
|
14
|
+
def method_missing(sym, *args)
|
15
|
+
@_store[@_namespace.last].send(sym, *args)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Removes ALL configuration parameters
|
19
|
+
def reset!
|
20
|
+
@_store = {:default => Configatron::Store.new}
|
21
|
+
end
|
22
|
+
|
23
|
+
# Allows for the temporary overriding of parameters in a block.
|
24
|
+
# Takes an optional Hash of parameters that will be applied before
|
25
|
+
# the block gets called. At the end of the block, the temporary
|
26
|
+
# settings are deleted and the original settings are reinstated.
|
27
|
+
def temp(options = nil)
|
28
|
+
begin
|
29
|
+
temp_start(options)
|
30
|
+
yield
|
31
|
+
rescue Exception => e
|
32
|
+
raise e
|
33
|
+
ensure
|
34
|
+
temp_end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def temp_start(options = nil)
|
39
|
+
n_space = rand
|
40
|
+
@_store[n_space] = @_store[@_namespace.last].deep_clone
|
41
|
+
@_namespace << n_space
|
42
|
+
if options
|
43
|
+
self.method_missing(:configure_from_hash, options)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def temp_end
|
48
|
+
@_store.delete(@_namespace.pop)
|
49
|
+
end
|
50
|
+
|
51
|
+
undef :inspect # :nodoc:
|
52
|
+
undef :nil? # :nodoc:
|
53
|
+
undef :test # :nodoc:
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class Class
|
2
|
+
|
3
|
+
# Returns access to configuration parameters named after the class.
|
4
|
+
#
|
5
|
+
# Examples:
|
6
|
+
# configatron.foo.bar = :bar
|
7
|
+
# configatron.a.b.c.d = 'D'
|
8
|
+
#
|
9
|
+
# class Foo
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# module A
|
13
|
+
# module B
|
14
|
+
# class C
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# Foo.to_configatron.bar # => :bar
|
20
|
+
# A::B::C.to_configatron.d # => 'D'
|
21
|
+
def to_configatron(*args)
|
22
|
+
self.name.to_configatron(*args)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
class String # :nodoc:
|
2
|
+
|
3
|
+
def to_configatron(*args)
|
4
|
+
name_spaces = (args + self.split("::")).flatten
|
5
|
+
name_spaces.collect!{|s| s.to_s.methodize}
|
6
|
+
configatron.send_with_chain(name_spaces)
|
7
|
+
end
|
8
|
+
|
9
|
+
def underscore # :nodoc:
|
10
|
+
self.to_s.gsub(/::/, '/').
|
11
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
12
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
13
|
+
tr("-", "_").
|
14
|
+
downcase
|
15
|
+
end
|
16
|
+
|
17
|
+
def methodize # :nodoc:
|
18
|
+
x = self
|
19
|
+
|
20
|
+
# if we get down to a nil or an empty string raise an exception!
|
21
|
+
raise NameError.new("#{self} cannot be converted to a valid method name!") if x.nil? || x == ''
|
22
|
+
|
23
|
+
# get rid of the big stuff in the front/back
|
24
|
+
x.strip!
|
25
|
+
|
26
|
+
# if we get down to a nil or an empty string raise an exception!
|
27
|
+
raise NameError.new("#{self} cannot be converted to a valid method name!") if x.nil? || x == ''
|
28
|
+
|
29
|
+
x = x.underscore
|
30
|
+
|
31
|
+
# get rid of spaces and make the _
|
32
|
+
x.gsub!(' ', '_')
|
33
|
+
# get rid of everything that isn't 'safe' a-z, 0-9, ?, !, =, _
|
34
|
+
x.gsub!(/([^ a-zA-Z0-9\_\?\!\=]+)/n, '_')
|
35
|
+
|
36
|
+
# if we get down to a nil or an empty string raise an exception!
|
37
|
+
raise NameError.new("#{self} cannot be converted to a valid method name!") if x.nil? || x == ''
|
38
|
+
|
39
|
+
# condense multiple 'safe' non a-z chars to just one.
|
40
|
+
# ie. ___ becomes _ !!!! becomes ! etc...
|
41
|
+
[' ', '_', '?', '!', "="].each do |c|
|
42
|
+
x.squeeze!(c)
|
43
|
+
end
|
44
|
+
|
45
|
+
# if we get down to a nil or an empty string raise an exception!
|
46
|
+
raise NameError.new("#{self} cannot be converted to a valid method name!") if x.nil? || x == ''
|
47
|
+
|
48
|
+
#down case the whole thing
|
49
|
+
x.downcase!
|
50
|
+
|
51
|
+
# get rid of any characters at the beginning that aren't a-z
|
52
|
+
while !x.match(/^[a-z]/)
|
53
|
+
x.slice!(0)
|
54
|
+
|
55
|
+
# if we get down to a nil or an empty string raise an exception!
|
56
|
+
raise NameError.new("#{self} cannot be converted to a valid method name!") if x.nil? || x == ''
|
57
|
+
end
|
58
|
+
|
59
|
+
# let's trim this bad boy down a bit now that we've cleaned it up, somewhat.
|
60
|
+
# we should do this before cleaning up the end character, because it's possible to end up with a
|
61
|
+
# bad char at the end if you trim too late.
|
62
|
+
x = x[0..100] if x.length > 100
|
63
|
+
|
64
|
+
# get rid of any characters at the end that aren't safe
|
65
|
+
while !x.match(/[a-z0-9\?\!\=]$/)
|
66
|
+
x.slice!(x.length - 1)
|
67
|
+
# if we get down to a nil or an empty string raise an exception!
|
68
|
+
raise NameError.new("#{self} cannot be converted to a valid method name!") if x.nil? || x == ''
|
69
|
+
end
|
70
|
+
|
71
|
+
# if we get down to a nil or an empty string raise an exception!
|
72
|
+
raise NameError.new("#{self} cannot be converted to a valid method name!") if x.nil? || x == ''
|
73
|
+
|
74
|
+
# let's get rid of characters that don't belong in the 'middle' of the method.
|
75
|
+
orig_middle = x[1..(x.length - 2)]
|
76
|
+
n_middle = orig_middle.dup
|
77
|
+
|
78
|
+
['?', '!', "="].each do |c|
|
79
|
+
n_middle.gsub!(c, "_")
|
80
|
+
end
|
81
|
+
|
82
|
+
# the previous gsub can leave us with multiple underscores that need cleaning up.
|
83
|
+
n_middle.squeeze!("_")
|
84
|
+
|
85
|
+
x.gsub!(orig_middle, n_middle)
|
86
|
+
x.gsub!("_=", "=")
|
87
|
+
x
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Configatron
|
2
|
+
class ProtectedParameter < StandardError
|
3
|
+
def intialize(name)
|
4
|
+
super("Can not modify protected parameter: '#{name}'")
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
class LockedNamespace < StandardError
|
9
|
+
def initialize(name)
|
10
|
+
super("Cannot add new parameters to locked namespace: #{name.inspect}")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,283 @@
|
|
1
|
+
class Configatron
|
2
|
+
class Store
|
3
|
+
alias_method :send!, :send
|
4
|
+
|
5
|
+
# Takes an optional Hash of parameters
|
6
|
+
def initialize(options = {}, name = nil, parent = nil)
|
7
|
+
@_name = name
|
8
|
+
@_parent = parent
|
9
|
+
@_store = {}
|
10
|
+
configure_from_hash(options)
|
11
|
+
@_protected = []
|
12
|
+
end
|
13
|
+
|
14
|
+
# Returns a Hash representing the configurations
|
15
|
+
def to_hash
|
16
|
+
@_store
|
17
|
+
end
|
18
|
+
|
19
|
+
def heirarchy
|
20
|
+
path = [@_name]
|
21
|
+
parent = @_parent
|
22
|
+
until parent.nil?
|
23
|
+
path << parent.instance_variable_get('@_name')
|
24
|
+
parent = parent.instance_variable_get('@_parent')
|
25
|
+
end
|
26
|
+
path.compact!
|
27
|
+
path.reverse!
|
28
|
+
path.join('.')
|
29
|
+
end
|
30
|
+
|
31
|
+
def configatron_keys
|
32
|
+
return @_store.keys.collect{|k| k.to_s}.sort
|
33
|
+
end
|
34
|
+
|
35
|
+
# Checks whether or not a parameter exists
|
36
|
+
#
|
37
|
+
# Examples:
|
38
|
+
# configatron.i.am.alive = 'alive!'
|
39
|
+
# configatron.i.am.exists?(:alive) # => true
|
40
|
+
# configatron.i.am.exists?(:dead) # => false
|
41
|
+
def exists?(name)
|
42
|
+
@_store.has_key?(name.to_sym) || @_store.has_key?(name.to_s)
|
43
|
+
end
|
44
|
+
|
45
|
+
def inspect
|
46
|
+
path = [@_name]
|
47
|
+
parent = @_parent
|
48
|
+
until parent.nil?
|
49
|
+
path << parent.instance_variable_get('@_name')
|
50
|
+
parent = parent.instance_variable_get('@_parent')
|
51
|
+
end
|
52
|
+
path << 'configatron'
|
53
|
+
path.compact!
|
54
|
+
path.reverse!
|
55
|
+
f_out = []
|
56
|
+
@_store.each do |k, v|
|
57
|
+
if v.is_a?(Configatron::Store)
|
58
|
+
v.inspect.each_line do |line|
|
59
|
+
if line.match(/\n/)
|
60
|
+
line.each_line do |l|
|
61
|
+
l.strip!
|
62
|
+
f_out << l
|
63
|
+
end
|
64
|
+
else
|
65
|
+
line.strip!
|
66
|
+
f_out << line
|
67
|
+
end
|
68
|
+
end
|
69
|
+
else
|
70
|
+
f_out << "#{path.join('.')}.#{k} = #{v.inspect}"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
f_out.compact.sort.join("\n")
|
74
|
+
end
|
75
|
+
|
76
|
+
# Allows for the configuration of the system via a Hash
|
77
|
+
def configure_from_hash(options)
|
78
|
+
parse_options(options)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Allows for the configuration of the system from a YAML file.
|
82
|
+
# Takes the path to the YAML file. Also takes an optional parameter,
|
83
|
+
# <tt>:hash</tt>, that indicates a specific hash that should be
|
84
|
+
# loaded from the file.
|
85
|
+
def configure_from_yaml(path, opts = {})
|
86
|
+
begin
|
87
|
+
yml = ::Yamler.load(path)
|
88
|
+
yml = yml[opts[:hash]] unless opts[:hash].nil?
|
89
|
+
configure_from_hash(yml)
|
90
|
+
rescue Errno::ENOENT => e
|
91
|
+
puts e.message
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Returns true if there are no configuration parameters
|
96
|
+
def nil?
|
97
|
+
return @_store.empty?
|
98
|
+
end
|
99
|
+
|
100
|
+
# Retrieves a certain parameter and if that parameter
|
101
|
+
# doesn't exist it will return the default_value specified.
|
102
|
+
def retrieve(name, default_value = nil)
|
103
|
+
@_store[name.to_sym] || default_value
|
104
|
+
end
|
105
|
+
|
106
|
+
# Removes a parameter. In the case of a nested parameter
|
107
|
+
# it will remove all below it.
|
108
|
+
def remove(name)
|
109
|
+
@_store.delete(name.to_sym)
|
110
|
+
end
|
111
|
+
|
112
|
+
# Sets a 'default' value. If there is already a value specified
|
113
|
+
# it won't set the value.
|
114
|
+
def set_default(name, default_value)
|
115
|
+
unless @_store[name.to_sym]
|
116
|
+
@_store[name.to_sym] = parse_options(default_value)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def method_missing(sym, *args) # :nodoc:
|
121
|
+
if sym.to_s.match(/(.+)=$/)
|
122
|
+
name = sym.to_s.gsub("=", '').to_sym
|
123
|
+
raise Configatron::ProtectedParameter.new(name) if @_protected.include?(name) || methods_include?(name)
|
124
|
+
raise Configatron::LockedNamespace.new(@_name) if @_locked && !@_store.has_key?(name)
|
125
|
+
@_store[name] = parse_options(*args)
|
126
|
+
elsif @_store.has_key?(sym)
|
127
|
+
return @_store[sym]
|
128
|
+
else
|
129
|
+
store = Configatron::Store.new({}, sym, self)
|
130
|
+
@_store[sym] = store
|
131
|
+
return store
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def ==(other) # :nodoc:
|
136
|
+
self.to_hash == other
|
137
|
+
end
|
138
|
+
|
139
|
+
# Prevents a parameter from being reassigned. If called on a 'namespace' then
|
140
|
+
# all parameters below it will be protected as well.
|
141
|
+
def protect(name)
|
142
|
+
@_protected << name.to_sym
|
143
|
+
end
|
144
|
+
|
145
|
+
# Prevents all parameters from being reassigned.
|
146
|
+
def protect_all!
|
147
|
+
@_protected.clear
|
148
|
+
@_store.keys.each do |k|
|
149
|
+
val = self.send(k)
|
150
|
+
val.protect_all! if val.class == Configatron::Store
|
151
|
+
@_protected << k
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# Removes the protection of a parameter.
|
156
|
+
def unprotect(name)
|
157
|
+
@_protected.reject! { |e| e == name.to_sym }
|
158
|
+
end
|
159
|
+
|
160
|
+
def unprotect_all!
|
161
|
+
@_protected.clear
|
162
|
+
@_store.keys.each do |k|
|
163
|
+
val = self.send(k)
|
164
|
+
val.unprotect_all! if val.class == Configatron::Store
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# Prevents a namespace from having new parameters set. The lock is applied
|
169
|
+
# recursively to any namespaces below it.
|
170
|
+
def lock(name)
|
171
|
+
namespace = @_store[name.to_sym]
|
172
|
+
raise ArgumentError, "Namespace #{name.inspect} does not exist" if namespace.nil?
|
173
|
+
namespace.lock!
|
174
|
+
end
|
175
|
+
|
176
|
+
def unlock(name)
|
177
|
+
namespace = @_store[name.to_sym]
|
178
|
+
raise ArgumentError, "Namespace #{name.inspect} does not exist" if namespace.nil?
|
179
|
+
namespace.unlock!
|
180
|
+
end
|
181
|
+
|
182
|
+
# = DeepClone
|
183
|
+
#
|
184
|
+
# == Version
|
185
|
+
# 1.2006.05.23 (change of the first number means Big Change)
|
186
|
+
#
|
187
|
+
# == Description
|
188
|
+
# Adds deep_clone method to an object which produces deep copy of it. It means
|
189
|
+
# if you clone a Hash, every nested items and their nested items will be cloned.
|
190
|
+
# Moreover deep_clone checks if the object is already cloned to prevent endless recursion.
|
191
|
+
#
|
192
|
+
# == Usage
|
193
|
+
#
|
194
|
+
# (see examples directory under the ruby gems root directory)
|
195
|
+
#
|
196
|
+
# require 'rubygems'
|
197
|
+
# require 'deep_clone'
|
198
|
+
#
|
199
|
+
# include DeepClone
|
200
|
+
#
|
201
|
+
# obj = []
|
202
|
+
# a = [ true, false, obj ]
|
203
|
+
# b = a.deep_clone
|
204
|
+
# obj.push( 'foo' )
|
205
|
+
# p obj # >> [ 'foo' ]
|
206
|
+
# p b[2] # >> []
|
207
|
+
#
|
208
|
+
# == Source
|
209
|
+
# http://simplypowerful.1984.cz/goodlibs/1.2006.05.23
|
210
|
+
#
|
211
|
+
# == Author
|
212
|
+
# jan molic (/mig/at_sign/1984/dot/cz/)
|
213
|
+
#
|
214
|
+
# == Licence
|
215
|
+
# You can redistribute it and/or modify it under the same terms of Ruby's license;
|
216
|
+
# either the dual license version in 2003, or any later version.
|
217
|
+
#
|
218
|
+
def deep_clone( obj=self, cloned={} )
|
219
|
+
if cloned.has_key?( obj.object_id )
|
220
|
+
return cloned[obj.object_id]
|
221
|
+
else
|
222
|
+
begin
|
223
|
+
cl = obj.clone
|
224
|
+
rescue Exception
|
225
|
+
# unclonnable (TrueClass, Fixnum, ...)
|
226
|
+
cloned[obj.object_id] = obj
|
227
|
+
return obj
|
228
|
+
else
|
229
|
+
cloned[obj.object_id] = cl
|
230
|
+
cloned[cl.object_id] = cl
|
231
|
+
if cl.is_a?( Hash )
|
232
|
+
cl.clone.each { |k,v|
|
233
|
+
cl[k] = deep_clone( v, cloned )
|
234
|
+
}
|
235
|
+
elsif cl.is_a?( Array )
|
236
|
+
cl.collect! { |v|
|
237
|
+
deep_clone( v, cloned )
|
238
|
+
}
|
239
|
+
end
|
240
|
+
cl.instance_variables.each do |var|
|
241
|
+
v = cl.instance_eval( var.to_s )
|
242
|
+
v_cl = deep_clone( v, cloned )
|
243
|
+
cl.instance_eval( "#{var} = v_cl" )
|
244
|
+
end
|
245
|
+
return cl
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
protected
|
251
|
+
def lock!
|
252
|
+
@_locked = true
|
253
|
+
@_store.values.each { |store| store.lock! if store.is_a?(Configatron::Store) }
|
254
|
+
end
|
255
|
+
|
256
|
+
def unlock!
|
257
|
+
@_locked = false
|
258
|
+
@_store.values.each { |store| store.unlock! if store.is_a?(Configatron::Store) }
|
259
|
+
end
|
260
|
+
|
261
|
+
private
|
262
|
+
def methods_include?(name)
|
263
|
+
self.methods.include?(RUBY_VERSION > '1.9.0' ? name.to_sym : name.to_s)
|
264
|
+
end
|
265
|
+
|
266
|
+
def parse_options(options)
|
267
|
+
if options.is_a?(Hash)
|
268
|
+
options.each do |k,v|
|
269
|
+
if v.is_a?(Hash)
|
270
|
+
self.method_missing(k.to_sym).configure_from_hash(v)
|
271
|
+
else
|
272
|
+
self.method_missing("#{k.to_sym}=", v)
|
273
|
+
end
|
274
|
+
end
|
275
|
+
else
|
276
|
+
return options
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
undef :test # :nodoc:
|
281
|
+
|
282
|
+
end # Store
|
283
|
+
end # Configatron
|
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: markbates-configatron
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 2.3.2.20090731133933
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- markbates
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-07-31 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: yamler
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.1.0
|
24
|
+
version:
|
25
|
+
description: "configatron was developed by: markbates"
|
26
|
+
email: mark@markbates.com
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files:
|
32
|
+
- README
|
33
|
+
- LICENSE
|
34
|
+
files:
|
35
|
+
- lib/configatron/configatron.rb
|
36
|
+
- lib/configatron/core_ext/class.rb
|
37
|
+
- lib/configatron/core_ext/kernel.rb
|
38
|
+
- lib/configatron/core_ext/object.rb
|
39
|
+
- lib/configatron/core_ext/string.rb
|
40
|
+
- lib/configatron/errors.rb
|
41
|
+
- lib/configatron/store.rb
|
42
|
+
- lib/configatron.rb
|
43
|
+
- README
|
44
|
+
- LICENSE
|
45
|
+
has_rdoc: false
|
46
|
+
homepage: http://www.metabates.com
|
47
|
+
licenses:
|
48
|
+
post_install_message:
|
49
|
+
rdoc_options: []
|
50
|
+
|
51
|
+
require_paths:
|
52
|
+
- lib
|
53
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: "0"
|
58
|
+
version:
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: "0"
|
64
|
+
version:
|
65
|
+
requirements: []
|
66
|
+
|
67
|
+
rubyforge_project: magrathea
|
68
|
+
rubygems_version: 1.3.5
|
69
|
+
signing_key:
|
70
|
+
specification_version: 3
|
71
|
+
summary: A powerful Ruby configuration system.
|
72
|
+
test_files: []
|
73
|
+
|