omniconf 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  language: ruby
2
2
  rvm:
3
+ - 1.9.2
3
4
  - 1.9.3
4
- - ruby-head
5
5
  - ree
data/README.md CHANGED
@@ -4,18 +4,21 @@ A _RubyGem_ that provides an application-agnostic configuration merger.
4
4
 
5
5
  # Setup
6
6
 
7
- Configure and load desired backends by creating a new initializer `config/initializers/omniconf.rb`:
7
+ Configure and load desired back-ends by creating a new initializer:
8
+
9
+ - With a Rails application, just run `rails g omniconf:install`.
10
+ - Without a Rails application, you need to create a file along these lines (see below):
8
11
 
9
12
  ```ruby
10
13
  Omniconf.setup do |config|
11
14
  config.sources = {
12
- :yaml_config => {
15
+ :yaml => {
13
16
  :type => :yaml,
14
17
  :file => "config/settings.yml"
15
18
  },
16
- :database_config => {
19
+ :database => {
17
20
  :type => :active_record,
18
- :model_name => :ConfigValue
21
+ :model => "ConfigValue"
19
22
  }
20
23
  }
21
24
  end
@@ -44,13 +47,13 @@ $ rails c
44
47
  ```ruby
45
48
  > Omniconf.configuration.some_config_from_database = "def" # updates value in DB using ConfigValue model
46
49
  => "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)
50
+ > Omniconf.configuration.some_config_from_yaml = 456 # raises an exception because the value comes from YAML
51
+ => Omniconf::ReadOnlyConfigurationValue: cannot set 'some_config_from_yaml' because it belongs to a read-only back-end source (id: :yaml, type: Yaml)
49
52
  > Omniconf.configuration.api.username = "admin" # it works with nested values too
50
53
  => "admin"
51
54
  > Omniconf.configuration.brand_new_value = "whatever" # raises an exception because you've got to tell which back-end will store the new value
52
55
  => 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
56
+ > Omniconf.sources[:database].brand_new_value = "whatever" # adds a new record in ConfigValue model
54
57
  => "whatever"
55
58
  ```
56
59
 
@@ -61,15 +64,21 @@ Other parameters default to standard values for Rails.
61
64
 
62
65
  ### Yaml
63
66
 
64
- Nothing to configure apart from having a YAML file.
65
-
66
67
  _Note: read-only._
67
68
 
69
+ #### Parameters
70
+
71
+ - `:type => :yaml`
72
+ - `:file`: path to the YAML file to load (default: `config/settings.yml`)
73
+ - `:environment`: the root node to load from your YAML file (typically `development`, `production`, etc.)
74
+
68
75
  ### ActiveRecord
69
76
 
70
- Add `gem 'activerecord'` in your `Gemfile`.
77
+ #### Setup
71
78
 
72
- Create a new migration to add the config table: _(FIXME: add a rake task for this)_
79
+ - Add `gem 'activerecord'` in your `Gemfile`.
80
+ - With a Rails application, `rails g omniconf:install` has already created a migration for you.
81
+ - Without a Rails application, create a new migration to add the config table:
73
82
 
74
83
  ```ruby
75
84
  class CreateConfigValues < ActiveRecord::Migration
@@ -83,19 +92,20 @@ class CreateConfigValues < ActiveRecord::Migration
83
92
  end
