config-factory 0.0.7 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a1f210944b382bc0260157f164e45025db904802
4
- data.tar.gz: 9d0d655fbb1a219b3bb22ce320b2fd0480dde335
3
+ metadata.gz: 6a2b88cc897a3961acfe0506efeae2215de462b8
4
+ data.tar.gz: 0478ac34202073124995fd797b082bbcfa609551
5
5
  SHA512:
6
- metadata.gz: 2bd33c114f0cbcb09f70f8aacf9b9ffff28757a20a5d8039e0888d0f8575887d9d2c282c34d703fa39d77aa786c9d01b5a76d623004ce55cf2588231f0364f94
7
- data.tar.gz: 7526b42e798652f771d0c094ee574ecab591811bfadd26b5446d333fcd5e62f7e22107cb50c672270ba754a590954d65fe20a9340703cb3f6ebb380359064c76
6
+ metadata.gz: ac4f9910a57c0cdb78c9052f773cbef06ca811f881af87418103a2861ad5c9c17db8687f9cba42cdc465cb3092e713443ee039cae30705860e0f487692660d94
7
+ data.tar.gz: 971e76011a0ff96b3607392261bb88f48e708e0745406c7a28067932e8553bbd354690b396177e46a3040be0aa35837c20c96867d4322491f07305d59676a666
data/CHANGES.md CHANGED
@@ -1,6 +1,11 @@
1
+ ## 0.0.8 (4 April 2016)
2
+
3
+ - Support looking up implementation classes based on an argument
4
+ filter
5
+
1
6
  ## 0.0.7 (31 March 2016)
2
7
 
3
- - Support direct instantiation of concrete config classes without
8
+ - Support direct instantiation of implementation classes without
4
9
  product keys
5
10
 
6
11
  ## 0.0.6 (16 March 2016)
