settingslogic 2.0.5 → 2.0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION.yml +1 -1
- data/lib/settingslogic.rb +80 -34
- data/settingslogic.gemspec +2 -2
- data/spec/settings.rb +3 -0
- data/spec/settings.yml +4 -1
- data/spec/settingslogic_spec.rb +56 -4
- data/spec/spec_helper.rb +1 -1
- metadata +2 -2
data/VERSION.yml
CHANGED
data/lib/settingslogic.rb
CHANGED
@@ -4,12 +4,22 @@ require "erb"
|
|
4
4
|
# A simple settings solution using a YAML file. See README for more information.
|
5
5
|
class Settingslogic < Hash
|
6
6
|
class MissingSetting < StandardError; end
|
7
|
-
|
7
|
+
|
8
8
|
class << self
|
9
9
|
def name # :nodoc:
|
10
10
|
instance.key?("name") ? instance.name : super
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
|
+
# Enables Settings.get('nested.key.name') for dynamic access
|
14
|
+
def get(key)
|
15
|
+
parts = key.split('.')
|
16
|
+
curs = self
|
17
|
+
while p = parts.shift
|
18
|
+
curs = curs.send(p)
|
19
|
+
end
|
20
|
+
curs
|
21
|
+
end
|
22
|
+
|
13
23
|
def source(value = nil)
|
14
24
|
if value.nil?
|
15
25
|
@source
|
@@ -27,15 +37,16 @@ class Settingslogic < Hash
|
|
27
37
|
end
|
28
38
|
|
29
39
|
def [](key)
|
30
|
-
|
31
|
-
fetch(key.to_s,nil)
|
40
|
+
instance.fetch(key.to_s, nil)
|
32
41
|
end
|
33
42
|
|
34
|
-
def []=(key,val)
|
35
|
-
# Setting[:key] = 'value' for dynamic settings
|
36
|
-
|
43
|
+
def []=(key, val)
|
44
|
+
# Setting[:key][:key2] = 'value' for dynamic settings
|
45
|
+
val = new(val, source) if val.is_a? Hash
|
46
|
+
instance.store(key.to_s, val)
|
47
|
+
instance.create_accessor_for(key, val)
|
37
48
|
end
|
38
|
-
|
49
|
+
|
39
50
|
def load!
|
40
51
|
instance
|
41
52
|
true
|
@@ -48,12 +59,29 @@ class Settingslogic < Hash
|
|
48
59
|
|
49
60
|
private
|
50
61
|
def instance
|
51
|
-
@instance
|
62
|
+
return @instance if @instance
|
63
|
+
@instance = new
|
64
|
+
create_accessors!
|
65
|
+
@instance
|
52
66
|
end
|
53
67
|
|
54
68
|
def method_missing(name, *args, &block)
|
55
69
|
instance.send(name, *args, &block)
|
56
70
|
end
|
71
|
+
|
72
|
+
# It would be great to DRY this up somehow, someday, but it's difficult because
|
73
|
+
# of the singleton pattern. Basically this proxies Setting.foo to Setting.instance.foo
|
74
|
+
def create_accessors!
|
75
|
+
instance.each do |key,val|
|
76
|
+
create_accessor_for(key)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def create_accessor_for(key)
|
81
|
+
return unless key.to_s =~ /^\w+$/ # could have "some-setting:" which blows up eval
|
82
|
+
instance_eval "def #{key}; instance.send(:#{key}); end"
|
83
|
+
end
|
84
|
+
|
57
85
|
end
|
58
86
|
|
59
87
|
# Initializes a new settings object. You can initialize an object in any of the following ways:
|
@@ -67,7 +95,10 @@ class Settingslogic < Hash
|
|
67
95
|
# if you are using this in rails. If you pass a string it should be an absolute path to your settings file.
|
68
96
|
# Then you can pass a hash, and it just allows you to access the hash via methods.
|
69
97
|
def initialize(hash_or_file = self.class.source, section = nil)
|
98
|
+
#puts "new! #{hash_or_file}"
|
70
99
|
case hash_or_file
|
100
|
+
when nil
|
101
|
+
raise Errno::ENOENT, "No file specified as Settingslogic source"
|
71
102
|
when Hash
|
72
103
|
self.replace hash_or_file
|
73
104
|
else
|
@@ -75,39 +106,54 @@ class Settingslogic < Hash
|
|
75
106
|
hash = hash[self.class.namespace] if self.class.namespace
|
76
107
|
self.replace hash
|
77
108
|
end
|
78
|
-
@section = section ||
|
109
|
+
@section = section || self.class.source # so end of error says "in application.yml"
|
79
110
|
create_accessors!
|
80
111
|
end
|
81
112
|
|
82
113
|
# Called for dynamically-defined keys, and also the first key deferenced at the top-level, if load! is not used.
|
83
114
|
# Otherwise, create_accessors! (called by new) will have created actual methods for each key.
|
84
|
-
def method_missing(
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
end
|
115
|
+
def method_missing(name, *args, &block)
|
116
|
+
key = name.to_s
|
117
|
+
raise MissingSetting, "Missing setting '#{key}' in #{@section}" unless has_key? key
|
118
|
+
value = fetch(key)
|
119
|
+
create_accessor_for(key)
|
90
120
|
value.is_a?(Hash) ? self.class.new(value, "'#{key}' section in #{@section}") : value
|
91
121
|
end
|
92
122
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
123
|
+
def [](key)
|
124
|
+
fetch(key.to_s, nil)
|
125
|
+
end
|
126
|
+
|
127
|
+
def []=(key,val)
|
128
|
+
# Setting[:key][:key2] = 'value' for dynamic settings
|
129
|
+
val = self.class.new(val, @section) if val.is_a? Hash
|
130
|
+
store(key.to_s, val)
|
131
|
+
create_accessor_for(key, val)
|
132
|
+
end
|
133
|
+
|
134
|
+
# This handles naming collisions with Sinatra/Vlad/Capistrano. Since these use a set()
|
135
|
+
# helper that defines methods in Object, ANY method_missing ANYWHERE picks up the Vlad/Sinatra
|
136
|
+
# settings! So settings.deploy_to title actually calls Object.deploy_to (from set :deploy_to, "host"),
|
137
|
+
# rather than the app_yml['deploy_to'] hash. Jeezus.
|
138
|
+
def create_accessors!
|
139
|
+
self.each do |key,val|
|
140
|
+
create_accessor_for(key)
|
111
141
|
end
|
142
|
+
end
|
112
143
|
|
144
|
+
# Use instance_eval/class_eval because they're actually more efficient than define_method{}
|
145
|
+
# http://stackoverflow.com/questions/185947/ruby-definemethod-vs-def
|
146
|
+
# http://bmorearty.wordpress.com/2009/01/09/fun-with-rubys-instance_eval-and-class_eval/
|
147
|
+
def create_accessor_for(key, val=nil)
|
148
|
+
return unless key.to_s =~ /^\w+$/ # could have "some-setting:" which blows up eval
|
149
|
+
instance_variable_set("@#{key}", val) if val
|
150
|
+
self.class.class_eval <<-EndEval
|
151
|
+
def #{key}
|
152
|
+
return @#{key} if @#{key}
|
153
|
+
raise MissingSetting, "Missing setting '#{key}' in #{@section}" unless has_key? '#{key}'
|
154
|
+
value = fetch('#{key}')
|
155
|
+
@#{key} = value.is_a?(Hash) ? self.class.new(value, "'#{key}' section in #{@section}") : value
|
156
|
+
end
|
157
|
+
EndEval
|
158
|
+
end
|
113
159
|
end
|
data/settingslogic.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{settingslogic}
|
8
|
-
s.version = "2.0.
|
8
|
+
s.version = "2.0.6"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Ben Johnson of Binary Logic"]
|
12
|
-
s.date = %q{2010-02-
|
12
|
+
s.date = %q{2010-02-12}
|
13
13
|
s.email = %q{bjohnson@binarylogic.com}
|
14
14
|
s.extra_rdoc_files = [
|
15
15
|
"LICENSE",
|
data/spec/settings.rb
CHANGED
data/spec/settings.yml
CHANGED
data/spec/settingslogic_spec.rb
CHANGED
@@ -20,7 +20,7 @@ describe "Settingslogic" do
|
|
20
20
|
it "should enable erb" do
|
21
21
|
Settings.setting3.should == 25
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
it "should namespace settings" do
|
25
25
|
Settings2.setting1_child.should == "saweet"
|
26
26
|
Settings2.deep.another.should == "my value"
|
@@ -37,9 +37,13 @@ describe "Settingslogic" do
|
|
37
37
|
end
|
38
38
|
|
39
39
|
it "should not collide with global methods" do
|
40
|
+
Settings3.nested.collides.does.should == 'not either'
|
41
|
+
Settings3[:nested] = 'fooey'
|
42
|
+
Settings3[:nested].should == 'fooey'
|
43
|
+
Settings3.nested.should == 'fooey'
|
40
44
|
Settings3.collides.does.should == 'not'
|
41
45
|
end
|
42
|
-
|
46
|
+
|
43
47
|
it "should raise a helpful error message" do
|
44
48
|
e = nil
|
45
49
|
begin
|
@@ -49,7 +53,7 @@ describe "Settingslogic" do
|
|
49
53
|
end
|
50
54
|
e.should_not be_nil
|
51
55
|
e.message.should =~ /Missing setting 'missing' in/
|
52
|
-
|
56
|
+
|
53
57
|
e = nil
|
54
58
|
begin
|
55
59
|
Settings.language.missing
|
@@ -71,14 +75,62 @@ describe "Settingslogic" do
|
|
71
75
|
e.message.should =~ /Missing setting 'erlang' in 'language' section/
|
72
76
|
|
73
77
|
Settings.language['erlang'].should be_nil
|
74
|
-
Settings.language['erlang']
|
78
|
+
Settings.language['erlang'] = 5
|
75
79
|
Settings.language['erlang'].should == 5
|
76
80
|
|
77
81
|
Settings.language['erlang'] = {'paradigm' => 'functional'}
|
78
82
|
Settings.language.erlang.paradigm.should == 'functional'
|
83
|
+
Settings.respond_to?('erlang').should be_false
|
79
84
|
|
80
85
|
Settings.reload!
|
81
86
|
Settings.language['erlang'].should be_nil
|
87
|
+
|
88
|
+
Settings.language[:erlang] ||= 5
|
89
|
+
Settings.language[:erlang].should == 5
|
90
|
+
|
91
|
+
Settings.language[:erlang] = {}
|
92
|
+
Settings.language[:erlang][:paradigm] = 'functional'
|
93
|
+
Settings.language.erlang.paradigm.should == 'functional'
|
94
|
+
|
95
|
+
Settings[:toplevel] = '42'
|
96
|
+
Settings.toplevel.should == '42'
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should raise an error on a nil source argument" do
|
100
|
+
class NoSource < Settingslogic; end
|
101
|
+
e = nil
|
102
|
+
begin
|
103
|
+
NoSource.foo.bar
|
104
|
+
rescue => e
|
105
|
+
e.should be_kind_of Errno::ENOENT
|
106
|
+
end
|
107
|
+
e.should_not be_nil
|
108
|
+
end
|
109
|
+
|
110
|
+
# This one edge case currently does not pass, because it requires very
|
111
|
+
# esoteric code in order to make it pass. It was judged not worth fixing,
|
112
|
+
# as it introduces significant complexity for minor gain.
|
113
|
+
# it "should handle reloading top-level settings"
|
114
|
+
# Settings[:inspect] = 'yeah baby'
|
115
|
+
# Settings.inspect.should == 'yeah baby'
|
116
|
+
# Settings.reload!
|
117
|
+
# Settings.inspect.should == 'Settings'
|
118
|
+
# end
|
119
|
+
|
120
|
+
it "should handle oddly-named settings" do
|
121
|
+
Settings.language['some-dash-setting#'] = 'dashtastic'
|
122
|
+
Settings.language['some-dash-setting#'].should == 'dashtastic'
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should support instance usage as well" do
|
126
|
+
settings = SettingsInst.new(Settings.source)
|
127
|
+
settings.setting1.setting1_child.should == "saweet"
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should be able to get() a key with dot.notation" do
|
131
|
+
Settings.get('setting1.setting1_child').should == "saweet"
|
132
|
+
Settings.get('setting1.deep.another').should == "my value"
|
133
|
+
Settings.get('setting1.deep.child.value').should == 2
|
82
134
|
end
|
83
135
|
|
84
136
|
# Put this test last or else call to .instance will load @instance,
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: settingslogic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Johnson of Binary Logic
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-02-
|
12
|
+
date: 2010-02-12 00:00:00 -05:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|