configure 0.1.0 → 0.2.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/README.rdoc CHANGED
@@ -10,37 +10,162 @@ The most simple way <tt>configure</tt> can be used is to pass a block to the <tt
10
10
  receive back a hash with the configuration values.
11
11
 
12
12
  laser = Configure.process do
13
- title "red laser"
13
+ label "red laser"
14
14
  wave_length 700
15
15
  end
16
16
 
17
17
  laser == {
18
- :title => "red laser",
18
+ :label => "red laser",
19
19
  :wave_length => 700
20
20
  } # => true
21
21
 
22
+ === Nested configurations
23
+
22
24
  It is also possible to pass blocks to configuration keys and combine multiple values to an array.
23
25
 
24
26
  laser = Configure.process do
25
- title "red and violet pulse laser"
26
- pulses do
27
+ label "red and violet pulse laser"
28
+ pulse do
27
29
  wave_length 700
28
30
  duration 20
29
31
  end
30
- pulses do
32
+ pulse do
31
33
  wave_length 400
32
34
  duration 20
33
35
  end
34
36
  end
35
37
 
36
38
  laser == {
37
- :title => "red and violet pulse laser",
38
- :pulses => [
39
+ :label => "red and violet pulse laser",
40
+ :pulse => [
39
41
  { :wave_length => 700, :duration => 20 },
40
42
  { :wave_length => 400, :duration => 20 }
41
43
  ]
42
44
  } # => true
43
45
 
46
+ If arguments and a block is passed to a configuration key, the arguments are stored at the <tt>:arguments</tt> key
47
+ inside the nested configuration.
48
+
49
+ laser = Configure.process do
50
+ label "red pulse laser"
51
+ pulse "test" do
52
+ wave_length 700
53
+ duration 20
54
+ end
55
+ end
56
+
57
+ laser = {
58
+ :label => "red pulse laser",
59
+ :pulse => {
60
+ :wave_length => 700,
61
+ :duration => 20,
62
+ :arguments => [ "test" ]
63
+ }
64
+
65
+ } # => true
66
+
67
+ == Schema
68
+
69
+ In order receive a more structured configuration, a schema can be passed to the <tt>Configure.process</tt> method. This
70
+ schema is a simple hash, that can be created - off course - using the configure syntax ;-)
71
+
72
+ === Defaults
73
+
74
+ schema = Configure::Schema.build do
75
+ defaults do
76
+ wave_length 700
77
+ duration 20
78
+ end
79
+ end
80
+
81
+ laser = Configure.process schema do
82
+ label "violet pulse laser"
83
+ wave_length 400
84
+ end
85
+
86
+ laser = {
87
+ :label => "violet pulse laser",
88
+ :wave_length => 400,
89
+ :duration => 20
90
+ } # => true
91
+
92
+ === Nested schemas
93
+
94
+ Nested schema have to go to the <tt>nested</tt> section.
95
+
96
+ schema = Configure::Schema.build do
97
+ nested do
98
+ pulse do
99
+ defaults do
100
+ wave_length 700
101
+ duration 20
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ If a nested schema provides the key <tt>argument_keys</tt>, the arguments that have been passed along with the nested
108
+ configuration, are stored to these keys. The rest will be assigned to the <tt>:arguments</tt> key as before.
109
+
110
+ schema = Configure::Schema.build do
111
+ nested do
112
+ pulse do
113
+ argument_keys :wave_length, :duration
114
+ end
115
+ end
116
+ end
117
+
118
+ laser = Configure.process schema do
119
+ pulse 700, 20, :extra do
120
+ label "red pulse"
121
+ end
122
+ end
123
+
124
+ laser == {
125
+ :pulse => {
126
+ :wave_length => 700,
127
+ :duration => 20,
128
+ :arguments => [ :extra ]
129
+ :label => "red pulse"
130
+ }
131
+ } # => true
132
+
133
+ === Configuration class
134
+
135
+ Sometimes, a hash may not be the best data structure to store the configuration. If a more strict structure is needed,
136
+ the attribute <tt>:configuration_class</tt> can be set the schema. When the configuration is processed, the class is
137
+ instantiated and the values are set via <tt>key=</tt> or the <tt>[]=</tt> methods. If both is not possible, an exception
138
+ is raised.
139
+
140
+ class Laser
141
+
142
+ attr_accessor :label
143
+ attr_accessor :wave_length
144
+ attr_accessor :duration
145
+
146
+ end
147
+
148
+ schema = Configure::Schema.build do
149
+ configuration_class Laser
150
+ defaults do
151
+ duration 20
152
+ end
153
+ end
154
+
155
+ laser = Configure.process schema do
156
+ label "red laser"
157
+ wave_length 700
158
+ end
159
+
160
+ laser.is_a? Laser # => true
161
+ laser.label == "red laser" # => true
162
+ laser.wave_length == 700 # => true
163
+ laser.duration == 20 # => true
164
+
165
+ Configure.process schema do
166
+ invalid_key "value"
167
+ end # => raises Configure::Injector::Error
168
+
44
169
  == Development