84
93
  ```
85
94
 
86
- ### Redis
87
-
88
- Add `gem 'redis'` in your `Gemfile`.
95
+ #### Parameters
89
96
 
90
- Not yet implemented.
97
+ - `:type => :active_record`
98
+ - `:model`: name of the ActiveRecord model (default: `ConfigValue`)
99
+ - `:config_file`: path to the database settings (default: `config/database.yml`)
100
+ - `:environment`: environment to load (typically `development`, `production`, etc.)
91
101
 
92
102
  ## Reserved words
93
103
 
94
104
  /!\ You should not use these names as configuration keys:
95
105
 
96
- - `to_hash`: it's a helper which returns the configuration as a hash
106
+ - `to_hash`: returns the configuration as a hash
97
107
  - `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)`)
108
+ - `get_or_default`: returns the value if it exists or creates it otherwise (usage: `get_or_default(key, default_value)`)
99
109
  - `method_missing`: used internally
100
110
  - everything which starts with `__` (double underscore): used internally
101
111
 
@@ -103,7 +113,7 @@ Not yet implemented.
103
113
 
104
114
  `rake`
105
115
 
106
- Tested against _ree_, _ruby-1.9.3_ and _ruby-head_.
116
+ Tested against _ree_, _ruby-1.9.2_ and _ruby-1.9.3_ thanks to [Travis CI](http://travis-ci.org/#!/Picklive/omniconf "It rocks!").
107
117
 
108
118
  # License
109
119
 
@@ -6,7 +6,7 @@ module Omniconf
6
6
  def initialize id, params
7
7
  @source_id = id
8
8
  defaults = {
9
- :model_name => :ConfigValue
9
+ :model => 'ConfigValue'
10
10
  }
11
11
  defaults.merge!({
12
12
  :environment => Rails.env,
@@ -24,7 +24,7 @@ module Omniconf
24
24
  begin
25
25
  records = @model.all
26
26
  rescue ::ActiveRecord::StatementInvalid => e
27
- Omniconf.logger.warn "Could not load #{@params[:model_name]} model, ignoring this configuration source."
27
+ Omniconf.logger.warn "Could not load #{@params[:model]} model, ignoring this configuration source."
28
28
  return
29
29
  end
30
30
 
@@ -54,7 +54,7 @@ module Omniconf
54
54
 
55
55
  def setup
56
56
  # define the ActiveRecord model if missing
57
- unless Object.const_defined? @params[:model_name]
57
+ unless Object.const_defined? @params[:model]
58
58
  unless ::ActiveRecord::Base.connected?
59
59
  ::ActiveRecord::Base.configurations = YAML::load(IO.read(@params[:config_file]))
60
60
  ::ActiveRecord::Base.establish_connection(@params[:environment])
@@ -62,12 +62,13 @@ module Omniconf
62
62
 
63
63
  klass = Class.new ::ActiveRecord::Base do
64
64
  validates_uniqueness_of :key
65
+ serialize :value
65
66
  end
66
67
 
67
- Object.const_set @params[:model_name], klass
68
+ Object.const_set @params[:model], klass
68
69
  end
69
70
 
70
- @model ||= @params[:model_name].to_s.constantize
71
+ @model ||= @params[:model].constantize
71
72
  end
72
73
  end
73
74
  end
@@ -5,6 +5,7 @@ module Omniconf
5
5
  class Base
6
6
  attr_reader :source_id
7
7
  attr_reader :configuration
8
+ def reload_configuration!; load_configuration! end
8
9
  end
9
10
  end
10
11
  end
@@ -1,3 +1,4 @@
1
+ require 'yaml'
1
2
  require 'omniconf/adapters/read_only'
2
3
 
3
4
  module Omniconf
@@ -67,9 +67,14 @@ module Omniconf
67
67
  # add a catch-all exception
68
68
  # (more descriptive than "undefined method `xxx' for nil:NilClass")
69
69
  class << value
70
- def method_missing *args
71
- raise UnknownConfigurationValue,
72
- "cannot get a configuration value with no parent"
70
+ def method_missing method, *args
71
+ if method == :to_str
72
+ # need to raise NoMethodError here for Ruby 1.9.2 compatibility
73
+ raise NoMethodError, "undefined method `#{method}' for #{self}"
74
+ else
75
+ raise UnknownConfigurationValue,
76
+ "cannot get a configuration value with no parent"
77
+ end
73
78
  end
74
79
  end
75
80
  end
@@ -1,3 +1,3 @@
1
1
  module Omniconf
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/lib/omniconf.rb CHANGED
@@ -22,11 +22,10 @@ module Omniconf
22
22
  end
23
23
 
24
24
  def logger
25
- unless @logger
26
- @logger = Logger.new(STDOUT)
27
- @logger.level = @settings.logger_level if @settings
28
- @logger.level = Logger::INFO if @logger.level.nil?
29
- end
25
+ return @logger ||= Rails.logger if defined? Rails
26
+
27
+ @logger ||= Logger.new(STDOUT)
28
+ @logger.level = @settings.logger_level || Logger::INFO
30
29
  @logger
31
30
  end
32
31
 
