configurations 1.1.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -3,3 +3,4 @@
3
3
  Gemfile.lock
4
4
  doc
5
5
  coverage
6
+ *.gem
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.0.0-p481
1
+ 1.9.3-p547
data/README.md CHANGED
@@ -112,13 +112,81 @@ MyGem.configure do |c|
112
112
  end
113
113
  ```
114
114
 
115
+ ### Fourth way: Custom asserted or changed values
116
+
117
+ If you need further assertions or you need to change a value before it gets stored in the configuration, consider passing a block
118
+
119
+ ```
120
+ module MyGem
121
+ include Configurations
122
+ configurable :foo do |value|
123
+
124
+ # The return value is what gets assigned, unless it is nil,
125
+ # in which case the original value persists
126
+ #
127
+ value + ' ooooh my'
128
+ end
129
+ configurable String, bar: :baz do |value|
130
+
131
+ # value is guaranteed to be a string at this point
132
+ #
133
+ unless %w(bi ba bu).include?(value)
134
+ raise ArgumentError, 'baz needs to be one of bi, ba, bu'
135
+ end
136
+ end
137
+ end
138
+ ```
139
+
140
+ Gives your users:
141
+
142
+ ```
143
+ MyGem.configure do |c|
144
+ c.foo = 'FOO'
145
+ c.bar.baz = %w(bi)
146
+
147
+ # This would raise the ArgumentError in the block
148
+ # c.bar.baz = %w(boooh)
149
+ end
150
+ ```
151
+
115
152
  Gives you:
116
153
 
117
154
  ```
118
- MyGem.configuration.foo #=> 100% String
119
- MyGem.configuration.bar.baz #=> 100% Array
155
+ MyGem.configuration.foo #=> 'FOO ooooh my'
156
+ MyGem.configuration.bar.baz #=> one of %w(bi ba bu)
120
157
  ```
121
158
 
159
+ ### Configuration Methods
160
+
161
+ You might want to define methods on your configuration which use configuration values to bring out another value.
162
+ This is what `configuration_method` is here to help you with:
163
+
164
+ ```
165
+ module MyGem
166
+ include Configurations
167
+ configurable :foo, :bar
168
+ configuration_method :foobar do |arg|
169
+ foo + bar + arg
170
+ end
171
+ end
172
+ ```
173
+
174
+ Your users do:
175
+
176
+ ```
177
+ MyGem.configure do |c|
178
+ c.foo = 'FOO'
179
+ c.bar = 'BAR'
180
+ end
181
+ ```
182
+
183
+ You get:
184
+
185
+ ```
186
+ MyGem.configuration.foobar('ARG') #=> 'FOOBARARG'
187
+ ```
188
+
189
+
122
190
  ### Defaults:
123
191
 
124
192
  ```
@@ -134,7 +202,6 @@ end
134
202
 
135
203
  ```
136
204
  MyGem.configuration.to_h #=> a Hash
137
-
138
205
  ```
139
206
 
140
207
  ### Some caveats
@@ -12,5 +12,5 @@ module Configurations
12
12
 
13
13
  # Version number of Configurations
14
14
  #
15
- VERSION = '1.1.0'
15
+ VERSION = '1.3.0'
16
16
  end
@@ -49,11 +49,31 @@ module Configurations
49
49
  # configurable can be used to set the properties which should be configurable, as well as a type which
50
50
  # the given property should be asserted to
51
51
  # @param [Class, Symbol, Hash] properties a type as a first argument to type assert (if any) or nested properties to allow for setting
52
+ # @param [Proc] block a block with arity 2 to evaluate when a property is set. It will be given: property name and value
52
53
  #
53
- def configurable(*properties)
54
+ def configurable(*properties, &block)
54
55
  type = properties.shift if properties.first.is_a?(Class)
55
56
  @configurable ||= {}
56
- @configurable.merge!(to_configurable_hash(properties, type))
57
+ @configurable.merge!(to_configurable_hash(properties, type, &block))
58
+ end
59
+
60
+ # returns whether a property is set to be configurable
61
+ # @param [Symbol] property the property to ask status for
62
+ #
63
+ def configurable?(property)
64
+ @configurable.is_a?(Hash) && @configurable.has_key?(property)
65
+ end
66
+
67
+ # configuration method can be used to retrieve properties from the configuration which use your gem's context
68
+ # @param [Class, Symbol, Hash] properties properties for retrieval
69
+ # @param [Proc] block the block to evaluate
70
+ #
71
+ def configuration_method(method, &block)
72
+ raise ArgumentError, "#{method} can not be both a configurable property and a configuration method" if configurable?(method)
73
+
74
+ Configuration.class_eval do
75
+ define_method method, &block
76
+ end
57
77
  end
58
78
 
59
79
  private
