configure 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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