omniconf 0.0.1.pre → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.rspec CHANGED
@@ -1,3 +1,2 @@
1
1
  --color
2
- --format progress
3
- --drb
2
+ --format d
data/.rvmrc CHANGED
@@ -1 +1 @@
1
- rvm --create ree@omniconf
1
+ rvm --create 1.9.3@omniconf
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - ruby-head
5
+ - ree
data/Gemfile CHANGED
@@ -2,7 +2,3 @@ source "http://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in omniconf.gemspec
4
4
  gemspec
5
-
6
- # FIXME: remove this as soon as the official Gem works with Ruby 1.8
7
- # (see https://github.com/Offirmo/recursive-open-struct/pull/1)
8
- gem "recursive-open-struct", :git => "git://github.com/Picklive/recursive-open-struct.git"
data/LICENSE ADDED
@@ -0,0 +1,5 @@
1
+ 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:
2
+
3
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
4
+
5
+ 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.
data/README.md CHANGED
@@ -1,42 +1,67 @@
1
- *THIS STUFF IS STILL ALPHA!*
1
+ # Omniconf [![Build Status](https://secure.travis-ci.org/Picklive/omniconf.png)](http://travis-ci.org/#!/Picklive/omniconf)
2
+
3
+ A _RubyGem_ that provides an application-agnostic configuration merger.
2
4
 
3
5
  # Setup
4
6
 
5
7
  Configure and load desired backends by creating a new initializer `config/initializers/omniconf.rb`:
6
8
 
7
- Omniconf.setup do |config|
8
- config.sources = {
9
- :yaml_config => {
10
- :type => :yaml,
11
- :file => "config/settings.yml"
12
- },
13
- :database_config => {
14
- :type => :active_record,
15
- :model_name => :ConfigValue
16
- }
17
- }
18
- end
9
+ ```ruby
10
+ Omniconf.setup do |config|
11
+ config.sources = {
12
+ :yaml_config => {
13
+ :type => :yaml,
14
+ :file => "config/settings.yml"
15
+ },
16
+ :database_config => {
17
+ :type => :active_record,
18
+ :model_name => :ConfigValue
19
+ }
20
+ }
21
+ end
22
+ ```
19
23
 
20
24
  # Usage
21
25
 
22
- $ rails c
23
- > Omniconf.configuration.some_config_from_database # value from ConfigValue model
24
- "abc"
25
- > Omniconf.configuration_hash["some_config_from_database"] # if you prefer the hash way
26
- "abc"
27
- > Omniconf.configuration.some_config_from_yaml # value from config/settings.yml
28
- 123
29
- > Omniconf.configuration.api.username # it works with nested values too
30
- "root"
31
-
32
- ## Backend sources
26
+ ## Getting values
27
+
28
+ ```ruby
29
+ $ rails c
30
+ > Omniconf.configuration.some_config_from_database # value from ConfigValue model
31
+ => "abc"
32
+ > Omniconf.configuration.some_config_from_yaml # value from config/settings.yml
33
+ => 123
34
+ > Omniconf.configuration.api.username # it works with nested values too
35
+ => "root"
36
+ > Omniconf.configuration.api # outputs a hash if nested
37
+ => {"username"=>"root"}
38
+ > Omniconf.configuration.non_existant.config_value # raises an exception
39
+ => Omniconf::UnknownConfigurationValue: cannot get a configuration value with no parent
40
+ ```
41
+
42
+ ## Setting values
43
+
44
+ ```ruby
45
+ > Omniconf.configuration.some_config_from_database = "def" # updates value in DB using ConfigValue model
46
+ => "def"
47
+ > Omniconf.configuration.some_config_from_yaml = 456 # raises an exception because the value comes from YAML - who would want to update a YAML file?!
48
+ => Omniconf::ReadOnlyConfigurationValue: cannot set 'some_config_from_yaml' because it belongs to a read-only back-end source (id: :yaml_config, type: Yaml)
49
+ > Omniconf.configuration.api.username = "admin" # it works with nested values too
50
+ => "admin"
51
+ > Omniconf.configuration.brand_new_value = "whatever" # raises an exception because you've got to tell which back-end will store the new value
52
+ => Omniconf::UnknownConfigurationValue: cannot set a configuration value with no parent
53
+ > Omniconf.sources[:database_config].brand_new_value = "whatever" # adds a new row in ConfigValue model
54
+ => "whatever"
55
+ ```
56
+
57
+ ## Back-end sources
33
58
 
