ns-options 0.4.1 → 1.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/Gemfile +2 -1
- data/LICENSE +1 -1
- data/README.md +187 -301
- data/Rakefile +1 -1
- data/lib/ns-options/assert_macros.rb +9 -12
- data/lib/ns-options/boolean.rb +2 -0
- data/lib/ns-options/namespace.rb +34 -134
- data/lib/ns-options/namespace_advisor.rb +35 -0
- data/lib/ns-options/namespace_data.rb +166 -0
- data/lib/ns-options/namespaces.rb +23 -12
- data/lib/ns-options/option.rb +50 -24
- data/lib/ns-options/options.rb +23 -49
- data/lib/ns-options/proxy.rb +40 -53
- data/lib/ns-options/proxy_method.rb +54 -0
- data/lib/ns-options/root_methods.rb +77 -0
- data/lib/ns-options/version.rb +1 -1
- data/lib/ns-options.rb +18 -8
- data/ns-options.gemspec +3 -4
- data/test/helper.rb +3 -10
- data/test/support/app.rb +3 -1
- data/test/support/proxy.rb +4 -0
- data/test/support/type_class_proxy.rb +29 -0
- data/test/support/user.rb +5 -5
- data/test/{integration/app_test.rb → system/app_tests.rb} +8 -6
- data/test/{integration/proxy_test.rb → system/proxy_tests.rb} +12 -0
- data/test/system/type_class_proxy_tests.rb +108 -0
- data/test/system/user_tests.rb +146 -0
- data/test/unit/{ns-options/boolean_test.rb → boolean_tests.rb} +5 -4
- data/test/unit/namespace_advisor_tests.rb +69 -0
- data/test/unit/namespace_data_tests.rb +336 -0
- data/test/unit/namespace_tests.rb +205 -0
- data/test/unit/namespaces_tests.rb +99 -0
- data/test/unit/{ns-options/option_test.rb → option_tests.rb} +155 -93
- data/test/unit/options_tests.rb +152 -0
- data/test/unit/proxy_method_tests.rb +87 -0
- data/test/unit/{ns-options/proxy_test.rb → proxy_tests.rb} +52 -0
- data/test/unit/root_methods_tests.rb +126 -0
- metadata +58 -63
- data/lib/ns-options/errors/invalid_name.rb +0 -15
- data/lib/ns-options/has_options.rb +0 -53
- data/lib/ns-options/helper/advisor.rb +0 -88
- data/lib/ns-options/helper.rb +0 -87
- data/test/integration/user_test.rb +0 -94
- data/test/unit/ns-options/has_options_test.rb +0 -90
- data/test/unit/ns-options/helper/advisor_test.rb +0 -148
- data/test/unit/ns-options/helper_test.rb +0 -56
- data/test/unit/ns-options/namespace_test.rb +0 -432
- data/test/unit/ns-options/namespaces_test.rb +0 -55
- data/test/unit/ns-options/options_test.rb +0 -221
- /data/test/unit/{ns-options/assert_macros_test.rb → assert_macros_tests.rb} +0 -0
data/README.md
CHANGED
@@ -1,102 +1,61 @@
|
|
1
|
-
#
|
1
|
+
# NsOptions
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
## Installation
|
6
|
-
|
7
|
-
Add the following to your Gemfile and `bundle install`:
|
8
|
-
|
9
|
-
gem 'ns-options'
|
3
|
+
A Ruby DSL for defining, organizing and accessing options. Use namespaces to organize options. Read and write option values using accessors.
|
10
4
|
|
11
5
|
## Usage
|
12
6
|
|
13
|
-
### Defining Options
|
14
|
-
|
15
|
-
The basic usage of Namespace Options is to be able to define options for a module or class:
|
16
|
-
|
17
7
|
```ruby
|
8
|
+
require 'ns-options'
|
9
|
+
|
18
10
|
module App
|
19
11
|
include NsOptions
|
12
|
+
|
20
13
|
options(:settings) do
|
21
14
|
option :root, Pathname
|
22
15
|
option :stage
|
23
16
|
end
|
24
17
|
end
|
25
|
-
```
|
26
|
-
|
27
|
-
The code above makes the `App` module now have options, accessible through the settings method. The options can be read and written to like a normal accessor:
|
28
18
|
|
29
|
-
```ruby
|
30
19
|
App.settings.root = "/a/path/to/the/root"
|
31
|
-
App.settings.root.join("log", "test.log")
|
32
|
-
|
33
|
-
App.settings.stage = "development"
|
34
|
-
App.settings.stage # => "development"
|
35
|
-
```
|
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 an `Object` which allows it to accept any value. You can define you're own type classes as well and use them:
|
38
|
-
|
39
|
-
```ruby
|
40
|
-
class Stage < String
|
41
|
-
|
42
|
-
def initialize(value)
|
43
|
-
super(value.to_s)
|
44
|
-
end
|
45
|
-
|
46
|
-
def development?
|
47
|
-
self == "development"
|
48
|
-
end
|
49
|
-
|
50
|
-
end
|
51
|
-
|
52
|
-
|
53
|
-
App.settings.define do
|
54
|
-
option :stage, Stage
|
55
|
-
end
|
20
|
+
App.settings.root.join("log", "test.log") #=> "/a/path/to/the/root/log/test.log" (a Pathname instance)
|
56
21
|
|
57
22
|
App.settings.stage = "development"
|
58
|
-
App.settings.stage
|
59
|
-
App.settings.stage = "test"
|
60
|
-
App.settings.stage.development? # => false
|
23
|
+
App.settings.stage #=> "development"
|
61
24
|
```
|
62
25
|
|
63
|
-
|
26
|
+
The code above defines a `settings` reader on `App`. The options can be read and written to using their accessors
|
64
27
|
|
65
28
|
### Namespaces
|
66
29
|
|
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
|
-
|
69
30
|
```ruby
|
70
|
-
|
71
|
-
|
72
|
-
App.settings.namespace(:data) do
|
73
|
-
option :server
|
74
|
-
end
|
31
|
+
options(:settings) do
|
75
32
|
|
76
|
-
|
77
|
-
|
33
|
+
namespace :grouped_stuff do
|
34
|
+
option :something
|
35
|
+
option :something_else
|
78
36
|
end
|
79
37
|
|
80
38
|
end
|
81
39
|
```
|
82
40
|
|
83
|
-
|
41
|
+
Namespaces allow you to organize your options. You access the namespace and its options through their accessors.
|
84
42
|
|
85
43
|
```ruby
|
86
|
-
|
87
|
-
|
88
|
-
App.settings.server # => NoMethodError
|
89
|
-
App.settings.data.server # => 127.0.0.1:1234
|
44
|
+
App.settings.grouped_stuff.something = 1
|
45
|
+
App.settings.grouped_stuff.something # => 1
|
90
46
|
```
|
91
47
|
|
92
48
|
### Less Verbose Definitions
|
93
49
|
|
94
|
-
As an alternative to the above definition syntax, you can use
|
50
|
+
As an alternative to the above definition syntax, you can use a less-verbose syntax:
|
51
|
+
|
95
52
|
* `opts` for `options`
|
96
53
|
* `opt` for `option`
|
97
54
|
* `ns` for `namespace`
|
98
55
|
|
99
56
|
```ruby
|
57
|
+
require 'ns-options'
|
58
|
+
|
100
59
|
module App
|
101
60
|
include NsOptions
|
102
61
|
|
@@ -108,343 +67,266 @@ module App
|
|
108
67
|
opt :something
|
109
68
|
end
|
110
69
|
end
|
70
|
+
|
111
71
|
end
|
112
72
|
```
|
113
73
|
|
114
|
-
|
74
|
+
### Dynamically Defined Options
|
115
75
|
|
116
|
-
|
76
|
+
Not all options have to be defined formally ahead of time. You can write any option value you like at any time.
|
117
77
|
|
118
78
|
```ruby
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
option :home_page
|
123
|
-
end
|
124
|
-
|
125
|
-
end
|
79
|
+
App.settings.a_value #=> NoMethodError: undefined method `a_value'...
|
80
|
+
App.settings.a_value = 1
|
81
|
+
App.settings.a_value #=> 1
|
126
82
|
```
|
127
83
|
|
128
|
-
|
84
|
+
### Mass Assigning Options
|
85
|
+
|
86
|
+
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. You can even give it keys that aren't pre-defined options - new options will be created for them
|
129
87
|
|
130
88
|
```ruby
|
131
|
-
|
132
|
-
|
89
|
+
App.settings.apply({
|
90
|
+
:root => "/path/to/project",
|
91
|
+
:stage => "development"
|
92
|
+
:new_value => 1
|
93
|
+
})
|
94
|
+
|
95
|
+
App.settings.root #=> "/path/to/project"
|
96
|
+
App.settings.stage #=> "development"
|
97
|
+
App.settings.new_value #=> 1
|
133
98
|
```
|
134
99
|
|
135
|
-
|
100
|
+
To get a hash of values for a namespace, just call its `to_hash` method.
|
136
101
|
|
137
|
-
|
138
|
-
user = User.new
|
139
|
-
user.preferences.home_page = "/home" # makes a lot more sense to do this
|
140
|
-
user2 = User.new
|
141
|
-
user2.preferences.home_page = "/not_home"
|
142
|
-
user.preferences.home_page == user2.preferences.home_page # => false, they are completely separate
|
143
|
-
```
|
102
|
+
## Class Behavior
|
144
103
|
|
145
|
-
|
104
|
+
Using `NsOptions` on a `Class` uses namespaces to create separate sets of options for every instance of your class. This different instances to have different options values but share the same definition.
|
105
|
+
|
106
|
+
To illustrate:
|
146
107
|
|
147
108
|
```ruby
|
148
109
|
class User
|
149
110
|
include NsOptions
|
150
111
|
options(:preferences) do
|
151
112
|
option :home_page
|
152
|
-
namespace :view do
|
153
|
-
option :background_color, ViewColor
|
154
|
-
end
|
155
113
|
end
|
156
114
|
|
157
115
|
end
|
158
116
|
|
159
|
-
User.preferences
|
160
|
-
user = User.new
|
161
|
-
user.preferences.home_page # => nil, does not return '/home'
|
162
|
-
user.preferences.object_id != User.preferences.object_id # => true, they are different objects, just with the same definition
|
163
|
-
user.preferences.view.background_color = "green"
|
164
|
-
user.preferences.view.background_color # => returns an instance of ViewColor
|
117
|
+
User.preferences # => NsOptions::Namespace instance
|
165
118
|
```
|
166
119
|
|
167
|
-
|
168
|
-
|
169
|
-
Not all options have to be defined formally. Though defining an option through the `option` method allows the most functionality and allows for quickly seeing what can be used, Namespace Options allows you to write options that have not been defined:
|
120
|
+
A `preferences` namespace is created for the `User` class. For each instiance of `User` created, `NsOptions` will setup an identical _copy_ of their class's namespace. However, each instance sets and maintains unique option values.
|
170
121
|
|
171
122
|
```ruby
|
172
|
-
|
123
|
+
user1 = User.new
|
124
|
+
user1.preferences.home_page = "/home"
|
125
|
+
|
126
|
+
user2 = User.new
|
127
|
+
user2.preferences.home_page = "/not_home"
|
173
128
|
|
174
|
-
|
129
|
+
user.preferences.home_page == user2.preferences.home_page #=> false
|
175
130
|
```
|
176
131
|
|
177
|
-
|
132
|
+
## Options
|
178
133
|
|
179
|
-
###
|
134
|
+
### Type Classes
|
180
135
|
|
181
|
-
|
136
|
+
Options can be defined with a given "type class". If none is specified, `Object` is used.
|
182
137
|
|
183
138
|
```ruby
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
option :file_path
|
188
|
-
option :home_page
|
189
|
-
|
190
|
-
namespace(:movie_resolution) do
|
191
|
-
option :height, Integer
|
192
|
-
option :width, Integer
|
193
|
-
end
|
194
|
-
end
|
139
|
+
options :settings do
|
140
|
+
option :opt1
|
141
|
+
option :opt2, MyCustomTypeClass
|
195
142
|
end
|
196
|
-
|
197
|
-
project = Project.new
|
198
|
-
project.settings.apply({
|
199
|
-
:file_path => "/path/to/project",
|
200
|
-
:movie_resolution => { :height => 800, :width => 600 }
|
201
|
-
})
|
202
|
-
|
203
|
-
project.settings.file_path # => "/path/to/project"
|
204
|
-
project.settings.movie_resolution.height # => 800
|
205
|
-
project.settings.movie_resolution.width # => 600
|
206
143
|
```
|
207
144
|
|
208
|
-
|
145
|
+
Understanding what NsOptions will do with your type class is important. First, option values will be cast to your type class. If you write a value that is not of a matching type, NsOptions will try to _coerce_ the value.
|
209
146
|
|
210
147
|
```ruby
|
211
|
-
|
212
|
-
|
148
|
+
# no type coercion is done here, the value is of the right type
|
149
|
+
settings.opt2 = MyCustomTypeClass.new(123)
|
213
150
|
|
214
|
-
|
215
|
-
project.settings.not_a_namespace # => { :yes => true }
|
216
|
-
```
|
151
|
+
class BetterCustomTypeClass < MyCustomTypeClass; end
|
217
152
|
|
218
|
-
|
153
|
+
# again, no type coercion is done, as BetterCustomTypeClass is a kind of MyCustomTypeClass
|
154
|
+
settings.opt2 = BetterCustomTypeClass.new(456)
|
219
155
|
|
220
|
-
|
221
|
-
#
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
156
|
+
# here, type coercion is performed
|
157
|
+
# this is the equivalent of doing: `settings.opt2 = MyCustomTypeClass.new(789)`
|
158
|
+
settings.opt2 = 789
|
159
|
+
|
160
|
+
# nil is never coerced, if you set a value to nil, it's just nil
|
161
|
+
App.setting.stage = nil
|
226
162
|
```
|
227
163
|
|
228
|
-
|
164
|
+
For type coercion to work, your type class's initializer must work given only a single argument.
|
229
165
|
|
230
|
-
|
166
|
+
### Default Type Classes
|
231
167
|
|
232
168
|
```ruby
|
233
|
-
|
234
|
-
options(:dynamic) do
|
235
|
-
option :rand, :default => Proc.new { rand(1000) }
|
236
|
-
end
|
169
|
+
class MyCustomFixNum < Struct.new(:num); end
|
237
170
|
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
# same goes for dynamically defined options
|
242
|
-
dynamic.not_originally_defined = Proc.new { rand(1000) }
|
243
|
-
dynamic.not_originally_defined #=> 110
|
244
|
-
dynamic.not_originally_defined #=> 931
|
245
|
-
```
|
171
|
+
options :settings do
|
172
|
+
option_type_class Fixnum
|
246
173
|
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
option :something, :default => "123"
|
251
|
-
option :else, :default => Proc.new { self.something }
|
174
|
+
option :opt1, :default => 1
|
175
|
+
option :opt2, :default => 2
|
176
|
+
option :opt3, MyCustomFixNum, :default => 3
|
252
177
|
end
|
253
178
|
|
254
|
-
|
255
|
-
|
179
|
+
settings.opt1.class #=> Fixnum
|
180
|
+
settings.opt3.class #=> Fixnum
|
181
|
+
settings.opt3.class #=> MyCustomFixNum
|
256
182
|
```
|
257
183
|
|
258
|
-
|
184
|
+
By default, NsOptions will use `Object` for an option's type class if none is specified. You can override this on a per-namespaces basis using the `option_type_class` method. Call this and all options will be defined using the given class by default.
|
185
|
+
|
186
|
+
Note, this setting applies recursively, so all child namespaces honor it as well. You can override this by specifying a new type class on your child namespaces.
|
259
187
|
|
260
188
|
```ruby
|
261
|
-
|
262
|
-
|
263
|
-
|
189
|
+
# you can use an abbreviated syntax
|
190
|
+
#...
|
191
|
+
options :settings do
|
192
|
+
opt_type_class Fixnum
|
264
193
|
|
265
|
-
|
194
|
+
option :opt1, :default => 1
|
195
|
+
#...
|
196
|
+
|
197
|
+
# you can also pass in the option type class when defining the ns
|
198
|
+
#...
|
199
|
+
options :settings, Fixnum do
|
200
|
+
option :opt1, :default => 1
|
201
|
+
#...
|
266
202
|
```
|
267
203
|
|
268
|
-
###
|
204
|
+
### Ruby Classes As A Type Class
|
269
205
|
|
270
|
-
|
206
|
+
NsOptions will allow you to use many of Ruby's standard objects as type classes and still handle coercing values appropriately.
|
271
207
|
|
272
208
|
```ruby
|
273
|
-
module
|
209
|
+
module Example
|
274
210
|
include NsOptions
|
275
|
-
options :settings do
|
276
|
-
option :stage, Stage
|
277
|
-
end
|
278
|
-
end
|
279
|
-
|
280
|
-
App.settings.stage = Stage.new("development") # no type coercion is done here, the value is already a Stage
|
281
211
|
|
282
|
-
|
283
|
-
|
212
|
+
options :stuff do
|
213
|
+
option :string, String
|
214
|
+
option :integer, Integer
|
215
|
+
option :float, Float
|
216
|
+
option :symbol, Symbol
|
217
|
+
option :hash, Hash
|
218
|
+
option :array, Array
|
219
|
+
end
|
284
220
|
end
|
285
221
|
|
286
|
-
|
222
|
+
Example.stuff.string = 1
|
223
|
+
Example.stuff.string #=> "1", the same as doing String(1)
|
224
|
+
Example.stuff.integer = 5.0
|
225
|
+
Example.stuff.integer # => 5, this time it's Integer(5.0)
|
226
|
+
Example.stuff.float = "5.0"
|
227
|
+
Example.stuff.float #=> 5.0, same as Float("5.0")
|
287
228
|
|
288
|
-
|
229
|
+
Example.stuff.symbol = "awesome"
|
230
|
+
Example.stuff.symbol #=> :awesome
|
231
|
+
Example.stuff.hash = { :a => 'b' }
|
232
|
+
Example.stuff.hash # => returns the same hash
|
233
|
+
Example.stuff.array = [ 1, 2, 3 ]
|
234
|
+
Example.stuff.array # => returns the same array
|
289
235
|
```
|
290
236
|
|
291
|
-
|
237
|
+
### Rules
|
292
238
|
|
293
|
-
|
294
|
-
class Root < Pathname
|
295
|
-
def initialize(path, app_name)
|
296
|
-
super("#{path}/#{app_name}")
|
297
|
-
end
|
298
|
-
end
|
299
|
-
```
|
239
|
+
An option can be defined with certain rules that extend the behavior of the option.
|
300
240
|
|
301
|
-
|
241
|
+
#### Default
|
302
242
|
|
303
243
|
```ruby
|
304
|
-
|
305
|
-
|
306
|
-
app_name ||= App.settings.name # this might be one way to solve this
|
307
|
-
super("#{path}/#{app_name}")
|
308
|
-
end
|
244
|
+
settings do
|
245
|
+
option :opt1, :default => "development"
|
309
246
|
end
|
247
|
+
settings.opt1 #=> 'development'
|
248
|
+
settings.opt1 = 'production' #=> 'production'
|
310
249
|
```
|
311
250
|
|
312
|
-
|
313
|
-
|
314
|
-
### Custom type class return values
|
315
|
-
|
316
|
-
It may be useful to use a custom type class as a silent value handler. You don't necessarily care that this option is some handler class - you just want flexible ways to set its value and get a meaningful return value when you read it.
|
317
|
-
|
318
|
-
When reading option values, NsOptions will first check and see if the option value responds to the `returned_value` method. If it does, NsOptions will return that value instead of the instance of the type class. If not it will return the type class instance as normal.
|
251
|
+
A default value runs through the same logic as if you set the value manually, so it will be coerced if necessary.
|
319
252
|
|
320
|
-
|
253
|
+
#### Required
|
321
254
|
|
322
255
|
```ruby
|
323
|
-
|
324
|
-
|
325
|
-
# remove any trailing '/'
|
326
|
-
# ensure single leading '/'
|
327
|
-
|
328
|
-
def initialize(value)
|
329
|
-
@hosted_at = value.sub(/\/+$/, '').sub(/^\/*/, '/')
|
330
|
-
end
|
331
|
-
|
332
|
-
def returned_value
|
333
|
-
@hosted_at
|
334
|
-
end
|
256
|
+
settings do
|
257
|
+
option :opt1, :required => true
|
335
258
|
end
|
336
259
|
|
337
|
-
|
338
|
-
|
260
|
+
settings.required_set? #=> false
|
261
|
+
settings.root = "/path/to/somewhere"
|
262
|
+
settings.required_set? #=> true
|
263
|
+
```
|
339
264
|
|
340
|
-
|
341
|
-
option :hosted_at, HostedAt
|
342
|
-
end
|
343
|
-
end
|
265
|
+
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.
|
344
266
|
|
345
|
-
|
346
|
-
thing.is.hosted_at # => nil
|
347
|
-
thing.is.hosted_at = "path/to/resource/"
|
348
|
-
thing.is.hosted_at # => "/path/to/resource"
|
349
|
-
```
|
267
|
+
The built in `required_set?` method checks to see if all the options for the namespace that have been marked `:required => true` are set. It will recursively check any sub-namespaces.
|
350
268
|
|
351
|
-
|
269
|
+
#### Args
|
352
270
|
|
353
|
-
|
271
|
+
Another rule that you can specify is args.
|
354
272
|
|
355
273
|
```ruby
|
356
|
-
|
357
|
-
|
358
|
-
options :stuff do
|
359
|
-
option :string, String
|
360
|
-
option :integer, Integer
|
361
|
-
option :float, Float
|
362
|
-
option :symbol, Symbol
|
363
|
-
option :hash, Hash
|
364
|
-
option :array, Array
|
365
|
-
end
|
274
|
+
class MyCustomTypeClass
|
275
|
+
def initialize(value, arg1, arg2); end
|
366
276
|
end
|
367
277
|
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
Example.stuff.integer # => 5, this time it's Integer(5.0)
|
372
|
-
Example.stuff.float = "5.0"
|
373
|
-
Example.stuff.float # => 5.0, same as Float("5.0")
|
374
|
-
```
|
375
|
-
|
376
|
-
`Symbol`, `Hash` and `Array` work, but ruby doesn't provide built in type casting for these.
|
278
|
+
settings do
|
279
|
+
option :opt1, MyCustomTypeClass, :args => lambda{ ["arg 1's value", "arg 2's value"] }
|
280
|
+
end
|
377
281
|
|
378
|
-
|
379
|
-
|
380
|
-
Example.stuff.symbol # => :awesome, watch out, this will try calling to_sym on the passed value, so it can error
|
381
|
-
Example.stuff.hash = { :a => 'b' }
|
382
|
-
Example.stuff.hash # => returns the same hash, does Hash.new.merge(value)
|
383
|
-
Example.stuff.array = [ 1, 2, 3 ]
|
384
|
-
Example.stuff.array # => returns the same array, Array is the only one that works without anything special, Array.new(value)
|
282
|
+
# equivalent to: `settings.opt1 = MyCustomTypeClass.new("a value", "arg 1's value", "arg 2's value")
|
283
|
+
settings.opt1 = 'a value'
|
385
284
|
```
|
386
285
|
|
387
|
-
|
286
|
+
This allows you to pass additional arguments when coercing option values. The first argument will always be the value to coerce. Any additional arguments will be appended on after the value when calling the initializer.
|
287
|
+
|
388
288
|
|
389
|
-
|
289
|
+
### Lazily eval'd options
|
390
290
|
|
391
|
-
|
291
|
+
Sometimes, you may want to set an option to a value that shouldn'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.
|
392
292
|
|
393
|
-
|
293
|
+
Here are some examples:
|
394
294
|
|
395
295
|
```ruby
|
396
|
-
|
397
|
-
|
296
|
+
# dynamic value
|
297
|
+
options(:dynamic) do
|
298
|
+
option :rand, :default => Proc.new { rand(1000) }
|
398
299
|
end
|
399
|
-
App.settings.stage # => instead of nil this will be 'development'
|
400
|
-
```
|
401
|
-
|
402
|
-
A default value runs through the same logic as if you set the value manually, so it will be coerced if necessary.
|
403
300
|
|
404
|
-
|
301
|
+
dynamic.rand #=> 347
|
302
|
+
dynamic.rand #=> 529
|
405
303
|
|
406
|
-
It's also possible to flag an option as _required_.
|
407
304
|
|
408
|
-
|
409
|
-
|
410
|
-
option :
|
305
|
+
# self referential value
|
306
|
+
options(:selfref) do
|
307
|
+
option :something, :default => "123"
|
308
|
+
option :else, :default => Proc.new { self.something }
|
411
309
|
end
|
412
310
|
|
413
|
-
|
414
|
-
|
415
|
-
|
311
|
+
selfref.something #=> "123"
|
312
|
+
selfref.else #=> "123"
|
313
|
+
selfref.something = 456
|
314
|
+
selfref.else #=> 456
|
416
315
|
```
|
417
316
|
|
418
|
-
|
419
|
-
|
420
|
-
The built in `required_set?` method checks to see if all the options for the namespace that have been marked `:required => true` are set. It does not recursively check any child namespaces.
|
421
|
-
|
422
|
-
#### Args
|
423
|
-
|
424
|
-
Another rule that you can specify is args. This allows you to pass more arguments to a type class.
|
317
|
+
If you really want your option to read and write Procs and not do this lazy eval behavior, just define the option with a `Proc` type class.
|
425
318
|
|
426
319
|
```ruby
|
427
|
-
|
428
|
-
|
429
|
-
app_name = app_name.respond_to?(:call) ? app_name.call : app_name
|
430
|
-
super("#{path}/#{app_name}")
|
431
|
-
end
|
432
|
-
end
|
433
|
-
|
434
|
-
App.settings do
|
435
|
-
option :name
|
436
|
-
option :root, Root, :args => lambda{ App.settings.name }
|
320
|
+
options(:explicit) do
|
321
|
+
option :a_proc, Proc, :default => Proc.new { rand(1000) }
|
437
322
|
end
|
438
323
|
|
439
|
-
|
440
|
-
App.settings.root = "/path/to"
|
441
|
-
App.settings.root # => /path/to/example, uses the args rule to build the path
|
324
|
+
explicit.a_proc #=> <the proc obj>
|
442
325
|
```
|
443
326
|
|
444
|
-
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.
|
445
|
-
|
446
327
|
## NsOptions::Proxy
|
447
|
-
|
328
|
+
|
329
|
+
Mix in `NsOptions::Proxy` to any module/class to make it proxy a namespace. This essentially turns your receiver into a namespace - you can interact with it just as if it were a namespace object. For example:
|
448
330
|
|
449
331
|
```ruby
|
450
332
|
module Something
|
@@ -469,7 +351,7 @@ Something.each do |opt_name, opt_value|
|
|
469
351
|
end
|
470
352
|
```
|
471
353
|
|
472
|
-
|
354
|
+
While your `Something` behaves like a namespace, you can still define methods and add to it just as you would normally in Ruby:
|
473
355
|
|
474
356
|
```ruby
|
475
357
|
module Something
|
@@ -493,9 +375,7 @@ end
|
|
493
375
|
|
494
376
|
### Proxy initialization
|
495
377
|
|
496
|
-
Mixing in Proxy
|
497
|
-
|
498
|
-
A Module example
|
378
|
+
Mixing in Proxy will add a default initializer for you as well. This initializer allows you to call `new` on your proxy, passing it a hash of key-values. These key values will be applied to the proxy using the `Namespace#apply` logic. This allows you to use Proxy objects as option types and maintain the option type-casting and defaulting behavior.
|
499
379
|
|
500
380
|
```ruby
|
501
381
|
module Things
|
@@ -513,18 +393,24 @@ t = Thing.new(:one => 1, :two => 2, :three => 3)
|
|
513
393
|
t.to_hash # => {:one => 1, :two => 2, :three => 3}
|
514
394
|
```
|
515
395
|
|
516
|
-
|
396
|
+
## Installation
|
517
397
|
|
518
|
-
|
519
|
-
class Thing
|
520
|
-
include NsOptions::Proxy
|
398
|
+
Add this line to your application's Gemfile:
|
521
399
|
|
522
|
-
|
523
|
-
option :two
|
524
|
-
end
|
400
|
+
gem 'ns-options'
|
525
401
|
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
402
|
+
And then execute:
|
403
|
+
|
404
|
+
$ bundle
|
405
|
+
|
406
|
+
Or install it yourself as:
|
407
|
+
|
408
|
+
$ gem install ns-options
|
409
|
+
|
410
|
+
## Contributing
|
411
|
+
|
412
|
+
1. Fork it
|
413
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
414
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
415
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
416
|
+
5. Create new Pull Request
|