45
170
 
46
171
  Development has been done test-driven and the code follows at most the Clean Code paradigms. Code smells has been
@@ -2,21 +2,78 @@
2
2
  # Creates a configuration from the passed class and provides methods to inject values.
3
3
  class Configure::Injector
4
4
 
5
- attr_reader :configuration
5
+ # The error will be raised if the configuration value can't be set or get.
6
+ class Error < StandardError; end
6
7
 
7
- def initialize(configuration_class)
8
- @configuration_class = configuration_class
9
- @configuration = @configuration_class.new
8
+ attr_reader :schema
9
+
10
+ def initialize(schema)
11
+ @schema = schema
12
+ @configuration = self.configuration_class.new
13
+ end
14
+
15
+ def configuration_class
16
+ @schema[:configuration_class] || Hash
17
+ end
18
+
19
+ def defaults
20
+ @schema[:defaults] || { }
10
21
  end
11
22
 
12
- def put_block(key, &block)
23
+ def put_block(key, arguments, &block)
24
+ nested_schema = (self.schema[:nested] || { })[key] || { }
25
+ nested_configuration = Configure.process_configuration nested_schema, &block
26
+ Arguments.new(nested_schema, nested_configuration, arguments).put
13
27
  value = Value.new @configuration, key
14
- value.put Configure.process_configuration(@configuration_class, &block)
28
+ value.put_or_combine nested_configuration
15
29
  end
16
30
 
17
31
  def put_arguments(key, arguments)
18
32
  value = Value.new @configuration, key
19
- value.put arguments.size == 1 ? arguments.first : arguments
33
+ value.put_single_or_multiple arguments
34
+ end
35
+
36
+ def configuration
37
+ apply_defaults
38
+ @configuration
39
+ end
40
+
41
+ private
42
+
43
+ def apply_defaults
44
+ self.defaults.each do |key, default_value|
45
+ value = Value.new @configuration, key
46
+ value.put_if_nil default_value
47
+ end
48
+ end
49
+
50
+ # Injector for the arguments in a nested configuration.
51
+ class Arguments
52
+
53
+ def initialize(schema, configuration, arguments)
54
+ @schema, @configuration, @arguments = schema, configuration, arguments.dup
55
+ end
56
+
57
+ def put
58
+ put_to_specified_keys
59
+ put_to_argument_key
60
+ end
61
+
62
+ def put_to_specified_keys
63
+ return if @arguments.empty?
64
+ argument_keys = [ @schema[:argument_keys] ].flatten.compact
65
+ argument_keys.each do |argument_key|
66
+ arguments_value = Value.new @configuration, argument_key
67
+ arguments_value.put @arguments.shift
68
+ end
69
+ end
70
+
71
+ def put_to_argument_key
72
+ return if @arguments.empty?
73
+ arguments_value = Value.new @configuration, :arguments
74
+ arguments_value.put @arguments
75
+ end
76
+
20
77
  end
21
78
 
22
79
  # Injector for a single configuration value.
