config_newton 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.rdoc +80 -0
- data/Rakefile +45 -0
- data/VERSION +1 -0
- data/lib/config_newton.rb +133 -0
- data/spec/config_newton/configuration_spec.rb +100 -0
- data/spec/config_newton_spec.rb +72 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +11 -0
- metadata +88 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Michael Bleigh
|
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.
|
data/README.rdoc
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
= ConfigNewton
|
2
|
+
|
3
|
+
A common pattern for Ruby libraries is to have some kind of configuration that can be set at the library level. ConfigNewton is a simple library that provides a user-friendly way to expose that configuration.
|
4
|
+
|
5
|
+
== Installation
|
6
|
+
|
7
|
+
gem install config_newton
|
8
|
+
|
9
|
+
== Usage
|
10
|
+
|
11
|
+
require 'config_newton'
|
12
|
+
|
13
|
+
# The library author sets it up like this:
|
14
|
+
|
15
|
+
module MyLibrary
|
16
|
+
include ConfigNewton
|
17
|
+
|
18
|
+
configurable do
|
19
|
+
property :email
|
20
|
+
property :special_sauce, :default => true
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# And the end user can set it like this:
|
25
|
+
|
26
|
+
MyLibrary.configure do |config|
|
27
|
+
config.email = 'dude@somewhere.com'
|
28
|
+
config.special_sauce = false
|
29
|
+
end
|
30
|
+
|
31
|
+
# And the library author can use it like this:
|
32
|
+
|
33
|
+
module MyLibrary
|
34
|
+
class Client
|
35
|
+
# ... somewhere in the code ...
|
36
|
+
MyLibrary.config.email # => 'dude@somewhere.com'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
== Other Useful Bits
|
41
|
+
|
42
|
+
There are a few other useful features of ConfigNewton. For instance, you can convert the configuration to a hash at any time, or just access it like one:
|
43
|
+
|
44
|
+
MyLibrary.config.to_hash # => {:email => 'dude@somewhere.com', :special_sauce => false}
|
45
|
+
MyLibrary[:special_sauce] # => false
|
46
|
+
|
47
|
+
You can also load user settings from a YAML file quite easily. Let's say you're developing a Rails plugin. You can easily load a <tt>config</tt> directory YAML file like this:
|
48
|
+
|
49
|
+
MyLibrary.config.load_from("#{Rails.root}/config/my_library.yml", Rails.env)
|
50
|
+
|
51
|
+
The second argument specified is the "root key" for the configuration, allowing you to easily specify environments or other conditions. Using our examples above the user would create a YAML file in <tt>config/my_library.yml</tt> and populate it like this:
|
52
|
+
|
53
|
+
development:
|
54
|
+
email: just@testing.com
|
55
|
+
special_sauce: false
|
56
|
+
|
57
|
+
production
|
58
|
+
email: bob@example.com
|
59
|
+
special_sauce: true
|
60
|
+
|
61
|
+
== Roadmap
|
62
|
+
|
63
|
+
There's still further I'd like to take this library, here are a few thoughts:
|
64
|
+
|
65
|
+
Hierarchical Configuration :: Create multi-level configurations that retain an intuitive interface both for the library developer and the user.
|
66
|
+
Typed Configurations :: Implement a simple casting system to automatically convert values into desired types.
|
67
|
+
|
68
|
+
|
69
|
+
== Note on Patches/Pull Requests
|
70
|
+
|
71
|
+
* Fork the project.
|
72
|
+
* Make your feature addition or bug fix.
|
73
|
+
* Add tests for it. This is important so I don't break it in a future version unintentionally.
|
74
|
+
* Commit, do not mess with rakefile, version, or history.
|
75
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
76
|
+
* Send me a pull request. Bonus points for topic branches.
|
77
|
+
|
78
|
+
== Copyright
|
79
|
+
|
80
|
+
Copyright (c) 2010 Intridea, Inc. and Michael Bleigh. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "config_newton"
|
8
|
+
gem.summary = %Q{A simple tool to add class-level configuration to libraries.}
|
9
|
+
gem.description = %Q{Library authors can now easily add class-level configuration including YAML loading with just a few lines of code.}
|
10
|
+
gem.email = "michael@intridea.com"
|
11
|
+
gem.homepage = "http://github.com/intridea/config_newton"
|
12
|
+
gem.authors = ["Michael Bleigh"]
|
13
|
+
gem.add_development_dependency "rspec", ">= 1.2.9"
|
14
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
15
|
+
end
|
16
|
+
Jeweler::GemcutterTasks.new
|
17
|
+
rescue LoadError
|
18
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'spec/rake/spectask'
|
22
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
23
|
+
spec.libs << 'lib' << 'spec'
|
24
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
25
|
+
end
|
26
|
+
|
27
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
28
|
+
spec.libs << 'lib' << 'spec'
|
29
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
30
|
+
spec.rcov = true
|
31
|
+
end
|
32
|
+
|
33
|
+
task :spec => :check_dependencies
|
34
|
+
|
35
|
+
task :default => :spec
|
36
|
+
|
37
|
+
require 'rake/rdoctask'
|
38
|
+
Rake::RDocTask.new do |rdoc|
|
39
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
40
|
+
|
41
|
+
rdoc.rdoc_dir = 'rdoc'
|
42
|
+
rdoc.title = "config_newton #{version}"
|
43
|
+
rdoc.rdoc_files.include('README*')
|
44
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
45
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,133 @@
|
|
1
|
+
module ConfigNewton
|
2
|
+
def self.included(base)
|
3
|
+
base.extend(ClassMethods)
|
4
|
+
base.class_eval do
|
5
|
+
@configuration = ConfigNewton::Configuration.new
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
# Configure the class either with a hash
|
11
|
+
# or by providing a block. See the documentation
|
12
|
+
# for Configuration#set.
|
13
|
+
def configure(options = {}, &block)
|
14
|
+
config.set(options, &block)
|
15
|
+
end
|
16
|
+
|
17
|
+
# A list of configurable properties.
|
18
|
+
def configurables
|
19
|
+
configuration.properties
|
20
|
+
end
|
21
|
+
|
22
|
+
# The configuration object.
|
23
|
+
def configuration
|
24
|
+
@configuration
|
25
|
+
end
|
26
|
+
alias :config :configuration
|
27
|
+
|
28
|
+
protected
|
29
|
+
|
30
|
+
# Add either a single configurable property to
|
31
|
+
# the class/module or provide a block that will
|
32
|
+
# allow you to set multiple at once.
|
33
|
+
#
|
34
|
+
# <b>Example:</b>
|
35
|
+
#
|
36
|
+
#
|
37
|
+
def configurable(property = nil, options = {}, &block)
|
38
|
+
if block_given?
|
39
|
+
@configuration.instance_eval(&block)
|
40
|
+
else
|
41
|
+
@configuration.add(property, options)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class Configuration
|
47
|
+
def initialize
|
48
|
+
@hash = {}
|
49
|
+
@properties = []
|
50
|
+
@defaults = {}
|
51
|
+
end
|
52
|
+
|
53
|
+
attr_reader :properties
|
54
|
+
|
55
|
+
# Add a new property that can be set and read
|
56
|
+
# on this configuration.
|
57
|
+
#
|
58
|
+
# <b>Options:</b>
|
59
|
+
#
|
60
|
+
# <tt>:default</tt> :: Specify a default value.
|
61
|
+
def add(name, options = {})
|
62
|
+
raise ArgumentError, "Property name cannot be blank." unless name && name != ""
|
63
|
+
@properties << name.to_sym
|
64
|
+
@defaults[name.to_sym] = options[:default] if options[:default]
|
65
|
+
self.class.class_eval <<-RUBY
|
66
|
+
def #{name}
|
67
|
+
self[:#{name}]
|
68
|
+
end
|
69
|
+
|
70
|
+
def #{name}=(value)
|
71
|
+
self[:#{name}] = value
|
72
|
+
end
|
73
|
+
RUBY
|
74
|
+
end
|
75
|
+
alias :property :add
|
76
|
+
|
77
|
+
# Read an individual property on the configuration.
|
78
|
+
def [](property)
|
79
|
+
@hash[property.to_sym] || @defaults[property.to_sym]
|
80
|
+
end
|
81
|
+
|
82
|
+
# Set an individual property on the configuration.
|
83
|
+
def []=(property, value)
|
84
|
+
@hash[property.to_sym] = value
|
85
|
+
end
|
86
|
+
|
87
|
+
# Converts the configuration into a symbol-keyed
|
88
|
+
# hash.
|
89
|
+
def to_hash
|
90
|
+
@defaults.merge(@hash)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Set the properties of the configuration
|
94
|
+
# either by providing a hash or by providing
|
95
|
+
# a block. The block will yield a configuration
|
96
|
+
# object that has method setters and getters.
|
97
|
+
#
|
98
|
+
# Example:
|
99
|
+
#
|
100
|
+
# MyClass.config.set do |config|
|
101
|
+
# config.property = 123
|
102
|
+
# end
|
103
|
+
def set(properties = {}, &block)
|
104
|
+
if block_given?
|
105
|
+
yield self
|
106
|
+
else
|
107
|
+
@hash.merge!(properties.inject({}){|h,(k,v)| h[k.to_sym] = v; h})
|
108
|
+
end
|
109
|
+
self
|
110
|
+
end
|
111
|
+
|
112
|
+
# Load configuration from a YAML string. Provide
|
113
|
+
# a root node if you want something other than
|
114
|
+
# the entire yaml document to be utilized. For
|
115
|
+
# example, in Rails, you might provide the
|
116
|
+
# Rails environment as a root node.
|
117
|
+
def load(yaml_string, root_node = nil)
|
118
|
+
hash = YAML::load(yaml_string)
|
119
|
+
set(root_node ? hash[root_node] : hash)
|
120
|
+
end
|
121
|
+
|
122
|
+
# Load configuration from a YAML file specified
|
123
|
+
# by the path given (or a file pointer). Provide
|
124
|
+
# a root node if you want something other than
|
125
|
+
# the entire yaml document to be utilized. For
|
126
|
+
# example, in Rails, you might provide the
|
127
|
+
# Rails environment as a root node.
|
128
|
+
def load_from(file_or_path, root_node = 'configuration')
|
129
|
+
hash = YAML::load_file(file_or_path)
|
130
|
+
set(root_node ? hash[root_node] : hash)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ConfigNewton::Configuration do
|
4
|
+
subject { ConfigNewton::Configuration.new }
|
5
|
+
|
6
|
+
describe '#add' do
|
7
|
+
it 'should be able to add properties' do
|
8
|
+
subject.add(:abc)
|
9
|
+
subject.abc.should be_nil
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should be able to have a default' do
|
13
|
+
subject.add(:abc, :default => 123)
|
14
|
+
subject.abc.should == 123
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should raise an ArgumentError if no name is passed' do
|
18
|
+
lambda{subject.add}.should raise_error(ArgumentError)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#to_hash' do
|
23
|
+
it 'should be able to convert to a hash' do
|
24
|
+
subject.add(:foo, :default => 123)
|
25
|
+
subject.add(:bar)
|
26
|
+
subject.add(:baz, :default => 890)
|
27
|
+
subject.bar = 456
|
28
|
+
subject.baz = 789
|
29
|
+
subject.to_hash.should == {
|
30
|
+
:foo => 123,
|
31
|
+
:bar => 456,
|
32
|
+
:baz => 789
|
33
|
+
}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should be able to read and write to properties' do
|
38
|
+
subject.add(:abc)
|
39
|
+
subject.abc = 123
|
40
|
+
subject.abc.should == 123
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
describe '#[]' do
|
45
|
+
before do
|
46
|
+
subject.add(:abc)
|
47
|
+
subject.abc = 123
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should retrieve set values' do
|
51
|
+
subject[:abc].should == 123
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should convert to symbol' do
|
55
|
+
subject['abc'].should == 123
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '#[]=' do
|
60
|
+
before do
|
61
|
+
subject.add(:abc)
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'should set the value' do
|
65
|
+
subject[:abc] = 123
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'should convert to symbol' do
|
69
|
+
subject['abc'] = 123
|
70
|
+
end
|
71
|
+
|
72
|
+
after do
|
73
|
+
subject[:abc].should == 123
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe '#load' do
|
78
|
+
let(:yaml) {
|
79
|
+
<<-YAML
|
80
|
+
development:
|
81
|
+
email: bob@example.com
|
82
|
+
special_sauce: false
|
83
|
+
|
84
|
+
production:
|
85
|
+
email: frank@example.com
|
86
|
+
special_sauce: false
|
87
|
+
YAML
|
88
|
+
}
|
89
|
+
|
90
|
+
before do
|
91
|
+
subject.add(:email)
|
92
|
+
subject.add(:special_sauce, :default => true)
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'should be able to load from YAML' do
|
96
|
+
subject.load(yaml, 'development').email.should == 'bob@example.com'
|
97
|
+
subject.load(yaml, 'production').email.should == 'frank@example.com'
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class SampleClass
|
4
|
+
include ConfigNewton
|
5
|
+
end
|
6
|
+
|
7
|
+
describe ConfigNewton do
|
8
|
+
describe '.configure' do
|
9
|
+
before do
|
10
|
+
SampleClass.send(:configurable) do
|
11
|
+
property :foo, :default => 123
|
12
|
+
property :bar
|
13
|
+
property :baz, :default => 890
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should define the configure method on the class' do
|
18
|
+
SampleClass.should be_respond_to(:configure)
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'setting values' do
|
22
|
+
before do
|
23
|
+
SampleClass.config[:foo] = nil
|
24
|
+
SampleClass.config[:bar] = nil
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should accept a hash' do
|
28
|
+
SampleClass.configure(:foo => 330, :bar => 555)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should accept a block with an argument' do
|
32
|
+
SampleClass.configure do |config|
|
33
|
+
config.foo = 330
|
34
|
+
config.bar = 555
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
after do
|
39
|
+
SampleClass.config.to_hash.should == {
|
40
|
+
:foo => 330,
|
41
|
+
:bar => 555,
|
42
|
+
:baz => 890
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should accept a block with an argument' do
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '.configurable' do
|
53
|
+
it 'should allow for configurables to be created' do
|
54
|
+
SampleClass.send(:configurable, :abc)
|
55
|
+
SampleClass.config.should be_respond_to(:abc)
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should allow for block configurable creation' do
|
59
|
+
SampleClass.send(:configurable) do
|
60
|
+
property :abc, :default => 123
|
61
|
+
property :def
|
62
|
+
end
|
63
|
+
|
64
|
+
SampleClass.config.should be_respond_to(:def)
|
65
|
+
SampleClass.config.abc.should == 123
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'should be protected' do
|
69
|
+
lambda{SampleClass.configurable(:abc)}.should raise_error(NoMethodError)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: config_newton
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Michael Bleigh
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-07-23 00:00:00 -05:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rspec
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 1
|
29
|
+
- 2
|
30
|
+
- 9
|
31
|
+
version: 1.2.9
|
32
|
+
type: :development
|
33
|
+
version_requirements: *id001
|
34
|
+
description: Library authors can now easily add class-level configuration including YAML loading with just a few lines of code.
|
35
|
+
email: michael@intridea.com
|
36
|
+
executables: []
|
37
|
+
|
38
|
+
extensions: []
|
39
|
+
|
40
|
+
extra_rdoc_files:
|
41
|
+
- LICENSE
|
42
|
+
- README.rdoc
|
43
|
+
files:
|
44
|
+
- .document
|
45
|
+
- .gitignore
|
46
|
+
- LICENSE
|
47
|
+
- README.rdoc
|
48
|
+
- Rakefile
|
49
|
+
- VERSION
|
50
|
+
- lib/config_newton.rb
|
51
|
+
- spec/config_newton/configuration_spec.rb
|
52
|
+
- spec/config_newton_spec.rb
|
53
|
+
- spec/spec.opts
|
54
|
+
- spec/spec_helper.rb
|
55
|
+
has_rdoc: true
|
56
|
+
homepage: http://github.com/intridea/config_newton
|
57
|
+
licenses: []
|
58
|
+
|
59
|
+
post_install_message:
|
60
|
+
rdoc_options:
|
61
|
+
- --charset=UTF-8
|
62
|
+
require_paths:
|
63
|
+
- lib
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
segments:
|
69
|
+
- 0
|
70
|
+
version: "0"
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
segments:
|
76
|
+
- 0
|
77
|
+
version: "0"
|
78
|
+
requirements: []
|
79
|
+
|
80
|
+
rubyforge_project:
|
81
|
+
rubygems_version: 1.3.6
|
82
|
+
signing_key:
|
83
|
+
specification_version: 3
|
84
|
+
summary: A simple tool to add class-level configuration to libraries.
|
85
|
+
test_files:
|
86
|
+
- spec/config_newton/configuration_spec.rb
|
87
|
+
- spec/config_newton_spec.rb
|
88
|
+
- spec/spec_helper.rb
|