ncs_mdes_warehouse 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -5,3 +5,4 @@ docs
5
5
  reports
6
6
  *.sqlite3
7
7
  logs
8
+ pkg
data/CHANGELOG.md CHANGED
@@ -1,6 +1,13 @@
1
1
  NCS Navigator MDES Warehouse History
2
2
  ====================================
3
3
 
4
+ 0.0.2
5
+ -----
6
+
7
+ - Added `model_row` helper in Transformers::Database for
8
+ heuristic-based semi-automatic mapping of transactional data to
9
+ warehouse entries.
10
+
4
11
  0.0.1
5
12
  -----
6
13
 
@@ -2,6 +2,7 @@ require 'ncs_navigator/warehouse'
2
2
 
3
3
  require 'active_support/core_ext/string'
4
4
  require 'forwardable'
5
+ require 'data_mapper'
5
6
 
6
7
  module NcsNavigator::Warehouse::Transformers
7
8
  ##
@@ -93,6 +94,7 @@ module NcsNavigator::Warehouse::Transformers
93
94
  def repository
94
95
  @repository ||=
95
96
  begin
97
+ log.debug("Connecting DM repo #{repository_name.inspect} with #{connection_parameters.merge('password' => 'SUPPRESSED').inspect}")
96
98
  ::DataMapper.setup(repository_name, connection_parameters)
97
99
  ::DataMapper.repository(repository_name)
98
100
  end
@@ -173,6 +175,33 @@ module NcsNavigator::Warehouse::Transformers
173
175
  @repository_name ||= name.demodulize.underscore.to_sym
174
176
  end
175
177
 
178
+ ##
179
+ # What to do if are encountered by {#model_row}. Default is
180
+ # `:ignore` but may be set to `:fail` for all producers using
181
+ # this method. The value set here may be overridden per
182
+ # {#model_row} call.
183
+ #
184
+ # @return [:ignore, :fail]
185
+ def on_unused_columns(setting=nil)
186
+ if setting
187
+ @on_unused_columns = setting
188
+ else
189
+ @on_unused_columns ||= :ignore
190
+ end
191
+ end
192
+
193
+ ##
194
+ # Indicates a set of columns which which should not be checked
195
+ # for used-ness by {#model_row}, even if the `:unused` option is
196
+ # set to `:fail`.
197
+ def ignored_columns(*columns)
198
+ if columns.empty?
199
+ @ignored_columns ||= []
200
+ else
201
+ @ignored_columns = columns.collect(&:to_sym)
202
+ end
203
+ end
204
+
176
205
  ##
177
206
  # Defines the bcdatabase specification to use when connecting to
178
207
  # the database for this enumeration.
@@ -211,6 +240,126 @@ module NcsNavigator::Warehouse::Transformers
211
240
  def produce_records(name, options={}, &logic)
212
241
  record_producers << RecordProducer.new(name, options[:query], logic)
213
242
  end