34
59
  `:type` is the only required parameter.
35
60
  Other parameters default to standard values for Rails.
36
61
 
37
62
  ### Yaml
38
63
 
39
- Nothing to configure apart from a YAML file.
64
+ Nothing to configure apart from having a YAML file.
40
65
 
41
66
  _Note: read-only._
42
67
 
@@ -46,15 +71,17 @@ Add `gem 'activerecord'` in your `Gemfile`.
46
71
 
47
72
  Create a new migration to add the config table: _(FIXME: add a rake task for this)_
48
73
 
49
- class CreateConfigValues < ActiveRecord::Migration
50
- def change
51
- create_table :config_values do |t|
52
- t.string :key, :null => false
53
- t.string :value, :null => false
54
- end
55
- add_index :config_values, :key, :unique => true
56
- end
74
+ ```ruby
75
+ class CreateConfigValues < ActiveRecord::Migration
76
+ def change
77
+ create_table :config_values do |t|
78
+ t.string :key, :null => false
79
+ t.string :value, :null => false
57
80
  end
81
+ add_index :config_values, :key, :unique => true
82
+ end
83
+ end
84
+ ```
58
85
 
59
86
  ### Redis
60
87
 
@@ -62,9 +89,23 @@ Add `gem 'redis'` in your `Gemfile`.
62
89
 
63
90
  Not yet implemented.
64
91
 
92
+ ## Reserved words
93
+
94
+ /!\ You should not use these names as configuration keys:
95
+
96
+ - `to_hash`: it's a helper which returns the configuration as a hash
97
+ - `inspect`: outputs sub-values as a hash
98
+ - `get_or_default`: it's a helper which returns the value if it exists or creates it otherwise (usage: `get_or_default(key, default_value)`)
99
+ - `method_missing`: used internally
100
+ - everything which starts with `__` (double underscore): used internally
101
+
65
102
  # Testing
66
103
 
67
104
  `rake`
68
105
 
69
- Tested on _ree-1.8.7_ and _ruby-1.9.3_.
106
+ Tested against _ree_, _ruby-1.9.3_ and _ruby-head_.
107
+
108
+ # License
109
+
110
+ MIT
70
111
 
@@ -3,28 +3,57 @@ require 'active_record'
3
3
  module Omniconf
4
4
  module Adapter
5
5
  class ActiveRecord < Base
6
- attr_reader :model
7
-
8
- def initialize params
6
+ def initialize id, params
7
+ @source_id = id
9
8
  defaults = {
10
- :model_name => :ConfigValue,
9
+ :model_name => :ConfigValue
10
+ }
11
+ defaults.merge!({
11
12
  :environment => Rails.env,
12
13
  :config_file => File.join(Rails.root, 'config/database.yml')
13
- }
14
+ }) if defined? Rails
14
15
  @params = defaults.merge params
15
16
  end
16
17
 
17
18
  def load_configuration!
18
19
  setup
19
20
 
20
- @configuration_hash = {}
21
- @model.all.map do |record|
22
- @configuration_hash[record.key] = record.value
21
+ # create an empty config in case ActiveRecord raises
22
+ @configuration = Configuration.new self
23
+
24
+ begin
25
+ records = @model.all
26
+ rescue ::ActiveRecord::StatementInvalid => e
27
+ Omniconf.logger.warn "Could not load #{@params[:model_name]} model, ignoring this configuration source."
28
+ return
29
+ end
30
+
31
+ # build the configuration hash from DB (nesting on dots)
32
+ hash = {}
33
+ records.map do |record|
34
+ key, value = record.key.split('.'), record.value
35
+ el = key[0..-2].inject(hash) {|r,e| r[e] ||= {} }
36
+ raise unless el[key.last].nil?
37
+ el[key.last] = value
38
+ end
39
+ @configuration = Configuration.new self, hash
40
+
41
+ Omniconf.merge_configuration! @source_id
42
+ end
43
+
44
+ def set_value(key, value)
45
+ key = key.join('.')
46
+ if item = @model.find_by_key(key)
47
+ item.value = value
48
+ else
49
+ item = @model.new(:key => key, :value => value)
23
50
  end
