ns-options 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +8 -0
- data/README.markdown +151 -0
- data/Rakefile +5 -0
- data/lib/ns-options/has_options.rb +36 -0
- data/lib/ns-options/helper.rb +34 -0
- data/lib/ns-options/namespace.rb +136 -0
- data/lib/ns-options/namespaces.rb +22 -0
- data/lib/ns-options/option/boolean.rb +27 -0
- data/lib/ns-options/option.rb +70 -0
- data/lib/ns-options/options.rb +65 -0
- data/lib/ns-options/version.rb +3 -0
- data/lib/ns-options.rb +9 -0
- data/ns-options.gemspec +19 -0
- data/test/helper.rb +14 -0
- data/test/integration/app_test.rb +63 -0
- data/test/integration/user_test.rb +57 -0
- data/test/support/app.rb +12 -0
- data/test/support/user.rb +13 -0
- data/test/unit/ns-options/has_options_test.rb +67 -0
- data/test/unit/ns-options/helper_test.rb +63 -0
- data/test/unit/ns-options/namespace_test.rb +232 -0
- data/test/unit/ns-options/namespaces_test.rb +55 -0
- data/test/unit/ns-options/option/boolean_test.rb +67 -0
- data/test/unit/ns-options/option_test.rb +191 -0
- data/test/unit/ns-options/options_test.rb +227 -0
- metadata +117 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.markdown
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
# Namespace Options
|
2
|
+
|
3
|
+
Namespace Options provides a clean interface for defining, organizing and using options. Options are defined through a clean syntax that clearly shows what can be set and read. Options can be organized into namespaces as well. This allows multiple libraries to share common options but still have their own specific options separated. Reading and writing options is as simple as if they were accessors. Furthermore, you can provide your own _type_ class that allows you to provide extended functionality to a simple option.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add the following to your Gemfile and `bundle install`:
|
8
|
+
|
9
|
+
gem 'ns-options'
|
10
|
+
|
11
|
+
## Usage
|
12
|
+
|
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
|
+
```ruby
|
18
|
+
module App
|
19
|
+
include NsOptions::HasOptions
|
20
|
+
options(:settings) do
|
21
|
+
option :root, Pathname
|
22
|
+
option :stage
|
23
|
+
end
|
24
|
+
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
|
+
|
29
|
+
```ruby
|
30
|
+
App.settings.root = "/a/path/to/the/root"
|
31
|
+
App.settings.root.join("log", "test.log") # => "/a/path/to/the/root/log/test.log" (a Pathname instance)
|
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 a `String`. 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
|
56
|
+
|
57
|
+
App.settings.stage = "development"
|
58
|
+
App.settings.stage.development? # => true
|
59
|
+
App.settings.stage = "test"
|
60
|
+
App.settings.stage.development? # => false
|
61
|
+
```
|
62
|
+
|
63
|
+
This allows you to add extended functionality to your options. The only condition is that the `initialize` accepts a single argument. This argument is always the value that was used with the writer. For the above, the `Stage` class received `"development"` and `"test"` in it's initialize method.
|
64
|
+
|
65
|
+
### Namespaces
|
66
|
+
|
67
|
+
Namespaces allow you to organize and share options. With the previously mentioned `App` module and it's options you could create a namespace for another library:
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
module Data # data is a library for retrieving persisted data from some resource
|
71
|
+
|
72
|
+
App.settings.namespace(:data) do
|
73
|
+
option :server
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.config
|
77
|
+
App.settings.data
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
```
|
82
|
+
|
83
|
+
Now I can set a server option for data that is separate from the main `App` settings:
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
Data.config.server = "127.0.0.1:1234"
|
87
|
+
|
88
|
+
App.server # => NoMethodError
|
89
|
+
```
|
90
|
+
|
91
|
+
Since the data namespace was created from the `App` settings (which is also a namespace) it can access it's parent's options:
|
92
|
+
|
93
|
+
```ruby
|
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:
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
class User
|
101
|
+
include NsOptions::HasOptions
|
102
|
+
options(:preferences) do
|
103
|
+
option :home_page
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
```
|
108
|
+
|
109
|
+
A namespace will be created for the `User` class itself. Which can have options added and even set. Once a user instance is created, it will create a child namespace from the classes. Thus, it will be able to access and use any options on the class:
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
user = User.new
|
113
|
+
user.preferences.home_page = "/home"
|
114
|
+
```
|
115
|
+
|
116
|
+
### Dynamically Defined Options
|
117
|
+
|
118
|
+
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:
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
App.settings.logger = Logger.new(App.settings.root.join("log", "test.log"))
|
122
|
+
|
123
|
+
App.settings.logger.info("Hello World")
|
124
|
+
```
|
125
|
+
|
126
|
+
Writing to a namespace with a previously undefined option will create a new option. The type class will be pulled from whatever object you write with. In the above case, the option defined would have it's type class set to `Logger` and would try to convert any new values to an instance of `Logger`.
|
127
|
+
|
128
|
+
## License
|
129
|
+
|
130
|
+
Copyright (c) 2011 Collin Redding and Team Insight
|
131
|
+
|
132
|
+
Permission is hereby granted, free of charge, to any person
|
133
|
+
obtaining a copy of this software and associated documentation
|
134
|
+
files (the "Software"), to deal in the Software without
|
135
|
+
restriction, including without limitation the rights to use,
|
136
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
137
|
+
copies of the Software, and to permit persons to whom the
|
138
|
+
Software is furnished to do so, subject to the following
|
139
|
+
conditions:
|
140
|
+
|
141
|
+
The above copyright notice and this permission notice shall be
|
142
|
+
included in all copies or substantial portions of the Software.
|
143
|
+
|
144
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
145
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
146
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
147
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
148
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
149
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
150
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
151
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
module NsOptions
|
2
|
+
|
3
|
+
module HasOptions
|
4
|
+
class << self
|
5
|
+
|
6
|
+
def included(klass)
|
7
|
+
klass.class_eval do
|
8
|
+
extend NsOptions::HasOptions::DSL
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
module DSL
|
15
|
+
|
16
|
+
def options(name, key = nil, &block)
|
17
|
+
key ||= name.to_s
|
18
|
+
self.class_eval <<-DEFINE_METHOD
|
19
|
+
|
20
|
+
def #{name}(&block)
|
21
|
+
@#{name} ||= NsOptions::Helper.new_child_namespace(self, '#{name}', &block)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.#{name}(&block)
|
25
|
+
@#{name} ||= NsOptions::Helper.new_namespace('#{key}', &block)
|
26
|
+
end
|
27
|
+
|
28
|
+
DEFINE_METHOD
|
29
|
+
self.send(name, &block)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module NsOptions
|
2
|
+
|
3
|
+
module Helper
|
4
|
+
module_function
|
5
|
+
|
6
|
+
# Common method for creating a new namespace
|
7
|
+
def new_namespace(key, parent = nil, &block)
|
8
|
+
namespace = NsOptions::Namespace.new(key, parent)
|
9
|
+
namespace.define(&block)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Common method for creating a new child namespace, using the owner's class's options as the
|
13
|
+
# parent.
|
14
|
+
def new_child_namespace(owner, name, &block)
|
15
|
+
parent = owner.class.send(name)
|
16
|
+
method = "#{name}_key"
|
17
|
+
key = if owner.respond_to?(method)
|
18
|
+
owner.send(method)
|
19
|
+
else
|
20
|
+
"#{owner.class.to_s.split('::').last.downcase}_#{owner.object_id}"
|
21
|
+
end
|
22
|
+
namespace = parent.namespace(name, key)
|
23
|
+
namespace.define(&block)
|
24
|
+
end
|
25
|
+
|
26
|
+
def fetch_and_define_option(namespace, option_name)
|
27
|
+
option = namespace.options.fetch(option_name)
|
28
|
+
namespace.option(option.name, option.type_class, option.rules)
|
29
|
+
option
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
module NsOptions
|
2
|
+
|
3
|
+
class Namespace
|
4
|
+
attr_accessor :options, :metaclass
|
5
|
+
|
6
|
+
def initialize(key, parent = nil)
|
7
|
+
self.metaclass = (class << self; self; end)
|
8
|
+
self.options = NsOptions::Options.new(key, parent)
|
9
|
+
end
|
10
|
+
|
11
|
+
def required_set?
|
12
|
+
self.options.required_set?
|
13
|
+
end
|
14
|
+
|
15
|
+
# Define an option for this namespace. Add the option to the namespace's options collection
|
16
|
+
# and then define accessors for the option. With the following:
|
17
|
+
#
|
18
|
+
# namespace.option(:root, String, { :some_option => true })
|
19
|
+
#
|
20
|
+
# you will get accessors for root:
|
21
|
+
#
|
22
|
+
# namespace.root = "something" # set's the root option to 'something'
|
23
|
+
# namespace.root # => "something"
|
24
|
+
# namespace.root("something else") # set's the root option to `something-else`
|
25
|
+
#
|
26
|
+
# The defined option is returned as well.
|
27
|
+
def option(*args)
|
28
|
+
option = self.options.add(*args)
|
29
|
+
|
30
|
+
self.metaclass.class_eval <<-DEFINE_METHOD
|
31
|
+
|
32
|
+
def #{option.name}(*args)
|
33
|
+
if !args.empty?
|
34
|
+
self.send("#{option.name}=", *args)
|
35
|
+
else
|
36
|
+
self.options.get(:#{option.name})
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def #{option.name}=(*args)
|
41
|
+
value = args.size == 1 ? args.first : args
|
42
|
+
self.options.set(:#{option.name}, value)
|
43
|
+
end
|
44
|
+
|
45
|
+
DEFINE_METHOD
|
46
|
+
|
47
|
+
option
|
48
|
+
end
|
49
|
+
|
50
|
+
# Define a namespace under this namespace. Firstly, a new key is constructured from this current
|
51
|
+
# namespace's key and the name for the new namespace. The namespace is then added to the
|
52
|
+
# options collection. Finally a reader method is defined for accessing the namespace. With the
|
53
|
+
# following:
|
54
|
+
#
|
55
|
+
# parent_namespace.namespace(:specific) do
|
56
|
+
# option :root
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# you will get a reader for the namespace:
|
60
|
+
#
|
61
|
+
# parent_namespace.specific # => returns the namespace
|
62
|
+
# parent_namespace.specific.root = "something" # => options are accessed in the same way
|
63
|
+
#
|
64
|
+
# The defined namespaces is returned as well.
|
65
|
+
def namespace(name, key = nil, &block)
|
66
|
+
key = "#{self.options.key}:#{(key || name)}"
|
67
|
+
namespace = self.options.namespaces.add(name, key, self, &block)
|
68
|
+
|
69
|
+
self.metaclass.class_eval <<-DEFINE_METHOD
|
70
|
+
|
71
|
+
def #{name}(&block)
|
72
|
+
namespace = self.options.namespaces.get("#{name}")
|
73
|
+
namespace.define(&block) if block
|
74
|
+
namespace
|
75
|
+
end
|
76
|
+
|
77
|
+
DEFINE_METHOD
|
78
|
+
|
79
|
+
namespace
|
80
|
+
end
|
81
|
+
|
82
|
+
# The define method is provided for convenience and commonization. The internal system
|
83
|
+
# uses it to commonly use a block with a namespace. The method can be used externally when
|
84
|
+
# a namespace is created separately from where options are added/set on it. For example:
|
85
|
+
#
|
86
|
+
# parent_namespace.namespace(:specific)
|
87
|
+
#
|
88
|
+
# parent_namespace.specific.define do
|
89
|
+
# option :root
|
90
|
+
# end
|
91
|
+
#
|
92
|
+
# Will define a new namespace under the parent namespace and then will later on add options to
|
93
|
+
# it.
|
94
|
+
def define(&block)
|
95
|
+
if block && block.arity > 0
|
96
|
+
yield self
|
97
|
+
elsif block
|
98
|
+
self.instance_eval(&block)
|
99
|
+
end
|
100
|
+
self
|
101
|
+
end
|
102
|
+
|
103
|
+
# There are a number of cases we want to watch for:
|
104
|
+
# 1. A reader of a 'known' option. This case is for an option that's been defined for an
|
105
|
+
# ancestor of this namespace but not directly for this namespace. In this case we fetch
|
106
|
+
# the options definition and use it to define the option directly for this namespace.
|
107
|
+
# 2. A writer of a 'known' option. This case is similar to the above, but instead we are
|
108
|
+
# wanting to write a value. We need to fetch the option definition, define it and then
|
109
|
+
# we write the option as we normally would.
|
110
|
+
# 3. A dynamic writer. The option is not 'known' to the namespace, so we use the value and it's
|
111
|
+
# class to define the option for this namespace. Then we just use the writer as we normally
|
112
|
+
# would.
|
113
|
+
def method_missing(method, *args, &block)
|
114
|
+
option_name = method.to_s.gsub("=", "")
|
115
|
+
value = args.size == 1 ? args[0] : args
|
116
|
+
if args.empty? && self.respond_to?(option_name)
|
117
|
+
option = NsOptions::Helper.fetch_and_define_option(self, option_name)
|
118
|
+
self.send(option.name)
|
119
|
+
elsif !args.empty? && self.respond_to?(option_name)
|
120
|
+
option = NsOptions::Helper.fetch_and_define_option(self, option_name)
|
121
|
+
self.send("#{option.name}=", value)
|
122
|
+
elsif !args.empty?
|
123
|
+
option = self.option(option_name, value.class)
|
124
|
+
self.send("#{option.name}=", value)
|
125
|
+
else
|
126
|
+
super
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def respond_to?(method)
|
131
|
+
super || self.options.is_defined?(method.to_s.gsub("=", ""))
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module NsOptions
|
2
|
+
|
3
|
+
class Namespaces < Hash
|
4
|
+
|
5
|
+
def [](name)
|
6
|
+
super(name.to_sym)
|
7
|
+
end
|
8
|
+
def []=(name, value)
|
9
|
+
super(name.to_sym, value)
|
10
|
+
end
|
11
|
+
|
12
|
+
def add(name, key, parent = nil, &block)
|
13
|
+
self[name] = NsOptions::Helper.new_namespace(key, parent, &block)
|
14
|
+
end
|
15
|
+
|
16
|
+
def get(name)
|
17
|
+
self[name]
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module NsOptions
|
2
|
+
class Option
|
3
|
+
|
4
|
+
class Boolean
|
5
|
+
attr_accessor :actual
|
6
|
+
|
7
|
+
def initialize(value)
|
8
|
+
self.actual = value
|
9
|
+
end
|
10
|
+
|
11
|
+
def actual=(new_value)
|
12
|
+
@actual = self.convert(new_value)
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
def convert(value)
|
18
|
+
if [ nil, 0, '0', false, 'false' ].include?(value)
|
19
|
+
false
|
20
|
+
elsif value
|
21
|
+
true
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module NsOptions
|
2
|
+
|
3
|
+
class Option
|
4
|
+
autoload :Boolean, 'ns-options/option/boolean'
|
5
|
+
|
6
|
+
attr_accessor :name, :value, :type_class, :rules
|
7
|
+
|
8
|
+
def initialize(name, type_class, rules = {})
|
9
|
+
self.name = name.to_s
|
10
|
+
self.type_class = self.usable_type_class(type_class)
|
11
|
+
self.rules = rules
|
12
|
+
self.value = rules[:default]
|
13
|
+
end
|
14
|
+
|
15
|
+
def value
|
16
|
+
if self.type_class == NsOptions::Option::Boolean
|
17
|
+
@value and @value.actual
|
18
|
+
else
|
19
|
+
@value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def value=(new_value)
|
24
|
+
@value = if (new_value.class == self.type_class) || new_value.nil?
|
25
|
+
new_value
|
26
|
+
else
|
27
|
+
self.coerce(new_value)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def is_set?
|
32
|
+
self.value.respond_to?(:is_set?) ? self.value.is_set? : !self.value.nil?
|
33
|
+
end
|
34
|
+
|
35
|
+
def required?
|
36
|
+
!!self.rules[:required] || !!self.rules[:require]
|
37
|
+
end
|
38
|
+
|
39
|
+
def ==(other)
|
40
|
+
[ :name, :type_class, :rules, :value ].inject(true) do |bool, attribute|
|
41
|
+
bool && (self.send(attribute) == other.send(attribute))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
protected
|
46
|
+
|
47
|
+
def coerce(new_value)
|
48
|
+
if [ Integer, Float, String ].include?(self.type_class)
|
49
|
+
# ruby type conversion, i.e. String(1)
|
50
|
+
Object.send(self.type_class.to_s.to_sym, new_value)
|
51
|
+
elsif self.type_class == Hash
|
52
|
+
{}.merge(new_value)
|
53
|
+
else
|
54
|
+
self.type_class.new(new_value)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def usable_type_class(type_class)
|
59
|
+
if type_class == Fixnum
|
60
|
+
Integer
|
61
|
+
elsif [ TrueClass, FalseClass ].include?(type_class)
|
62
|
+
NsOptions::Option::Boolean
|
63
|
+
else
|
64
|
+
(type_class || String)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module NsOptions
|
2
|
+
|
3
|
+
class Options < Hash
|
4
|
+
attr_accessor :key, :parent, :children
|
5
|
+
alias :namespaces :children
|
6
|
+
|
7
|
+
def initialize(key, parent = nil)
|
8
|
+
self.key = key.to_s
|
9
|
+
self.parent = parent
|
10
|
+
self.children = NsOptions::Namespaces.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def [](name)
|
14
|
+
super(name.to_sym)
|
15
|
+
end
|
16
|
+
def []=(name, value)
|
17
|
+
super(name.to_sym, value)
|
18
|
+
end
|
19
|
+
|
20
|
+
def add(*args)
|
21
|
+
options = args.last.kind_of?(Hash) ? args.pop : {}
|
22
|
+
option = NsOptions::Option.new(args[0], args[1], options)
|
23
|
+
self[option.name] = option
|
24
|
+
end
|
25
|
+
|
26
|
+
def del(name)
|
27
|
+
self[name] = nil
|
28
|
+
end
|
29
|
+
alias :remove :del
|
30
|
+
|
31
|
+
def get(name)
|
32
|
+
option = self[name]
|
33
|
+
if option && !option.value.nil?
|
34
|
+
option.value
|
35
|
+
elsif self.parent_options
|
36
|
+
self.parent_options.get(name)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def set(name, new_value)
|
41
|
+
self[name].value = new_value
|
42
|
+
self[name]
|
43
|
+
end
|
44
|
+
|
45
|
+
def fetch(name)
|
46
|
+
self[name] || (self.parent_options && self.parent_options.fetch(name))
|
47
|
+
end
|
48
|
+
|
49
|
+
def is_defined?(name)
|
50
|
+
!!self[name] || !!(self.parent_options && self.parent_options.is_defined?(name))
|
51
|
+
end
|
52
|
+
|
53
|
+
def parent_options
|
54
|
+
self.parent and self.parent.options
|
55
|
+
end
|
56
|
+
|
57
|
+
def required_set?
|
58
|
+
self.values.reject{|option| !option.required? }.inject(true) do |bool, option|
|
59
|
+
bool && option.is_set?
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
data/lib/ns-options.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
module NsOptions
|
2
|
+
autoload :HasOptions, 'ns-options/has_options'
|
3
|
+
autoload :Helper, 'ns-options/helper'
|
4
|
+
autoload :Namespace, 'ns-options/namespace'
|
5
|
+
autoload :Namespaces, 'ns-options/namespaces'
|
6
|
+
autoload :Option, 'ns-options/option'
|
7
|
+
autoload :Options, 'ns-options/options'
|
8
|
+
autoload :VERSION, 'ns-options/version'
|
9
|
+
end
|
data/ns-options.gemspec
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/ns-options/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Collin Redding"]
|
6
|
+
gem.email = ["collin.redding@reelfx.com"]
|
7
|
+
gem.description = %q{Define and use namespaced options with a clean interface.}
|
8
|
+
gem.summary = %q{Define and use namespaced options with a clean interface.}
|
9
|
+
|
10
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
11
|
+
gem.files = `git ls-files`.split("\n")
|
12
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
13
|
+
gem.name = "ns-options"
|
14
|
+
gem.require_paths = ["lib"]
|
15
|
+
gem.version = NsOptions::VERSION
|
16
|
+
|
17
|
+
gem.add_development_dependency("assert", ["~>0.6.0"])
|
18
|
+
#gem.add_development_dependency("assert-mocha", ["~>0.1.0"])
|
19
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
root_path = File.expand_path("../..", __FILE__)
|
4
|
+
if !$LOAD_PATH.include?(root_path)
|
5
|
+
$LOAD_PATH.unshift(root_path)
|
6
|
+
end
|
7
|
+
require 'ns-options'
|
8
|
+
|
9
|
+
require 'test/support/app'
|
10
|
+
require 'test/support/user'
|
11
|
+
|
12
|
+
if defined?(Assert)
|
13
|
+
require 'assert-mocha'
|
14
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'assert'
|
2
|
+
|
3
|
+
module App
|
4
|
+
|
5
|
+
class BaseTest < Assert::Context
|
6
|
+
desc "the App module"
|
7
|
+
setup do
|
8
|
+
@module = App
|
9
|
+
end
|
10
|
+
subject{ @module }
|
11
|
+
|
12
|
+
should have_instance_methods :options, :settings
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
class DefineTest < BaseTest
|
17
|
+
desc "defined"
|
18
|
+
setup do
|
19
|
+
stage = @stage = "test"
|
20
|
+
root_path = @root_path = File.expand_path("../../..", __FILE__)
|
21
|
+
logger = @logger = Logger.new(File.join(@root_path, "log", "test.log"))
|
22
|
+
run = @run = true
|
23
|
+
@module.settings.define do |namespace|
|
24
|
+
namespace.stage stage
|
25
|
+
namespace.root = root_path
|
26
|
+
namespace.logger = logger
|
27
|
+
|
28
|
+
namespace.sub do
|
29
|
+
run_commands run
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
subject{ @module.settings }
|
34
|
+
|
35
|
+
should have_instance_methods :namespace, :option, :define, :options, :metaclass
|
36
|
+
should have_accessors :stage, :root, :logger
|
37
|
+
should have_instance_methods :sub
|
38
|
+
|
39
|
+
should "have set the stage to 'test'" do
|
40
|
+
assert_equal @stage, subject.stage
|
41
|
+
end
|
42
|
+
should "have set the root to this gem's dir" do
|
43
|
+
assert_equal Pathname.new(@root_path), subject.root
|
44
|
+
end
|
45
|
+
should "have set the logger to the passed logger" do
|
46
|
+
assert_equal @logger, subject.logger
|
47
|
+
assert_same @logger, subject.logger
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class SubNamespaceTest < DefineTest
|
52
|
+
desc "the sub namespace"
|
53
|
+
subject{ @module.settings.sub }
|
54
|
+
|
55
|
+
should "have set the run_commands option" do
|
56
|
+
assert_equal @run, subject.run_commands
|
57
|
+
end
|
58
|
+
should "have access to it's parent's options" do
|
59
|
+
assert_equal @stage, subject.stage
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|