ns-options 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +273 -16
- data/lib/ns-options/boolean.rb +25 -0
- data/lib/ns-options/errors/invalid_name.rb +15 -0
- data/lib/ns-options/has_options.rb +33 -7
- data/lib/ns-options/helper/advisor.rb +68 -0
- data/lib/ns-options/helper.rb +42 -19
- data/lib/ns-options/namespace.rb +55 -33
- data/lib/ns-options/namespaces.rb +1 -1
- data/lib/ns-options/option.rb +30 -28
- data/lib/ns-options/options.rb +29 -17
- data/lib/ns-options/version.rb +1 -1
- data/lib/ns-options.rb +9 -0
- data/log/.gitkeep +0 -0
- data/ns-options.gemspec +1 -1
- data/test/helper.rb +3 -8
- data/test/integration/app_test.rb +12 -6
- data/test/integration/user_test.rb +40 -2
- data/test/irb.rb +9 -0
- data/test/support/app.rb +8 -2
- data/test/support/user.rb +12 -3
- data/test/unit/ns-options/{option/boolean_test.rb → boolean_test.rb} +21 -5
- data/test/unit/ns-options/helper/advisor_test.rb +144 -0
- data/test/unit/ns-options/helper_test.rb +29 -40
- data/test/unit/ns-options/namespace_test.rb +186 -17
- data/test/unit/ns-options/namespaces_test.rb +1 -1
- data/test/unit/ns-options/option_test.rb +145 -52
- data/test/unit/ns-options/options_test.rb +83 -89
- metadata +18 -11
- data/lib/ns-options/option/boolean.rb +0 -27
data/README.markdown
CHANGED
@@ -16,7 +16,7 @@ The basic usage of Namespace Options is to be able to define options for a modul
|
|
16
16
|
|
17
17
|
```ruby
|
18
18
|
module App
|
19
|
-
include NsOptions
|
19
|
+
include NsOptions
|
20
20
|
options(:settings) do
|
21
21
|
option :root, Pathname
|
22
22
|
option :stage
|
@@ -34,7 +34,7 @@ App.settings.stage = "development"
|
|
34
34
|
App.settings.stage # => "development"
|
35
35
|
```
|
36
36
|
|
37
|
-
Because the `root` option specified `Pathname` as it's type, the option will always return an instance of `Pathname`. Since the `stage` option did not specify a type, it defaulted to
|
37
|
+
Because the `root` option specified `Pathname` as it's type, the option will always return an instance of `Pathname`. Since the `stage` option did not specify a type, it defaulted to an `Object` which allows it to accept any value. You can define you're own type classes as well and use them:
|
38
38
|
|
39
39
|
```ruby
|
40
40
|
class Stage < String
|
@@ -60,11 +60,11 @@ App.settings.stage = "test"
|
|
60
60
|
App.settings.stage.development? # => false
|
61
61
|
```
|
62
62
|
|
63
|
-
This allows you to add extended functionality to your options
|
63
|
+
This allows you to add extended functionality to your options and is where a lot of nice usability can be added. Defining your own type classes is explained in more detail later.
|
64
64
|
|
65
65
|
### Namespaces
|
66
66
|
|
67
|
-
Namespaces allow you to organize
|
67
|
+
Namespaces allow you to organize your options. With the previously mentioned `App` module and it's options you could create a namespace for another library:
|
68
68
|
|
69
69
|
```ruby
|
70
70
|
module Data # data is a library for retrieving persisted data from some resource
|
@@ -85,20 +85,17 @@ Now I can set a server option for data that is separate from the main `App` sett
|
|
85
85
|
```ruby
|
86
86
|
Data.config.server = "127.0.0.1:1234"
|
87
87
|
|
88
|
-
App.server # => NoMethodError
|
88
|
+
App.settings.server # => NoMethodError
|
89
|
+
App.settings.data.server # => 127.0.0.1:1234
|
89
90
|
```
|
90
91
|
|
91
|
-
|
92
|
+
#### With Classes
|
92
93
|
|
93
|
-
|
94
|
-
Data.config.stage # => "test", or whatever App.settings.stage would return
|
95
|
-
```
|
96
|
-
|
97
|
-
Namespaces and their ability to read their parent's options is internally used by Namespace Options. When you add options to a class like so:
|
94
|
+
Using `NsOptions` on a `Class` uses namespaces to create separate sets of options for every instance of your class created. This allows every instance to have different values for the set of options and not interfere with each other. For example with the following:
|
98
95
|
|
99
96
|
```ruby
|
100
97
|
class User
|
101
|
-
include NsOptions
|
98
|
+
include NsOptions
|
102
99
|
options(:preferences) do
|
103
100
|
option :home_page
|
104
101
|
end
|
@@ -106,11 +103,43 @@ class User
|
|
106
103
|
end
|
107
104
|
```
|
108
105
|
|
109
|
-
A namespace
|
106
|
+
A namespace is created for the `User` class in the same way it works for modules:
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
User.preferences # => NsOptions::Namespace instance
|
110
|
+
User.preferences.home_page = "/home" # you can set options at this level, though I'm not sure why you would
|
111
|
+
```
|
112
|
+
|
113
|
+
Additionally, `NsOptions` will setup instances of a class to have a _copy_ of their class's namespace.
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
user = User.new
|
117
|
+
user.preferences.home_page = "/home" # makes a lot more sense to do this
|
118
|
+
user2 = User.new
|
119
|
+
user2.preferences.home_page = "/not_home"
|
120
|
+
user.preferences.home_page == user2.preferences.home_page # => false, they are completely separate
|
121
|
+
```
|
122
|
+
|
123
|
+
The instance level namespaces are deep copies of the class one. This means every option and sub-namespaces will be included. Only values are not copied.
|
110
124
|
|
111
125
|
```ruby
|
126
|
+
class User
|
127
|
+
include NsOptions
|
128
|
+
options(:preferences) do
|
129
|
+
option :home_page
|
130
|
+
namespace :view do
|
131
|
+
option :background_color, ViewColor
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
|
137
|
+
User.preferences.home_page = "/home"
|
112
138
|
user = User.new
|
113
|
-
user.preferences.home_page
|
139
|
+
user.preferences.home_page # => nil, does not return '/home'
|
140
|
+
user.preferences.object_id != User.preferences.object_id # => true, they are different objects, just with the same definition
|
141
|
+
user.preferences.view.background_color = "green"
|
142
|
+
user.preferences.view.background_color # => returns an instance of ViewColor
|
114
143
|
```
|
115
144
|
|
116
145
|
### Dynamically Defined Options
|
@@ -123,7 +152,235 @@ App.settings.logger = Logger.new(App.settings.root.join("log", "test.log"))
|
|
123
152
|
App.settings.logger.info("Hello World")
|
124
153
|
```
|
125
154
|
|
126
|
-
Writing to a namespace with a previously undefined option will create a new option. The type class will be
|
155
|
+
Writing to a namespace with a previously undefined option will create a new option. The type class will be defaulted to `Object` as if you didn't provide it. This will allow you to set any value for the option so you have no guarantee on what it's value is and how it can be used.
|
156
|
+
|
157
|
+
### Mass Assigning Options
|
158
|
+
|
159
|
+
Sometimes, it's convenient to be able to set many options at once. This can be done by calling the `apply` method and giving it a hash of option names with values:
|
160
|
+
|
161
|
+
```ruby
|
162
|
+
class Project
|
163
|
+
include NsOptions
|
164
|
+
options(:settings) do
|
165
|
+
option :file_path
|
166
|
+
option :home_page
|
167
|
+
|
168
|
+
namespace(:movie_resolution) do
|
169
|
+
option :height, Integer
|
170
|
+
option :width, Integer
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
project = Project.new
|
176
|
+
project.settings.apply({
|
177
|
+
:file_path => "/path/to/project",
|
178
|
+
:movie_resolution => { :height => 800, :width => 600 }
|
179
|
+
})
|
180
|
+
|
181
|
+
project.settings.file_path # => "/path/to/project"
|
182
|
+
project.settings.movie_resolution.height # => 800
|
183
|
+
project.settings.movie_resolution.width # => 600
|
184
|
+
```
|
185
|
+
|
186
|
+
As the example shows, if you have a namespace and have a matching hash, it will automatically apply those values to that namespace. Also, if you include keys that are not defined options for your namespace, new options will be created for the values:
|
187
|
+
|
188
|
+
```ruby
|
189
|
+
project = Project.new
|
190
|
+
project.settings.apply({ :stereoscopic => true, :not_a_namespace => { :yes => true } })
|
191
|
+
|
192
|
+
project.settings.stereoscopic # => true
|
193
|
+
project.settings.not_a_namespace # => { :yes => true }
|
194
|
+
```
|
195
|
+
|
196
|
+
The reverse is also supported, so if you want a `Hash` version of your namespace, just ask your options for it.
|
197
|
+
|
198
|
+
```ruby
|
199
|
+
# a continuation of the previous block of code...
|
200
|
+
project.settings.to_hash # => { :stereoscopic => true, :not_a_namespace => { :yes => true } }
|
201
|
+
project.settings.each do |name, value|
|
202
|
+
# iterating over your options works as well
|
203
|
+
end
|
204
|
+
```
|
205
|
+
|
206
|
+
### Lazily eval'd options
|
207
|
+
|
208
|
+
Sometimes, you may want to set an option to a value that shouldn't (couldn't) be evaluated until the option is read. If you set an option equal to a Proc, the value of the option will be whatever the return value of the Proc is at the time the option is read. Here are some examples:
|
209
|
+
|
210
|
+
```ruby
|
211
|
+
# dynamic value
|
212
|
+
options(:dynamic) do
|
213
|
+
option :rand, :default => Proc.new { rand(1000) }
|
214
|
+
end
|
215
|
+
|
216
|
+
dynamic.rand #=> 347
|
217
|
+
dynamic.rand #=> 529
|
218
|
+
|
219
|
+
# same goes for dynamically defined options
|
220
|
+
dynamic.not_originally_defined = Proc.new { rand(1000) }
|
221
|
+
dynamic.not_originally_defined #=> 110
|
222
|
+
dynamic.not_originally_defined #=> 931
|
223
|
+
```
|
224
|
+
|
225
|
+
```ruby
|
226
|
+
# self referential value
|
227
|
+
options(:selfref) do
|
228
|
+
option :something, :default => "123"
|
229
|
+
option :else, :default => Proc.new { self.something }
|
230
|
+
end
|
231
|
+
|
232
|
+
selfref.something #=> "123"
|
233
|
+
selfref.else #=> "123"
|
234
|
+
```
|
235
|
+
|
236
|
+
If you really want your option to read and write Procs and not do this lazy eval behavior, just define the option as a Proc option
|
237
|
+
|
238
|
+
```ruby
|
239
|
+
options(:explicit) do
|
240
|
+
option :a_proc, Proc, :default => Proc.new { rand(1000) }
|
241
|
+
end
|
242
|
+
|
243
|
+
explicit.a_proc #=> <the proc obj>
|
244
|
+
```
|
245
|
+
|
246
|
+
### Custom Type Classes
|
247
|
+
|
248
|
+
As stated previously, type classes is where you can add a lot of functionality and usability to your options. To do this though, understanding what `NsOptions` will do with your type class is important. First, it's important to understand when `NsOptions` will try to _coerce_ a value. This is only done when a value is not a _kind of_ the option's type class or when the value is nil. For example:
|
249
|
+
|
250
|
+
```ruby
|
251
|
+
module App
|
252
|
+
include NsOptions
|
253
|
+
options :settings do
|
254
|
+
option :stage, Stage
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
App.settings.stage = Stage.new("development") # no type coercion is done here, the value is already a Stage
|
259
|
+
|
260
|
+
class BetterStage < Stage
|
261
|
+
# do something better
|
262
|
+
end
|
263
|
+
|
264
|
+
App.settings.stage = BetterStage.new("test") # again, no type coercion is done, as BetterStage is a kind of Stage
|
265
|
+
|
266
|
+
App.setting.stage = nil # nil is never coerced, if you set a value to nil, it's just nil
|
267
|
+
```
|
268
|
+
|
269
|
+
Next, when `NsOptions` chooses to coerce a value with your class, it will always create a new instance of your type class and pass the value as the first argument. Your `initialize` method needs to be defined to handle this:
|
270
|
+
|
271
|
+
```ruby
|
272
|
+
class Root < Pathname
|
273
|
+
def initialize(path, app_name)
|
274
|
+
super("#{path}/#{app_name}")
|
275
|
+
end
|
276
|
+
end
|
277
|
+
```
|
278
|
+
|
279
|
+
`Root`'s `initialize` method will not work for type coercion. The `app_name` argument will not be provided and Ruby will get angry. To solve this, make the `app_name` not required:
|
280
|
+
|
281
|
+
```ruby
|
282
|
+
class Root < Pathname
|
283
|
+
def initialize(path, app_name = nil)
|
284
|
+
app_name ||= App.settings.name # this might be one way to solve this
|
285
|
+
super("#{path}/#{app_name}")
|
286
|
+
end
|
287
|
+
end
|
288
|
+
```
|
289
|
+
|
290
|
+
With the revised `initialize` method, `NsOptions` will have no problems coercing values for your the type class. In some cases the above solution may not work for you, but don't worry. See the _Option Rules_ section for another way to solve this, specifically about the args rule. For an example of a custom type class, the included `NsOptions::Boolean` can be looked at. This is a special case, but it works as a type class with `NsOptions`.
|
291
|
+
|
292
|
+
### Ruby Classes As A Type Class
|
293
|
+
|
294
|
+
`NsOptions` will allow you to use many of Ruby's standard objects as type classes and still handle coercing values appropriately. Typically this is done with ruby's type casting:
|
295
|
+
|
296
|
+
```ruby
|
297
|
+
module Example
|
298
|
+
include NsOptions
|
299
|
+
options :stuff do
|
300
|
+
option :string, String
|
301
|
+
option :integer, Integer
|
302
|
+
option :float, Float
|
303
|
+
option :symbol, Symbol
|
304
|
+
option :hash, Hash
|
305
|
+
option :array, Array
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
Example.stuff.string = 1
|
310
|
+
Example.stuff.string # => "1", the same as doing String(1)
|
311
|
+
Example.stuff.integer = 5.0
|
312
|
+
Example.stuff.integer # => 5, this time it's Integer(5.0)
|
313
|
+
Example.stuff.float = "5.0"
|
314
|
+
Example.stuff.float # => 5.0, same as Float("5.0")
|
315
|
+
```
|
316
|
+
|
317
|
+
`Symbol`, `Hash` and `Array` work, but ruby doesn't provide a built in type casting for these.
|
318
|
+
|
319
|
+
```ruby
|
320
|
+
Example.stuff.symbol = "awesome"
|
321
|
+
Example.stuff.symbol # => :awesome, watch out, this will try calling to_sym on the passed value, so it can error
|
322
|
+
Example.stuff.hash = { :a => 'b' }
|
323
|
+
Example.stuff.hash # => returns the same hash, does Hash.new.merge(value)
|
324
|
+
Example.stuff.array = [ 1, 2, 3 ]
|
325
|
+
Example.stuff.array # => returns the same array, Array is the only one that works without anything special, Array.new(value)
|
326
|
+
```
|
327
|
+
|
328
|
+
### Option Rules
|
329
|
+
|
330
|
+
An option can be defined with certain rules (through a hash) that will extend the behavior of the option.
|
331
|
+
|
332
|
+
#### Default Value
|
333
|
+
|
334
|
+
The first rule is setting a default value.
|
335
|
+
|
336
|
+
```ruby
|
337
|
+
App.settings do
|
338
|
+
option :stage, Stage, :default => "development"
|
339
|
+
end
|
340
|
+
App.settings.stage # => instead of nil this will be 'development'
|
341
|
+
```
|
342
|
+
|
343
|
+
A default value runs through the same logic as if you set the value manually, so it will be coerced if necessary.
|
344
|
+
|
345
|
+
#### Required
|
346
|
+
|
347
|
+
It's also possible to flag an option as _required_.
|
348
|
+
|
349
|
+
```ruby
|
350
|
+
App.settings do
|
351
|
+
option :root, :required => true
|
352
|
+
end
|
353
|
+
|
354
|
+
App.settings.required_set? # => false, asking if the required options are set
|
355
|
+
App.settings.root = "/path/to/somewhere"
|
356
|
+
App.settings.required_set? # => true
|
357
|
+
```
|
358
|
+
|
359
|
+
To check if an option is set it will simply check if the value is not `nil`. If you are using a custom type class though, you can define an `is_set?` method and this will be used to check if an option is set.
|
360
|
+
|
361
|
+
#### Args
|
362
|
+
|
363
|
+
Another rule that you can specify is args. This allows you to pass more arguments to a type class.
|
364
|
+
|
365
|
+
```ruby
|
366
|
+
class Root < Pathname
|
367
|
+
def initialize(path, app_name = nil)
|
368
|
+
app_name = app_name.respond_to?(:call) ? app_name.call : app_name
|
369
|
+
super("#{path}/#{app_name}")
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
App.settings do
|
374
|
+
option :name
|
375
|
+
option :root, Root, :args => lambda{ App.settings.name }
|
376
|
+
end
|
377
|
+
|
378
|
+
App.settings.name = "example"
|
379
|
+
App.settings.root = "/path/to"
|
380
|
+
App.settings.root # => /path/to/example, uses the args rule to build the path
|
381
|
+
```
|
382
|
+
|
383
|
+
With the args rule, you can have a type class accept more than one argument. The first argument will always be the value to coerce. Any more arguments will be appended on after the value.
|
127
384
|
|
128
385
|
## License
|
129
386
|
|
@@ -148,4 +405,4 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
148
405
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
149
406
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
150
407
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
151
|
-
OTHER DEALINGS IN THE SOFTWARE.
|
408
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module NsOptions
|
2
|
+
class Boolean
|
3
|
+
|
4
|
+
attr_accessor :actual
|
5
|
+
|
6
|
+
def initialize(value)
|
7
|
+
self.actual = value
|
8
|
+
end
|
9
|
+
|
10
|
+
def actual=(new_value)
|
11
|
+
@actual = self.convert(new_value)
|
12
|
+
end
|
13
|
+
|
14
|
+
protected
|
15
|
+
|
16
|
+
def convert(value)
|
17
|
+
if [ nil, 0, '0', false, 'false', 'f', 'F' ].include?(value)
|
18
|
+
false
|
19
|
+
elsif value
|
20
|
+
true
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -13,19 +13,45 @@ module NsOptions
|
|
13
13
|
|
14
14
|
module DSL
|
15
15
|
|
16
|
+
# This is the main DSL method for creating a namespace of options for your class/module. This
|
17
|
+
# will define a class method for both classes and modules and an additional instance method
|
18
|
+
# for classes. The namespace is then created and returned by calling the class method version.
|
19
|
+
# For classes, the instance method will build an entirely new namespace from the class level
|
20
|
+
# namespace. This is so when you define options at the class level:
|
21
|
+
#
|
22
|
+
# class Something
|
23
|
+
# include NsOptions
|
24
|
+
# options(:settings) do
|
25
|
+
# option :root
|
26
|
+
# end
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# the namespaces at the instance level still get all the defined options, but are completely
|
30
|
+
# separate objects from the class and other instances. Modules only deal with a single
|
31
|
+
# namespace at the module level.
|
16
32
|
def options(name, key = nil, &block)
|
17
33
|
key ||= name.to_s
|
18
|
-
|
19
|
-
|
20
|
-
def #{name}(&block)
|
21
|
-
@#{name} ||= NsOptions::Helper.new_child_namespace(self, '#{name}', &block)
|
22
|
-
end
|
34
|
+
method_definitions = <<-CLASS_METHOD
|
23
35
|
|
24
36
|
def self.#{name}(&block)
|
25
|
-
@#{name} ||= NsOptions::
|
37
|
+
@#{name} ||= NsOptions::Namespace.new('#{key}', &block)
|
26
38
|
end
|
27
39
|
|
28
|
-
|
40
|
+
CLASS_METHOD
|
41
|
+
if self.kind_of?(Class)
|
42
|
+
method_definitions += <<-INSTANCE_METHOD
|
43
|
+
|
44
|
+
def #{name}(&block)
|
45
|
+
unless @#{name}
|
46
|
+
@#{name} = NsOptions::Namespace.new('#{key}', &block)
|
47
|
+
@#{name}.options.build_from(self.class.#{name}.options, @#{name})
|
48
|
+
end
|
49
|
+
@#{name}
|
50
|
+
end
|
51
|
+
|
52
|
+
INSTANCE_METHOD
|
53
|
+
end
|
54
|
+
self.class_eval(method_definitions)
|
29
55
|
self.send(name, &block)
|
30
56
|
end
|
31
57
|
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module NsOptions
|
2
|
+
module Helper
|
3
|
+
|
4
|
+
class Advisor
|
5
|
+
attr_accessor :namespace
|
6
|
+
|
7
|
+
def initialize(namespace)
|
8
|
+
self.namespace = namespace
|
9
|
+
end
|
10
|
+
|
11
|
+
def is_this_ok?(kind, name, from)
|
12
|
+
display = (kind == :option ? "option" : "sub-namespace")
|
13
|
+
if self.bad_methods.include?(name.to_sym)
|
14
|
+
message = self.bad_method_message(display, name)
|
15
|
+
exception = NsOptions::Errors::InvalidName.new(message, from)
|
16
|
+
raise(exception)
|
17
|
+
elsif self.is_already_defined?(name)
|
18
|
+
puts self.duplicate_message(name)
|
19
|
+
elsif self.not_recommended_methods.include?(name.to_sym)
|
20
|
+
puts self.not_recommended_method_message(display, name)
|
21
|
+
else
|
22
|
+
return false
|
23
|
+
end
|
24
|
+
puts "From: #{from.first}"
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
28
|
+
def is_this_option_ok?(name, from = nil)
|
29
|
+
self.is_this_ok?(:option, name, (from || caller))
|
30
|
+
end
|
31
|
+
|
32
|
+
def is_this_namespace_ok?(name, from = nil)
|
33
|
+
self.is_this_ok?(:namespace, name, (from || caller))
|
34
|
+
end
|
35
|
+
|
36
|
+
def is_already_defined?(name)
|
37
|
+
self.namespace.options.is_defined?(name) ||
|
38
|
+
self.namespace.options.is_namespace_defined?(name)
|
39
|
+
end
|
40
|
+
|
41
|
+
def bad_methods
|
42
|
+
@bad_methods ||= [ :option, :namespace, :define, :options ]
|
43
|
+
end
|
44
|
+
|
45
|
+
def not_recommended_methods
|
46
|
+
@not_recommended_methods ||= NsOptions::Namespace.instance_methods(false).map(&:to_sym)
|
47
|
+
end
|
48
|
+
|
49
|
+
def bad_method_message(kind, name)
|
50
|
+
[ "The #{kind} '#{name}' overwrites a namespace method that NsOptions depends on.",
|
51
|
+
"Please choose a different name for your #{kind}."
|
52
|
+
].join(" ")
|
53
|
+
end
|
54
|
+
def duplicate_message(name)
|
55
|
+
[ "WARNING! '#{name}' has already been defined and will be overwritten.",
|
56
|
+
"It's likely that it will not behave as expected."
|
57
|
+
].join(" ")
|
58
|
+
end
|
59
|
+
def not_recommended_method_message(kind, name)
|
60
|
+
[ "WARNING! The #{kind} '#{name}' overwrites a namespace method.",
|
61
|
+
"This will limit some of the functionality of NsOptions."
|
62
|
+
].join(" ")
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
data/lib/ns-options/helper.rb
CHANGED
@@ -1,34 +1,57 @@
|
|
1
1
|
module NsOptions
|
2
2
|
|
3
3
|
module Helper
|
4
|
+
autoload :Advisor, 'ns-options/helper/advisor'
|
5
|
+
|
4
6
|
module_function
|
5
7
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
8
|
+
def find_and_define_namespace(namespace, name)
|
9
|
+
sub_namespace = namespace.options.get_namespace(name)
|
10
|
+
self.define_namespace_methods(namespace, name)
|
11
|
+
sub_namespace
|
10
12
|
end
|
11
13
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
namespace = parent.namespace(name, key)
|
23
|
-
namespace.define(&block)
|
14
|
+
def define_namespace_methods(namespace, name)
|
15
|
+
namespace.metaclass.class_eval <<-DEFINE_METHOD
|
16
|
+
|
17
|
+
def #{name}(&block)
|
18
|
+
namespace = self.options.namespaces.get("#{name}")
|
19
|
+
namespace.define(&block) if block
|
20
|
+
namespace
|
21
|
+
end
|
22
|
+
|
23
|
+
DEFINE_METHOD
|
24
24
|
end
|
25
25
|
|
26
|
-
def
|
27
|
-
option = namespace.options
|
28
|
-
|
26
|
+
def find_and_define_option(namespace, option_name)
|
27
|
+
option = namespace.options[option_name]
|
28
|
+
self.define_option_methods(namespace, option)
|
29
29
|
option
|
30
30
|
end
|
31
31
|
|
32
|
+
def define_option_methods(namespace, option)
|
33
|
+
namespace.metaclass.class_eval <<-DEFINE_METHOD
|
34
|
+
|
35
|
+
def #{option.name}(*args)
|
36
|
+
if !args.empty?
|
37
|
+
self.send("#{option.name}=", *args)
|
38
|
+
else
|
39
|
+
self.options.get(:#{option.name})
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def #{option.name}=(*args)
|
44
|
+
value = args.size == 1 ? args.first : args
|
45
|
+
self.options.set(:#{option.name}, value)
|
46
|
+
end
|
47
|
+
|
48
|
+
DEFINE_METHOD
|
49
|
+
end
|
50
|
+
|
51
|
+
def advisor(namespace)
|
52
|
+
NsOptions::Helper::Advisor.new(namespace)
|
53
|
+
end
|
54
|
+
|
32
55
|
end
|
33
56
|
|
34
57
|
end
|