@@ -26,16 +83,42 @@ class Configure::Injector
26
83
  @configuration, @key = configuration, key
27
84
  end
28
85
 
29
- def get
30
- @configuration[@key]
86
+ def put_single_or_multiple(values)
87
+ self.put_or_combine values.size == 1 ? values.first : values
88
+ end
89
+
90
+ def put_or_combine(value)
91
+ self.put exists? ? [ get, value ].flatten : value
92
+ end
93
+
94
+ def put_if_nil(value)
95
+ self.put value if get.nil?
31
96
  end
32
97
 
33
98
  def put(value)
34
- @configuration[@key] = exists? ? [ get, value ].flatten : value
99
+ method_name = :"#{@key}="
100
+ if @configuration.respond_to?(method_name)
101
+ @configuration.send method_name, value
102
+ elsif @configuration.respond_to?(:[]=)
103
+ @configuration[@key] = value
104
+ else
105
+ raise Error, "couldn't set configuration value for key #{@key}!"
106
+ end
107
+ end
108
+
109
+ def get
110
+ method_name = :"#{@key}"
111
+ if @configuration.respond_to?(method_name)
112
+ @configuration.send method_name
113
+ elsif @configuration.respond_to?(:[])
114
+ @configuration[@key]
115
+ else
116
+ raise Error, "couldn't get configuration value for key #{@key}!"
117
+ end
35
118
  end
36
119
 
37
120
  def exists?
38
- @configuration.has_key? @key
121
+ !!self.get
39
122
  end
40
123
 
41
124
  end
@@ -8,7 +8,7 @@ class Configure::Sandbox
8
8
 
9
9
  def method_missing(method_name, *arguments, &block)
10
10
  if block_given?
11
- @injector.put_block method_name, &block
11
+ @injector.put_block method_name, arguments, &block
12
12
  else
13
13
  @injector.put_arguments method_name, arguments
14
14
  end
@@ -0,0 +1,12 @@
1
+
2
+ module Configure::Schema
3
+
4
+ SCHEMA = {
5
+
6
+ }.freeze
7
+
8
+ def self.build(&block)
9
+ Configure.process SCHEMA, &block
10
+ end
11
+
12
+ end
data/lib/configure.rb CHANGED
@@ -3,13 +3,14 @@ module Configure
3
3
 
4
4
  autoload :Injector, File.join(File.dirname(__FILE__), "configure", "injector")
5
5
  autoload :Sandbox, File.join(File.dirname(__FILE__), "configure", "sandbox")
6
+ autoload :Schema, File.join(File.dirname(__FILE__), "configure", "schema")
6
7
 
7
- def self.process(&block)
8
- process_configuration Hash, &block
8
+ def self.process(schema = { }, &block)
9
+ process_configuration schema, &block
9
10
  end
10
11
 
11
- def self.process_configuration(configuration_class, &block)
12
- injector = Injector.new configuration_class
12
+ def self.process_configuration(schema = { }, &block)
13
+ injector = Injector.new schema
13
14
  sandbox = Sandbox.new injector
14
15
  sandbox.instance_eval &block
15
16
  injector.configuration
