lib/settingslogic.rb in settingslogic-2.0.8 vs lib/settingslogic.rb in settingslogic-2.0.9
- old
+ new
@@ -1,17 +1,18 @@
require "yaml"
require "erb"
+require 'open-uri'
# A simple settings solution using a YAML file. See README for more information.
class Settingslogic < Hash
class MissingSetting < StandardError; end
class << self
def name # :nodoc:
self.superclass != Hash && 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
@@ -19,31 +20,19 @@
end
curs
end
def source(value = nil)
- if value.nil?
- @source
- else
- @source = value
- end
+ @source ||= value
end
def namespace(value = nil)
- if value.nil?
- @namespace
- else
- @namespace = value
- end
+ @namespace ||= value
end
def suppress_errors(value = nil)
- if value.nil?
- @suppress_errors
- else
- @suppress_errors = value
- end
+ @suppress_errors ||= value
end
def [](key)
instance.fetch(key.to_s, nil)
end
@@ -57,24 +46,24 @@
def load!
instance
true
end
-
+
def reload!
@instance = nil
load!
end
-
+
private
def instance
return @instance if @instance
@instance = new
create_accessors!
@instance
end
-
+
def method_missing(name, *args, &block)
instance.send(name, *args, &block)
end
# It would be great to DRY this up somehow, someday, but it's difficult because
@@ -108,11 +97,12 @@
when nil
raise Errno::ENOENT, "No file specified as Settingslogic source"
when Hash
self.replace hash_or_file
else
- hash = YAML.load(ERB.new(File.read(hash_or_file)).result).to_hash
+ file_contents = open(hash_or_file).read
+ hash = file_contents.empty? ? {} : YAML.load(ERB.new(file_contents).result).to_hash
if self.class.namespace
hash = hash[self.class.namespace] or return missing_key("Missing setting '#{self.class.namespace}' in #{hash_or_file}")
end
self.replace hash
end
@@ -139,10 +129,15 @@
val = self.class.new(val, @section) if val.is_a? Hash
store(key.to_s, val)
create_accessor_for(key, val)
end
+ # Returns an instance of a Hash object
+ def to_hash
+ Hash[self]
+ end
+
# 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
# settings! So settings.deploy_to title actually calls Object.deploy_to (from set :deploy_to, "host"),
# rather than the app_yml['deploy_to'] hash. Jeezus.
def create_accessors!
@@ -154,20 +149,41 @@
# 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 eval
- instance_variable_set("@#{key}", val) if val
+ instance_variable_set("@#{key}", val)
self.class.class_eval <<-EndEval
def #{key}
return @#{key} if @#{key}
return missing_key("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
+ @#{key} = if value.is_a?(Hash)
+ self.class.new(value, "'#{key}' section in #{@section}")
+ elsif value.is_a?(Array) && value.all?{|v| v.is_a? Hash}
+ value.map{|v| self.class.new(v)}
+ else
+ value
+ end
end
EndEval
end
-
+
+ def symbolize_keys
+
+ inject({}) do |memo, tuple|
+
+ k = (tuple.first.to_sym rescue tuple.first) || tuple.first
+
+ v = k.is_a?(Symbol) ? send(k) : tuple.last # make sure the value is accessed the same way Settings.foo.bar works
+
+ memo[k] = v && v.respond_to?(:symbolize_keys) ? v.symbolize_keys : v #recurse for nested hashes
+
+ memo
+ end
+
+ end
+
def missing_key(msg)
return nil if self.class.suppress_errors
raise MissingSetting, msg
end