ncs_mdes_warehouse 0.8.0 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +13 -0
- data/lib/ncs_navigator/warehouse/configuration.rb +21 -1
- data/lib/ncs_navigator/warehouse/transformers/database.rb +34 -13
- data/lib/ncs_navigator/warehouse/version.rb +1 -1
- data/spec/ncs_navigator/warehouse/configuration_spec.rb +6 -0
- data/spec/ncs_navigator/warehouse/transformers/database_spec.rb +38 -7
- metadata +8 -2
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,19 @@
|
|
1
1
|
NCS Navigator MDES Warehouse History
|
2
2
|
====================================
|
3
3
|
|
4
|
+
0.9.0
|
5
|
+
-----
|
6
|
+
|
7
|
+
- Support symbolic references to models in `produce_one_for_one` in the database
|
8
|
+
transformer DSL. This results in a minor *BREAKING CHANGE*:
|
9
|
+
`Database::DSL::OneToOneProducer#model` has been renamed `model_or_name` and
|
10
|
+
may give you either the model or a symbolic reference to it. There is a new
|
11
|
+
method `#model(configuration)` which takes a configuration instance and always
|
12
|
+
gives you a model. Similarly, `Database::DSL::OneToOneProducer#column_map` now
|
13
|
+
requires a configuration instance as a second parameter. (#2552)
|
14
|
+
|
15
|
+
- Fix probably-never-functional `Configuration#mdes_version` reader. (#2553)
|
16
|
+
|
4
17
|
0.8.0
|
5
18
|
-----
|
6
19
|
|
@@ -157,9 +157,24 @@ module NcsNavigator::Warehouse
|
|
157
157
|
|
158
158
|
@models_module = NcsNavigator::Warehouse::Models.const_get module_name
|
159
159
|
end
|
160
|
-
attr_reader :mdes_version
|
161
160
|
|
162
161
|
##
|
162
|
+
# Returns the configured MDES version string.
|
163
|
+
#
|
164
|
+
# N.b.: this method triggers loading the default MDES module and
|
165
|
+
# specification if {#mdes_version=} has not been called yet.
|
166
|
+
#
|
167
|
+
# @return [String] the configured MDES version.
|
168
|
+
def mdes_version
|
169
|
+
mdes.version
|
170
|
+
end
|
171
|
+
|
172
|
+
##
|
173
|
+
# Returns the specification for the active MDES version.
|
174
|
+
#
|
175
|
+
# N.b.: this method triggers loading the default MDES module and
|
176
|
+
# specification if {#mdes_version=} has not been called yet.
|
177
|
+
#
|
163
178
|
# @return [NcsNavigator::Mdes::Specification] the specification
|
164
179
|
# (provided by `ncs_mdes`) for the active MDES version.
|
165
180
|
def mdes
|
@@ -169,6 +184,11 @@ module NcsNavigator::Warehouse
|
|
169
184
|
attr_writer :mdes
|
170
185
|
|
171
186
|
##
|
187
|
+
# Returns the module namespacing the models for the active MDES version.
|
188
|
+
#
|
189
|
+
# N.b.: this method triggers loading the default MDES module and
|
190
|
+
# specification if {#mdes_version=} has not been called yet.
|
191
|
+
#
|
172
192
|
# @return [Module] the module namespacing the models for the
|
173
193
|
# active MDES version.
|
174
194
|
def models_module
|
@@ -274,8 +274,10 @@ module NcsNavigator::Warehouse::Transformers
|
|
274
274
|
# @param [Symbol] name the name of this producer; if you don't
|
275
275
|
# specify a `:query`, the default is to return every row from
|
276
276
|
# the application table with this name.
|
277
|
-
# @param [Class]
|
278
|
-
# the query will be mapped.
|
277
|
+
# @param [Class,Symbol] model_or_reference the warehouse model to which
|
278
|
+
# the results of the query will be mapped. It can be expressed either
|
279
|
+
# as the model class itself or the unqualified name of the model. (See
|
280
|
+
# {Configuration#model}.)
|
279
281
|
# @param [Hash] options
|
280
282
|
# @option options :query [String] the query to execute for this
|
281
283
|
# producer. If not specified, the query is `"SELECT * FROM #{name}"`.
|
@@ -292,13 +294,13 @@ module NcsNavigator::Warehouse::Transformers
|
|
292
294
|
# column map don't match them to anything.
|
293
295
|
#
|
294
296
|
# @return [void]
|
295
|
-
def produce_one_for_one(name,
|
297
|
+
def produce_one_for_one(name, model_or_reference, options={})
|
296
298
|
options[:column_map] =
|
297
299
|
(options[:column_map] || {}).inject({}) { |h, (k, v)| h[k.to_s] = v.to_s; h }
|
298
300
|
options[:ignored_columns] = (options[:ignored_columns] || []).collect(&:to_s)
|
299
301
|
|
300
302
|
record_producers <<
|
301
|
-
OneForOneProducer.new(name, options.delete(:query),
|
303
|
+
OneForOneProducer.new(name, options.delete(:query), model_or_reference, self, options)
|
302
304
|
end
|
303
305
|
end
|
304
306
|
|
@@ -313,11 +315,11 @@ module NcsNavigator::Warehouse::Transformers
|
|
313
315
|
##
|
314
316
|
# The class encapsulating one call to {DSL#produce_one_for_one}
|
315
317
|
class OneForOneProducer < RecordProducer
|
316
|
-
attr_reader :
|
318
|
+
attr_reader :model_or_reference, :options, :dsl_host
|
317
319
|
|
318
|
-
def initialize(name, query,
|
320
|
+
def initialize(name, query, model_or_reference, dsl_host, options)
|
319
321
|
super(name, query, self)
|
320
|
-
@
|
322
|
+
@model_or_reference = model_or_reference
|
321
323
|
@dsl_host = dsl_host
|
322
324
|
@options = options
|
323
325
|
end
|
@@ -325,14 +327,14 @@ module NcsNavigator::Warehouse::Transformers
|
|
325
327
|
##
|
326
328
|
# Produces a single instance of {#model} using the values in the
|
327
329
|
# row as mapped by {#column_map}.
|
328
|
-
def convert_row(row)
|
329
|
-
col_map = column_map(row.members)
|
330
|
+
def convert_row(row, meta)
|
331
|
+
col_map = column_map(row.members, meta[:configuration])
|
330
332
|
unused = row.members.collect(&:to_s) - col_map.keys - ignored_columns
|
331
333
|
|
332
334
|
if on_unused == :fail && !unused.empty?
|
333
335
|
raise UnusedColumnsForModelError.new(unused)
|
334
336
|
end
|
335
|
-
model.new(
|
337
|
+
model(meta[:configuration]).new(
|
336
338
|
col_map.inject({}) { |pv, (col_name, var_name)|
|
337
339
|
pv[var_name] = clean_value(row[col_name]);
|
338
340
|
pv
|
@@ -343,7 +345,23 @@ module NcsNavigator::Warehouse::Transformers
|
|
343
345
|
|
344
346
|
##
|
345
347
|
# Implemented so that this class behaves like a lambda.
|
346
|
-
def arity;
|
348
|
+
def arity; 2; end
|
349
|
+
|
350
|
+
##
|
351
|
+
# @param configuration [Configuration]
|
352
|
+
#
|
353
|
+
# @return [Class] the model for this producer. If {#model_or_reference}
|
354
|
+
# is a symbolic reference, this method resolves it using the given
|
355
|
+
# configuration.
|
356
|
+
def model(configuration)
|
357
|
+
case model_or_reference
|
358
|
+
when Class
|
359
|
+
model_or_reference
|
360
|
+
else
|
361
|
+
configuration.model(model_or_reference) or
|
362
|
+
fail("There is no table or model named #{model_or_reference.inspect} in MDES #{configuration.mdes_version}.")
|
363
|
+
end
|
364
|
+
end
|
347
365
|
|
348
366
|
def clean_value(v)
|
349
367
|
if v.respond_to?(:strip)
|
@@ -356,12 +374,15 @@ module NcsNavigator::Warehouse::Transformers
|
|
356
374
|
|
357
375
|
##
|
358
376
|
# @param [Array<String>] column_names
|
377
|
+
# @param [Configuration] configuration the configuration to use to resolve
|
378
|
+
# model references, if necessary.
|
379
|
+
#
|
359
380
|
# @return [Hash<String, String>] a mapping from the given
|
360
381
|
# column names to MDES variable names for the configured
|
361
382
|
# model. This mapping reflects both the configured explicit
|
362
383
|
# mapping and the heuristic.
|
363
|
-
def column_map(column_names)
|
364
|
-
available_props = model.properties.collect { |p| p.name.to_s }
|
384
|
+
def column_map(column_names, configuration)
|
385
|
+
available_props = model(configuration).properties.collect { |p| p.name.to_s }
|
365
386
|
available_props -= options[:column_map].values
|
366
387
|
|
367
388
|
column_names.inject(options[:column_map].dup) do |map, column|
|
@@ -124,6 +124,12 @@ module NcsNavigator::Warehouse
|
|
124
124
|
end
|
125
125
|
end
|
126
126
|
|
127
|
+
describe '#mdes_version', :slow, :use_mdes, :modifies_warehouse_state do
|
128
|
+
it 'is a shortcut for extracting the version from #mdes' do
|
129
|
+
spec_config.mdes_version.should == spec_mdes_version
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
127
133
|
describe '#mdes', :slow, :modifies_warehouse_state do
|
128
134
|
it 'Uses the default MDES version if called before mdes_version=' do
|
129
135
|
config.mdes.version.should == NcsNavigator::Warehouse::DEFAULT_MDES_VERSION
|
@@ -273,6 +273,7 @@ module NcsNavigator::Warehouse::Transformers
|
|
273
273
|
let(:options) { {} }
|
274
274
|
let(:cls) { sample_class }
|
275
275
|
let(:producer) { cls.record_producers.first }
|
276
|
+
let(:row_meta) { {} }
|
276
277
|
|
277
278
|
def make_row(contents)
|
278
279
|
Struct.new(*contents.keys).new.tap do |r|
|
@@ -284,7 +285,7 @@ module NcsNavigator::Warehouse::Transformers
|
|
284
285
|
|
285
286
|
def model_row(row)
|
286
287
|
cls.produce_one_for_one(:addresses, address_model, options)
|
287
|
-
producer.row_processor.call(make_row(row))
|
288
|
+
producer.row_processor.call(make_row(row), row_meta)
|
288
289
|
end
|
289
290
|
|
290
291
|
it 'maps a column to the property with the same name' do
|
@@ -314,6 +315,36 @@ module NcsNavigator::Warehouse::Transformers
|
|
314
315
|
# expect no errors
|
315
316
|
end
|
316
317
|
|
318
|
+
describe 'and a symbolic model' do
|
319
|
+
let(:address_model) { :Address }
|
320
|
+
let(:mock_config) { mock('Configuration') }
|
321
|
+
|
322
|
+
before do
|
323
|
+
row_meta[:configuration] = mock_config
|
324
|
+
mock_config.stub!(:model).and_return(Database::DSL::TestModels::Address)
|
325
|
+
end
|
326
|
+
|
327
|
+
it 'resolves the model using the configuration' do
|
328
|
+
mock_config.should_receive(:model).with(address_model).and_return(Database::DSL::TestModels::Address)
|
329
|
+
|
330
|
+
model_row(:street => '42 Foo').street.should == '42 Foo'
|
331
|
+
end
|
332
|
+
|
333
|
+
it 'throws an exception if the model does not exist' do
|
334
|
+
mock_config.should_receive(:model).with(address_model).and_return(nil)
|
335
|
+
mock_config.stub!(:mdes_version).and_return('3.4')
|
336
|
+
|
337
|
+
expect { model_row(:street => '42 Baz') }.
|
338
|
+
to raise_error('There is no table or model named :Address in MDES 3.4.')
|
339
|
+
end
|
340
|
+
|
341
|
+
it 'can retrieve the column map' do
|
342
|
+
options[:column_map] = { :street_loc => :street }
|
343
|
+
cls.produce_one_for_one(:addresses, address_model, options)
|
344
|
+
producer.column_map(%w(street street_loc), mock_config).keys.should == %w(street_loc)
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
317
348
|
describe 'and unused columns' do
|
318
349
|
it 'fails with unused columns if requested' do
|
319
350
|
options[:on_unused] = :fail
|
@@ -349,7 +380,7 @@ module NcsNavigator::Warehouse::Transformers
|
|
349
380
|
|
350
381
|
it 'fails appropriately' do
|
351
382
|
begin
|
352
|
-
producer.call(make_row
|
383
|
+
producer.call(make_row(:address_type => '-5', :address_length => '6'), row_meta)
|
353
384
|
fail "Exception not thrown"
|
354
385
|
rescue Database::UnusedColumnsForModelError => e
|
355
386
|
e.unused.should == %w(address_length)
|
@@ -358,13 +389,13 @@ module NcsNavigator::Warehouse::Transformers
|
|
358
389
|
|
359
390
|
it 'does not fail if the global setting is overridden' do
|
360
391
|
options[:on_unused] = :ignore
|
361
|
-
lambda { producer.call(make_row
|
392
|
+
lambda { producer.call(make_row(:address_type => '-5', :address_length => '6'), row_meta) }.
|
362
393
|
should_not raise_error
|
363
394
|
end
|
364
395
|
|
365
396
|
it 'can be ignored by modifying the global setting' do
|
366
397
|
cls.on_unused_columns :ignore
|
367
|
-
lambda { producer.call(make_row
|
398
|
+
lambda { producer.call(make_row(:address_type => '-5', :address_length => '6'), row_meta) }.
|
368
399
|
should_not raise_error
|
369
400
|
end
|
370
401
|
end
|
@@ -405,19 +436,19 @@ module NcsNavigator::Warehouse::Transformers
|
|
405
436
|
|
406
437
|
it 'does not include the default mapping in the total map' do
|
407
438
|
cls.produce_one_for_one(:addresses, address_model, options)
|
408
|
-
producer.column_map(%w(street street_loc)).keys.should == %w(street_loc)
|
439
|
+
producer.column_map(%w(street street_loc), configuration).keys.should == %w(street_loc)
|
409
440
|
end
|
410
441
|
|
411
442
|
it 'always includes explicit mapping values in the total map' do
|
412
443
|
cls.produce_one_for_one(:addresses, address_model, options)
|
413
|
-
producer.column_map(%w(street)).keys.should == %w(street_loc)
|
444
|
+
producer.column_map(%w(street), configuration).keys.should == %w(street_loc)
|
414
445
|
end
|
415
446
|
end
|
416
447
|
|
417
448
|
describe 'the processor' do
|
418
449
|
it 'responds to :arity' do
|
419
450
|
cls.produce_one_for_one(:addresses, address_model, options)
|
420
|
-
producer.row_processor.arity.should ==
|
451
|
+
producer.row_processor.arity.should == 2
|
421
452
|
end
|
422
453
|
end
|
423
454
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ncs_mdes_warehouse
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-10-05 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: ncs_mdes
|
@@ -2379,12 +2379,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
2379
2379
|
- - ! '>='
|
2380
2380
|
- !ruby/object:Gem::Version
|
2381
2381
|
version: '0'
|
2382
|
+
segments:
|
2383
|
+
- 0
|
2384
|
+
hash: -1614550889051803335
|
2382
2385
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
2383
2386
|
none: false
|
2384
2387
|
requirements:
|
2385
2388
|
- - ! '>='
|
2386
2389
|
- !ruby/object:Gem::Version
|
2387
2390
|
version: '0'
|
2391
|
+
segments:
|
2392
|
+
- 0
|
2393
|
+
hash: -1614550889051803335
|
2388
2394
|
requirements: []
|
2389
2395
|
rubyforge_project:
|
2390
2396
|
rubygems_version: 1.8.24
|