@@ -0,0 +1,96 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
2
+
3
+ describe Configure do
4
+
5
+ describe "process with schema" do
6
+
7
+ before :each do
8
+ @configuration_class = Class.new
9
+ @configuration_class.send :attr_accessor, :test_key_one
10
+ @configuration_class.send :attr_accessor, :test_key_two
11
+
12
+ configuration_class = @configuration_class
13
+ @schema = described_class::Schema.build do
14
+ configuration_class configuration_class
15
+ defaults do
16
+ test_key_one "default value"
17
+ end
18
+ nested do
19
+ test_key_two do
20
+ argument_keys :test_key_three, :test_key_four
21
+ defaults do
22
+ test_key_five "default value"
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ it "should use the specified configuration class to create a configuration instance" do
30
+ configuration = described_class.process @schema do
31
+
32
+ end
33
+
34
+ configuration.should be_instance_of(@configuration_class)
35
+ end
36
+
37
+ it "should inject the given values" do
38
+ configuration = described_class.process @schema do
39
+ test_key_one "one"
40
+ end
41
+
42
+ configuration.test_key_one.should == "one"
43
+ end
44
+
45
+ it "should use the specified default values" do
46
+ configuration = described_class.process @schema do
47
+
48
+ end
49
+
50
+ configuration.test_key_one.should == "default value"
51
+ end
52
+
53
+ it "should evaluate the default values in nested schemas" do
54
+ configuration = described_class.process @schema do
55
+ test_key_two do
56
+
57
+ end
58
+ end
59
+
60
+ configuration.test_key_two.should == {
61
+ :test_key_five => "default value"
62
+ }
63
+ end
64
+
65
+ it "should transfer the arguments to a nested configuration to the specified argument_keys" do
66
+ configuration = described_class.process @schema do
67
+ test_key_two "three", "four" do
68
+ test_key_five "five"
69
+ end
70
+ end
71
+
72
+ configuration.test_key_two.should == {
73
+ :test_key_three => "three",
74
+ :test_key_four => "four",
75
+ :test_key_five => "five"
76
+ }
77
+ end
78
+
79
+ it "should transfer the argument-rest to a nested :arguments key" do
80
+ configuration = described_class.process @schema do
81
+ test_key_two "three", "four", "extra" do
82
+ test_key_five "five"
83
+ end
84
+ end
85
+
86
+ configuration.test_key_two.should == {
87
+ :test_key_three => "three",
88
+ :test_key_four => "four",
89
+ :test_key_five => "five",
90
+ :arguments => [ "extra" ]
91
+ }
92
+ end
93
+
94
+ end
95
+
96
+ end
@@ -2,7 +2,7 @@ require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
2
2
 
3
3
  describe Configure do
4
4
 
5
- describe "process" do
5
+ describe "process without schema" do
6
6
 
7
7
  it "should turn method call into hash values" do
8
8
  configuration = described_class.process do
@@ -65,6 +65,18 @@ describe Configure do
65
65
  }
66
66
  end
67
67
 
68
+ it "should set the :arguments key in nested configurations with the passed arguments" do
69
+ configuration = described_class.process do
70
+ test_key "one", "two" do
71
+ nested_test_key "three"
72
+ end
73
+ end
74
+
75
+ configuration.should == {
76
+ :test_key => { :arguments => [ "one", "two" ], :nested_test_key => "three" }
77
+ }
78
+ end
79
+
68
80
  end
69
81
 
70
82
  end
@@ -3,13 +3,33 @@ require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_hel
3
3
  describe Configure::Injector do
4
4
 
5
5
  before :each do
6
- @injector = described_class.new Hash
6
+ @configuration_class = Class.new
7
+ @configuration_class.send :attr_accessor, :test_key
8
+ @configuration_class.send :attr_accessor, :another_test_key
9
+
10
+ @nested_schema = { }
11
+ @schema = {
12
+ :configuration_class => @configuration_class,
13
+ :nested => {
14
+ :test_key => @nested_schema
15
+ },
16
+ :defaults => {
17
+ :another_test_key => "default value"
18
+ }
19
+ }
20
+ @injector = described_class.new @schema
7
21
  end
8
22
 
9
23
  describe "initialize" do
10
24
 
11
25
  it "should create a configuration" do
12
- @injector.configuration.should be_instance_of(Hash)
26
+ configuration = @injector.configuration
27
+ configuration.should be_instance_of(@schema[:configuration_class])
28
+ end
29
+
30
+ it "should set the default values" do
31
+ configuration = @injector.configuration
32
+ configuration.another_test_key.should == "default value"
13
33
  end
14
34
 
15
35
  end
@@ -18,23 +38,41 @@ describe Configure::Injector do
18
38
 
19
39
  before :each do
20
40
  @block = Proc.new { }
21
- Configure.stub(:process_configuration => :nested_configuration)
41
+ @arguments = [ "one" ]
42
+ @nested_configuration = { }
43
+ Configure.stub(:process_configuration => @nested_configuration)
22
44
  end
