settingslogic-master/ 0000755 0001750 0001750 00000000000 12070150257 015406 5 ustar avtobiff avtobiff settingslogic-master/Gemfile.lock 0000644 0001750 0001750 00000000646 12070150257 017636 0 ustar avtobiff avtobiff PATH
remote: .
specs:
settingslogic (2.0.9)
GEM
remote: http://rubygems.org/
specs:
diff-lcs (1.1.3)
rake (10.0.3)
rspec (2.12.0)
rspec-core (~> 2.12.0)
rspec-expectations (~> 2.12.0)
rspec-mocks (~> 2.12.0)
rspec-core (2.12.2)
rspec-expectations (2.12.1)
diff-lcs (~> 1.1.3)
rspec-mocks (2.12.1)
PLATFORMS
ruby
DEPENDENCIES
rake
rspec
settingslogic!
settingslogic-master/settingslogic.gemspec 0000644 0001750 0001750 00000001643 12070150257 021635 0 ustar avtobiff avtobiff # -*- encoding: utf-8 -*-
$:.push File.expand_path("../lib", __FILE__)
Gem::Specification.new do |s|
s.name = "settingslogic"
s.version = "2.0.9"
s.platform = Gem::Platform::RUBY
s.authors = ["Ben Johnson"]
s.email = ["bjohnson@binarylogic.com"]
s.homepage = "http://github.com/binarylogic/settingslogic"
s.summary = %q{A simple and straightforward settings solution that uses an ERB enabled YAML file and a singleton design pattern.}
s.description = %q{A simple and straightforward settings solution that uses an ERB enabled YAML file and a singleton design pattern.}
s.add_development_dependency 'rake'
s.add_development_dependency 'rspec'
s.files = `git ls-files`.split("\n")
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
s.require_paths = ["lib"]
end settingslogic-master/LICENSE 0000644 0001750 0001750 00000002100 12070150257 016404 0 ustar avtobiff avtobiff Copyright (c) 2008 Ben Johnson of Binary Logic (binarylogic.com)
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. settingslogic-master/lib/ 0000755 0001750 0001750 00000000000 12070150257 016154 5 ustar avtobiff avtobiff settingslogic-master/lib/settingslogic.rb 0000644 0001750 0001750 00000013626 12070150257 021367 0 ustar avtobiff avtobiff 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
curs = curs.send(p)
end
curs
end
def source(value = nil)
@source ||= value
end
def namespace(value = nil)
@namespace ||= value
end
def suppress_errors(value = nil)
@suppress_errors ||= value
end
def [](key)
instance.fetch(key.to_s, nil)
end
def []=(key, val)
# Setting[:key][:key2] = 'value' for dynamic settings
val = new(val, source) if val.is_a? Hash
instance.store(key.to_s, val)
instance.create_accessor_for(key, val)
end
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
# of the singleton pattern. Basically this proxies Setting.foo to Setting.instance.foo
def create_accessors!
instance.each do |key,val|
create_accessor_for(key)
end
end
def create_accessor_for(key)
return unless key.to_s =~ /^\w+$/ # could have "some-setting:" which blows up eval
instance_eval "def #{key}; instance.send(:#{key}); end"
end
end
# Initializes a new settings object. You can initialize an object in any of the following ways:
#
# Settings.new(:application) # will look for config/application.yml
# Settings.new("application.yaml") # will look for application.yaml
# Settings.new("/var/configs/application.yml") # will look for /var/configs/application.yml
# Settings.new(:config1 => 1, :config2 => 2)
#
# Basically if you pass a symbol it will look for that file in the configs directory of your rails app,
# if you are using this in rails. If you pass a string it should be an absolute path to your settings file.
# Then you can pass a hash, and it just allows you to access the hash via methods.
def initialize(hash_or_file = self.class.source, section = nil)
#puts "new! #{hash_or_file}"
case hash_or_file
when nil
raise Errno::ENOENT, "No file specified as Settingslogic source"
when Hash
self.replace hash_or_file
else
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
@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(name, *args, &block)
key = name.to_s
return missing_key("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)
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, 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!
self.each do |key,val|
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 eval
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} = 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
end
settingslogic-master/README.rdoc 0000644 0001750 0001750 00000011637 12070150257 017224 0 ustar avtobiff avtobiff = Settingslogic
Settingslogic is a simple configuration / settings solution that uses an ERB enabled YAML file. It has been great for
our apps, maybe you will enjoy it too. Settingslogic works with Rails, Sinatra, or any Ruby project.
== Helpful links
* Documentation: http://rdoc.info/projects/binarylogic/settingslogic
* Repository: http://github.com/binarylogic/settingslogic/tree/master
* Issues: http://github.com/binarylogic/settingslogic/issues
== Installation
gem install settingslogic
== Usage
=== 1. Define your class
Instead of defining a Settings constant for you, that task is left to you. Simply create a class in your application
that looks like:
class Settings < Settingslogic
source "#{Rails.root}/config/application.yml"
namespace Rails.env
end
Name it Settings, name it Config, name it whatever you want. Add as many or as few as you like. A good place to put
this file in a rails app is app/models/settings.rb
I felt adding a settings file in your app was more straightforward, less tricky, and more flexible.
=== 2. Create your settings
Notice above we specified an absolute path to our settings file called "application.yml". This is just a typical YAML file.
Also notice above that we specified a namespace for our environment. A namespace is just an optional string that corresponds
to a key in the YAML file.
Using a namespace allows us to change our configuration depending on our environment:
# config/application.yml
defaults: &defaults
cool:
saweet: nested settings
neat_setting: 24
awesome_setting: <%= "Did you know 5 + 5 = #{5 + 5}?" %>
development:
<<: *defaults
neat_setting: 800
test:
<<: *defaults
production:
<<: *defaults
_Note_: Certain Ruby/Bundler versions include a version of the Psych YAML parser which incorrectly handles merges (the `<<` in the example above.)
If your default settings seem to be overwriting your environment-specific settings, including the following lines in your config/boot.rb file may solve the problem:
require 'yaml'
YAML::ENGINE.yamler= 'syck'
=== 3. Access your settings
>> Rails.env
=> "development"
>> Settings.cool
=> "#"
>> Settings.cool.saweet
=> "nested settings"
>> Settings.neat_setting
=> 800
>> Settings.awesome_setting
=> "Did you know 5 + 5 = 10?"
You can use these settings anywhere, for example in a model:
class Post < ActiveRecord::Base
self.per_page = Settings.pagination.posts_per_page
end
=== 4. Optional / dynamic settings
Often, you will want to handle defaults in your application logic itself, to reduce the number of settings
you need to put in your YAML file. You can access an optional setting by using Hash notation:
>> Settings.messaging.queue_name
=> Exception: Missing setting 'queue_name' in 'message' section in 'application.yml'
>> Settings.messaging['queue_name']
=> nil
>> Settings.messaging['queue_name'] ||= 'user_mail'
=> "user_mail"
>> Settings.messaging.queue_name
=> "user_mail"
Modifying our model example:
class Post < ActiveRecord::Base
self.per_page = Settings.posts['per_page'] || Settings.pagination.per_page
end
This would allow you to specify a custom value for per_page just for posts, or
to fall back to your default value if not specified.
=== 5. Suppressing Exceptions Conditionally
Raising exceptions for missing settings helps highlight configuration problems. However, in a
Rails app it may make sense to suppress this in production and return nil for missing settings.
While it's useful to stop and highlight an error in development or test environments, this is
often not the right answer for production.
class Settings < Settingslogic
source "#{Rails.root}/config/application.yml"
namespace Rails.env
suppress_errors Rails.env.production?
end
>> Settings.non_existent_key
=> nil
== Note on Sinatra / Capistrano / Vlad
Each of these frameworks uses a +set+ convention for settings, which actually defines methods
in the global Object namespace:
set :application, "myapp" # does "def application" globally
This can cause collisions with Settingslogic, since those methods are global. Luckily, the
solution is to just add a call to load! in your class:
class Settings < Settingslogic
source "#{Rails.root}/config/application.yml"
namespace Rails.env
load!
end
It's probably always safest to add load! to your class, since this guarantees settings will be
loaded at that time, rather than lazily later via method_missing.
Finally, you can reload all your settings later as well:
Settings.reload!
This is useful if you want to support changing your settings YAML without restarting your app.
== Author
Copyright (c) 2008-2010 {Ben Johnson}[http://github.com/binarylogic] of {Binary Logic}[http://www.binarylogic.com],
released under the MIT license. Support for optional settings and reloading by {Nate Wiger}[http://nate.wiger.org].
settingslogic-master/spec/ 0000755 0001750 0001750 00000000000 12070150257 016340 5 ustar avtobiff avtobiff settingslogic-master/spec/spec_helper.rb 0000644 0001750 0001750 00000000567 12070150257 021166 0 ustar avtobiff avtobiff $LOAD_PATH.unshift(File.dirname(__FILE__))
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
require 'rspec'
require 'settingslogic'
require 'settings'
require 'settings2'
require 'settings3'
require 'settings4'
require 'settings_empty'
# Needed to test Settings3
Object.send :define_method, 'collides' do
'collision'
end
RSpec.configure do |config|
end
settingslogic-master/spec/settings.yml 0000644 0001750 0001750 00000000526 12070150257 020726 0 ustar avtobiff avtobiff setting1:
setting1_child: saweet
deep:
another: my value
child:
value: 2
setting2: 5
setting3: <%= 5 * 5 %>
name: test
language:
haskell:
paradigm: functional
smalltalk:
paradigm: object oriented
collides:
does: not
nested:
collides:
does: not either
array:
-
name: first
-
name: second
settingslogic-master/spec/settings2.rb 0000644 0001750 0001750 00000000154 12070150257 020607 0 ustar avtobiff avtobiff class Settings2 < Settingslogic
source "#{File.dirname(__FILE__)}/settings.yml"
namespace "setting1"
end settingslogic-master/spec/settings4.rb 0000644 0001750 0001750 00000000154 12070150257 020611 0 ustar avtobiff avtobiff class Settings4 < Settingslogic
source "#{File.dirname(__FILE__)}/settings.yml"
suppress_errors true
end settingslogic-master/spec/settingslogic_spec.rb 0000644 0001750 0001750 00000013624 12070150257 022563 0 ustar avtobiff avtobiff require File.expand_path(File.dirname(__FILE__) + "/spec_helper")
describe "Settingslogic" do
it "should access settings" do
Settings.setting2.should == 5
end
it "should access nested settings" do
Settings.setting1.setting1_child.should == "saweet"
end
it "should access settings in nested arrays" do
Settings.array.first.name.should == "first"
end
it "should access deep nested settings" do
Settings.setting1.deep.another.should == "my value"
end
it "should access extra deep nested settings" do
Settings.setting1.deep.child.value.should == 2
end
it "should enable erb" do
Settings.setting3.should == 25
end
it "should namespace settings" do
Settings2.setting1_child.should == "saweet"
Settings2.deep.another.should == "my value"
end
it "should return the namespace" do
Settings.namespace.should be_nil
Settings2.namespace.should == 'setting1'
end
it "should distinguish nested keys" do
Settings.language.haskell.paradigm.should == 'functional'
Settings.language.smalltalk.paradigm.should == 'object oriented'
end
it "should not collide with global methods" do
Settings3.nested.collides.does.should == 'not either'
Settings3[:nested] = 'fooey'
Settings3[:nested].should == 'fooey'
Settings3.nested.should == 'fooey'
Settings3.collides.does.should == 'not'
end
it "should raise a helpful error message" do
e = nil
begin
Settings.missing
rescue => e
e.should be_kind_of Settingslogic::MissingSetting
end
e.should_not be_nil
e.message.should =~ /Missing setting 'missing' in/
e = nil
begin
Settings.language.missing
rescue => e
e.should be_kind_of Settingslogic::MissingSetting
end
e.should_not be_nil
e.message.should =~ /Missing setting 'missing' in 'language' section/
end
it "should handle optional / dynamic settings" do
e = nil
begin
Settings.language.erlang
rescue => e
e.should be_kind_of Settingslogic::MissingSetting
end
e.should_not be_nil
e.message.should =~ /Missing setting 'erlang' in 'language' section/
Settings.language['erlang'].should be_nil
Settings.language['erlang'] = 5
Settings.language['erlang'].should == 5
Settings.language['erlang'] = {'paradigm' => 'functional'}
Settings.language.erlang.paradigm.should == 'functional'
Settings.respond_to?('erlang').should be_false
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'
Settings[:toplevel] = '42'
Settings.toplevel.should == '42'
end
it "should raise an error on a nil source argument" do
class NoSource < Settingslogic; end
e = nil
begin
NoSource.foo.bar
rescue => e
e.should be_kind_of Errno::ENOENT
end
e.should_not be_nil
end
it "should allow suppressing errors" do
Settings4.non_existent_key.should be_nil
end
# This one edge case currently does not pass, because it requires very
# esoteric code in order to make it pass. It was judged not worth fixing,
# as it introduces significant complexity for minor gain.
# it "should handle reloading top-level settings"
# Settings[:inspect] = 'yeah baby'
# Settings.inspect.should == 'yeah baby'
# Settings.reload!
# Settings.inspect.should == 'Settings'
# end
it "should handle oddly-named settings" do
Settings.language['some-dash-setting#'] = 'dashtastic'
Settings.language['some-dash-setting#'].should == 'dashtastic'
end
it "should handle settings with nil value" do
Settings["flag"] = true
Settings["flag"] = nil
Settings.flag.should == nil
end
it "should handle settings with false value" do
Settings["flag"] = true
Settings["flag"] = false
Settings.flag.should == false
end
it "should support instance usage as well" do
settings = SettingsInst.new(Settings.source)
settings.setting1.setting1_child.should == "saweet"
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
# If .name is not a property, delegate to superclass
it "should respond with Module.name" do
Settings2.name.should == "Settings2"
end
# If .name is called on Settingslogic itself, handle appropriately
# by delegating to Hash
it "should have the parent class always respond with Module.name" do
Settingslogic.name.should == 'Settingslogic'
end
# If .name is a property, respond with that instead of delegating to superclass
it "should allow a name setting to be overriden" do
Settings.name.should == 'test'
end
it "should allow symbolize_keys" do
Settings.reload!
result = Settings.language.haskell.symbolize_keys
result.class.should == Hash
result.should == {:paradigm => "functional"}
end
it "should allow symbolize_keys on nested hashes" do
Settings.reload!
result = Settings.language.symbolize_keys
result.class.should == Hash
result.should == {
:haskell => {:paradigm => "functional"},
:smalltalk => {:paradigm => "object oriented"}
}
end
it "should handle empty file" do
SettingsEmpty.keys.should eql([])
end
# Put this test last or else call to .instance will load @instance,
# masking bugs.
it "should be a hash" do
Settings.send(:instance).should be_is_a(Hash)
end
describe "#to_hash" do
it "should return a new instance of a Hash object" do
Settings.to_hash.should be_kind_of(Hash)
Settings.to_hash.class.name.should == "Hash"
Settings.to_hash.object_id.should_not == Settings.object_id
end
end
end
settingslogic-master/spec/settings_empty.rb 0000644 0001750 0001750 00000000140 12070150257 021736 0 ustar avtobiff avtobiff class SettingsEmpty < Settingslogic
source "#{File.dirname(__FILE__)}/settings_empty.yml"
end
settingslogic-master/spec/settings_empty.yml 0000644 0001750 0001750 00000000000 12070150257 022127 0 ustar avtobiff avtobiff settingslogic-master/spec/settings.rb 0000644 0001750 0001750 00000000174 12070150257 020527 0 ustar avtobiff avtobiff class Settings < Settingslogic
source "#{File.dirname(__FILE__)}/settings.yml"
end
class SettingsInst < Settingslogic
end settingslogic-master/spec/settings3.rb 0000644 0001750 0001750 00000000155 12070150257 020611 0 ustar avtobiff avtobiff class Settings3 < Settingslogic
source "#{File.dirname(__FILE__)}/settings.yml"
load! # test of load
end settingslogic-master/Gemfile 0000644 0001750 0001750 00000000030 12070150257 016672 0 ustar avtobiff avtobiff source :rubygems
gemspec settingslogic-master/Rakefile 0000644 0001750 0001750 00000000204 12070150257 017047 0 ustar avtobiff avtobiff require 'bundler'
Bundler::GemHelper.install_tasks
require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new
task :default => :spec