├── spec ├── settings_empty.yml ├── settings_empty.rb ├── settings2.rb ├── settings3.rb ├── settings4.rb ├── settings.rb ├── spec_helper.rb ├── settings.yml └── settingslogic_spec.rb ├── Gemfile ├── .travis.yml ├── .gitignore ├── Rakefile ├── Gemfile.lock ├── settingslogic.gemspec ├── LICENSE ├── README.rdoc └── lib └── settingslogic.rb /spec/settings_empty.yml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gemspec 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 1.8.7 4 | - 1.9.3 5 | - 2.0.0 6 | - 2.1.2 7 | -------------------------------------------------------------------------------- /spec/settings_empty.rb: -------------------------------------------------------------------------------- 1 | class SettingsEmpty < Settingslogic 2 | source "#{File.dirname(__FILE__)}/settings_empty.yml" 3 | end 4 | -------------------------------------------------------------------------------- /spec/settings2.rb: -------------------------------------------------------------------------------- 1 | class Settings2 < Settingslogic 2 | source "#{File.dirname(__FILE__)}/settings.yml" 3 | namespace "setting1" 4 | end -------------------------------------------------------------------------------- /spec/settings3.rb: -------------------------------------------------------------------------------- 1 | class Settings3 < Settingslogic 2 | source "#{File.dirname(__FILE__)}/settings.yml" 3 | load! # test of load 4 | end -------------------------------------------------------------------------------- /spec/settings4.rb: -------------------------------------------------------------------------------- 1 | class Settings4 < Settingslogic 2 | source "#{File.dirname(__FILE__)}/settings.yml" 3 | suppress_errors true 4 | end -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.log 3 | *.sqlite3 4 | pkg/* 5 | coverage/* 6 | doc/* 7 | benchmarks/* 8 | .bundle 9 | vendor/bundle 10 | .rvmrc 11 | -------------------------------------------------------------------------------- /spec/settings.rb: -------------------------------------------------------------------------------- 1 | class Settings < Settingslogic 2 | source "#{File.dirname(__FILE__)}/settings.yml" 3 | end 4 | 5 | class SettingsInst < Settingslogic 6 | end -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler' 2 | Bundler::GemHelper.install_tasks 3 | 4 | require 'rspec/core/rake_task' 5 | RSpec::Core::RakeTask.new 6 | 7 | task :default => :spec -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift(File.dirname(__FILE__)) 2 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) 3 | require 'rspec' 4 | require 'settingslogic' 5 | require 'settings' 6 | require 'settings2' 7 | require 'settings3' 8 | require 'settings4' 9 | require 'settings_empty' 10 | 11 | # Needed to test Settings3 12 | Object.send :define_method, 'collides' do 13 | 'collision' 14 | end 15 | 16 | RSpec.configure do |config| 17 | end 18 | -------------------------------------------------------------------------------- /spec/settings.yml: -------------------------------------------------------------------------------- 1 | setting1: 2 | setting1_child: saweet 3 | deep: 4 | another: my value 5 | child: 6 | value: 2 7 | 8 | setting2: 5 9 | setting3: <%= 5 * 5 %> 10 | name: test 11 | 12 | language: 13 | haskell: 14 | paradigm: functional 15 | smalltalk: 16 | paradigm: object oriented 17 | 18 | collides: 19 | does: not 20 | nested: 21 | collides: 22 | does: not either 23 | 24 | array: 25 | - 26 | name: first 27 | - 28 | name: second 29 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | settingslogic (2.0.9) 5 | 6 | GEM 7 | remote: https://rubygems.org/ 8 | specs: 9 | diff-lcs (1.1.3) 10 | rake (10.0.3) 11 | rspec (2.12.0) 12 | rspec-core (~> 2.12.0) 13 | rspec-expectations (~> 2.12.0) 14 | rspec-mocks (~> 2.12.0) 15 | rspec-core (2.12.2) 16 | rspec-expectations (2.12.1) 17 | diff-lcs (~> 1.1.3) 18 | rspec-mocks (2.12.1) 19 | 20 | PLATFORMS 21 | ruby 22 | 23 | DEPENDENCIES 24 | rake 25 | rspec 26 | settingslogic! 27 | -------------------------------------------------------------------------------- /settingslogic.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | $:.push File.expand_path("../lib", __FILE__) 3 | 4 | Gem::Specification.new do |s| 5 | s.name = "settingslogic" 6 | s.version = "2.0.9" 7 | s.platform = Gem::Platform::RUBY 8 | s.authors = ["Ben Johnson"] 9 | s.email = ["bjohnson@binarylogic.com"] 10 | s.homepage = "http://github.com/binarylogic/settingslogic" 11 | s.summary = %q{A simple and straightforward settings solution that uses an ERB enabled YAML file and a singleton design pattern.} 12 | s.description = %q{A simple and straightforward settings solution that uses an ERB enabled YAML file and a singleton design pattern.} 13 | 14 | s.add_development_dependency 'rake' 15 | s.add_development_dependency 'rspec' 16 | 17 | s.files = `git ls-files`.split("\n") 18 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") 19 | s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } 20 | s.require_paths = ["lib"] 21 | end -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008 Ben Johnson of Binary Logic (binarylogic.com) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | = Settingslogic 2 | 3 | == Project Moved: This project has been moved to https://github.com/settingslogic/settingslogic 4 | 5 | Settingslogic is a simple configuration / settings solution that uses an ERB enabled YAML file. It has been great for 6 | our apps, maybe you will enjoy it too. Settingslogic works with Rails, Sinatra, or any Ruby project. 7 | 8 | {Gem Version}[http://badge.fury.io/rb/settingslogic] 9 | {Build Status}[https://travis-ci.org/settingslogic/settingslogic] 10 | {Inline docs}[http://inch-ci.org/github/settingslogic/settingslogic] 11 | 12 | == Helpful links 13 | 14 | * Documentation: http://rdoc.info/projects/binarylogic/settingslogic 15 | * Repository: http://github.com/binarylogic/settingslogic/tree/master 16 | * Issues: http://github.com/binarylogic/settingslogic/issues 17 | 18 | == Installation 19 | 20 | gem install settingslogic 21 | 22 | == Usage 23 | 24 | === 1. Define your class 25 | 26 | Instead of defining a Settings constant for you, that task is left to you. Simply create a class in your application 27 | that looks like: 28 | 29 | class Settings < Settingslogic 30 | source "#{Rails.root}/config/application.yml" 31 | namespace Rails.env 32 | end 33 | 34 | 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 35 | this file in a rails app is app/models/settings.rb 36 | 37 | I felt adding a settings file in your app was more straightforward, less tricky, and more flexible. 38 | 39 | === 2. Create your settings 40 | 41 | Notice above we specified an absolute path to our settings file called "application.yml". This is just a typical YAML file. 42 | Also notice above that we specified a namespace for our environment. A namespace is just an optional string that corresponds 43 | to a key in the YAML file. 44 | 45 | Using a namespace allows us to change our configuration depending on our environment: 46 | 47 | # config/application.yml 48 | defaults: &defaults 49 | cool: 50 | saweet: nested settings 51 | neat_setting: 24 52 | awesome_setting: <%= "Did you know 5 + 5 = #{5 + 5}?" %> 53 | 54 | development: 55 | <<: *defaults 56 | neat_setting: 800 57 | 58 | test: 59 | <<: *defaults 60 | 61 | production: 62 | <<: *defaults 63 | 64 | _Note_: Certain Ruby/Bundler versions include a version of the Psych YAML parser which incorrectly handles merges (the `<<` in the example above.) 65 | 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: 66 | 67 | require 'yaml' 68 | YAML::ENGINE.yamler= 'syck' 69 | 70 | === 3. Access your settings 71 | 72 | >> Rails.env 73 | => "development" 74 | 75 | >> Settings.cool 76 | => "#" 77 | 78 | >> Settings.cool.saweet 79 | => "nested settings" 80 | 81 | >> Settings.neat_setting 82 | => 800 83 | 84 | >> Settings.awesome_setting 85 | => "Did you know 5 + 5 = 10?" 86 | 87 | You can use these settings anywhere, for example in a model: 88 | 89 | class Post < ActiveRecord::Base 90 | self.per_page = Settings.pagination.posts_per_page 91 | end 92 | 93 | === 4. Optional / dynamic settings 94 | 95 | Often, you will want to handle defaults in your application logic itself, to reduce the number of settings 96 | you need to put in your YAML file. You can access an optional setting by using Hash notation: 97 | 98 | >> Settings.messaging.queue_name 99 | => Exception: Missing setting 'queue_name' in 'message' section in 'application.yml' 100 | 101 | >> Settings.messaging['queue_name'] 102 | => nil 103 | 104 | >> Settings.messaging['queue_name'] ||= 'user_mail' 105 | => "user_mail" 106 | 107 | >> Settings.messaging.queue_name 108 | => "user_mail" 109 | 110 | Modifying our model example: 111 | 112 | class Post < ActiveRecord::Base 113 | self.per_page = Settings.posts['per_page'] || Settings.pagination.per_page 114 | end 115 | 116 | This would allow you to specify a custom value for per_page just for posts, or 117 | to fall back to your default value if not specified. 118 | 119 | === 5. Suppressing Exceptions Conditionally 120 | 121 | Raising exceptions for missing settings helps highlight configuration problems. However, in a 122 | Rails app it may make sense to suppress this in production and return nil for missing settings. 123 | While it's useful to stop and highlight an error in development or test environments, this is 124 | often not the right answer for production. 125 | 126 | class Settings < Settingslogic 127 | source "#{Rails.root}/config/application.yml" 128 | namespace Rails.env 129 | suppress_errors Rails.env.production? 130 | end 131 | 132 | >> Settings.non_existent_key 133 | => nil 134 | 135 | == Note on Sinatra / Capistrano / Vlad 136 | 137 | Each of these frameworks uses a +set+ convention for settings, which actually defines methods 138 | in the global Object namespace: 139 | 140 | set :application, "myapp" # does "def application" globally 141 | 142 | This can cause collisions with Settingslogic, since those methods are global. Luckily, the 143 | solution is to just add a call to load! in your class: 144 | 145 | class Settings < Settingslogic 146 | source "#{Rails.root}/config/application.yml" 147 | namespace Rails.env 148 | load! 149 | end 150 | 151 | It's probably always safest to add load! to your class, since this guarantees settings will be 152 | loaded at that time, rather than lazily later via method_missing. 153 | 154 | Finally, you can reload all your settings later as well: 155 | 156 | Settings.reload! 157 | 158 | This is useful if you want to support changing your settings YAML without restarting your app. 159 | 160 | == Author 161 | 162 | Copyright (c) 2008-2010 {Ben Johnson}[http://github.com/binarylogic] of {Binary Logic}[http://www.binarylogic.com], 163 | released under the MIT license. Support for optional settings and reloading by {Nate Wiger}[http://nate.wiger.org]. 164 | -------------------------------------------------------------------------------- /lib/settingslogic.rb: -------------------------------------------------------------------------------- 1 | require "yaml" 2 | require "erb" 3 | require 'open-uri' 4 | 5 | # A simple settings solution using a YAML file. See README for more information. 6 | class Settingslogic < Hash 7 | class MissingSetting < StandardError; end 8 | 9 | class << self 10 | def name # :nodoc: 11 | self.superclass != Hash && instance.key?("name") ? instance.name : super 12 | end 13 | 14 | # Enables Settings.get('nested.key.name') for dynamic access 15 | def get(key) 16 | parts = key.split('.') 17 | curs = self 18 | while p = parts.shift 19 | curs = curs.send(p) 20 | end 21 | curs 22 | end 23 | 24 | def source(value = nil) 25 | @source ||= value 26 | end 27 | 28 | def namespace(value = nil) 29 | @namespace ||= value 30 | end 31 | 32 | def suppress_errors(value = nil) 33 | @suppress_errors ||= value 34 | end 35 | 36 | def [](key) 37 | instance.fetch(key.to_s, nil) 38 | end 39 | 40 | def []=(key, val) 41 | # Setting[:key][:key2] = 'value' for dynamic settings 42 | val = new(val, source) if val.is_a? Hash 43 | instance.store(key.to_s, val) 44 | instance.create_accessor_for(key, val) 45 | end 46 | 47 | def load! 48 | instance 49 | true 50 | end 51 | 52 | def reload! 53 | @instance = nil 54 | load! 55 | end 56 | 57 | private 58 | def instance 59 | return @instance if @instance 60 | @instance = new 61 | create_accessors! 62 | @instance 63 | end 64 | 65 | def method_missing(name, *args, &block) 66 | instance.send(name, *args, &block) 67 | end 68 | 69 | # It would be great to DRY this up somehow, someday, but it's difficult because 70 | # of the singleton pattern. Basically this proxies Setting.foo to Setting.instance.foo 71 | def create_accessors! 72 | instance.each do |key,val| 73 | create_accessor_for(key) 74 | end 75 | end 76 | 77 | def create_accessor_for(key) 78 | return unless key.to_s =~ /^\w+$/ # could have "some-setting:" which blows up eval 79 | instance_eval "def #{key}; instance.send(:#{key}); end" 80 | end 81 | 82 | end 83 | 84 | # Initializes a new settings object. You can initialize an object in any of the following ways: 85 | # 86 | # Settings.new(:application) # will look for config/application.yml 87 | # Settings.new("application.yaml") # will look for application.yaml 88 | # Settings.new("/var/configs/application.yml") # will look for /var/configs/application.yml 89 | # Settings.new(:config1 => 1, :config2 => 2) 90 | # 91 | # Basically if you pass a symbol it will look for that file in the configs directory of your rails app, 92 | # if you are using this in rails. If you pass a string it should be an absolute path to your settings file. 93 | # Then you can pass a hash, and it just allows you to access the hash via methods. 94 | def initialize(hash_or_file = self.class.source, section = nil) 95 | #puts "new! #{hash_or_file}" 96 | case hash_or_file 97 | when nil 98 | raise Errno::ENOENT, "No file specified as Settingslogic source" 99 | when Hash 100 | self.replace hash_or_file 101 | else 102 | file_contents = open(hash_or_file).read 103 | hash = file_contents.empty? ? {} : YAML.load(ERB.new(file_contents).result).to_hash 104 | if self.class.namespace 105 | hash = hash[self.class.namespace] or return missing_key("Missing setting '#{self.class.namespace}' in #{hash_or_file}") 106 | end 107 | self.replace hash 108 | end 109 | @section = section || self.class.source # so end of error says "in application.yml" 110 | create_accessors! 111 | end 112 | 113 | # Called for dynamically-defined keys, and also the first key deferenced at the top-level, if load! is not used. 114 | # Otherwise, create_accessors! (called by new) will have created actual methods for each key. 115 | def method_missing(name, *args, &block) 116 | key = name.to_s 117 | return missing_key("Missing setting '#{key}' in #{@section}") unless has_key? key 118 | value = fetch(key) 119 | create_accessor_for(key) 120 | value.is_a?(Hash) ? self.class.new(value, "'#{key}' section in #{@section}") : value 121 | end 122 | 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 | # Returns an instance of a Hash object 135 | def to_hash 136 | Hash[self] 137 | end 138 | 139 | # This handles naming collisions with Sinatra/Vlad/Capistrano. Since these use a set() 140 | # helper that defines methods in Object, ANY method_missing ANYWHERE picks up the Vlad/Sinatra 141 | # settings! So settings.deploy_to title actually calls Object.deploy_to (from set :deploy_to, "host"), 142 | # rather than the app_yml['deploy_to'] hash. Jeezus. 143 | def create_accessors! 144 | self.each do |key,val| 145 | create_accessor_for(key) 146 | end 147 | end 148 | 149 | # Use instance_eval/class_eval because they're actually more efficient than define_method{} 150 | # http://stackoverflow.com/questions/185947/ruby-definemethod-vs-def 151 | # http://bmorearty.wordpress.com/2009/01/09/fun-with-rubys-instance_eval-and-class_eval/ 152 | def create_accessor_for(key, val=nil) 153 | return unless key.to_s =~ /^\w+$/ # could have "some-setting:" which blows up eval 154 | instance_variable_set("@#{key}", val) 155 | self.class.class_eval <<-EndEval 156 | def #{key} 157 | return @#{key} if @#{key} 158 | return missing_key("Missing setting '#{key}' in #{@section}") unless has_key? '#{key}' 159 | value = fetch('#{key}') 160 | @#{key} = if value.is_a?(Hash) 161 | self.class.new(value, "'#{key}' section in #{@section}") 162 | elsif value.is_a?(Array) && value.all?{|v| v.is_a? Hash} 163 | value.map{|v| self.class.new(v)} 164 | else 165 | value 166 | end 167 | end 168 | EndEval 169 | end 170 | 171 | def symbolize_keys 172 | 173 | inject({}) do |memo, tuple| 174 | 175 | k = (tuple.first.to_sym rescue tuple.first) || tuple.first 176 | 177 | v = k.is_a?(Symbol) ? send(k) : tuple.last # make sure the value is accessed the same way Settings.foo.bar works 178 | 179 | memo[k] = v && v.respond_to?(:symbolize_keys) ? v.symbolize_keys : v #recurse for nested hashes 180 | 181 | memo 182 | end 183 | 184 | end 185 | 186 | def missing_key(msg) 187 | return nil if self.class.suppress_errors 188 | 189 | raise MissingSetting, msg 190 | end 191 | end 192 | -------------------------------------------------------------------------------- /spec/settingslogic_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.dirname(__FILE__) + "/spec_helper") 2 | 3 | describe "Settingslogic" do 4 | it "should access settings" do 5 | Settings.setting2.should == 5 6 | end 7 | 8 | it "should access nested settings" do 9 | Settings.setting1.setting1_child.should == "saweet" 10 | end 11 | 12 | it "should access settings in nested arrays" do 13 | Settings.array.first.name.should == "first" 14 | end 15 | 16 | it "should access deep nested settings" do 17 | Settings.setting1.deep.another.should == "my value" 18 | end 19 | 20 | it "should access extra deep nested settings" do 21 | Settings.setting1.deep.child.value.should == 2 22 | end 23 | 24 | it "should enable erb" do 25 | Settings.setting3.should == 25 26 | end 27 | 28 | it "should namespace settings" do 29 | Settings2.setting1_child.should == "saweet" 30 | Settings2.deep.another.should == "my value" 31 | end 32 | 33 | it "should return the namespace" do 34 | Settings.namespace.should be_nil 35 | Settings2.namespace.should == 'setting1' 36 | end 37 | 38 | it "should distinguish nested keys" do 39 | Settings.language.haskell.paradigm.should == 'functional' 40 | Settings.language.smalltalk.paradigm.should == 'object oriented' 41 | end 42 | 43 | it "should not collide with global methods" do 44 | Settings3.nested.collides.does.should == 'not either' 45 | Settings3[:nested] = 'fooey' 46 | Settings3[:nested].should == 'fooey' 47 | Settings3.nested.should == 'fooey' 48 | Settings3.collides.does.should == 'not' 49 | end 50 | 51 | it "should raise a helpful error message" do 52 | e = nil 53 | begin 54 | Settings.missing 55 | rescue => e 56 | e.should be_kind_of Settingslogic::MissingSetting 57 | end 58 | e.should_not be_nil 59 | e.message.should =~ /Missing setting 'missing' in/ 60 | 61 | e = nil 62 | begin 63 | Settings.language.missing 64 | rescue => e 65 | e.should be_kind_of Settingslogic::MissingSetting 66 | end 67 | e.should_not be_nil 68 | e.message.should =~ /Missing setting 'missing' in 'language' section/ 69 | end 70 | 71 | it "should handle optional / dynamic settings" do 72 | e = nil 73 | begin 74 | Settings.language.erlang 75 | rescue => e 76 | e.should be_kind_of Settingslogic::MissingSetting 77 | end 78 | e.should_not be_nil 79 | e.message.should =~ /Missing setting 'erlang' in 'language' section/ 80 | 81 | Settings.language['erlang'].should be_nil 82 | Settings.language['erlang'] = 5 83 | Settings.language['erlang'].should == 5 84 | 85 | Settings.language['erlang'] = {'paradigm' => 'functional'} 86 | Settings.language.erlang.paradigm.should == 'functional' 87 | Settings.respond_to?('erlang').should be_false 88 | 89 | Settings.reload! 90 | Settings.language['erlang'].should be_nil 91 | 92 | Settings.language[:erlang] ||= 5 93 | Settings.language[:erlang].should == 5 94 | 95 | Settings.language[:erlang] = {} 96 | Settings.language[:erlang][:paradigm] = 'functional' 97 | Settings.language.erlang.paradigm.should == 'functional' 98 | 99 | Settings[:toplevel] = '42' 100 | Settings.toplevel.should == '42' 101 | end 102 | 103 | it "should raise an error on a nil source argument" do 104 | class NoSource < Settingslogic; end 105 | e = nil 106 | begin 107 | NoSource.foo.bar 108 | rescue => e 109 | e.should be_kind_of Errno::ENOENT 110 | end 111 | e.should_not be_nil 112 | end 113 | 114 | it "should allow suppressing errors" do 115 | Settings4.non_existent_key.should be_nil 116 | end 117 | 118 | # This one edge case currently does not pass, because it requires very 119 | # esoteric code in order to make it pass. It was judged not worth fixing, 120 | # as it introduces significant complexity for minor gain. 121 | # it "should handle reloading top-level settings" 122 | # Settings[:inspect] = 'yeah baby' 123 | # Settings.inspect.should == 'yeah baby' 124 | # Settings.reload! 125 | # Settings.inspect.should == 'Settings' 126 | # end 127 | 128 | it "should handle oddly-named settings" do 129 | Settings.language['some-dash-setting#'] = 'dashtastic' 130 | Settings.language['some-dash-setting#'].should == 'dashtastic' 131 | end 132 | 133 | it "should handle settings with nil value" do 134 | Settings["flag"] = true 135 | Settings["flag"] = nil 136 | Settings.flag.should == nil 137 | end 138 | 139 | it "should handle settings with false value" do 140 | Settings["flag"] = true 141 | Settings["flag"] = false 142 | Settings.flag.should == false 143 | end 144 | 145 | it "should support instance usage as well" do 146 | settings = SettingsInst.new(Settings.source) 147 | settings.setting1.setting1_child.should == "saweet" 148 | end 149 | 150 | it "should be able to get() a key with dot.notation" do 151 | Settings.get('setting1.setting1_child').should == "saweet" 152 | Settings.get('setting1.deep.another').should == "my value" 153 | Settings.get('setting1.deep.child.value').should == 2 154 | end 155 | 156 | # If .name is not a property, delegate to superclass 157 | it "should respond with Module.name" do 158 | Settings2.name.should == "Settings2" 159 | end 160 | 161 | # If .name is called on Settingslogic itself, handle appropriately 162 | # by delegating to Hash 163 | it "should have the parent class always respond with Module.name" do 164 | Settingslogic.name.should == 'Settingslogic' 165 | end 166 | 167 | # If .name is a property, respond with that instead of delegating to superclass 168 | it "should allow a name setting to be overriden" do 169 | Settings.name.should == 'test' 170 | end 171 | 172 | it "should allow symbolize_keys" do 173 | Settings.reload! 174 | result = Settings.language.haskell.symbolize_keys 175 | result.class.should == Hash 176 | result.should == {:paradigm => "functional"} 177 | end 178 | 179 | it "should allow symbolize_keys on nested hashes" do 180 | Settings.reload! 181 | result = Settings.language.symbolize_keys 182 | result.class.should == Hash 183 | result.should == { 184 | :haskell => {:paradigm => "functional"}, 185 | :smalltalk => {:paradigm => "object oriented"} 186 | } 187 | end 188 | 189 | it "should handle empty file" do 190 | SettingsEmpty.keys.should eql([]) 191 | end 192 | 193 | # Put this test last or else call to .instance will load @instance, 194 | # masking bugs. 195 | it "should be a hash" do 196 | Settings.send(:instance).should be_is_a(Hash) 197 | end 198 | 199 | describe "#to_hash" do 200 | it "should return a new instance of a Hash object" do 201 | Settings.to_hash.should be_kind_of(Hash) 202 | Settings.to_hash.class.name.should == "Hash" 203 | Settings.to_hash.object_id.should_not == Settings.object_id 204 | end 205 | end 206 | 207 | end 208 | --------------------------------------------------------------------------------