@@ -63,8 +83,13 @@ module Configurations
63
83
  # @param [Class] type the type to assert, if any
64
84
  # @return a hash with configurable values pointing to their types
65
85
  #
66
- def to_configurable_hash(properties, type)
67
- Hash[properties.zip(Array(type) * properties.size)]
86
+ def to_configurable_hash(properties, type, &block)
87
+ assertion_hash = {}
88
+ assertion_hash.merge! block: block if block_given?
89
+ assertion_hash.merge! type: type if type
90
+
91
+ assertions = ([assertion_hash] * properties.size)
92
+ Hash[properties.zip(assertions)]
68
93
  end
69
94
  end
70
95
  end
@@ -59,21 +59,21 @@ module Configurations
59
59
  #
60
60
  def method_missing(method, *args, &block)
61
61
  property = method.to_s[0..-2].to_sym
62
+ value = args.first
62
63
 
63
64
  if _is_writer?(method) && @_writeable && _configurable?(property)
64
- _assert_type!(property, args.first)
65
- @configuration[property] = args.first
65
+ _assign!(property, value)
66
66
  elsif !_is_writer?(method) && @_writeable || _configured?(method)
67
67
  @configuration[method]
68
68
  else
69
- super
69
+ ::Kernel.send(method, *args, &block)
70
70
  end
71
71
  end
72
72
 
73
73
  # Respond to missing according to the method_missing implementation
74
74
  #
75
75
  def respond_to_missing?(method, include_private = false)
76
- is_setter?(method) || @_writeable || _configured?(method) || super
76
+ is_setter?(method) || @_writeable || _configured?(method) || ::Kernel.respond_to_missing?(method, include_private)
77
77
  end
78
78
 
79
79
  # Set the configuration to writeable or read only. Access to writer methods is only allowed within the
@@ -98,8 +98,6 @@ module Configurations
98
98
  end
99
99
  end
100
100
 
101
- private
102
-
103
101
  # @param [Symbol] property The property to test for configurability
104
102
  # @return [Boolean] whether the given property is configurable
105
103
  #
@@ -107,6 +105,15 @@ module Configurations
107
105
  _arbitrarily_configurable? or @configurable.has_key?(property)
108
106
  end
109
107
 
108
+
109
+ # @return [Boolean] whether this configuration is arbitrarily configurable
110
+ #
111
+ def _arbitrarily_configurable?
112
+ @configurable.nil? or @configurable.empty?
113
+ end
114
+
115
+ private
116
+
110
117
  # @param [Symbol] property The property to test for
111
118
  # @return [Boolean] whether the given property has been configured
112
119
  #
@@ -127,10 +134,10 @@ module Configurations
127
134
  def _evaluate_configurable!
128
135
  return if _arbitrarily_configurable?
129
136
 
130
- @configurable.each do |k, type|
137
+ @configurable.each do |k, assertion|
131
138
  if k.is_a?(::Hash)
132
139
  k.each do |property, nested|
133
- @configuration[property] = Configuration.new(nil, _to_configurable_hash(nested, type))
140
+ @configuration[property] = Configuration.new(nil, _to_configurable_hash(nested, assertion))
134
141
  end
135
142
  end
136
143
  end
@@ -140,9 +147,20 @@ module Configurations
140
147
  # @param [Class] type the type to assert, if any
141
148
  # @return a hash with configurable values pointing to their types
142
149
  #
143
- def _to_configurable_hash(value, type)
150
+ def _to_configurable_hash(value, assertion)
144
151
  value = [value] unless value.is_a?(::Array)
145
- ::Hash[value.zip([type].flatten*value.size)]
152
+ ::Hash[value.zip([assertion].flatten*value.size)]
153
+ end
154
+
155
+ # Assigns a value after running the assertions
156
+ # @param [Symbol] property the property to type test
157
+ # @param [Any] value the given value
158
+ #
159
+ def _assign!(property, value)
160
+ v = _evaluate_block!(property, value)
161
+ value = v unless v.nil?
162
+ _assert_type!(property, value)
163
+ @configuration[property] = value
146
164
  end
147
165
 
148
166
  # Type assertion for configurable properties
@@ -151,18 +169,29 @@ module Configurations
151
169
  # @raise [ConfigurationError] if the given value has the wrong type
152
170
  #
153
171
  def _assert_type!(property, value)
154
- return if _arbitrarily_configurable?
172
+ return unless _evaluable?(property, :type)
155
173
 
156
- expected_type = @configurable[property]
157
- return if expected_type.nil?
174
+ assertion = @configurable[property][:type]
175
+ ::Kernel.raise ConfigurationError, "Expected #{property} to be configured with #{expected_type}, but got #{value.class.inspect}", caller unless value.is_a?(assertion)
176
+ end
158
177
 