23
45
 
24
- it "should process a newly created configuration" do
25
- Configure.should_receive(:process_configuration).with(Hash, &@block)
26
- @injector.put_block :test_key, &@block
46
+ it "should process a new (nested) configuration" do
47
+ Configure.should_receive(:process_configuration).with(@nested_schema, &@block).and_return(@nested_configuration)
48
+ @injector.put_block :test_key, @arguments, &@block
27
49
  end
28
50
 
29
51
  it "should nest the configuration" do
30
- @injector.put_block :test_key, &@block
31
- @injector.configuration[:test_key].should == :nested_configuration
52
+ @injector.put_block :test_key, @arguments, &@block
53
+ @injector.configuration.test_key.should == @nested_configuration
54
+ end
55
+
56
+ it "should set the :arguments key in the nested configuration" do
57
+ @injector.put_block :test_key, @arguments, &@block
58
+ @injector.configuration.test_key[:arguments].should == @arguments
59
+ end
60
+
61
+ it "should not set the :arguments key in the nested configuration if no arguments are given" do
62
+ @injector.put_block :test_key, [ ], &@block
63
+ @injector.configuration.test_key.should_not have_key(:arguments)
64
+ end
65
+
66
+ it "should set the :argument_keys with the argument values if specified" do
67
+ @injector.schema[:nested][:test_key][:argument_keys] = :another_test_key
68
+ @injector.put_block :test_key, @arguments, &@block
69
+ @injector.configuration.test_key[:another_test_key].should == "one"
32
70
  end
33
71
 
34
72
  it "should combine nested configurations to an array" do
35
- @injector.put_block :test_key, &@block
36
- @injector.put_block :test_key, &@block
37
- @injector.configuration[:test_key].should == [ :nested_configuration, :nested_configuration ]
73
+ @injector.put_block :test_key, @arguments, &@block
74
+ @injector.put_block :test_key, @arguments, &@block
75
+ @injector.configuration.test_key.should == [ @nested_configuration, @nested_configuration ]
38
76
  end
39
77
 
40
78
  end
@@ -43,18 +81,42 @@ describe Configure::Injector do
43
81
 
44
82
  it "should assign a value" do
45
83
  @injector.put_arguments :test_key, [ "one" ]
46
- @injector.configuration[:test_key].should == "one"
84
+ @injector.configuration.test_key.should == "one"
85
+ end
86
+
87
+ it "should override a value" do
88
+ @injector.put_arguments :another_test_key, [ "one" ]
89
+ @injector.configuration.another_test_key.should == "one"
47
90
  end
48
91
 
49
92
  it "should assign an array of values" do
50
93
  @injector.put_arguments :test_key, [ "one", "two" ]
51
- @injector.configuration[:test_key].should == [ "one", "two" ]
94
+ @injector.configuration.test_key.should == [ "one", "two" ]
52
95
  end
53
96
 
54
97
  it "should combine existing with new values" do
55
98
  @injector.put_arguments :test_key, [ "one" ]
56
99
  @injector.put_arguments :test_key, [ "two" ]
57
- @injector.configuration[:test_key].should == [ "one", "two" ]
100
+ @injector.configuration.test_key.should == [ "one", "two" ]
101
+ end
102
+
103
+ it "should raise a #{described_class::Error} if key can't be set" do
104
+ lambda do
105
+ @injector.put_arguments :invalid_key, [ "one" ]
106
+ end.should raise_error(described_class::Error)
107
+ end
108
+
109
+ end
110
+
111
+ describe "configuration" do
112
+
113
+ before :each do
114
+ @injector.put_arguments :test_key, [ "one" ]
115
+ end
116
+
117
+ it "should return an instance of :configuration_class" do
118
+ configuration = @injector.configuration
119
+ configuration.should be_instance_of(@schema[:configuration_class])
58
120
  end
59
121
 
60
122
  end
@@ -14,8 +14,8 @@ describe Configure::Sandbox do
14
14
 
15
15
  it "should pass method calls with a block to the injector's :put_block" do
