accessible 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.ruby-version +1 -1
- data/README.md +196 -39
- data/lib/accessible.rb +13 -10
- data/lib/accessible/version.rb +1 -1
- data/test/accessible_test.rb +22 -172
- data/test/class_methods_test.rb +190 -0
- data/test/hash_methods_test.rb +0 -1
- metadata +5 -4
- data/.ruby-gemset +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: efe7ae168ae14ba3851146219742ec4df0a012d2
|
4
|
+
data.tar.gz: c15fe8888c9c05f8c26a8da5912e0a5ce763d7b1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a212b3ed52ba27dbfe4460a53f777103c120400634968e03bbdd0ac154a638dcd4da53d34410c2c7a99103b4f4ec089c0439cf337909d2403f71909dbafc1819
|
7
|
+
data.tar.gz: 354a11f3b2d58d8b2e97d9be7578231fdc835ff7ffd0388e544ae77122567f93f21614b22e7d6f0f0965467469d2161f73aa7a4bdbc0d5fb48075ba9bc267822
|
data/.gitignore
CHANGED
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
2.1.0
|
data/README.md
CHANGED
@@ -1,10 +1,32 @@
|
|
1
1
|
# Accessible
|
2
|
-
[![Build Status](https://travis-ci.org/saclark/accessible.svg)](https://travis-ci.org/saclark/accessible) [![Coverage Status](https://coveralls.io/repos/saclark/accessible/badge.svg)](https://coveralls.io/r/saclark/accessible)
|
2
|
+
[![Gem Version](https://badge.fury.io/rb/accessible.svg)](http://badge.fury.io/rb/accessible) [![Build Status](https://travis-ci.org/saclark/accessible.svg)](https://travis-ci.org/saclark/accessible) [![Coverage Status](https://coveralls.io/repos/saclark/accessible/badge.svg)](https://coveralls.io/r/saclark/accessible)
|
3
3
|
|
4
|
-
A simple and flexible means of configuration for Ruby applications.
|
4
|
+
A simple and flexible means of setting up configuration for Ruby applications.
|
5
5
|
|
6
|
-
#
|
7
|
-
|
6
|
+
# Usage
|
7
|
+
```ruby
|
8
|
+
MyConfig = Accessible.create do |config|
|
9
|
+
confg.load 'config/defaults.yml'
|
10
|
+
config.merge 'config/environments.yml', ENV['APP_ENV']
|
11
|
+
config.merge { :foo => 'bar' }
|
12
|
+
end
|
13
|
+
|
14
|
+
MyConfig.wake_me_up.before.you_go.go?
|
15
|
+
# => true
|
16
|
+
```
|
17
|
+
|
18
|
+
# Documentation
|
19
|
+
|
20
|
+
## Creating an "Accessible" class
|
21
|
+
Assign the result of calling Accessible's `create` method, passing it a block in which you may `load`/`merge` any number of data sources.
|
22
|
+
```ruby
|
23
|
+
MyConfig = Accessible.create do |config|
|
24
|
+
confg.load 'config/defaults.yml'
|
25
|
+
config.merge 'config/environments/dev.yml'
|
26
|
+
end
|
27
|
+
```
|
28
|
+
|
29
|
+
Alternatively, you can create a class, include the `Accessible` module, and `load`/`merge` your data.
|
8
30
|
```ruby
|
9
31
|
class MyConfig
|
10
32
|
include Accessible
|
@@ -14,59 +36,194 @@ class MyConfig
|
|
14
36
|
end
|
15
37
|
```
|
16
38
|
|
17
|
-
|
39
|
+
## Methods
|
40
|
+
The following class methods are available on any class created with/including Accessible.
|
18
41
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
42
|
+
* [`Accessor methods`](#user-content-accessor-methods)
|
43
|
+
* [`#load`](#user-content-load)
|
44
|
+
* [`#merge`](#user-content-merge)
|
45
|
+
* [`#[]`](#user-content--)
|
46
|
+
* [`#[]=`](#user-content--1)
|
47
|
+
* [`#to_h`](#user-content-to_h)
|
23
48
|
|
24
|
-
|
49
|
+
### Accessor methods
|
50
|
+
When you load data into your class, getter and setter methods are defined on the class and recursively through the loaded data for each key. This way you can easily walk through your data:
|
51
|
+
```ruby
|
52
|
+
MyConfig.my_deeply.nested.data_is.accessible
|
53
|
+
```
|
25
54
|
|
26
|
-
#
|
27
|
-
There are two ways to access your configuration data.
|
55
|
+
The main difference between accessor methods and [`[]`](#user-content--) and [`[]=`](#user-content--1) is that the accessor methods raise an error if a matching key does not exist.
|
28
56
|
|
29
|
-
|
57
|
+
Examples:
|
30
58
|
```ruby
|
31
|
-
|
32
|
-
|
33
|
-
:
|
34
|
-
:
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
59
|
+
MyConfig = Accessible.create do |config|
|
60
|
+
config.load({
|
61
|
+
:characters => {
|
62
|
+
:calvin => {
|
63
|
+
:alter_egos => [
|
64
|
+
{ :name => 'Spaceman Spiff' },
|
65
|
+
{ :name => 'Tracer Bullet' }
|
66
|
+
]
|
67
|
+
}
|
39
68
|
}
|
40
|
-
}
|
41
|
-
|
69
|
+
})
|
70
|
+
end
|
71
|
+
|
72
|
+
MyConfig.characters.calvin.alter_egos[0].name
|
73
|
+
# => 'Spaceman Spiff'
|
74
|
+
MyConfig.characters[:calvin].alter_egos[0].name
|
75
|
+
# => 'Spaceman Spiff'
|
76
|
+
|
77
|
+
MyConfig[:characters].calvin.alter_egos[1].name = 'Stupendous Man'
|
78
|
+
# => 'Stupendous Man'
|
79
|
+
MyConfig.characters.calvin
|
80
|
+
# => {
|
81
|
+
# :alter_egos => [
|
82
|
+
# { :name => 'Spaceman Spiff' },
|
83
|
+
# { :name => 'Stupendous Man' }
|
84
|
+
# ]
|
85
|
+
# }
|
86
|
+
|
87
|
+
MyConfig.does_not_exist
|
88
|
+
# => NoMethodError
|
89
|
+
MyConfig[:does_not_exist]
|
90
|
+
# => nil
|
91
|
+
|
92
|
+
MyConfig.does_not_exist = 'foo'
|
93
|
+
# => NoMethodError
|
94
|
+
MyConfig[:new_key] = 'a new value'
|
95
|
+
# => 'a new value'
|
96
|
+
MyConfig.new_key
|
97
|
+
# => 'a new value'
|
98
|
+
```
|
99
|
+
|
100
|
+
Be careful with this one though. It is best to treat its return value as read-only due to surprising results when used in combination with `[]=` ([read why](#user-content--1)).
|
101
|
+
|
102
|
+
### load
|
103
|
+
`load(data_source, key = nil) -> data`
|
104
|
+
|
105
|
+
Loads data into your class, wiping out any previously loaded data. It accepts a data source as well as an optional second parameter representing the name of a specific key within the data source from which data should be loaded.
|
106
|
+
|
107
|
+
A data source can be any of the following:
|
108
|
+
|
109
|
+
__Hash__
|
110
|
+
Loads the given hash:
|
111
|
+
```ruby
|
112
|
+
MyConfig.load({ :names => ['Calvin', 'Hobbes'] })
|
113
|
+
```
|
114
|
+
|
115
|
+
__String__
|
116
|
+
The given string should represent a file path to an existing yaml file to be loaded. An error will be throw if the file cannot be found:
|
117
|
+
```ruby
|
118
|
+
MyConfig.load('config/env_config.yml')
|
119
|
+
```
|
120
|
+
|
121
|
+
__Symbol__
|
122
|
+
The given symbol should represent the name of a `.yml` file located in a `/config` directory (relative to the working directory of the running process):
|
123
|
+
```ruby
|
124
|
+
MyConfig.load(:env_config)
|
42
125
|
```
|
43
126
|
|
44
|
-
|
127
|
+
Therefore, the following are equivalent
|
128
|
+
```ruby
|
129
|
+
# These are the same
|
130
|
+
MyConfig.load(:env_config)
|
131
|
+
MyConfig.load('config/env_config.yml')
|
132
|
+
```
|
133
|
+
|
134
|
+
### merge
|
135
|
+
`merge(data_source, namespace = nil) -> data`
|
136
|
+
|
137
|
+
Equivalent to [`load`](#user-content-load) with the exception that the data source is _merged_ (i.e. entries with duplicate keys are overwritten) with previously loaded data.
|
45
138
|
|
46
|
-
|
139
|
+
### []
|
140
|
+
`[key] -> value`
|
141
|
+
|
142
|
+
Gets data from your class. Returns `nil` if the key does not exist, making this method useful for assigning default values in the absence of a key.
|
47
143
|
```ruby
|
48
|
-
MyConfig.
|
144
|
+
MyConfig.load({ :calvin => 'Spaceman Spiff' })
|
145
|
+
|
146
|
+
MyConfig[:calvin]
|
147
|
+
# => 'Spaceman Spiff'
|
49
148
|
|
50
|
-
MyConfig
|
51
|
-
|
149
|
+
MyConfig[:susie]
|
150
|
+
# => nil
|
52
151
|
|
53
|
-
MyConfig
|
54
|
-
|
152
|
+
person = MyConfig[:susie] || 'Hobbes'
|
153
|
+
# => 'Hobbes'
|
55
154
|
```
|
56
155
|
|
57
|
-
|
156
|
+
### []=
|
157
|
+
`[key] = value -> value`
|
58
158
|
|
59
|
-
|
159
|
+
Sets data on your class.
|
60
160
|
```ruby
|
61
|
-
MyConfig
|
161
|
+
MyConfig.load({})
|
62
162
|
|
63
|
-
MyConfig[:
|
64
|
-
MyConfig[:
|
163
|
+
MyConfig[:calvin] = 'Spaceman Spiff'
|
164
|
+
MyConfig[:calvin]
|
165
|
+
# => 'Spaceman Spiff'
|
65
166
|
|
66
|
-
MyConfig[:
|
67
|
-
MyConfig[:
|
167
|
+
MyConfig[:calvin] = 'Stupendous Man'
|
168
|
+
MyConfig[:calvin]
|
169
|
+
# => 'Stupendous Man'
|
68
170
|
```
|
69
171
|
|
70
|
-
|
172
|
+
Note, however, __this is _not_ functionally equivalent to setting values on the result of calling `to_h` on your class or it's values.__ (e.g. `MyConfig.to_h[:foo] = 'bar'`).
|
173
|
+
|
174
|
+
The subtle difference here is that using `[]=` directly on the class ensures that the appropriate accessor methods are defined on the class, it's data, and the value being set. Thus, the following works:
|
175
|
+
```ruby
|
176
|
+
MyConfig.load({ :calvin => { :superhero => 'Spaceman Spiff' } })
|
177
|
+
|
178
|
+
MyConfig[:calvin] = { :superhero => 'Stupendous Man' }
|
179
|
+
|
180
|
+
MyConfig.calvin
|
181
|
+
# => { :superhero => 'Stupendous Man' }
|
182
|
+
|
183
|
+
MyConfig.calvin.superhero
|
184
|
+
# => 'Stupendous Man'
|
185
|
+
|
186
|
+
MyConfig.calvin[:detective] = 'Tracer Bullet'
|
187
|
+
MyConfig.calvin.detective
|
188
|
+
# => 'Tracer Bullet'
|
189
|
+
|
190
|
+
MyConfig[:susie] = 'Derkins'
|
191
|
+
MyConfig.susie
|
192
|
+
# => 'Derkins'
|
193
|
+
```
|
71
194
|
|
72
|
-
|
195
|
+
Contrast this with the following behavior:
|
196
|
+
```ruby
|
197
|
+
MyConfig.load({ :calvin => { :superhero => 'Spaceman Spiff' } })
|
198
|
+
|
199
|
+
MyConfig.to_h[:calvin] = { :superhero => 'Stupendous Man' }
|
200
|
+
|
201
|
+
# The following only works by coincidence because an accessor for :calvin
|
202
|
+
# was defined when the config data was initially loaded
|
203
|
+
MyConfig.calvin
|
204
|
+
# => { :superhero => 'Stupendous Man' }
|
205
|
+
|
206
|
+
MyConfig.calvin.superhero
|
207
|
+
# => NoMethodError: undefined method `superhero' for {:superhero=>"Stupendous Man"}:Hash
|
208
|
+
|
209
|
+
MyConfig.calvin.to_h[:detective] = 'Tracer Bullet'
|
210
|
+
MyConfig.calvin.detective
|
211
|
+
# => NoMethodError: undefined method `detective' for {:superhero=>"Stupendous Man", :detective=>"Tracer Bullet"}:Hash
|
212
|
+
|
213
|
+
MyConfig.to_h[:susie] = 'Derkins'
|
214
|
+
MyConfig.susie
|
215
|
+
# => NoMethodError: undefined method `susie' for MyConfig:Class
|
216
|
+
```
|
217
|
+
|
218
|
+
As you can see, being sure to only use `[]=` on your class and it's values _directly_ ensures the proper accessors are maintained. Setting values on the return value of `to_h` is a recipe for disaster.
|
219
|
+
|
220
|
+
### to_h
|
221
|
+
`to_h -> data`
|
222
|
+
|
223
|
+
Returns all data loaded to your class as a hash.
|
224
|
+
```ruby
|
225
|
+
MyConfig.load({ :names => ['Calvin', 'Hobbes'] })
|
226
|
+
|
227
|
+
MyConfig.to_h
|
228
|
+
# => { :names => ['Calvin', 'Hobbes'] }
|
229
|
+
```
|
data/lib/accessible.rb
CHANGED
@@ -8,24 +8,27 @@ module Accessible
|
|
8
8
|
base.extend(ClassMethods)
|
9
9
|
end
|
10
10
|
|
11
|
+
def self.create
|
12
|
+
klass = Class.new { extend ClassMethods }
|
13
|
+
yield klass if block_given?
|
14
|
+
klass
|
15
|
+
end
|
16
|
+
|
11
17
|
module ClassMethods
|
12
18
|
def to_h
|
13
19
|
@data ||= {}
|
14
20
|
end
|
15
21
|
|
16
|
-
def load(
|
22
|
+
def load(data_source, namespace = nil)
|
17
23
|
to_h.clear
|
18
|
-
merge(
|
24
|
+
merge(data_source, namespace)
|
19
25
|
end
|
20
26
|
|
21
|
-
def merge(
|
22
|
-
|
27
|
+
def merge(data_source, namespace = nil)
|
28
|
+
source_data = DataLoader.load_source(data_source)
|
29
|
+
new_data = namespace ? source_data.fetch(namespace) : source_data
|
23
30
|
|
24
|
-
|
25
|
-
@data = HashMethods.deep_merge(to_h, new_data.fetch(namespace))
|
26
|
-
else
|
27
|
-
@data = HashMethods.deep_merge(to_h, new_data)
|
28
|
-
end
|
31
|
+
@data = HashMethods.deep_merge(to_h, new_data)
|
29
32
|
|
30
33
|
Accessorizers.accessorize_obj(self)
|
31
34
|
Accessorizers.accessorize_data(to_h)
|
@@ -40,7 +43,7 @@ module Accessible
|
|
40
43
|
def []=(key, new_value)
|
41
44
|
Accessorizers.define_accessors(to_h, key)
|
42
45
|
Accessorizers.define_accessors(self, key)
|
43
|
-
to_h[key] =
|
46
|
+
to_h[key] = Accessorizers.accessorize_data(new_value)
|
44
47
|
end
|
45
48
|
end
|
46
49
|
end
|
data/lib/accessible/version.rb
CHANGED
data/test/accessible_test.rb
CHANGED
@@ -2,186 +2,36 @@ require_relative 'test_helper'
|
|
2
2
|
|
3
3
|
class AccessibleTest < Minitest::Spec
|
4
4
|
describe Accessible do
|
5
|
-
|
6
|
-
|
7
|
-
describe '#to_h' do
|
8
|
-
it 'should create `@data` if it does not exist' do
|
9
|
-
config.instance_variable_defined?(:@data).must_equal(false)
|
10
|
-
config.to_h
|
11
|
-
config.instance_variable_defined?(:@data).must_equal(true)
|
12
|
-
end
|
13
|
-
|
14
|
-
it 'should assign an empty hash to `@data` if it does not exist' do
|
15
|
-
config.instance_variable_defined?(:@data).must_equal(false)
|
16
|
-
config.to_h
|
17
|
-
config.instance_variable_get(:@data).must_equal({})
|
18
|
-
end
|
19
|
-
|
20
|
-
it 'should return a Hash instance' do
|
21
|
-
config.to_h.must_be_instance_of(Hash)
|
22
|
-
config.load({ :adding => ['data'] })
|
23
|
-
config.to_h.must_be_instance_of(Hash)
|
24
|
-
end
|
25
|
-
|
26
|
-
it 'should return `@data`' do
|
27
|
-
config.to_h.must_equal(config.instance_variable_get(:@data))
|
28
|
-
end
|
5
|
+
before do
|
6
|
+
@expected_class_methods = Accessible::ClassMethods.public_instance_methods
|
29
7
|
end
|
30
8
|
|
31
|
-
describe '#
|
32
|
-
it 'should
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
config.to_h.must_equal({ :b => 'b' })
|
37
|
-
end
|
38
|
-
|
39
|
-
it 'should assign all data if no namespace given' do
|
40
|
-
config.load({ :a => 'a', :b => 'b' })
|
41
|
-
config.to_h.must_equal({ :a => 'a', :b => 'b' })
|
42
|
-
end
|
43
|
-
|
44
|
-
it 'should assign namespaced data if namespace given' do
|
45
|
-
config.load({ :a => 'a', :foo => { :b => 'b'} }, :foo)
|
46
|
-
config.to_h.must_equal({ :b => 'b' })
|
47
|
-
end
|
48
|
-
|
49
|
-
it 'should raise a KeyError if the given namespace is not found' do
|
50
|
-
proc do
|
51
|
-
config.load({ :a => 'a', :foo => { :b => 'b'} }, :bar)
|
52
|
-
end.must_raise(KeyError)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
describe '#merge' do
|
57
|
-
it 'should accept a hash and load it' do
|
58
|
-
config.merge({ :a => 'a' })
|
59
|
-
config.to_h.must_equal({ :a => 'a' })
|
60
|
-
end
|
61
|
-
|
62
|
-
it 'should accept a symbol and load the corresponding config file' do
|
63
|
-
config.merge(:my_config)
|
64
|
-
config.to_h.must_equal({ 'data' => 'data from config/my_config.yml' })
|
65
|
-
end
|
66
|
-
|
67
|
-
it 'should accept a file path and load the file' do
|
68
|
-
config.merge('config/more_configs/my_config.yaml')
|
69
|
-
config.to_h.must_equal({ 'data' => 'data from config/more_configs/my_config.yaml' })
|
70
|
-
end
|
71
|
-
|
72
|
-
it 'should raise an error when not given a hash, symbol, or string' do
|
73
|
-
begin
|
74
|
-
config.merge(100)
|
75
|
-
rescue RuntimeError => e
|
76
|
-
e.message.must_equal("Invalid data source: 100")
|
9
|
+
describe '#included' do
|
10
|
+
it 'should extended the including class with `ClassMethods`' do
|
11
|
+
IncludedTest = Class.new { include Accessible }
|
12
|
+
@expected_class_methods.each do |method|
|
13
|
+
IncludedTest.must_respond_to(method)
|
77
14
|
end
|
78
15
|
end
|
79
|
-
|
80
|
-
it 'should not require an initial source to have been `#load`ed' do
|
81
|
-
config.merge({ :a => 'a' })
|
82
|
-
config.to_h.must_equal({ :a => 'a' })
|
83
|
-
end
|
84
|
-
|
85
|
-
it 'should mutate `@data`' do
|
86
|
-
config.load({ :foo => 'original' })
|
87
|
-
config.merge({ :foo => 'new' })
|
88
|
-
config.to_h.must_equal({ :foo => 'new' })
|
89
|
-
end
|
90
|
-
|
91
|
-
it 'should merge all data if no namespace given' do
|
92
|
-
config.load({ :a => 'a'})
|
93
|
-
config.merge({ :a => 'a', :b => 'b' })
|
94
|
-
config.to_h.must_equal({ :a => 'a', :b => 'b' })
|
95
|
-
end
|
96
|
-
|
97
|
-
it 'should merge namespaced data if namespace given' do
|
98
|
-
config.load({ :a => 'a' })
|
99
|
-
config.merge({ :foo => { :a => 'new a' }, :b => 'b' }, :foo)
|
100
|
-
config.to_h.must_equal({ :a => 'new a' })
|
101
|
-
end
|
102
|
-
|
103
|
-
it 'should raise a KeyError if the given namespace is not found' do
|
104
|
-
proc do
|
105
|
-
config.merge({ :a => 'a', :foo => { :b => 'b'} }, :bar)
|
106
|
-
end.must_raise(KeyError)
|
107
|
-
end
|
108
|
-
|
109
|
-
it 'should perform a deep merge' do
|
110
|
-
h1 = { :a => true, :b => { :c => [1, 2, 3] } }
|
111
|
-
h2 = { :a => false, :b => { :x => [3, 4, 5] } }
|
112
|
-
|
113
|
-
config.load(h1)
|
114
|
-
config.merge(h2)
|
115
|
-
config.to_h.must_equal({
|
116
|
-
:a => false,
|
117
|
-
:b => {
|
118
|
-
:c => [1, 2, 3],
|
119
|
-
:x => [3, 4, 5]
|
120
|
-
}
|
121
|
-
})
|
122
|
-
end
|
123
|
-
|
124
|
-
it 'should define acessors on `@data`' do
|
125
|
-
config.merge({ :a => 'a' })
|
126
|
-
config.to_h.singleton_methods.must_include(:a)
|
127
|
-
config.to_h.singleton_methods.must_include(:a=)
|
128
|
-
end
|
129
|
-
|
130
|
-
it 'should define accessors on the class' do
|
131
|
-
config.merge({ :a => 'a' })
|
132
|
-
config.singleton_methods.must_include(:a)
|
133
|
-
config.singleton_methods.must_include(:a=)
|
134
|
-
end
|
135
|
-
|
136
|
-
it 'should return a Hash instance' do
|
137
|
-
config.merge({}).must_be_instance_of(Hash)
|
138
|
-
end
|
139
|
-
|
140
|
-
it 'should return the result of calling #to_h after the merge' do
|
141
|
-
config.load({ :a => 'a' })
|
142
|
-
merge_result = config.merge({ :b => 'b' })
|
143
|
-
merge_result.must_equal(config.to_h)
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
describe '#[]' do
|
148
|
-
it 'should delegate to `@data`' do
|
149
|
-
config.load({ :a => 'a' })
|
150
|
-
config[:a].must_equal(config.instance_variable_get(:@data)[:a])
|
151
|
-
config[:b].must_equal(config.instance_variable_get(:@data)[:b])
|
152
|
-
end
|
153
16
|
end
|
154
17
|
|
155
|
-
describe '#
|
156
|
-
it 'should
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
config[:a] = 'a'
|
163
|
-
config.instance_variable_get(:@data).singleton_methods.must_include(:a)
|
164
|
-
config.instance_variable_get(:@data).singleton_methods.must_include(:a=)
|
165
|
-
end
|
166
|
-
|
167
|
-
it 'should define accessors on the class for the given key' do
|
168
|
-
config[:a] = 'a'
|
169
|
-
config.singleton_methods.must_include(:a)
|
170
|
-
config.singleton_methods.must_include(:a=)
|
171
|
-
end
|
172
|
-
|
173
|
-
it 'should define accessors on set values' do
|
174
|
-
config[:a] = { :foo => 'foo value' }
|
175
|
-
config.a.singleton_methods.must_include(:foo)
|
176
|
-
config.a.foo.must_equal('foo value')
|
177
|
-
end
|
178
|
-
|
179
|
-
it 'should delegate to `@data`' do
|
180
|
-
(config[:a] = 'a').must_equal(config.instance_variable_get(:@data)[:a] = 'a')
|
18
|
+
describe '#create' do
|
19
|
+
it 'should return a `Class` instance extended with `ClassMethods`' do
|
20
|
+
CreateTestConst1 = Accessible.create
|
21
|
+
CreateTestConst1.must_be_instance_of(Class)
|
22
|
+
@expected_class_methods.each do |method|
|
23
|
+
CreateTestConst1.must_respond_to(method)
|
24
|
+
end
|
181
25
|
end
|
182
26
|
|
183
|
-
it 'should
|
184
|
-
|
27
|
+
it 'should yield the extended `Class` instance to a block if given' do
|
28
|
+
Accessible.create do |klass|
|
29
|
+
klass.name.must_be_nil
|
30
|
+
klass.must_be_instance_of(Class)
|
31
|
+
@expected_class_methods.each do |method|
|
32
|
+
klass.must_respond_to(method)
|
33
|
+
end
|
34
|
+
end
|
185
35
|
end
|
186
36
|
end
|
187
37
|
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
class ClassMethodsTest < Minitest::Spec
|
4
|
+
describe Accessible::ClassMethods do
|
5
|
+
let(:config) { Class.new { extend Accessible::ClassMethods } }
|
6
|
+
|
7
|
+
describe '#to_h' do
|
8
|
+
it 'should create `@data` if it does not exist' do
|
9
|
+
config.instance_variable_defined?(:@data).must_equal(false)
|
10
|
+
config.to_h
|
11
|
+
config.instance_variable_defined?(:@data).must_equal(true)
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should assign an empty hash to `@data` if it does not exist' do
|
15
|
+
config.instance_variable_defined?(:@data).must_equal(false)
|
16
|
+
config.to_h
|
17
|
+
config.instance_variable_get(:@data).must_equal({})
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should return a Hash instance' do
|
21
|
+
config.to_h.must_be_instance_of(Hash)
|
22
|
+
config.load({ :adding => ['data'] })
|
23
|
+
config.to_h.must_be_instance_of(Hash)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should return `@data`' do
|
27
|
+
config.to_h.must_equal(config.instance_variable_get(:@data))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#load' do
|
32
|
+
it 'should reassign `@data` to the given data' do
|
33
|
+
config.load({ :a => 'a' })
|
34
|
+
config.to_h.must_equal({ :a => 'a' })
|
35
|
+
config.load({ :b => 'b' })
|
36
|
+
config.to_h.must_equal({ :b => 'b' })
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should assign all data if no namespace given' do
|
40
|
+
config.load({ :a => 'a', :b => 'b' })
|
41
|
+
config.to_h.must_equal({ :a => 'a', :b => 'b' })
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should assign namespaced data if namespace given' do
|
45
|
+
config.load({ :a => 'a', :foo => { :b => 'b'} }, :foo)
|
46
|
+
config.to_h.must_equal({ :b => 'b' })
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should raise a KeyError if the given namespace is not found' do
|
50
|
+
proc do
|
51
|
+
config.load({ :a => 'a', :foo => { :b => 'b'} }, :bar)
|
52
|
+
end.must_raise(KeyError)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe '#merge' do
|
57
|
+
it 'should accept a hash and load it' do
|
58
|
+
config.merge({ :a => 'a' })
|
59
|
+
config.to_h.must_equal({ :a => 'a' })
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should accept a symbol and load the corresponding config file' do
|
63
|
+
config.merge(:my_config)
|
64
|
+
config.to_h.must_equal({ 'data' => 'data from config/my_config.yml' })
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'should accept a file path and load the file' do
|
68
|
+
config.merge('config/more_configs/my_config.yaml')
|
69
|
+
config.to_h.must_equal({ 'data' => 'data from config/more_configs/my_config.yaml' })
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'should raise an error when not given a hash, symbol, or string' do
|
73
|
+
begin
|
74
|
+
config.merge(100)
|
75
|
+
rescue RuntimeError => e
|
76
|
+
e.message.must_equal("Invalid data source: 100")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'should not require an initial source to have been `#load`ed' do
|
81
|
+
config.merge({ :a => 'a' })
|
82
|
+
config.to_h.must_equal({ :a => 'a' })
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'should mutate `@data`' do
|
86
|
+
config.load({ :foo => 'original' })
|
87
|
+
config.merge({ :foo => 'new' })
|
88
|
+
config.to_h.must_equal({ :foo => 'new' })
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'should merge all data if no namespace given' do
|
92
|
+
config.load({ :a => 'a'})
|
93
|
+
config.merge({ :a => 'a', :b => 'b' })
|
94
|
+
config.to_h.must_equal({ :a => 'a', :b => 'b' })
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'should merge namespaced data if namespace given' do
|
98
|
+
config.load({ :a => 'a' })
|
99
|
+
config.merge({ :foo => { :a => 'new a' }, :b => 'b' }, :foo)
|
100
|
+
config.to_h.must_equal({ :a => 'new a' })
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'should raise a KeyError if the given namespace is not found' do
|
104
|
+
proc do
|
105
|
+
config.merge({ :a => 'a', :foo => { :b => 'b'} }, :bar)
|
106
|
+
end.must_raise(KeyError)
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'should perform a deep merge' do
|
110
|
+
h1 = { :a => true, :b => { :c => [1, 2, 3] } }
|
111
|
+
h2 = { :a => false, :b => { :x => [3, 4, 5] } }
|
112
|
+
|
113
|
+
config.load(h1)
|
114
|
+
config.merge(h2)
|
115
|
+
config.to_h.must_equal({
|
116
|
+
:a => false,
|
117
|
+
:b => {
|
118
|
+
:c => [1, 2, 3],
|
119
|
+
:x => [3, 4, 5]
|
120
|
+
}
|
121
|
+
})
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'should define acessors on `@data`' do
|
125
|
+
config.merge({ :a => 'a' })
|
126
|
+
config.to_h.singleton_methods.must_include(:a)
|
127
|
+
config.to_h.singleton_methods.must_include(:a=)
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'should define accessors on the class' do
|
131
|
+
config.merge({ :a => 'a' })
|
132
|
+
config.singleton_methods.must_include(:a)
|
133
|
+
config.singleton_methods.must_include(:a=)
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'should return a Hash instance' do
|
137
|
+
config.merge({}).must_be_instance_of(Hash)
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'should return the result of calling #to_h after the merge' do
|
141
|
+
config.load({ :a => 'a' })
|
142
|
+
merge_result = config.merge({ :b => 'b' })
|
143
|
+
merge_result.must_equal(config.to_h)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
describe '#[]' do
|
148
|
+
it 'should delegate to `@data`' do
|
149
|
+
config.load({ :a => 'a' })
|
150
|
+
config[:a].must_equal(config.instance_variable_get(:@data)[:a])
|
151
|
+
config[:b].must_equal(config.instance_variable_get(:@data)[:b])
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
describe '#[]=' do
|
156
|
+
it 'should add the key value pair to `@data`' do
|
157
|
+
config[:a] = 'a'
|
158
|
+
config.instance_variable_get(:@data)[:a].must_equal('a')
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'should define acessors on `@data` for the given key' do
|
162
|
+
config[:a] = 'a'
|
163
|
+
config.instance_variable_get(:@data).singleton_methods.must_include(:a)
|
164
|
+
config.instance_variable_get(:@data).singleton_methods.must_include(:a=)
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'should define accessors on the class for the given key' do
|
168
|
+
config[:a] = 'a'
|
169
|
+
config.singleton_methods.must_include(:a)
|
170
|
+
config.singleton_methods.must_include(:a=)
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'should define accessors on set values' do
|
174
|
+
config[:a] = { :foo => 'foo value' }
|
175
|
+
config.a.singleton_methods.must_include(:foo)
|
176
|
+
config.a.foo.must_equal('foo value')
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'should delegate to `@data`' do
|
180
|
+
config.load({ :a => 'a' })
|
181
|
+
(config[:a] = 'new a').must_equal(config.instance_variable_get(:@data)[:a] = 'new a')
|
182
|
+
(config[:b] = 'b').must_equal(config.instance_variable_get(:@data)[:b] = 'b')
|
183
|
+
end
|
184
|
+
|
185
|
+
it 'should return the set value' do
|
186
|
+
config.send(:[]=, :a, 'a').must_equal('a')
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
data/test/hash_methods_test.rb
CHANGED
@@ -25,7 +25,6 @@ class HashMethodsTest < Minitest::Spec
|
|
25
25
|
|
26
26
|
it 'should not pass non-hash values to the block' do
|
27
27
|
raise_if_executed = proc { raise('block was excuted') }
|
28
|
-
class SubclassedHash < Hash; end
|
29
28
|
Accessible::HashMethods.each_hash('foo', &raise_if_executed)
|
30
29
|
Accessible::HashMethods.each_hash(:foo, &raise_if_executed)
|
31
30
|
Accessible::HashMethods.each_hash(100, &raise_if_executed)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: accessible
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Scott Clark
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-10-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -89,7 +89,6 @@ extra_rdoc_files: []
|
|
89
89
|
files:
|
90
90
|
- ".coveralls.yml"
|
91
91
|
- ".gitignore"
|
92
|
-
- ".ruby-gemset"
|
93
92
|
- ".ruby-version"
|
94
93
|
- ".travis.yml"
|
95
94
|
- Gemfile
|
@@ -108,6 +107,7 @@ files:
|
|
108
107
|
- lib/accessible/version.rb
|
109
108
|
- test/accessible_test.rb
|
110
109
|
- test/accessorizers_test.rb
|
110
|
+
- test/class_methods_test.rb
|
111
111
|
- test/data_loader_test.rb
|
112
112
|
- test/hash_methods_test.rb
|
113
113
|
- test/test_helper.rb
|
@@ -131,13 +131,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
131
131
|
version: '0'
|
132
132
|
requirements: []
|
133
133
|
rubyforge_project:
|
134
|
-
rubygems_version: 2.
|
134
|
+
rubygems_version: 2.4.6
|
135
135
|
signing_key:
|
136
136
|
specification_version: 4
|
137
137
|
summary: Simple and flexible ruby app configuration.
|
138
138
|
test_files:
|
139
139
|
- test/accessible_test.rb
|
140
140
|
- test/accessorizers_test.rb
|
141
|
+
- test/class_methods_test.rb
|
141
142
|
- test/data_loader_test.rb
|
142
143
|
- test/hash_methods_test.rb
|
143
144
|
- test/test_helper.rb
|
data/.ruby-gemset
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
Accessible
|