data/README.md CHANGED
@@ -9,122 +9,297 @@ A gem for creating configuration classes using the
9
9
  [Abstract Factory](https://web.archive.org/web/20111109224959/http://www.informit.com/articles/article.aspx?p=1398599),
10
10
  pattern, with run-time configuration provided by hashes or YAML files.
11
11
 
12
- ## Example
12
+ - [Factory lookup patterns](#factory-lookup-patterns)
13
+ - [Looking up concrete factory classes based on a key value](#looking-up-concrete-factory-classes-based-on-a-key-value)
14
+ - [Finding concrete factory classes based on an argument filter](#finding-concrete-factory-classes-based-on-an-argument-filter)
15
+ - [Instantiating implementations directly](#instantiating-implementations-directly)
16
+ - [Environments](#environments)
17
+ - [Multiple environments](#multiple-environments)
18
+ - [Single environment](#single-environment)
13
19
 
14
- The abstract configuration factory declares a `key`, which is used to look up the concrete
15
- config class for a given configuration. Concrete implementations register themselves with a
16
- DSL method named after the `key` value.
20
+ ## Factory lookup patterns
17
21
 
18
- In the example below, the `SourceConfig` abstract factory declares the key `:protocol`; the
19
- concrete classes `OAISourceConfig` and `ResyncSourceConfig` register themselves with
20
- `protocol: 'OAI'` and `protocol: 'Resync'`, respectively. `SourceConfig.for_environment()`
21
- will then look for a `protocol:` line in the configuration file to determine which
22
- registered concrete class to instantiate.
22
+ ### Looking up concrete factory classes based on a key value
23
+
24
+ In the basic use case, an abstract factory class defines a configuration key:
23
25
 
24
26
  ```ruby
25
27
  class SourceConfig
26
28
  include Config::Factory
27
- key :protocol
29
+ key :protocol # <- configuration key
28
30
  end
31
+ ```
29
32
 
30
- class OAISourceConfig < SourceConfig
31
- protocol 'OAI'
33
+ This creates a corresponding DSL method (here `:protocol`), which implementation
34
+ classes use to register themselves.
32
35
 
36
+ ```ruby
37
+ class OAISourceConfig < SourceConfig
38
+ protocol 'OAI' # <- registers OAISourceConfig as implementation
39
+ # for the "OAI" protocol
33
40
  def initialize(oai_base_url:, metadata_prefix:, set: nil, seconds_granularity: false)
41
+ # ...
34
42
  end
35
43
  end
36
44
 
37
45
  class ResyncSourceConfig < SourceConfig
38
- protocol 'Resync'
39
-
46
+ protocol 'Resync' # <- registers ResyncSourceConfig as implementation
47
+ # for the "Resync" protocol
40
48
  def initialize(capability_list_url:)
49
+ # ...
41
50
  end
42
51
  end
43
52
  ```
44
53
 
45
- ### Single-environment example
46
-
47
- Configuration file:
54
+ At run time, `SourceConfig` finds the `protocol:` key in the configuration, and
55
+ based on the value `'OAI'`, instantiates an `OAISourceConfig`, passing the remaining
56
+ arguments to the `OAISourceConfig` initializer.
48
57
 
49
58
  ```YAML
50
59
  source:
51
- protocol: OAI
60
+ protocol: OAI # <- indicates we want an OAISourceConfig
61
+
62
+ # these arguments will be passed to the OAISourceConfig initializer
52
63
  oai_base_url: http://oai.example.org/oai
53
64
  metadata_prefix: some_prefix
54
65
  set: some_set
55
66
  seconds_granularity: true
56
67
  ```
57
68
 
58
- Loading:
59
-
60
69
  ```ruby
61
- environment = Environment.load_file('spec/data/single-environment.yml')
62
- # => #<Config::Factory::Environment:0x007fe8d3883240 @name=:production, @configs={"source"=>{"protocol"=>"OAI", "oai_base_url"=>"http://oai.example.org/oai", "metadata_prefix"=>"some_prefix", "set"=>"some_set", "seconds_granularity"=>true}}>
63
- source_config = SourceConfig.for_environment(environment, :source)
64
- # => #<OAISourceConfig:0x007fe8d38b3990 @oai_base_url="http://oai.example.org/oai", @metadata_prefix="some_prefix", @set="some_set", @seconds_granularity=true>
70
+ config = YAML.load_file('config.yml')
71
+ SourceConfig.build_from(config, :source)
72
+ # => #<OAISourceConfig:0x007fc8f14a58f0>
65
73
  ```
66
74
 
67
- ### Multiple-environment example
75
+ ### Finding concrete factory classes based on an argument filter
76
+
77
+ In some cases (e.g., compatibility with existing configuration files), it may
78
+ be necessary to have the implementation class examine the entire configuration
79
+ hash to determine whether it can support a given configuration.
80
+
81
+ ```ruby
82
+ class PersistenceConfig
83
+ include Config::Factory
84
+ # note no configuration key given
85
+ end
86
+
87
+ class DBPersistenceConfig < PersistenceConfig
88
+ attr_reader :connection_info
68
89
 
69
- Configuration file:
90
+ # Applies if we find 'adapter:' in the config file
91
+ can_build_if { |config| config.key?(:adapter) }
92
+
93
+ def initialize(connection_info)
94
+ @connection_info = connection_info
95
+ end
96
+ end
97
+
98
+ class XMLPersistenceConfig < PersistenceConfig
99
+ attr_reader :connection_info
100
+
101
+ # Applies if we find 'path:' in the config file
102
+ # and its value ends in '.xml'
103
+ can_build_if do |config|
104
+ config[:path] && config[:path].end_with?('.xml')
105
+ end
106
+
107
+ def initialize(connection_info)
108
+ @connection_info = connection_info
109
+ end
110
+ end
111
+ ```
112
+
113
+ This configuration will build a `DBPersistenceConfig`:
70
114
 
71
115
  ```YAML
72
- test:
73
- source:
74
- protocol: Resync
75
- capability_list_url: http://localhost:8888/capabilitylist.xml
116
+ persistence:
117
+ adapter: sqlite3
118
+ database: db/development.sqlite3
119
+ pool: 5
120
+ timeout: 5000
121
+ ```
76
122
 
77
- production:
78
- source:
79
- protocol: OAI
80
- oai_base_url: http://oai.example.org/oai
81
- metadata_prefix: some_prefix
82
- set: some_set
83
- seconds_granularity: true
123
+ ```ruby
124
+ config = YAML.load_file('config.yml')
125
+ PersistenceConfig.build_from(config, :persistence)
126
+ # => #<DBPersistenceConfig:0x007fc8f14c4d18>
84
127
  ```
85
128
 
86
- Loading:
129
+ Whereas this configuration will build an `XMLConfig`:
130
+
131
+ ```YAML
132
+ persistence:
133
+ path: config/persistence.xml
134
+ ```
87
135
 
88
136
  ```ruby
89
- environments = Environments.load_file('spec/data/multiple_environments.yml')
90
- # => {:test=>#<Config::Factory::Environment:0x007fe8d3863dc8 @name=:test, @configs={"source"=>{"protocol"=>"Resync", "capability_list_url"=>"http://localhost:8888/capabilitylist.xml"}}>, :production=>#<Config::Factory::Environment:0x007fe8d3863be8 @name=:production, @configs={"source"=>{"protocol"=>"OAI", "oai_base_url"=>"http://oai.example.org/oai", "metadata_prefix"=>"some_prefix", "set"=>"some_set", "seconds_granularity"=>true}}>}
91
- test_env = environments[:test]
92
- # => #<Config::Factory::Environment:0x007fe8d383a400 @name=:test, @configs={"source"=>{"protocol"=>"Resync", "capability_list_url"=>"http://localhost:8888/capabilitylist.xml"}}>
93
- source_config = SourceConfig.for_environment(test_env, :source)
94
- # => #<ResyncSourceConfig:0x007fe8d48180c0 @capability_list_url="http://localhost:8888/capabilitylist.xml">
137
+ config = YAML.load_file('config.yml')
138
+ PersistenceConfig.build_from(config, :persistence)
139
+ # => #<XMLPersistenceConfig:0x007fc8f14ed420>
95
140
  ```
96
141
 
97
- ## Config classes with only one implementation
142
+ ### Instantiating implementations directly
98
143
 
99
- `config-factory` also supports instantiating concrete configuration classes directly.
100
- In this case, we simply don't declare a `key` for the class, and the configuration hash
101
- will be passed directly to the initializer of the concrete class.
144
+ Finally, you may have a mix of abstract and concrete factories, so that some factories
145
+ don't need any lookup and can just be instantiated directly.
102
146
 
103
147
  ```ruby
104
- class DBConfig
148
+ class SolrConfig
105
149
  include Config::Factory
106
150
 
107
- def initialize(connection_info)
108
- @connection_info = connection_info
151
+ def initialize(url:, proxy: nil, open_timeout: 60, read_timeout: 120)
152
+ # ...
109
153
  end
110
154
  end
111
155
  ```
112
156
 
113
157
  ```YAML
158
+ solr:
159
+ url: http://solr.example.org/
160
+ proxy: http://foo:bar@proxy.example.com/
161
+ open_timeout: 120
162
+ read_timeout: 300
163
+ ```
164
+
165
+ ```ruby
166
+ config = YAML.load_file('config.yml')
167
+ SolrConfig.build_from(config, :solr)
168
+ # => #<SolrConfig:0x007fc8f1504f08>
169
+ ```
170
+
171
+ ## Environments
172
+
173
+ The YAML examples above each show only the configuration for a single factory. However,
174
+ `Config::Factory` also supports a structured configuration file with configurations for
175
+ multiple factories, optionally organized into environments.
176
+
177
+ ### Multiple environments
178
+
179
+ The `Environments.load_file()` method loads a multi-environment config file as a hash
180
+ of `Environment` instances.
181
+
182
+ ```ruby
183
+ envs = Environments.load_file('config.yml')
184
+ # => {:defaults=>#<Environment:0x007f8e9a578818>,
185
+ # :development=>#<Environment:0x007f8e9a578728>,
186
+ # :test=>#<Environment:0x007f8e9a578660>,
187
+ # :production=>#<Environment:0x007f8e9a578520>}
188
+ test = envs[:test]
189
+ # => #<Environment:0x007f8e9a578660>
190
+ ```
191
+
192
+ The `AbstractFactory.for_environment()` method takes an environment instance and a
193
+ configuration section name.
194
+
195
+ ```ruby
196
+ source = SourceConfig.for_environment(test, :source)
197
+ # => #<ResyncSourceConfig:0x007f8e9a54a878>
198
+ index = IndexConfig.for_environment(test, :index)
199
+ # => #<SolrConfig:0x007f8e9a5383a8>
200
+ persistence = PersistenceConfig.for_environment(test, :persistence)
201
+ # => #<DBConfig:0x007f8e9a5019e8>
202
+ ```
203
+
204
+ The configuration file for the examples above. Note that standard YAML features such
205
+ as references are supported.
206
+
207
+ ```YAML
208
+ defaults: &defaults
209
+ source:
210
+ protocol: OAI
211
+ oai_base_url: http://oai.example.org/oai
212
+ metadata_prefix: some_prefix
213
+ set: some_set
214
+ seconds_granularity: true
215
+ index:
216
+ adapter: solr
217
+ url: http://solr.example.org/
218
+ proxy: http://foo:bar@proxy.example.com/
219
+ open_timeout: 120
220
+ read_timeout: 300
221
+
222
+ development:
223
+ <<: *defaults
224
+ persistence:
225
+ adapter: mysql2
226
+ encoding: utf8
227
+ pool: 5
228
+ database: example_dev
229
+ host: mysql-dev.example.org
230
+ port: 3306
231
+ index:
232
+ adapter: solr
233
+ url: http://solr-dev.example.org/
234
+ proxy: http://foo:bar@proxy.example.com/
235
+ open_timeout: 120
236
+ read_timeout: 300
237
+
114
238
  test:
115
- db:
239
+ persistence:
116
240
  adapter: sqlite3
117
241
  database: ':memory:'
118
242
  pool: 5
119
243
  timeout: 5000
244
+ source:
245
+ protocol: Resync
246
+ capability_list_url: http://localhost:8888/capabilitylist.xml
247
+ index:
248
+ adapter: solr
249
+ url: http://localhost:8000/solr/
120
250
 
121
251
  production:
122
- db:
252
+ <<: *defaults
253
+ persistence:
123
254
  adapter: mysql2
124
- host: mydb.example.org
125
- database: myapp
126
- username: myuser
127
- password: blank
128
- port: 3306
129
255
  encoding: utf8
256
+ pool: 5
257
+ database: example_prod
258
+ host: mysql.example.org
259
+ port: 3306
130
260
  ```
261
+
262
+ The `Environments` module supports arbitrary environment names, but the standard ones
263
+ are `:defaults`, `:development`, `:test`, `:stage`, `:staging,` and `:production`. The
264
+ `Environments.load_file` method will warn if none of these are present, as this might
265
+ indicate loading a single-environment config file as multiple by mistake.
266
+
267
+ ### Single environment
268
+
269
+ For a single-environment config file, use the `load_file()` method on the `Environment`
270
+ class itself (not the `Environments` module, plural):
271
+
272
+ ```ruby
273
+ env = Environment.load_file('config.yml')
274
+ # => #<Environment:0x007f8e9a49b1c0>
275
+ persistence = PersistenceConfig.for_environment(env, :persistence)
276
+ # => #<DBConfig:0x007f8e9a45abe8>
277
+ source = SourceConfig.for_environment(env, :source)
278
+ # => #<OAISourceConfig:0x007f8e9a482fa8>
279
+ index = IndexConfig.for_environment(env, :index)
280
+ # => #<SolrConfig:0x007f8e9a4438a8>
281
+ ```
282
+
283
+ ```YAML
284
+ persistence:
285
+ adapter: mysql2
286
+ encoding: utf8
287
+ pool: 5
288
+ database: example_prod
289
+ host: mysql-dev.example.org
290
+ port: 3306
291
+ source:
292
+ protocol: OAI
293
+ oai_base_url: http://oai.example.org/oai
294
+ metadata_prefix: some_prefix
295
+ set: some_set
296
+ seconds_granularity: true
297
+ index:
298
+ adapter: solr
299
+ url: http://solr.example.org/
300
+ proxy: http://foo:bar@proxy.example.com/
301
+ open_timeout: 120
302
+ read_timeout: 300
303
+ ```
304
+
305
+ By default, a single environment uses the environment name `:production`.
@@ -5,16 +5,24 @@ module Config
5
5
  end
6
6
 
7
7
  module AbstractFactory
8
- attr_reader :product_key
8
+ attr_reader :impl_key
9
9
 
10
10
  def key(k)
11
- @product_key = k
12
- registry = products
11
+ @impl_key = k
12
+ registry = impls_by_key
13
13
  define_singleton_method(k) do |v|
14
14
  registry[v] = self
15
15
  end
16
16
  end
17
17
 
18
+ def self.extended(mod)
19
+ filter_registry = {}
20
+ mod.define_singleton_method(:can_build_if) do |&block|
21
+ filter_registry[self] = block
22
+ end
23
+ mod.define_singleton_method(:filters_by_impl_class) { filter_registry }
24
+ end
25
+
18
26
  def for_environment(env, config_name)
19
27
  arg_hash = env.args_for(config_name)
20
28
  fail ArgumentError, "no #{self} arguments found for config #{config_name} in environment #{env}" unless arg_hash
@@ -26,22 +34,31 @@ module Config
26
34
  for_environment(env, config_name)
27
35
  end
28
36
 
29
- def build_from(arg_hash)
37
+ def build_from(arg_hash, section_name = nil)
30
38
  fail ArgumentError, "nil argument hash passed to #{self}.build_from" unless arg_hash
31
39
  args = deep_symbolize_keys(arg_hash)
32
- product_class = find_product_class(args)
33
- product_class.new(args)
40
+ args = args[section_name] if section_name
41
+ impl_class = find_impl_class(args)
42
+ impl_class.new(args)
34
43
  end
35
44
 
36
45
  private
37
46
 
38
- def find_product_class(args)
39
- return self unless product_key
40
- fail ArgumentError, "product key #{product_key} not found in argument hash #{args}" unless args.key?(product_key)
41
- key_value = args.delete(product_key)
42
- product_class = products[key_value]
43
- fail ArgumentError, "No #{name} product class found for #{product_key}: #{key_value}" unless product_class
44
- product_class
47
+ def find_impl_class(args)
48
+ pk = impl_key
49
+ return impl_for_key(pk, args) if pk
50
+ filters_by_impl_class.each_pair do |impl_class, filter|
51
+ return impl_class if filter.call(args)
52
+ end
53
+ self
54
+ end
55
+
56
+ def impl_for_key(key_sym, args)
57
+ fail ArgumentError, "implementation key #{key_sym} not found in argument hash #{args}" unless args.key?(key_sym)
58
+ key_value = args.delete(key_sym)
59
+ impl_class = impls_by_key[key_value]
60
+ fail ArgumentError, "No #{name} implementation found for #{key_sym}: #{key_value}" unless impl_class
61
+ impl_class
45
62
  end
46
63
 
47
64
  def deep_symbolize_keys(val)
@@ -51,9 +68,10 @@ module Config
51
68
  end.to_h
52
69
  end
53
70
 
54
- def products
55
- @products ||= {}
71
+ def impls_by_key
72
+ @impls_by_key ||= {}
56
73
  end
74
+
57
75
  end
58
76
  end
59
77
  end
@@ -4,7 +4,7 @@ module Config
4
4
  NAME = 'config-factory'
5
5
 
6
6
  # The version of this gem
7
- VERSION = '0.0.7'
7
+ VERSION = '0.0.8'
8
8
 
9
9
  # The copyright notice for this gem
10
10
  COPYRIGHT = 'Copyright (c) 2016 The Regents of the University of California'
@@ -18,7 +18,7 @@ development:
18
18
  adapter: mysql2
19
19
  encoding: utf8
20
20
  pool: 5
21
- database: example_pord
21
+ database: example_dev
22
22
  host: mysql-dev.example.org
23
23
  port: 3306
24
24
  index:
@@ -47,6 +47,6 @@ production:
47
47
  adapter: mysql2
48
48
  encoding: utf8
49
49
  pool: 5
50
- database: example_pord
50
+ database: example_prod
51
51
  host: mysql.example.org
52
52
  port: 3306
@@ -0,0 +1,5 @@
1
+ persistence:
2
+ adapter: sqlite3
3
+ database: db/production.sqlite3
4
+ pool: 5
5
+ timeout: 5000
@@ -1,8 +1,8 @@
1
- db:
1
+ persistence:
2
2
  adapter: mysql2
3
3
  encoding: utf8
4
4
  pool: 5
5
- database: example_pord
5
+ database: example_prod
6
6
  host: mysql-dev.example.org
7
7
  port: 3306
8
8
  source:
@@ -7,31 +7,59 @@ module Config
7
7
  describe '#build_from' do
8
8
  it 'builds the correct class with the correct config' do
9
9
  config_hash = { protocol: 'OAI', oai_base_url: 'http://oai.example.org/oai', metadata_prefix: 'some_prefix', set: 'some_set', seconds_granularity: true }
10
- product = SourceConfig.build_from(config_hash)
11
- expect(product).to be_an(OAISourceConfig)
10
+ impl = SourceConfig.build_from(config_hash)
11
+ expect(impl).to be_an(OAISourceConfig)
12
12
  args = { oai_base_url: URI('http://oai.example.org/oai'), metadata_prefix: 'some_prefix', set: 'some_set', seconds_granularity: true }
13
13
  args.each do |k, v|
14
- expect(product.send(k)).to eq(v)
14
+ expect(impl.send(k)).to eq(v)
15
15
  end
16
16
  end
17
17
 
18
- it 'raises a sensible exception if no product found for the key' do
18
+ it 'raises a sensible exception if no impl found for the key' do
19
19
  config_hash = { protocol: 'Elvis' }
20
20
  expect { SourceConfig.build_from(config_hash) }.to raise_error(ArgumentError, /SourceConfig.*protocol.*Elvis/)
21
21
  end
22
22
 
23
- it 'supports concrete factories without product keys' do
23
+ it 'supports implementation factories without impl keys' do
24
24
  config_hash = {
25
25
  adapter: 'mysql2',
26
26
  encoding: 'utf8',
27
27
  pool: 5,
28
- database: 'example_pord',
28
+ database: 'example_prod',
29
29
  host: 'mysql-dev.example.org',
30
30
  port: 3306
31
31
  }
32
- product = DBConfig.build_from(config_hash)
33
- expect(product).to be_a(DBConfig)
34
- expect(product.connection_info).to eq(config_hash)
32
+ impl = MysqlConfig.build_from(config_hash)
33
+ expect(impl).to be_a(MysqlConfig)
34
+ expect(impl.connection_info).to eq(config_hash)
35
+ end
36
+
37
+ it "can build a class without a declared key, so long as it's registered" do
38
+ config_hash = {
39
+ adapter: 'mysql2',
40
+ encoding: 'utf8',
41
+ pool: 5,
42
+ database: 'example_prod',
43
+ host: 'mysql-dev.example.org',
44
+ port: 3306
45
+ }
46
+ impl = PersistenceConfig.build_from(config_hash)
47
+ expect(impl).to be_a(DBConfig)
48
+ expect(impl.connection_info).to eq(config_hash)
49
+ end
50
+
51
+ it 'works with section headers' do
52
+ expected_info = {
53
+ adapter: 'sqlite3',
54
+ database: 'db/production.sqlite3',
55
+ pool: 5,
56
+ timeout: 5000
57
+ }
58
+
59
+ hash = YAML.load_file('spec/data/raw-section.yml')
60
+ impl = PersistenceConfig.build_from(hash, :persistence)
61
+ expect(impl).to be_a(DBConfig)
62
+ expect(impl.connection_info).to eq(expected_info)
35
63
  end
36
64
  end
37
65
 
@@ -41,15 +69,15 @@ module Config
41
69
  env = instance_double(Environment)
42
70
  expect(env).to receive(:args_for).with(:source) { config_hash }
43
71
 
44
- product = SourceConfig.for_environment(env, :source)
45
- expect(product).to be_an(OAISourceConfig)
72
+ impl = SourceConfig.for_environment(env, :source)
73
+ expect(impl).to be_an(OAISourceConfig)
46
74
  args = { oai_base_url: URI('http://oai.example.org/oai'), metadata_prefix: 'some_prefix', set: 'some_set', seconds_granularity: true }
47
75
  args.each do |k, v|
48
- expect(product.send(k)).to eq(v)
76
+ expect(impl.send(k)).to eq(v)
49
77
  end
50
78
  end
51
79
 
52
- it 'raises a sensible exception if no product found for the key' do
80
+ it 'raises a sensible exception if no impl found for the key' do
53
81
  config_hash = { protocol: 'Elvis' }
54
82
  env = instance_double(Environment)
55
83
  expect(env).to receive(:args_for).with(:source) { config_hash }
@@ -59,11 +87,11 @@ module Config
59
87
 
60
88
  describe '#from_file' do
61
89
  it 'builds the correct class with the correct config' do
62
- product = SourceConfig.from_file('spec/data/single-environment.yml', :source)
63
- expect(product).to be_an(OAISourceConfig)
90
+ impl = SourceConfig.from_file('spec/data/single-environment.yml', :source)
91
+ expect(impl).to be_an(OAISourceConfig)
64
92
  args = { oai_base_url: URI('http://oai.example.org/oai'), metadata_prefix: 'some_prefix', set: 'some_set', seconds_granularity: true }
65
93
  args.each do |k, v|
66
- expect(product.send(k)).to eq(v)
94
+ expect(impl.send(k)).to eq(v)
67
95
  end
68
96
  end
69
97
  end
@@ -44,6 +44,9 @@ module Config
44
44
  env = Environment.load_file('spec/data/single-environment.yml')
45
45
  expect(env).to be_an(Environment)
46
46
  expect(env.name).to eq(Environments::DEFAULT_ENVIRONMENT)
47
+
48
+ pers = PersistenceConfig.for_environment(env, :persistence)
49
+ expect(pers).to be_a(DBConfig)
47
50
  end
48
51
 
49
52
  it 'raises an error for malformed files' do
@@ -70,7 +73,7 @@ module Config
70
73
  'adapter' => 'mysql2',
71
74
  'encoding' => 'utf8',
72
75
  'pool' => 5,
73
- 'database' => 'example_pord',
76
+ 'database' => 'example_prod',
74
77
  'host' => 'mysql-dev.example.org',
75
78
  'port' => 3306
76
79
  },
@@ -1,4 +1,5 @@
1
1
  require 'spec_helper'
2
+ require_relative 'fixtures'
2
3
 
3
4
  module Config
4
5
  module Factory
@@ -16,6 +17,53 @@ module Config
16
17
  end
17
18
  end
18
19
 
20
+ it 'supports YAML references' do
21
+ envs = Environments.load_file('spec/data/multiple-environments.yml')
22
+
23
+ defaults = envs[:defaults]
24
+ source_default = SourceConfig.for_environment(defaults, :source)
25
+ expect(source_default).to be_an(OAISourceConfig)
26
+ expect(source_default.source_uri).to eq(URI('http://oai.example.org/oai'))
27
+ index_default = IndexConfig.for_environment(defaults, :index)
28
+ expect(index_default).to be_a(SolrConfig)
29
+ expect(index_default.uri).to eq(URI('http://solr.example.org/'))
30
+
31
+ development = envs[:development]
32
+ source_dev = SourceConfig.for_environment(development, :source)
33
+ expect(source_dev).to be_an(OAISourceConfig)
34
+ expect(source_dev.source_uri).to eq(URI('http://oai.example.org/oai'))
35
+ persistence_dev = PersistenceConfig.for_environment(development, :db)
36
+ expect(persistence_dev).to be_a(DBConfig)
37
+ expect(persistence_dev.connection_info[:adapter]).to eq('mysql2')
38
+ expect(persistence_dev.connection_info[:database]).to eq('example_dev')
39
+ index_dev = IndexConfig.for_environment(development, :index)
40
+ expect(index_dev).to be_a(SolrConfig)
41
+ expect(index_dev.uri).to eq(URI('http://solr-dev.example.org/'))
42
+
43
+ test = envs[:test]
44
+ persistence_test = PersistenceConfig.for_environment(test, :db)
45
+ expect(persistence_test).to be_a(DBConfig)
46
+ expect(persistence_test.connection_info[:adapter]).to eq('sqlite3')
47
+ source_test = SourceConfig.for_environment(test, :source)
48
+ expect(source_test).to be_a(ResyncSourceConfig)
49
+ expect(source_test.source_uri).to eq(URI('http://localhost:8888/capabilitylist.xml'))
50
+ index_test = IndexConfig.for_environment(test, :index)
51
+ expect(index_test).to be_a(SolrConfig)
52
+ expect(index_test.uri).to eq(URI('http://localhost:8000/solr/'))
53
+
54
+ production = envs[:production]
55
+ source_production = SourceConfig.for_environment(production, :source)
56
+ expect(source_production).to be_an(OAISourceConfig)
57
+ expect(source_production.source_uri).to eq(URI('http://oai.example.org/oai'))
58
+ index_production = IndexConfig.for_environment(production, :index)
59
+ expect(index_production).to be_a(SolrConfig)
60
+ expect(index_production.uri).to eq(URI('http://solr.example.org/'))
61
+ persistence_production = PersistenceConfig.for_environment(production, :db)
62
+ expect(persistence_production).to be_a(DBConfig)
63
+ expect(persistence_production.connection_info[:adapter]).to eq('mysql2')
64
+ expect(persistence_production.connection_info[:database]).to eq('example_prod')
65
+ end
66
+
19
67
  it 'reads a standard ActiveRecord DB config' do
20
68
  envs = Environments.load_file('spec/data/db-config.yml')
21
69
  expect(envs).to be_a(Hash)
@@ -36,12 +36,42 @@ class ResyncSourceConfig < SourceConfig
36
36
  attr_reader :capability_list_url
37
37
 
38
38
  def initialize(capability_list_url:)
39
- super(url: capability_list_url)
39
+ super(source_url: capability_list_url)
40
40
  @capability_list_url = source_uri
41
41
  end
42
42
  end
43
43
 
44
- class DBConfig
44
+ # PersistenceConfig
45
+
46
+ class PersistenceConfig
47
+ include Config::Factory
48
+ end
49
+
50
+ class DBConfig < PersistenceConfig
51
+ attr_reader :connection_info
52
+
53
+ can_build_if { |config| config.key?(:adapter) }
54
+
55
+ def initialize(connection_info)
56
+ @connection_info = connection_info
57
+ end
58
+ end
59
+
60
+ class XMLConfig < PersistenceConfig
61
+ attr_reader :connection_info
62
+
63
+ can_build_if do |config|
64
+ config[:path] && config[:path].end_with?('.xml')
65
+ end
66
+
67
+ def initialize(connection_info)
68
+ @connection_info = connection_info
69
+ end
70
+ end
71
+
72
+ # MysqlConfig
73
+
74
+ class MysqlConfig
45
75
  include Config::Factory
46
76
 
47
77
  attr_reader :connection_info
@@ -66,7 +96,7 @@ class IndexConfig
66
96
  end
67
97
 
68
98
  class SolrConfig < IndexConfig
69
- adapter 'Solr'
99
+ adapter 'solr'
70
100
 
71
101
  attr_reader :url
72
102
  attr_reader :proxy
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: config-factory
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Moles
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-03-31 00:00:00.000000000 Z
11
+ date: 2016-04-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -122,6 +122,7 @@ files:
122
122
  - spec/.rubocop.yml
123
123
  - spec/data/db-config.yml
124
124
  - spec/data/multiple-environments.yml
125
+ - spec/data/raw-section.yml
125
126
  - spec/data/single-environment.yml
126
127
  - spec/spec_helper.rb
127
128
  - spec/unit/config/factory/abstract_factory_spec.rb
@@ -157,6 +158,7 @@ test_files:
157
158
  - spec/.rubocop.yml
158
159
  - spec/data/db-config.yml
159
160
  - spec/data/multiple-environments.yml
161
+ - spec/data/raw-section.yml
160
162
  - spec/data/single-environment.yml
161
163
  - spec/spec_helper.rb
162
164
  - spec/unit/config/factory/abstract_factory_spec.rb