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 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