24
- merge_configuration! @configuration_hash
51
+ item.save!
52
+ item.value
25
53
  end
26
54
 
27
55
  def setup
56
+ # define the ActiveRecord model if missing
28
57
  unless Object.const_defined? @params[:model_name]
29
58
  unless ::ActiveRecord::Base.connected?
30
59
  ::ActiveRecord::Base.configurations = YAML::load(IO.read(@params[:config_file]))
@@ -33,32 +62,6 @@ module Omniconf
33
62
 
34
63
  klass = Class.new ::ActiveRecord::Base do
35
64
  validates_uniqueness_of :key
36
-
37
- def self.[]=(key, value)
38
- if item = self.find_by_key(key.to_s)
39
- item.value = value.to_s
40
- else
41
- item = self.new(:key => key.to_s, :value => value.to_s)
42
- end
43
- item.save!
44
- item.value
45
- end
46
-
47
- def self.[](key)
48
- if item = find_by_key(key.to_s)
49
- return item.value
50
- end
51
- end
52
-
53
- def self.get_or_default(key, default)
54
- if item = find_by_key(key.to_s)
55
- return item.value
56
- else
57
- self[key] = default
58
- return default
59
- end
60
- end
61
-
62
65
  end
63
66
 
64
67
  Object.const_set @params[:model_name], klass
@@ -1,27 +1,10 @@
1
- require 'recursive_open_struct'
1
+ require 'omniconf/configuration'
2
2
 
3
3
  module Omniconf
4
4
  module Adapter
5
5
  class Base
6
- attr_accessor :configuration_hash
7
-
8
- def configuration
9
- RecursiveOpenStruct.new @configuration_hash
10
- end
11
-
12
- def merge_configuration! with_config
13
- Omniconf.logger.debug "Merged global configuration BEFORE: #{Omniconf.configuration_hash.inspect}"
14
- adapter = self.class.ancestors.first.to_s.split(':').last
15
- Omniconf.logger.debug "Merging from #{adapter} configuration: #{with_config.inspect}"
16
- Omniconf.configuration_hash.merge!(with_config) do |key, old_val, new_val|
17
- Omniconf.logger.warn \
18
- "'#{key}' has been overriden with value from #{adapter} configuration " <<
19
- "(old value: #{old_val.inspect}, new value: #{new_val.inspect})" if new_val != old_val
20
- new_val
21
- end
22
- Omniconf.logger.debug "Merged global configuration AFTER: #{Omniconf.configuration_hash.inspect}"
23
- Omniconf.configuration = RecursiveOpenStruct.new Omniconf.configuration_hash
24
- end
6
+ attr_reader :source_id
7
+ attr_reader :configuration
25
8
  end
26
9
  end
27
10
  end
@@ -0,0 +1,12 @@
1
+ module Omniconf
2
+ module Adapter
3
+ module ReadOnly
4
+ def set_value(key, value)
5
+ raise ReadOnlyConfigurationValue, "cannot set '#{key.join('.')}' " <<
6
+ "because it belongs to a read-only back-end source " <<
7
+ "(id: #{@source_id.inspect}, type: #{self.class.to_s.demodulize})"
8
+ end
9
+ end
10
+ end
11
+ end
12
+
@@ -1,21 +1,26 @@
1
+ require 'omniconf/adapters/read_only'
2
+
1
3
  module Omniconf
2
4
  module Adapter
3
5
  class Yaml < Base
6
+ include ReadOnly
4
7
 
5
- def initialize params
6
- defaults = {
8
+ def initialize id, params
9
+ @source_id = id
10
+ defaults = {}
11
+ defaults.merge!({
7
12
  :environment => Rails.env,
8
13
  :file => File.join(Rails.root, 'config/settings.yml')
9
- }
14
+ }) if defined? Rails
10
15
  @params = defaults.merge params
11
16
  end
12
17
 
13
18
  def load_configuration!
14
- yaml = ::YAML.load_file(@params[:file])
15
- @configuration_hash = yaml[@params[:environment]]
16
- merge_configuration! @configuration_hash
17
- end
19
+ yaml = ::YAML.load_file(@params[:file])[@params[:environment]]
20
+ @configuration = Omniconf::Configuration.new(self, yaml)
18
21
 