243
+
244
+ ##
245
+ # Performs automatic conversion from a row struct to a
246
+ # an instance of a particular warehouse model. This conversion
247
+ # uses several heuristics to apply values from the row to the
248
+ # model instance. Each column in the row will be converted into
249
+ # at most one model property value. In the order that they are
250
+ # applied, the heuristics are:
251
+ #
252
+ # * If there's a `:prefix` option, the column is named {X},
253
+ # and there's a property named {prefix}{X}, use that
254
+ # property.
255
+ # * If the column is named {X} and there's a property named
256
+ # {X}, use that property.
257
+ # * If the column is named {X}_code and there's a property
258
+ # named {X}, use that property.
259
+ # * If the column is named {X}_code and there's a property
260
+ # named {X}_id, use that property.
261
+ # * If the column is named {X}_other and there's a property
262
+ # named {X}_oth, use that property.
263
+ #
264
+ # Separately, any property value in the instance may be
265
+ # explicitly set via a hash passed as the `:explicit`
266
+ # option. Property values in `:explicit` take precedence over
267
+ # the heuristically-determined values.
268
+ #
269
+ # @param [Class] model the warehouse model class (e.g.,
270
+ # `NcsNavigator::Warehouse::Models::TwoPointZero::Person`)
271
+ # @param [Object] row a DataMapper row struct that is the source
272
+ # of the data for the instance. (This is the kind of object
273
+ # that is yielded to {#produce_records} blocks.)
274
+ # @param [Hash] options Options controlling the created
275
+ # instance.
276
+ #
277
+ # @option options :prefix [String] a prefix to use when looking
278
+ # for matching property values. (See above.)
279
+ # @option options :column_map [Hash<Symbol, Symbol>] explicit
280
+ # mapping from column name to model property name. This
281
+ # mapping is consulted before the heuristics are applied and
282
+ # before `:property_values` is used.
283
+ # @option options :property_values [Hash<Symbol, Object>]
284
+ # explicit values to use. Keys are model property names and
285
+ # values are the desired values. Any values in this hash trump
286
+ # the heuristically-determined values.
287
+ # @option options :on_unused [:ignore,:fail] what to do when
288
+ # there are columns in the row which are not used.
289
+ # @option options :ignored_columns [Array<String,Symbol>]
290
+ # columns to consider "used" even if the heuristic doesn't
291
+ # match them to anything.
292
+ #
293
+ # @return [Object] an instance of `model`.
294
+ def model_row(model, row, options={})
295
+ unused_behavior = options[:on_unused] || on_unused_columns
296
+ pv, unused = create_property_values(model, row, options)
297
+
298
+ unused -= ignored_columns
299
+ unused -= options[:ignored_or_used].collect(&:to_sym) if options[:ignored_or_used]
300
+
301
+ if unused_behavior == :fail && !unused.empty?
302
+ raise UnusedColumnsForModelError.new(unused)
303
+ end
304
+ model.new(pv)
305
+ end
306
+
307
+ ##
308
+ # Returns a two-member array. The first member is the matched
309
+ # property values. The second is the columns from the row which
310
+ # were not matched to anything.
311
+ def create_property_values(model, row, options)
312
+ column_map = (options[:column_map] || {}).inject({}) { |h, (k, v)| h[k.to_s] = v.to_s; h }
313
+
314
+ pv = (options[:property_values] || {}).inject({}) { |h, (k, v)| h[k.to_s] = v; h }
315
+ column_map.values.each { |prop| pv.delete(prop) }
316
+
317
+ available_props = model.properties.collect { |p| p.name.to_s }
318
+ available_props -= pv.keys
319
+
320
+ unused = []
321
+ row.members.each do |column|
322
+ prop =
323
+ if column_map[column.to_s]
324
+ column_map[column.to_s]
325
+ else
326
+ [
327
+ [//, ''],
328
+ [/_code$/, ''],
329
+ [/_code$/, '_id'],
330
+ [/_other$/, '_oth'],
331
+ ].collect do |pattern, substution|
332
+ if column =~ pattern
333
+ prefixed_property_name(available_props,
334
+ column.to_s.sub(pattern, substution), options[:prefix])
335
+ end
336
+ end.compact.first
337
+ end
338
+ if prop
339
+ available_props.delete(prop)
340
+ pv[prop] = row[column]
341
+ else
342
+ unused << column.to_sym
343
+ end
344
+ end
345
+ [pv, unused]
346
+ end
347
+ private :create_property_values
348
+
349
+ ##
350
+ # Determines if the model has a property with the given name,
351
+ # with or without the prefix.
352
+ #
353
+ # @return [String,nil] the name of an existing property on the
354
+ # model, either with or without the prefix; or nil.
355
+ def prefixed_property_name(available_props, name, prefix)
356
+ if prefix && available_props.include?(prop = "#{prefix}#{name}")
357
+ prop
358
+ elsif available_props.include?(name)
359
+ name
360
+ end
361
+ end
362
+ private :prefixed_property_name
214
363
  end
215
364
 
216
365
  ##
@@ -232,5 +381,16 @@ module NcsNavigator::Warehouse::Transformers
232
381
  EnumTransformer.new(new(configuration, options))
233
382
  end
234
383
  end
384
+
385
+ class UnusedColumnsForModelError < StandardError
386
+ attr_accessor :unused
387
+
388
+ def initialize(unused)
389
+ super(
390
+ "#{unused.size} unused column#{'s' unless unused.size == 1} when building model. " +
391
+ "Use :ignored_or_used => %w(#{unused.join(' ')}) or :unused => :ignore to suppress this error.")
392
+ @unused = unused
393
+ end
394
+ end
235
395
  end
236
396
  end
@@ -1,5 +1,5 @@
1
1
  module NcsNavigator
2
2
  module Warehouse
3
- VERSION = '0.0.1'
3
+ VERSION = '0.0.2'
4
4
  end
5
5
  end
@@ -18,7 +18,7 @@ Gem::Specification.new do |s|
18
18
  s.add_dependency 'ncs_mdes', '~> 0.4', '>= 0.4.2'
19
19
  s.add_dependency 'ncs_navigator_configuration', '~> 0.1'
20
20
 
21
- s.add_dependency 'activesupport', '~> 3.0.9'
21
+ s.add_dependency 'activesupport', '~> 3.0'
22
22
  s.add_dependency 'i18n', '~> 0.6' # required by activesupport
23
23
 
24
24
  s.add_dependency 'thor', '~> 0.14.6'
@@ -167,6 +167,15 @@ module NcsNavigator::Warehouse::Transformers
167
167
 
168
168
  acc.should == %w(A)
169
169
  end
170
+
171
+ it 'can execute just one producer by calling :to_a' do
172
+ execute_sql(
173
+ "INSERT INTO people (id) VALUES ('B')",
174
+ "INSERT INTO more_people (id) VALUES ('Q')"
175
+ )
176
+
177
+ enumerator.to_a(:people).should == %w(B)
178
+ end
170
179
  end
171
180
  end
172
181
 
@@ -190,5 +199,181 @@ module NcsNavigator::Warehouse::Transformers
190
199
  end
191
200
  end
192
201
  end
202
+
203
+ describe Database::DSL do
204
+ describe '#on_unused_columns' do
205
+ it 'defaults to :ignore' do
206
+ sample_class.on_unused_columns.should == :ignore
207
+ end
208
+
209
+ it 'can be set' do
210
+ sample_class { on_unused_columns :fail }.on_unused_columns.should == :fail
211
+ end
212
+ end
213
+
214
+ describe '#model_row' do
215
+ # DataMapper models can't be anonymous
216
+ module Database::DSL::TestModels
217
+ class Address
218
+ include ::DataMapper::Resource
219
+
220
+ property :address_id, String, :key => true
221
+ property :street, NcsNavigator::Warehouse::DataMapper::NcsString
222
+ property :address_type,
223
+ NcsNavigator::Warehouse::DataMapper::NcsString,
224
+ { :required => true, :length => 1..2, :set => ["1", "2", "3", "4", "5", "-5", "-6", "-4"] }
225
+ property :address_type_oth,
226
+ NcsNavigator::Warehouse::DataMapper::NcsString,
227
+ { :pii => :possible, :length => 0..255 }
228
+ end
229
+
230
+ Address.finalize
231
+ end
232
+
233
+ let(:address_model) { Database::DSL::TestModels::Address }
234
+
235
+ let(:options) { {} }
236
+ let(:cls) { sample_class }
237
+
238
+ def make_row(contents)
239
+ Struct.new(*contents.keys).new.tap do |r|
240
+ contents.each_pair do |c, v|
241
+ r.send("#{c}=", v)
242
+ end
243
+ end
244
+ end
245
+
246
+ def model_row(row)
247
+ cls.model_row(address_model, make_row(row), options)
248
+ end
249
+
250
+ it 'maps a column to the property with the same name' do
251
+ model_row(:street => '123 Anymain Dr.').street.should == '123 Anymain Dr.'
252
+ end
253
+
254
+ it 'maps a column named {X}_code to a property named {X}' do
255
+ model_row(:address_type_code => '5').address_type.should == '5'
256
+ end
257
+
258
+ it 'maps a column named {X}_code to a property named {X}_id' do
259
+ model_row(:address_code => '-7').address_id.should == '-7'
260
+ end
261
+
262
+ it 'maps a column named {X}_other to a property named {X}_oth' do
263
+ model_row(:address_type_other => 'Elephant graveyard').address_type_oth.
264
+ should == 'Elephant graveyard'
265
+ end
266
+
267
+ describe 'and unused columns' do
268
+ it 'fails with unused columns if requested' do
269
+ options[:on_unused] = :fail
270
+ begin
271
+ model_row(:address_type => '-5', :address_length => '6')
272
+ fail "Exception not thrown"
273
+ rescue Database::UnusedColumnsForModelError => e
274
+ e.unused.should == [:address_length]
275
+ end
276
+ end
277
+
278
+ it 'does not fail if the unused column is ignored in the options' do
279
+ options[:on_unused] = :fail
280
+ options[:ignored_or_used] = %w(address_length)
281
+ lambda { model_row(:address_type => '-5', :address_length => '6') }.
282
+ should_not raise_error
283
+ end
284
+
285
+ it 'does not fail if the unused column is ignored at the class level' do
286
+ options[:on_unused] = :fail
287
+ cls.ignored_columns 'address_length'
288
+ lambda { model_row(:address_type => '-5', :address_length => '6') }.
289
+ should_not raise_error
290
+ end
291
+
292
+ describe 'when #on_unused_columns is set to :fail' do
293
+ let(:cls) {
294
+ sample_class do
295
+ on_unused_columns :fail
296
+ end
297
+ }
298
+
299
+ it 'fails appropriately' do
300
+ begin
301
+ model_row(:address_type => '-5', :address_length => '6')
302
+ fail "Exception not thrown"
303
+ rescue Database::UnusedColumnsForModelError => e
304
+ e.unused.should == [:address_length]
305
+ end
306
+ end
307
+
308
+ it 'does not fail if the global setting is overridden' do
309
+ options[:on_unused] = :ignore
310
+ lambda { model_row(:address_type => '-5', :address_length => '6') }.
311
+ should_not raise_error
312
+ end
313
+ end
314
+ end
315
+
316
+ describe 'with a prefix' do
317
+ before do
318
+ options[:prefix] = 'address_'
319
+ end
320
+
321
+ it 'maps a column to {prefix}_{property_name}' do
322
+ model_row(:type => '-4').address_type.should == '-4'
323
+ end
324
+
325
+ it 'still maps a column to {property_name} if there is no prefixed version' do
326
+ model_row(:street => '123 Anymain Dr.').street.should == '123 Anymain Dr.'
327
+ end
328
+
329
+ it 'maps a column named {X}_code to {prefix}{X}' do
330
+ model_row(:type_code => '1').address_type.should == '1'
331
+ end
332
+ end
333
+
334
+ describe 'with a column map' do
335
+ before do
336
+ options[:column_map] = { :street_loc => :street }
337
+ end
338
+
339
+ it 'uses the value specified by the map' do
340
+ model_row(:street_loc => '123 Anymain Dr.').street.should == '123 Anymain Dr.'
341
+ end
342
+
343
+ it 'prefers the mapped value to a heuristically-determined value' do
344
+ model_row(
345
+ :street_loc => '123 Anymain Dr.', :street => '456 Anywhere St.'
346
+ ).street.should == '123 Anymain Dr.'
347
+ end
348
+
349
+ it 'prefers the mapped column value to an explicit property value' do
350
+ options[:property_values] = { :street => '456 Anywhere St.' }
351
+ model_row(:street_loc => '123 Anymain Dr.').street.should == '123 Anymain Dr.'
352
+ end
353
+ end
354
+
355
+ describe 'with property value mappings' do
356
+ before do
357
+ options[:property_values] = {
358
+ :street => '456 Anywhere St.'
359
+ }
360
+ end
361
+
362
+ it 'prefers the explicit mapping to a column' do
363
+ model_row(:street => '123 Anymain Dr.').street.should == '456 Anywhere St.'
364
+ end
365
+
366
+ it 'reports the column as unused' do
367
+ options[:on_unused] = :fail
368
+ begin
369
+ model_row(:street => '123 Anymain Dr.').street.should == '456 Anywhere St.'
370
+ fail "Exception not thrown"
371
+ rescue Database::UnusedColumnsForModelError => e
372
+ e.unused.should include(:street)
373
+ end
374
+ end
375
+ end
376
+ end
377
+ end
193
378
  end
194
379
  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.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-10-28 00:00:00.000000000Z
12
+ date: 2011-11-04 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: ncs_mdes
16
- requirement: &2154987140 !ruby/object:Gem::Requirement
16
+ requirement: &2159237800 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -24,10 +24,10 @@ dependencies:
24
24
  version: 0.4.2
25
25
  type: :runtime
26
26
  prerelease: false
27
- version_requirements: *2154987140
27
+ version_requirements: *2159237800
28
28
  - !ruby/object:Gem::Dependency
29
29
  name: ncs_navigator_configuration
30
- requirement: &2154986380 !ruby/object:Gem::Requirement
30
+ requirement: &2159236980 !ruby/object:Gem::Requirement
31
31
  none: false
32
32
  requirements:
33
33
  - - ~>
@@ -35,21 +35,21 @@ dependencies:
35
35
  version: '0.1'
36
36
  type: :runtime
37
37
  prerelease: false
38
- version_requirements: *2154986380
38
+ version_requirements: *2159236980
39
39
  - !ruby/object:Gem::Dependency
40
40
  name: activesupport
41
- requirement: &2154985920 !ruby/object:Gem::Requirement
41
+ requirement: &2159236440 !ruby/object:Gem::Requirement
42
42
  none: false
43
43
  requirements:
44
44
  - - ~>
45
45
  - !ruby/object:Gem::Version
46
- version: 3.0.9
46
+ version: '3.0'
47
47
  type: :runtime
48
48
  prerelease: false
49
- version_requirements: *2154985920
49
+ version_requirements: *2159236440
50
50
  - !ruby/object:Gem::Dependency
51
51
  name: i18n
52
- requirement: &2154985420 !ruby/object:Gem::Requirement
52
+ requirement: &2159235940 !ruby/object:Gem::Requirement
53
53
  none: false
54
54
  requirements:
55
55
  - - ~>
@@ -57,10 +57,10 @@ dependencies:
57
57
  version: '0.6'
58
58
  type: :runtime
59
59
  prerelease: false
60
- version_requirements: *2154985420
60
+ version_requirements: *2159235940
61
61
  - !ruby/object:Gem::Dependency
62
62
  name: thor
63
- requirement: &2154984960 !ruby/object:Gem::Requirement
63
+ requirement: &2159235400 !ruby/object:Gem::Requirement
64
64
  none: false
65
65
  requirements:
66
66
  - - ~>
@@ -68,10 +68,10 @@ dependencies:
68
68
  version: 0.14.6
69
69
  type: :runtime
70
70
  prerelease: false
71
- version_requirements: *2154984960
71
+ version_requirements: *2159235400
72
72
  - !ruby/object:Gem::Dependency
73
73
  name: rubyzip
74
- requirement: &2154984420 !ruby/object:Gem::Requirement
74
+ requirement: &2159234800 !ruby/object:Gem::Requirement
75
75
  none: false
76
76
  requirements:
77
77
  - - ~>
@@ -79,10 +79,10 @@ dependencies:
79
79
  version: 0.9.4
80
80
  type: :runtime
81
81
  prerelease: false
82
- version_requirements: *2154984420
82
+ version_requirements: *2159234800
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: nokogiri
85
- requirement: &2154983960 !ruby/object:Gem::Requirement
85
+ requirement: &2159234100 !ruby/object:Gem::Requirement
86
86
  none: false
87
87
  requirements:
88
88
  - - ~>
@@ -90,10 +90,10 @@ dependencies:
90
90
  version: 1.5.0
91
91
  type: :runtime
92
92
  prerelease: false
93
- version_requirements: *2154983960
93
+ version_requirements: *2159234100
94
94
  - !ruby/object:Gem::Dependency
95
95
  name: builder
96
- requirement: &2154983480 !ruby/object:Gem::Requirement
96
+ requirement: &2159233480 !ruby/object:Gem::Requirement
97
97
  none: false
98
98
  requirements:
99
99
  - - ~>
@@ -101,10 +101,10 @@ dependencies:
101
101
  version: '3.0'
102
102
  type: :runtime
103
103
  prerelease: false
104
- version_requirements: *2154983480
104
+ version_requirements: *2159233480
105
105
  - !ruby/object:Gem::Dependency
106
106
  name: data_mapper
107
- requirement: &2154982940 !ruby/object:Gem::Requirement
107
+ requirement: &2159232820 !ruby/object:Gem::Requirement
108
108
  none: false
109
109
  requirements:
110
110
  - - ~>
@@ -112,10 +112,10 @@ dependencies:
112
112
  version: 1.2.0
113
113
  type: :runtime
114
114
  prerelease: false
115
- version_requirements: *2154982940
115
+ version_requirements: *2159232820
116
116
  - !ruby/object:Gem::Dependency
117
117
  name: bcdatabase
118
- requirement: &2154982440 !ruby/object:Gem::Requirement
118
+ requirement: &2159232220 !ruby/object:Gem::Requirement
119
119
  none: false
120
120
  requirements:
121
121
  - - ~>
@@ -123,10 +123,10 @@ dependencies:
123
123
  version: '1.1'
124
124
  type: :runtime
125
125
  prerelease: false
126
- version_requirements: *2154982440
126
+ version_requirements: *2159232220
127
127
  - !ruby/object:Gem::Dependency
128
128
  name: dm-postgres-adapter
129
- requirement: &2154981960 !ruby/object:Gem::Requirement
129
+ requirement: &2159231600 !ruby/object:Gem::Requirement
130
130
  none: false
131
131
  requirements:
132
132
  - - ~>
@@ -134,10 +134,10 @@ dependencies:
134
134
  version: 1.2.0
135
135
  type: :runtime
136
136
  prerelease: false
137
- version_requirements: *2154981960
137
+ version_requirements: *2159231600
138
138
  - !ruby/object:Gem::Dependency
139
139
  name: rspec
140
- requirement: &2154981460 !ruby/object:Gem::Requirement
140
+ requirement: &2159230940 !ruby/object:Gem::Requirement
141
141
  none: false
142
142
  requirements:
143
143
  - - ~>
@@ -145,10 +145,10 @@ dependencies:
145
145
  version: '2.6'
146
146
  type: :development
147
147
  prerelease: false
148
- version_requirements: *2154981460
148
+ version_requirements: *2159230940
149
149
  - !ruby/object:Gem::Dependency
150
150
  name: rake
151
- requirement: &2154981000 !ruby/object:Gem::Requirement
151
+ requirement: &2159230280 !ruby/object:Gem::Requirement
152
152
  none: false
153
153
  requirements:
154
154
  - - ~>
@@ -156,10 +156,10 @@ dependencies:
156
156
  version: 0.9.2
157
157
  type: :development
158
158
  prerelease: false
159
- version_requirements: *2154981000
159
+ version_requirements: *2159230280
160
160
  - !ruby/object:Gem::Dependency
161
161
  name: yard
162
- requirement: &2154953340 !ruby/object:Gem::Requirement
162
+ requirement: &2159225600 !ruby/object:Gem::Requirement
163
163
  none: false
164
164
  requirements:
165
165
  - - ~>
@@ -167,10 +167,10 @@ dependencies:
167
167
  version: 0.7.2
168
168
  type: :development
169
169
  prerelease: false
170
- version_requirements: *2154953340
170
+ version_requirements: *2159225600
171
171
  - !ruby/object:Gem::Dependency
172
172
  name: ci_reporter
173
- requirement: &2154952780 !ruby/object:Gem::Requirement
173
+ requirement: &2159225100 !ruby/object:Gem::Requirement
174
174
  none: false
175
175
  requirements:
176
176
  - - ~>
@@ -178,10 +178,10 @@ dependencies:
178
178
  version: 1.6.5
179
179
  type: :development
180
180
  prerelease: false
181
- version_requirements: *2154952780
181
+ version_requirements: *2159225100
182
182
  - !ruby/object:Gem::Dependency
183
183
  name: fakefs
184
- requirement: &2154952200 !ruby/object:Gem::Requirement
184
+ requirement: &2159224500 !ruby/object:Gem::Requirement
185
185
  none: false
186
186
  requirements:
187
187
  - - ~>
@@ -189,7 +189,7 @@ dependencies:
189
189
  version: 0.4.0
190
190
  type: :development
191
191
  prerelease: false
192
- version_requirements: *2154952200
192
+ version_requirements: *2159224500
193
193
  description:
194
194
  email:
195
195
  - r-sutphin@northwestern.edu
@@ -529,7 +529,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
529
529
  version: '0'
530
530
  segments:
531
531
  - 0
532
- hash: 1308600136922814816
532
+ hash: 3545445795691480899
533
533
  required_rubygems_version: !ruby/object:Gem::Requirement
534
534
  none: false
535
535
  requirements:
@@ -538,7 +538,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
538
538
  version: '0'
539
539
  segments:
540
540
  - 0
541
- hash: 1308600136922814816
541
+ hash: 3545445795691480899
542
542
  requirements: []
543
543
  rubyforge_project:
544
544
  rubygems_version: 1.8.10