settingslogic-master/0000755000175000017500000000000012070150257015406 5ustar avtobiffavtobiffsettingslogic-master/Gemfile.lock0000644000175000017500000000064612070150257017636 0ustar avtobiffavtobiffPATH 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.gemspec0000644000175000017500000000164312070150257021635 0ustar avtobiffavtobiff# -*- 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"] endsettingslogic-master/LICENSE0000644000175000017500000000210012070150257016404 0ustar avtobiffavtobiffCopyright (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/0000755000175000017500000000000012070150257016154 5ustar avtobiffavtobiffsettingslogic-master/lib/settingslogic.rb0000644000175000017500000001362612070150257021367 0ustar avtobiffavtobiffrequire "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.rdoc0000644000175000017500000001163712070150257017224 0ustar avtobiffavtobiff= 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/0000755000175000017500000000000012070150257016340 5ustar avtobiffavtobiffsettingslogic-master/spec/spec_helper.rb0000644000175000017500000000056712070150257021166 0ustar avtobiffavtobiff$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.yml0000644000175000017500000000052612070150257020726 0ustar avtobiffavtobiffsetting1: 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.rb0000644000175000017500000000015412070150257020607 0ustar avtobiffavtobiffclass Settings2 < Settingslogic source "#{File.dirname(__FILE__)}/settings.yml" namespace "setting1" endsettingslogic-master/spec/settings4.rb0000644000175000017500000000015412070150257020611 0ustar avtobiffavtobiffclass Settings4 < Settingslogic source "#{File.dirname(__FILE__)}/settings.yml" suppress_errors true endsettingslogic-master/spec/settingslogic_spec.rb0000644000175000017500000001362412070150257022563 0ustar avtobiffavtobiffrequire 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.rb0000644000175000017500000000014012070150257021736 0ustar avtobiffavtobiffclass SettingsEmpty < Settingslogic source "#{File.dirname(__FILE__)}/settings_empty.yml" end settingslogic-master/spec/settings_empty.yml0000644000175000017500000000000012070150257022127 0ustar avtobiffavtobiffsettingslogic-master/spec/settings.rb0000644000175000017500000000017412070150257020527 0ustar avtobiffavtobiffclass Settings < Settingslogic source "#{File.dirname(__FILE__)}/settings.yml" end class SettingsInst < Settingslogic endsettingslogic-master/spec/settings3.rb0000644000175000017500000000015512070150257020611 0ustar avtobiffavtobiffclass Settings3 < Settingslogic source "#{File.dirname(__FILE__)}/settings.yml" load! # test of load endsettingslogic-master/Gemfile0000644000175000017500000000003012070150257016672 0ustar avtobiffavtobiffsource :rubygems gemspecsettingslogic-master/Rakefile0000644000175000017500000000020412070150257017047 0ustar avtobiffavtobiffrequire 'bundler' Bundler::GemHelper.install_tasks require 'rspec/core/rake_task' RSpec::Core::RakeTask.new task :default => :spec