22
+ Omniconf.merge_configuration! @source_id
23
+ end
19
24
  end
20
25
  end
21
26
  end
@@ -0,0 +1,9 @@
1
+ module Omniconf
2
+ class BlankSlate
3
+ instance_methods.each do |method|
4
+ undef_method method unless method =~ /^__/ or
5
+ [:inspect, :instance_of?, :object_id, :should].include? method.to_sym
6
+ end
7
+ end
8
+ end
9
+
@@ -0,0 +1,91 @@
1
+ require 'omniconf/blank_state'
2
+
3
+ module Omniconf
4
+ class Configuration < Omniconf::BlankSlate
5
+ # XXX: Every method defined in this class overrides config value accessors,
6
+ # hence it inherits from BlankSlate (and the gross 'method_missing')
7
+
8
+ def initialize(adapter, table = {}, parent = nil)
9
+ @__adapter, @__parent = adapter, parent
10
+ @__table = table.recursive_stringify_keys!
11
+ end
12
+
13
+ # Helpful helper which returns the object as a hash
14
+ def to_hash
15
+ @__table
16
+ end
17
+
18
+ def inspect
19
+ @__table.inspect
20
+ end
21
+
22
+ def get_or_default(key, default)
23
+ raise "key cannot be nested" if key.include? '.'
24
+ __send__ :"#{key}=", default unless value = __send__(:"#{key}")
25
+ value || default
26
+ end
27
+
28
+ def method_missing(method, *args)
29
+ raise NoMethodError, "undefined method `#{method}' for #{self}" \
30
+ if method.to_s[0..1] == '__' # will save hours tracking down heisenbugs
31
+
32
+ len = args.length
33
+ if new_key = method.to_s.chomp!('=') # write
34
+ if len == 1
35
+ key, value = new_key, args[0]
36
+ else
37
+ raise ArgumentError, "wrong number of arguments (#{len} for 1)"
38
+ end
39
+
40
+ if @__adapter # should not be nil expect for testing purposes
41
+ # update the actual source data (e.g. does the SQL query)
42
+ full_key, parent = [key], @__parent
43
+ while parent # build the full nested key from parents
44
+ full_key = full_key.unshift parent[:root]
45
+ parent = parent[:object].__parent
46
+ end
47
+
48
+ @__adapter.set_value(full_key, value) # notify the adapter
49
+ else
50
+ Omniconf.logger.warn "No adapter to notify"
51
+ end
52
+
53
+ @__table[key] = value # update our internal config hash
54
+
55
+ if @__adapter.is_a? Omniconf::Adapter::Base
56
+ # we need to merge the global config
57
+ Omniconf.merge_configuration! @__adapter.source_id
58
+ end
59
+
60
+ elsif len == 0 # read
61
+ key = method.to_s
62
+ value = @__table[key]
63
+ if value.is_a?(Hash)
64
+ Configuration.new(@__adapter, value, {:root => key, :object => self})
65
+ else
66
+ unless value
67
+ # add a catch-all exception
68
+ # (more descriptive than "undefined method `xxx' for nil:NilClass")
69
+ class << value
70
+ def method_missing *args
71
+ raise UnknownConfigurationValue,
72
+ "cannot get a configuration value with no parent"
73
+ end
74
+ end
75
+ end
76
+ value
77
+ end
78
+
79
+ else
80
+ raise NoMethodError, "undefined method `#{method}' for #{self}"
81
+ end
82
+ end
83
+
84
+ protected
85
+ attr_reader :__parent
86
+
87
+ private
88
+ attr_reader :__table
89
+ end
90
+ end
91
+
@@ -0,0 +1,7 @@
1
+ module Omniconf
2
+ # Raised when trying to get or set a non-existent configuration value
3
+ class UnknownConfigurationValue < StandardError; end
4
+
5
+ # Raised when trying to set a read-only configuration value
6
+ class ReadOnlyConfigurationValue < StandardError; end
7
+ end
@@ -0,0 +1,30 @@
1
+ class Hash
2
+ # Recursive version of Rails' stringify_keys!
3
+ def recursive_stringify_keys!
4
+ self.keys.each do |key|
5
+ self[key.to_s] = self.delete(key) unless key.is_a? String
6
+ self[key.to_s].recursive_stringify_keys! if self[key.to_s].is_a? Hash
7
+ end
8
+ self
9
+ end
10
+
11
+ def recursive_merge! with_hash
12
+ self.merge!(with_hash) do |key, old_val, new_val|
13
+ if new_val.is_a?(Hash) and old_val.is_a?(Hash)
14
+ # merge sub-hashes together
15
+ new_val = old_val.recursive_merge! new_val
16
+ else
17
+ # yield block when overriding
18
+ yield key, old_val, new_val if block_given? and new_val != old_val
19
+ end
20
+ new_val
21
+ end
22
+ end
23
+ end
24
+
25
+ class String
26
+ def demodulize
27
+ self.gsub(/^.*::/, '')
28
+ end
29
+ end
30
+
@@ -1,3 +1,3 @@
1
1
  module Omniconf
