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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7ba9fa465e93322e6b9f7c2c37714e3c7cb96932
4
- data.tar.gz: ad0299925c1b42408e90d184f1bbf49b531fd841
3
+ metadata.gz: efe7ae168ae14ba3851146219742ec4df0a012d2
4
+ data.tar.gz: c15fe8888c9c05f8c26a8da5912e0a5ce763d7b1
5
5
  SHA512:
6
- metadata.gz: f81549a605ef57707f46b34e1a4f1cd212dc6defef01c91773045e3975c4922959a73f8f97327c2fa235c011053ec21bd4f4850b7dbc1030f85e93627bec3503
7
- data.tar.gz: 8fa230b3242fa3ceb3a220d0d6cfa14ccd04751a35ce6a4f3dc4ed54f592e28c9dd8f66a524d85d5874c2c612f1dabac041e2dfdcafe4508312fa3ce88d6cc94
6
+ metadata.gz: a212b3ed52ba27dbfe4460a53f777103c120400634968e03bbdd0ac154a638dcd4da53d34410c2c7a99103b4f4ec089c0439cf337909d2403f71909dbafc1819
7
+ data.tar.gz: 354a11f3b2d58d8b2e97d9be7578231fdc835ff7ffd0388e544ae77122567f93f21614b22e7d6f0f0965467469d2161f73aa7a4bdbc0d5fb48075ba9bc267822
data/.gitignore CHANGED
@@ -4,6 +4,7 @@
4
4
  .config
5
5
  .yardoc
6
6
  Gemfile.lock
7
+ vendor/
7
8
  InstalledFiles
8
9
  _yardoc
9
10
  coverage
@@ -1 +1 @@
1
- ruby-2.1.0
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
- # Set up a configuration class
7
- Create a class in your project, include the `Accessible` module, and `load` a data source. You may then also `merge` in any number of additional data sources.
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
- > Note: The `load` method will clear out any previously loaded data and replace it with the new data, whereas `merge` will merge in new data with previously loaded data (i.e. matching keys will be overridden and new keys will be added).
39
+ ## Methods
40
+ The following class methods are available on any class created with/including Accessible.
18
41
 
19
- Both `load` and `merge` can take any of the following as a data source:
20
- - A filepath to a yaml file (i.e. `'config/default.yml'`)
21
- - A symbol representing the name of a yaml file found in `config/` (i.e. `:default`)
22
- - A raw hash of data (i.e. `{ :base_url => 'http://www.example.com' }`)
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
- Both methods also accept an optional second parameter: the name of a specific key within the data source, whose data is the only data that should be loaded from the source. This is provided in case you prefer to maintain a single configuration file with default and environment specific data separated out under different keys.
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
- # Access your configuration data
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
- Let's imagine the following data is loaded:
57
+ Examples:
30
58
  ```ruby
31
- {
32
- :environments => {
33
- :dev => {
34
- :users => [
35
- { :name => 'user0' },
36
- { :name => 'user1' },
37
- { :name => 'user2' }
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
- __Methods__
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
- You can get and set data by calling methods on your data set that match the name of the keys. If you try to get or set a key does not exist, an error will be thrown.
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.environments.dev.users[1].name # => 'user1'
144
+ MyConfig.load({ :calvin => 'Spaceman Spiff' })
145
+
146
+ MyConfig[:calvin]
147
+ # => 'Spaceman Spiff'
49
148
 
50
- MyConfig.environments.dev.users[1].name = 'new_user1'
51
- MyConfig.environments.dev.users[1].name # => 'new_user1'
149
+ MyConfig[:susie]
150
+ # => nil
52
151
 
53
- MyConfig.does_not_exist # => NoMethodError
54
- MyConfig.does_not_exist = 'foo' # => NoMethodError
152
+ person = MyConfig[:susie] || 'Hobbes'
153
+ # => 'Hobbes'
55
154
  ```
56
155
 
57
- __Brackets__
156
+ ### []=
157
+ `[key] = value -> value`
58
158
 
59
- You may also use the familiar `:[]` and `:[]=` methods, which behave the same as they do on any hash. `:[]` will return `nil` if the key does not exist, `:[]=` will create and set the key-value pair if the key does not exist. This makes `:[]` useful for providing default values.
159
+ Sets data on your class.
60
160
  ```ruby
61
- MyConfig[:environments][:dev][:users][1][:name] # => 'user1'
161
+ MyConfig.load({})
62
162
 
63
- MyConfig[:environments][:dev][:users][1][:name] = 'new_user1'
64
- MyConfig[:environments][:dev][:users][1][:name] # => 'new_user1'
163
+ MyConfig[:calvin] = 'Spaceman Spiff'
164
+ MyConfig[:calvin]
165
+ # => 'Spaceman Spiff'
65
166
 
66
- MyConfig[:does_not_exist] || 'default' # => 'default'
67
- MyConfig[:new_key] = 'foo' # => `:new_key => 'foo'` pair is created
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
- Check out the unit tests for the nitty-gritty of how things should work.
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
+ ```
@@ -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(source, namespace = nil)
22
+ def load(data_source, namespace = nil)
17
23
  to_h.clear
18
- merge(source, namespace)
24
+ merge(data_source, namespace)
19
25
  end
20
26
 
21
- def merge(source, namespace = nil)
22
- new_data = DataLoader.load_source(source)
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
- if namespace
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] = Accessible::Accessorizers.accessorize_data(new_value)
46
+ to_h[key] = Accessorizers.accessorize_data(new_value)
44
47
  end
45
48
  end
46
49
  end
@@ -1,3 +1,3 @@
1
1
  module Accessible
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -2,186 +2,36 @@ require_relative 'test_helper'
2
2
 
3
3
  class AccessibleTest < Minitest::Spec
4
4
  describe Accessible do
5
- let(:config) { Class.new { include Accessible } }
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 '#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")
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 '#[]=' 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[: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 return the set value' do
184
- config.send(:[]=, :a, 'a').must_equal('a')
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
@@ -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.1.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-03-09 00:00:00.000000000 Z
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.2.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
@@ -1 +0,0 @@
1
- Accessible