ncs_mdes_warehouse 0.8.0 → 0.9.0

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.
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] model the warehouse model to which results of
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, model, options={})
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), model, self, options)
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 :model, :options, :dsl_host
318
+ attr_reader :model_or_reference, :options, :dsl_host
317
319
 
318
- def initialize(name, query, model, dsl_host, options)
320
+ def initialize(name, query, model_or_reference, dsl_host, options)
319
321
  super(name, query, self)
320
- @model = model
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; 1; end
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|
@@ -1,5 +1,5 @@
1
1
  module NcsNavigator
2
2
  module Warehouse
3
- VERSION = '0.8.0'
3
+ VERSION = '0.9.0'
4
4
  end
5
5
  end
@@ -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 :address_type => '-5', :address_length => '6')
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 :address_type => '-5', :address_length => '6') }.
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 :address_type => '-5', :address_length => '6') }.
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 == 1
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.8.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-09-22 00:00:00.000000000 Z
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