Skip to content
Browse files

real-world bugfixes from Vlad and sub-key errors

  • Loading branch information...
1 parent e6fec8e commit 04fddcaf5d0e889bd53e67ef3c767b6295e99296 Nate Wiger committed with
Showing with 73 additions and 25 deletions.
  1. +52 −22 lib/settingslogic.rb
  2. +20 −2 spec/settingslogic_spec.rb
  3. +1 −1 spec/spec_helper.rb
View
74 lib/settingslogic.rb
@@ -10,6 +10,16 @@ def name # :nodoc:
instance.key?("name") ? instance.name : super
end
+ # Enables Settings.get('nested.key.name') for dynamic access
+ def get(key)
+ parts = key.split('.')
+ curs = self
+ while p = parts.shift
+ curs = curs.send(p)
+ end
+ curs
+ end
+
def source(value = nil)
if value.nil?
@source
@@ -27,13 +37,15 @@ def namespace(value = nil)
end
def [](key)
- # Setting.key.value or Setting[:key][:value] or Setting['key']['value']
- fetch(key.to_s,nil)
+ # Setting[:key][:key2] or Setting['key']['key2']
+ instance.fetch(key.to_s, nil)
end
- def []=(key,val)
- # Setting[:key] = 'value' for dynamic settings
- store(key.to_s,val)
+ def []=(key, val)
+ # Setting[:key][:key2] = 'value' for dynamic settings
+ val = self.class.new(val, source) if val.is_a? Hash
+ instance.store(key.to_s, val)
+ instance.create_accessor_for(key.to_s, val)
end
def load!
@@ -75,21 +87,32 @@ def initialize(hash_or_file = self.class.source, section = nil)
hash = hash[self.class.namespace] if self.class.namespace
self.replace hash
end
- @section = section || hash_or_file # so end of error says "in application.yml"
+ @section = section || self.class.source # so end of error says "in application.yml"
create_accessors!
end
# Called for dynamically-defined keys, and also the first key deferenced at the top-level, if load! is not used.
# Otherwise, create_accessors! (called by new) will have created actual methods for each key.
- def method_missing(key, *args, &block)
- begin
- value = fetch(key.to_s)
- rescue IndexError
- raise MissingSetting, "Missing setting '#{key}' in #{@section}"
- end
+ def method_missing(name, *args, &block)
+ key = name.to_s
+ raise MissingSetting, "Missing setting '#{key}' in #{@section}" unless has_key? key
+ value = fetch(key)
+ create_accessor_for(key)
value.is_a?(Hash) ? self.class.new(value, "'#{key}' section in #{@section}") : value
end
+ def [](key)
+ # Setting[:key][:key2] or Setting['key']['key2']
+ fetch(key.to_s, nil)
+ end
+
+ def []=(key,val)
+ # Setting[:key][:key2] = 'value' for dynamic settings
+ val = self.class.new(val, @section) if val.is_a? Hash
+ store(key.to_s, val)
+ create_accessor_for(key.to_s, val)
+ end
+
private
# This handles naming collisions with Sinatra/Vlad/Capistrano. Since these use a set()
# helper that defines methods in Object, ANY method_missing ANYWHERE picks up the Vlad/Sinatra
@@ -97,17 +120,24 @@ def method_missing(key, *args, &block)
# rather than the app_yml['deploy_to'] hash. Jeezus.
def create_accessors!
self.each do |key,val|
- # Use instance_eval/class_eval because they're actually more efficient than define_method{}
- # http://stackoverflow.com/questions/185947/ruby-definemethod-vs-def
- # http://bmorearty.wordpress.com/2009/01/09/fun-with-rubys-instance_eval-and-class_eval/
- self.class.class_eval <<-EndEval
- def #{key}
- return @#{key} if @#{key} # cache (performance)
- value = fetch('#{key}')
- @#{key} = value.is_a?(Hash) ? self.class.new(value, "'#{key}' section in #{@section}") : value
- end
- EndEval
+ create_accessor_for(key)
end
end
+ # Use instance_eval/class_eval because they're actually more efficient than define_method{}
+ # http://stackoverflow.com/questions/185947/ruby-definemethod-vs-def
+ # http://bmorearty.wordpress.com/2009/01/09/fun-with-rubys-instance_eval-and-class_eval/
+ def create_accessor_for(key, val=nil)
+ return unless key.to_s =~ /^\w+$/ # could have "some-setting:" which blows up class_eval
+ instance_variable_set("@#{key}", val)
+ self.class.class_eval <<-EndEval
+ def #{key}
+ return @#{key} if @#{key}
+ raise MissingSetting, "Missing setting '#{key}' in #{@section}" unless has_key? '#{key}'
+ value = fetch('#{key}')
+ @#{key} = value.is_a?(Hash) ? self.class.new(value, "'#{key}' section in #{@section}") : value
+ end
+ EndEval
+ end
+
end
View
22 spec/settingslogic_spec.rb
@@ -49,7 +49,7 @@
end
e.should_not be_nil
e.message.should =~ /Missing setting 'missing' in/
-
+
e = nil
begin
Settings.language.missing
@@ -71,7 +71,7 @@
e.message.should =~ /Missing setting 'erlang' in 'language' section/
Settings.language['erlang'].should be_nil
- Settings.language['erlang'] ||= 5
+ Settings.language['erlang'] = 5
Settings.language['erlang'].should == 5
Settings.language['erlang'] = {'paradigm' => 'functional'}
@@ -79,6 +79,24 @@
Settings.reload!
Settings.language['erlang'].should be_nil
+
+ Settings.language[:erlang] ||= 5
+ Settings.language[:erlang].should == 5
+
+ Settings.language[:erlang] = {}
+ Settings.language[:erlang][:paradigm] = 'functional'
+ Settings.language.erlang.paradigm.should == 'functional'
+ end
+
+ it "should handle badly-named settings" do
+ Settings.language['some-dash-setting#'] = 'dashtastic'
+ Settings.language['some-dash-setting#'].should == 'dashtastic'
+ end
+
+ it "should be able to get() a key with dot.notation" do
+ Settings.get('setting1.setting1_child').should == "saweet"
+ Settings.get('setting1.deep.another').should == "my value"
+ Settings.get('setting1.deep.child.value').should == 2
end
# Put this test last or else call to .instance will load @instance,
View
2 spec/spec_helper.rb
@@ -11,7 +11,7 @@
# Needed to test Settings3
def collides
- 'collision'
+ @collides = 'collision'
end
Spec::Runner.configure do |config|

0 comments on commit 04fddca

Please sign in to comment.
Something went wrong with that request. Please try again.