159
- ::Kernel.raise ConfigurationError, "Expected #{property} to be configured with #{expected_type}, but got #{value.class.inspect}", caller unless value.is_a?(expected_type)
178
+ # Block assertion for configurable properties
179
+ # @param [Symbol] property the property to type test
180
+ # @param [Any] value the given value
181
+ #
182
+ def _evaluate_block!(property, value)
183
+ return value unless _evaluable?(property, :block)
184
+
185
+ evaluation = @configurable[property][:block]
186
+ evaluation.call(value)
160
187
  end
161
188
 
162
- # @return [Boolean] whether this configuration is arbitrarily configurable
189
+ # @param [Symbol] property The property to test for
190
+ # @param [Symbol] assertion_type The evaluation type type to test for
191
+ # @return [Boolean] whether the given property is assertable
163
192
  #
164
- def _arbitrarily_configurable?
165
- @configurable.nil? or @configurable.empty?
193
+ def _evaluable?(property, evaluation)
194
+ @configurable and @configurable.has_key?(property) and @configurable[property].is_a?(::Hash) and @configurable[property].has_key?(evaluation)
166
195
  end
167
196
 
168
197
  # @param [Symbol] method the method to test for
@@ -0,0 +1,46 @@
1
+ require 'test_helper'
2
+
3
+ class TestStricterConfigurationWithBlock < Minitest::Test
4
+
5
+ BlocksConfigurationTestModule = testmodule_for(Configurations)
6
+ BlocksConfigurationTestModule.module_eval do
7
+ configurable :property1, :property2 do |value|
8
+ value.to_s + 'oooh'
9
+ end
10
+ configurable String, :property3, property4: [:property5, :property6] do |value|
11
+ raise ArgumentError, 'TEST2' unless %w(hello bye).include?(value)
12
+ value
13
+ end
14
+ end
15
+
16
+ def setup
17
+ BlocksConfigurationTestModule.configure do |c|
18
+ c.property1 = :one
19
+ c.property2 = :two
20
+ c.property3 = 'hello'
21
+ c.property4.property5 = 'hello'
22
+ c.property4.property6 = 'bye'
23
+ end
24
+
25
+ @configuration = BlocksConfigurationTestModule.configuration
26
+ end
27
+
28
+ def test_configurable_when_set_configurable_with_block
29
+ assert_equal 'oneoooh', @configuration.property1
30
+ assert_equal 'twooooh', @configuration.property2
31
+ end
32
+
33
+ def test_nested_configurable_when_set_configurable_with_block
34
+ assert_equal 'hello', @configuration.property4.property5
35
+ assert_equal 'bye', @configuration.property4.property6
36
+ end
37
+
38
+ def test_evaluates_block_for_nested_properties_when_set_configurable_with_block
39
+ assert_raises ArgumentError, 'TEST2' do
40
+ BlocksConfigurationTestModule.configure do |c|
41
+ c.property4.property5 = 'oh'
42
+ end
43
+ end
44
+ end
45
+
46
+ end
@@ -0,0 +1,67 @@
1
+ require 'test_helper'
2
+
3
+ class TestConfigurationMethods < Minitest::Test
4
+
5
+ ConfigurationMethodsClassModule = testmodule_for(Configurations)
6
+ ConfigurationMethodsClassModule.module_eval do
7
+ class MyClass
8
+ attr_reader :props
9
+ def initialize(*props)
10
+ @props = props
11
+ end
12
+ end
13
+
14
+ context = 'CONTEXT'
15
+ configurable :property1, :property2
16
+ configuration_method :method1 do
17
+ MyClass.new(property1, property2)
18
+ end
19
+ configuration_method :method2 do
20
+ context + property1.to_s
21
+ end
22
+ configuration_method :method3 do |arg|
23
+ arg + property1.to_s
24
+ end
25
+ configuration_method :kernel_raise do
26
+ raise StandardError, 'hell'
27
+ end
28
+ end
29
+
30
+ def setup
31
+ ConfigurationMethodsClassModule.configure do |c|
32
+ c.property1 = :one
33
+ c.property2 = :two
34
+ end
35
+
36
+ @configuration = ConfigurationMethodsClassModule.configuration
37
+ end
38
+
39
+ def test_configuration_method
40
+ assert_equal [:one, :two], @configuration.method1.props
41
+ end
42
+
43
+ def test_configuration_method_with_context
44
+ assert_equal 'CONTEXTone', @configuration.method2
45
+ end
46
+
47
+ def test_configuration_method_with_arguments
48
+ assert_equal 'ARGone', @configuration.method3('ARG')
49
+ end
50
+
51
+ def test_kernel_methods_in_configuration_method
52
+ assert_raises StandardError, 'hell' do
53
+ @configuration.kernel_raise
54
+ end
55
+ end
56
+
57
+ def test_configuration_method_overwrite
58
+ assert_raises ArgumentError do
59
+ ConfigurationMethodsClassModule.module_eval do
60
+ configuration_method :property2 do |c|
61
+ MyClass.new(c.property2)
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ end
metadata CHANGED
@@ -1,7 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: configurations
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.3.0
5
+ prerelease:
5
6
  platform: ruby