16
16
  block = Proc.new { }
17
- @injector.should_receive(:put_block).with(:test_key, &block)
18
- @sandbox.test_key &block
17
+ @injector.should_receive(:put_block).with(:test_key, [ "one" ], &block)
18
+ @sandbox.test_key "one", &block
19
19
  end
20
20
 
21
21
  end
@@ -0,0 +1,19 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper"))
2
+
3
+ describe Configure::Schema do
4
+
5
+ describe "#self.build" do
6
+
7
+ before :each do
8
+ @block = Proc.new { }
9
+ Configure.stub(:process)
10
+ end
11
+
12
+ it "should use configure to build the schema hash" do
13
+ Configure.should_receive(:process).with(described_class::SCHEMA, &@block)
14
+ described_class.build &@block
15
+ end
16
+
17
+ end
18
+
19
+ end
@@ -13,8 +13,8 @@ describe Configure do
13
13
  end
14
14
 
15
15
  it "should call :process_configuration" do
16
- described_class.should_receive(:process_configuration).with(Hash, &@block)
17
- described_class.process &@block
16
+ described_class.should_receive(:process_configuration).with(:schema, &@block)
17
+ described_class.process :schema, &@block
18
18
  end
19
19
 
20
20
  end
@@ -30,22 +30,22 @@ describe Configure do
30
30
  end
31
31
 
32
32
  it "should initialize the injector" do
33
- described_class::Injector.should_receive(:new).with(Hash).and_return(@injector)
34
- described_class.process_configuration Hash, &@block
33
+ described_class::Injector.should_receive(:new).with(:schema).and_return(@injector)
34
+ described_class.process_configuration :schema, &@block
35
35
  end
36
36
 
37
37
  it "should initialize the sandbox" do
38
38
  described_class::Sandbox.should_receive(:new).with(@injector).and_return(@sandbox)
39
- described_class.process_configuration Hash, &@block
39
+ described_class.process_configuration :schema, &@block
40
40
  end
41
41
 
42
42
  it "should evaluate the block in the sandbox" do
43
43
  @sandbox.should_receive(:instance_eval).with(&@block)
44
- described_class.process_configuration Hash, &@block
44
+ described_class.process_configuration :schema, &@block
45
45
  end
46
46
 
47
47
  it "should return the configuration" do
48
- configuration = described_class.process_configuration Hash, &@block
48
+ configuration = described_class.process_configuration :schema, &@block
49
49
  configuration.should == :configuration
50
50
  end
51
51
 
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: configure
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.1.0
5
+ version: 0.2.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - "Philipp Br\xC3\xBCll"
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-03-14 00:00:00 +01:00
13
+ date: 2011-03-15 00:00:00 +01:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -50,11 +50,14 @@ files:
50
50
  - lib/configure.rb
51
51
  - lib/configure/injector.rb
52
52
  - lib/configure/sandbox.rb
53
+ - lib/configure/schema.rb
53
54
  - spec/lib/configure_spec.rb
54
55
  - spec/lib/configure/injector_spec.rb
56
+ - spec/lib/configure/schema_spec.rb
55
57
  - spec/lib/configure/sandbox_spec.rb
56
58
  - spec/spec_helper.rb
57
- - spec/acceptance/configure_spec.rb
59
+ - spec/acceptance/configure_without_schema_spec.rb
60
+ - spec/acceptance/configure_with_schema_spec.rb
58
61
  has_rdoc: true
59
62
  homepage: http://github.com/phifty/configure
60
63
  licenses: []
@@ -86,5 +89,7 @@ summary: Configure offers an easy way for configure your application using a DSL
86
89
  test_files:
87
90
  - spec/lib/configure_spec.rb
88
91
  - spec/lib/configure/injector_spec.rb
92
+ - spec/lib/configure/schema_spec.rb
89
93
  - spec/lib/configure/sandbox_spec.rb
90
- - spec/acceptance/configure_spec.rb
94
+ - spec/acceptance/configure_without_schema_spec.rb
95
+ - spec/acceptance/configure_with_schema_spec.rb