2
- VERSION = "0.0.1.pre"
2
+ VERSION = "0.0.1"
3
3
  end
data/lib/omniconf.rb CHANGED
@@ -1,20 +1,28 @@
1
1
  require 'logger'
2
- require "omniconf/version"
3
- require "omniconf/settings"
2
+ require 'omniconf/version'
3
+ require 'omniconf/helpers'
4
+ require 'omniconf/errors'
5
+ require 'omniconf/settings'
6
+ require 'omniconf/adapters/base'
4
7
 
5
8
  module Omniconf
6
9
  class << self
7
10
  attr_reader :settings # Omniconf settings
8
- attr_accessor :configuration # Global configuration as a RecursiveOpenStruct
9
- attr_writer :configuration_hash # Global configuration as an Hash
11
+ attr_reader :configuration # global merged configuration
12
+ alias_method :conf, :configuration # shortcut for the lazy people
10
13
 
11
- def configuration_hash
12
- @configuration_hash ||= {}
14
+ def setup
15
+ @settings ||= Settings.new
16
+ yield @settings
17
+
18
+ register_sources
19
+
20
+ @settings.load_configuration = true if @settings.load_configuration.nil?
21
+ load_configuration! if @settings.load_configuration
13
22
  end
14
23
 
15
24
  def logger
16
25
  unless @logger
17
- # @logger ||= defined? Rails.logger ? Rails.logger : Logger.new(STDOUT)
18
26
  @logger = Logger.new(STDOUT)
19
27
  @logger.level = @settings.logger_level if @settings
20
28
  @logger.level = Logger::INFO if @logger.level.nil?
@@ -22,39 +30,68 @@ module Omniconf
22
30
  @logger
23
31
  end
24
32
 
25
- def setup
26
- @settings = Settings.new
27
- yield @settings
28
-
29
- register_sources
30
-
31
- @settings.load_configuration = true if @settings.load_configuration.nil?
32
- load_configuration! if @settings.load_configuration
33
+ # Returns a mapping on source adapters
34
+ def sources
35
+ @settings.sources.to_a.inject({}) do |result, (source_id, source)|
36
+ result[source_id] = source[:adapter].configuration
37
+ result
38
+ end
33
39
  end
34
40
 
35
41
  def load_configuration!
42
+ @configuration = Omniconf::Configuration.new self
36
43
  @settings.sources.each do |source_id, params|
37
44
  params[:adapter].load_configuration!
38
- Omniconf.logger.info "Loaded configuration from #{source_id} source"
45
+ Omniconf.logger.info "Loaded configuration from #{source_id.inspect} source"
39
46
  end
40
47
  end
41
48
 
42
- private
49
+ alias_method :reload_configuration!, :load_configuration!
50
+
51
+ def merge_configuration! source_id
52
+ Omniconf.logger.debug "Merging from #{source_id.inspect} source"
53
+ source_config = Omniconf.sources[source_id].to_hash
54
+ global_config = Omniconf.configuration.to_hash
55
+ global_config.recursive_merge!(source_config) do |key, old_val, new_val|
56
+ Omniconf.logger.warn \
57
+ "'#{key}' has been overriden with value from #{source_id.inspect} source " <<
58
+ "(old value: #{old_val.inspect}, new value: #{new_val.inspect})"
59
+ end
60
+ end
43
61
 