@@ -0,0 +1,29 @@
1
+ module Omniconf
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ desc "Installs Omniconf and generates the necessary migration"
5
+
6
+ include Rails::Generators::Migration
7
+
8
+ def self.source_root
9
+ File.expand_path("../templates", __FILE__)
10
+ end
11
+
12
+ def self.next_migration_number(dirname)
13
+ Time.now.strftime("%Y%m%d%H%M%S")
14
+ end
15
+
16
+ def copy_initializer
17
+ template 'omniconf.rb.erb', 'config/initializers/omniconf.rb'
18
+ end
19
+
20
+ def create_migrations
21
+ Dir["#{self.class.source_root}/migrations/*.rb"].sort.each do |filepath|
22
+ name = File.basename(filepath)
23
+ migration_template "migrations/#{name}", "db/migrate/#{name.gsub(/^\d+_/,'')}"
24
+ sleep 1 # ensure next migration gets a new number
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,9 @@
1
+ class CreateConfigValues < ActiveRecord::Migration
2
+ def change
3
+ create_table :config_values do |t|
4
+ t.string :key, :null => false
5
+ t.string :value, :null => false
6
+ end
7
+ add_index :config_values, :key, :unique => true
8
+ end
9
+ end
@@ -0,0 +1,13 @@
1
+ Omniconf.setup do |config|
2
+ config.sources = {
3
+ :yaml => {
4
+ :type => :yaml,
5
+ :file => "config/settings.yml"
6
+ },
7
+ :database => {
8
+ :type => :active_record,
9
+ :model => "ConfigValue"
10
+ }
11
+ }
12
+ end
13
+
@@ -3,5 +3,8 @@ test:
3
3
  nested:
4
4
  yaml_key: nested_yaml_value
5
5
  overridden: by_yaml
6
+ some_integer: 1337
7
+ some_float: 3.14
8
+ some_array: [1, 2, 3]
6
9
  development:
7
10
  yaml_key: yaml_value_dev
@@ -4,7 +4,7 @@ require 'omniconf/adapters/active_record'
4
4
  describe Omniconf::Adapter::ActiveRecord do
5
5
  def load_configuration