6
7
  authors:
7
8
  - Beat Richartz
@@ -13,6 +14,7 @@ dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: minitest
15
16
  requirement: !ruby/object:Gem::Requirement
17
+ none: false
16
18
  requirements:
17
19
  - - ~>
18
20
  - !ruby/object:Gem::Version
@@ -20,6 +22,7 @@ dependencies:
20
22
  type: :development
21
23
  prerelease: false
22
24
  version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
23
26
  requirements:
24
27
  - - ~>
25
28
  - !ruby/object:Gem::Version
@@ -27,6 +30,7 @@ dependencies:
27
30
  - !ruby/object:Gem::Dependency
28
31
  name: yard
29
32
  requirement: !ruby/object:Gem::Requirement
33
+ none: false
30
34
  requirements:
31
35
  - - ~>
32
36
  - !ruby/object:Gem::Version
@@ -34,6 +38,7 @@ dependencies:
34
38
  type: :development
35
39
  prerelease: false
36
40
  version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
37
42
  requirements:
38
43
  - - ~>
39
44
  - !ruby/object:Gem::Version
@@ -41,6 +46,7 @@ dependencies:
41
46
  - !ruby/object:Gem::Dependency
42
47
  name: rake
43
48
  requirement: !ruby/object:Gem::Requirement
49
+ none: false
44
50
  requirements:
45
51
  - - ~>
46
52
  - !ruby/object:Gem::Version
@@ -48,6 +54,7 @@ dependencies:
48
54
  type: :development
49
55
  prerelease: false
50
56
  version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
51
58
  requirements:
52
59
  - - ~>
53
60
  - !ruby/object:Gem::Version
@@ -72,37 +79,42 @@ files:
72
79
  - lib/configurations/configurable.rb
73
80
  - lib/configurations/configuration.rb
74
81
  - lib/configurations/error.rb
82
+ - test/configurations/test_configurable_with_blocks_configuration.rb
75
83
  - test/configurations/test_configuration.rb
84
+ - test/configurations/test_configuration_methods.rb
76
85
  - test/configurations/test_stricter_configuration.rb
77
86
  - test/support/testmodules.rb
78
87
  - test/test_helper.rb
79
88
  homepage: http://github.com/beatrichartz/configurations
80
89
  licenses:
81
90
  - MIT
82
- metadata: {}
83
91
  post_install_message:
84
92
  rdoc_options: []
85
93
  require_paths:
86
94
  - lib
87
95
  required_ruby_version: !ruby/object:Gem::Requirement
96
+ none: false
88
97
  requirements:
89
- - - '>='
98
+ - - ! '>='
90
99
  - !ruby/object:Gem::Version
91
100
  version: '0'
92
101
  required_rubygems_version: !ruby/object:Gem::Requirement
102
+ none: false
93
103
  requirements:
94
- - - '>='
104
+ - - ! '>='
95
105
  - !ruby/object:Gem::Version
96
106
  version: '0'
97
107
  requirements: []
98
108
  rubyforge_project:
99
- rubygems_version: 2.0.14
109
+ rubygems_version: 1.8.23.2
100
110
  signing_key:
101
- specification_version: 4
111
+ specification_version: 3
102
112
  summary: Configurations with a configure block from arbitrary to type-restricted for
103
113
  your gem or other ruby code.
104
114
  test_files:
115
+ - test/configurations/test_configurable_with_blocks_configuration.rb
105
116
  - test/configurations/test_configuration.rb
117
+ - test/configurations/test_configuration_methods.rb
106
118
  - test/configurations/test_stricter_configuration.rb
107
119
  - test/support/testmodules.rb
108
120
  - test/test_helper.rb
checksums.yaml DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- SHA1:
3
- metadata.gz: 039597b55adc73ff8cddc05edd7c199f5f2c252f
4
- data.tar.gz: 1dec69ab7582283ab60f950dc3caa96e92ff2b95
5
- SHA512:
6
- metadata.gz: e7a927942a4bce9f187a1f491f9469ac898cc62e2687c3b2111163d6b8b665ebaea70767f52fabcdb2e11c1544bc005168610317632ecabe8211373c993897d3
7
- data.tar.gz: 97ae55c4e7c2ccae76aab97aa75c912c4b79bbf4a5fc7ebdb752b7150d9b0be821598fc9f060f484e7b456a1a153072a5a5672260c2ca96548d1c2789c6ca89e