62
+ def set_value(key, value)
63
+ found = false
64
+ @settings.sources.to_a.reverse.each do |source_id, source|
65
+ # We try to find the original source overriding the last merged one,
66
+ # hence we scan sources backwards
67
+ adapter = source[:adapter]
68
+ config = adapter.configuration.to_hash
69
+ element = key[0..-2].inject(config) { |result, el| result[el] }
70
+ if element and element[key.last] # we've found it in the current source
71
+ element[key.last] = value
72
+ adapter.set_value(key, value)
73
+ found = true
74
+ break
75
+ end
76
+ end
77
+ raise UnknownConfigurationValue,
78
+ "cannot set a configuration value with no parent" unless found
79
+ end
80
+
81
+ private
44
82
  def register_sources
83
+ return unless @settings.sources
45
84
  @settings.sources.each do |source_id, params|
46
85
  adapter_file = params[:type].to_s
47
- require "omniconf/adapters/base"
48
86
  require "omniconf/adapters/#{adapter_file}"
49
- adapter_class = adapter_file.to_s.split('_').map {|w| w.capitalize}.join
87
+ adapter_class = adapter_file.split('_').map {|w| w.capitalize}.join
50
88
  raise unless params[:adapter].nil?
51
89
  params[:adapter] = Omniconf::Adapter.class_eval do
52
- const_get(adapter_class).new(params)
90
+ const_get(adapter_class).new(source_id, params)
53
91
  end
54
- Omniconf.logger.info "Registered #{adapter_class}::#{source_id} source"
92
+ Omniconf.logger.debug "Registered #{adapter_class}::#{source_id} source"
55
93
  end
56
94
  end
57
-
58
95
  end
59
96
  end
60
97
 
data/omniconf.gemspec CHANGED
@@ -5,11 +5,11 @@ require "omniconf/version"
5
5
  Gem::Specification.new do |s|
6
6
  s.name = "omniconf"
7
7
  s.version = Omniconf::VERSION
8
- s.authors = ["Cédric Felizard"]
8
+ s.authors = ["Cedric Felizard"]
9
9
  s.email = ["cedric@picklive.com"]
10
10
  s.homepage = "https://github.com/Picklive/omniconf"
11
11
  s.summary = %q{Merge multiple configuration sources into one.}
12
- s.description = %q{Merge configurations from multiple backends for easy use in a Rails application.}
12
+ s.description = %q{Merge configurations from multiple back-ends for easy use in a complex application.}
13
13
 
14
14
  s.rubyforge_project = "omniconf"
15
15
 
@@ -18,10 +18,8 @@ Gem::Specification.new do |s|
18
18
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
19
  s.require_paths = ["lib"]
20
20
 
21
- s.add_runtime_dependency "recursive-open-struct"
22
-
23
- s.add_development_dependency "rake"
24
- s.add_development_dependency "rspec"
25
- s.add_development_dependency "sqlite3"
26
- s.add_development_dependency "activerecord"
21
+ s.add_development_dependency 'rake', '~> 0.9'
22
+ s.add_development_dependency 'rspec', '~> 2.8'
23
+ s.add_development_dependency 'sqlite3', '~> 1.3'
24
+ s.add_development_dependency 'activerecord', '~> 3.2'
27
25
  end
@@ -1,3 +1,5 @@
1
+ ActiveRecord::Migration.verbose = false
2
+
1
3
  ActiveRecord::Schema.define :version => 0 do
2
4
  create_table :config_values, :force => true do |t|
3
5
  t.string :key
@@ -5,6 +7,7 @@ ActiveRecord::Schema.define :version => 0 do
5
7
  end
6
8
  end
7
9
 
8
- ConfigValue.create!({:key => 'foo', :value => 'bar'})
9
- ConfigValue.create!({:key => 'foo2', :value => 'bar2'})
10
+ ConfigValue.create!({:key => 'ar_key', :value => 'ar_value'})
11
+ ConfigValue.create!({:key => 'nested.ar_key', :value => 'nested_ar_value'})
12
+ ConfigValue.create!({:key => 'nested.overridden', :value => 'by_ar'})
10
13