6
6
  @adapter = Omniconf::Adapter::ActiveRecord.new(:active_record, {
7
- :model_name => :ConfigValue,
7
+ :model => 'ConfigValue',
8
8
  :environment => 'test',
9
9
  :config_file => File.expand_path(
10
10
  '../../../fixtures/omniconf/adapters/active_record/config/database.yml',
@@ -21,54 +21,67 @@ describe Omniconf::Adapter::ActiveRecord do
21
21
  end
22
22
 
23
23
  describe "#configuration" do
24
+ def config
25
+ @adapter.configuration
26
+ end
27
+
24
28
  before do
25
29
  Omniconf.stub(:merge_configuration!)
26
30
  load_configuration
27
31
  end
28
32
 
29
- let(:configuration) { @adapter.configuration }
30
-
31
33
  describe "getting values" do
32
34
  it "returns a value" do
33
- configuration.ar_key.should == 'ar_value'
35
+ config.ar_key.should == 'ar_value'
34
36
  end
35
37
 
36
38
  it "returns a nested value" do
37
- configuration.nested.ar_key.should == 'nested_ar_value'
39
+ config.nested.ar_key.should == 'nested_ar_value'
38
40
  end
39
41
  end
40
42
 
41
43
  describe "setting values" do
42
- it "updates configuration values into database" do
43
- configuration.ar_key.should == 'ar_value'
44
- configuration.ar_key = 'new_ar_value'
45
- configuration.ar_key.should == 'new_ar_value'
44
+ it "updates config values into database" do
45
+ config.ar_key.should == 'ar_value'
46
+ config.ar_key = 'new_ar_value'
47
+ config.ar_key.should == 'new_ar_value'
46
48
  end
47
49
 
48
- it "creates configuration values into database" do
49
- configuration.new_ar_key.should be_nil
50
- configuration.new_ar_key = 'new_ar_value'
51
- configuration.new_ar_key.should == 'new_ar_value'
50
+ it "creates config values into database" do
51
+ config.new_ar_key.should be_nil
52
+ config.new_ar_key = 'new_ar_value'
53
+ config.new_ar_key.should == 'new_ar_value'
52
54
  end
53
-
54
- it "it doesn't cast values to String" # TODO serialize them and keep type
55
55
  end
56
56
 
57
57
  describe "#get_or_default" do
58
58
  it "creates a new default value" do
59
- configuration.newValue.should be_nil
59
+ config.newValue.should be_nil
60
60
  @adapter.should_receive(:set_value).with(['newValue'], 'a')
61
- configuration.get_or_default('newValue', 'a').should == 'a'
62
- configuration.get_or_default('newValue', 'b').should == 'a'
61
+ config.get_or_default('newValue', 'a').should == 'a'
62
+ config.get_or_default('newValue', 'b').should == 'a'
63
63
  end
64
64
 
65
65
  it "creates a new default nested value" do
66
- configuration.nested.newValue.should be_nil
66
+ config.nested.newValue.should be_nil
67
67
  @adapter.should_receive(:set_value).with(['nested', 'newValue'], 'a')
68
- configuration.nested.get_or_default('newValue', 'a').should == 'a'
69
- configuration.nested.get_or_default('newValue', 'b').should == 'a'
68
+ config.nested.get_or_default('newValue', 'a').should == 'a'
69
+ config.nested.get_or_default('newValue', 'b').should == 'a'
70
70
  end
71
71
  end
72
+
73
+ it "keeps value types" do
74
+ config.some_integer.should be_nil
75
+ config.some_float.should be_nil
76
+ config.some_array.should be_nil
77
+ config.some_integer = 1337
78
+ config.some_float = 3.14
79
+ config.some_array = [1, 2, 3]
80
+ @adapter.reload_configuration!
81
+ config.some_integer.should == 1337
82
+ config.some_float.should == 3.14
83
+ config.some_array.should == [1, 2, 3]
84
+ end
72
85
  end
73
86
  end
74
87
 
@@ -12,75 +12,84 @@ describe Omniconf::Adapter::Yaml do
12
12
  @adapter.load_configuration!
13
13
  end
14
14
 
15
+
15
16
  describe "#configuration" do
17
+ def config
18
+ @adapter.configuration
19
+ end
20
+
16
21
  before do
17
22
  Omniconf.stub(:merge_configuration!)
18
23
  load_configuration('test')
19
24
  end
20
25
 
21
- let(:configuration) { @adapter.configuration }
22
-
23
26
  describe "getting values" do
24
27
  it "returns a value" do
25
- configuration.yaml_key.should == 'yaml_value'
28
+ config.yaml_key.should == 'yaml_value'
26
29
  end
27
30
 
28
31
  it "returns a nested value" do
29
- configuration.nested.yaml_key.should == 'nested_yaml_value'
32
+ config.nested.yaml_key.should == 'nested_yaml_value'
30
33
  end
31
34
  end
32
35
 
33
36
  describe "setting values" do
34
37
  it "fails to update a value" do
35
- configuration.yaml_key.should_not be_nil
38
+ config.yaml_key.should_not be_nil
36
39
  expect {
37
- configuration.yaml_key = 'whatever'
40
+ config.yaml_key = 'whatever'
38
41
  }.to raise_error Omniconf::ReadOnlyConfigurationValue
39
42
  end
40
43
 
41
44
  it "fails to update a nested value" do
42
- configuration.nested.yaml_key.should_not be_nil
45
+ config.nested.yaml_key.should_not be_nil
43
46
  expect {
44
- configuration.nested.yaml_key = 'whatever'
47
+ config.nested.yaml_key = 'whatever'
45
48
  }.to raise_error Omniconf::ReadOnlyConfigurationValue
46
49
  end
47
50
 
48
51
  it "fails to create a value" do
49
- configuration.new_yaml_key.should be_nil
52
+ config.new_yaml_key.should be_nil
50
53
  expect {
51
- configuration.new_yaml_key = 'whatever'
54
+ config.new_yaml_key = 'whatever'
52
55
  }.to raise_error Omniconf::ReadOnlyConfigurationValue
53
56
  end
54
57
 
55
58
  it "fails to create a nested value" do
56
- configuration.nested.new_yaml_key.should be_nil
59
+ config.nested.new_yaml_key.should be_nil
57
60
  expect {
58
- configuration.nested.new_yaml_key = 'whatever'
61
+ config.nested.new_yaml_key = 'whatever'
59
62
  }.to raise_error Omniconf::ReadOnlyConfigurationValue
60
63
  end
61
64
  end
62
65
 
63
66
  describe "#get_or_default" do
64
67
  it "fails to create a new default value" do
65
- configuration.newValue.should be_nil
68
+ config.newValue.should be_nil
66
69
  expect {
67
- configuration.get_or_default('newValue', 'a').should == 'a'
70
+ config.get_or_default('newValue', 'a').should == 'a'
68
71
  }.to raise_error Omniconf::ReadOnlyConfigurationValue
69
- configuration.newValue.should be_nil
72
+ config.newValue.should be_nil
70
73
  end
71
74
 
72
75
  it "fails to create a new nested default value" do
73
- configuration.nested.newValue.should be_nil
76
+ config.nested.newValue.should be_nil
74
77
  expect {
75
- configuration.nested.get_or_default('newValue', 'a').should == 'a'
78
+ config.nested.get_or_default('newValue', 'a').should == 'a'
76
79
  }.to raise_error Omniconf::ReadOnlyConfigurationValue
77
- configuration.nested.newValue.should be_nil
80
+ config.nested.newValue.should be_nil
78
81
  end
79
82
  end
80
83
 
81
84
  it "loads the right environment" do
82
85
  load_configuration 'development'
83
- configuration.yaml_key.should == 'yaml_value_dev'
86
+ config.yaml_key.should == 'yaml_value_dev'
87
+ end
88
+
89
+ it "keeps value types" do
90
+ config.some_integer.should == 1337
91
+ config.some_float.should == 3.14
92
+ config.some_array.should == [1, 2, 3]
84
93
  end
85
94
  end
86
95
  end
@@ -30,7 +30,7 @@ describe Omniconf do
30
30
  },
31
31
  :database => {
32
32
  :type => :active_record,
33
- :model_name => :ConfigValue,
33
+ :model => 'ConfigValue',
34
34
  :environment => 'test',
35
35
  :config_file => File.expand_path(
36
36
  '../fixtures/omniconf/adapters/active_record/config/database.yml',
@@ -49,7 +49,9 @@ describe Omniconf do
49
49
  end
50
50
 
51
51
  describe "#configuration" do
52
- let(:config) { Omniconf.configuration }
52
+ def config
53
+ Omniconf.configuration
54
+ end
53
55
 
54
56
  it "returns an instance of Omniconf::Configuration" do
55
57
  config.should be_an_instance_of Omniconf::Configuration
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: omniconf
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-02-08 00:00:00.000000000 Z
12
+ date: 2012-02-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
16
- requirement: &2156203800 !ruby/object:Gem::Requirement
16
+ requirement: &2152356800 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0.9'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *2156203800
24
+ version_requirements: *2152356800
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rspec
27
- requirement: &2156201760 !ruby/object:Gem::Requirement
27
+ requirement: &2152342400 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '2.8'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *2156201760
35
+ version_requirements: *2152342400
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: sqlite3
38
- requirement: &2156200400 !ruby/object:Gem::Requirement
38
+ requirement: &2152340720 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '1.3'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *2156200400
46
+ version_requirements: *2152340720
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: activerecord
49
- requirement: &2156199340 !ruby/object:Gem::Requirement
49
+ requirement: &2152339860 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ~>
@@ -54,7 +54,7 @@ dependencies:
54
54
  version: '3.2'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *2156199340
57
+ version_requirements: *2152339860
58
58
  description: Merge configurations from multiple back-ends for easy use in a complex
59
59
  application.
60
60
  email:
@@ -82,6 +82,9 @@ files:
82
82
  - lib/omniconf/helpers.rb
83
83
  - lib/omniconf/settings.rb
84
84
  - lib/omniconf/version.rb
85
+ - lib/rails/generators/omniconf/install/install_generator.rb
86
+ - lib/rails/generators/omniconf/install/templates/migrations/1_create_config_values.rb
87
+ - lib/rails/generators/omniconf/install/templates/omniconf.rb.erb
85
88
  - omniconf.gemspec
86
89
  - spec/fixtures/omniconf/adapters/active_record/config/database.yml
87
90
  - spec/fixtures/omniconf/adapters/active_record/db/schema.rb
@@ -112,7 +115,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
112
115
  version: '0'
113
116
  requirements: []
114
117
  rubyforge_project: omniconf
115
- rubygems_version: 1.8.11
118
+ rubygems_version: 1.8.10
116
119
  signing_key:
117
120
  specification_version: 3
118
121
  summary: Merge multiple configuration sources into one.