duty_free 1.0.3 → 1.0.4

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
  SHA256:
3
- metadata.gz: 4451cbb29d90fb32347b1ff91e91a249da97769e7a64f2524efa912b8971bf86
4
- data.tar.gz: ee3b06059aa89be48329ef48605287e8cb1725dd9f61460d9b3d044354405540
3
+ metadata.gz: f6139333a59c0d3cfb29003ee8d076214906b49270f05de439a260dcf6db227b
4
+ data.tar.gz: 2e129dec49e960970b8116983d7b483fa0144511d315586f12a15a2df3ca52b7
5
5
  SHA512:
6
- metadata.gz: 953bb38945daf16d0f22daa921ad1e0f46881583d51145a59b1983869da8d79c0675640677f1774dbdb9321695aeb69dc29dd4f66250704be0199fed33ab84b6
7
- data.tar.gz: 8e3a0c166acb9bc22919218764e1e39ffa9d495e22ea93eaeb8ef6810b545a99f6af08bf594cc3958e7d9169ccfe5a7841c08b2799e4981066cb47968daaf26b
6
+ metadata.gz: 553fe2590a1803cf52b166290b6c2d2525f58d59a45bba5f1b2f1e1a8e6be42451f5e6642c1971195816bdbe8a3573f5327f5b1f9fedf46abe57762bc1131fd4
7
+ data.tar.gz: 1ded83dc81de148793c4e98ad809e31efff083b650cd69af07b99e305f0ada3e1a4ccc749a00569838397d018ff1817d40a8a2e89a7ae0964cc8ea4afe4f8ef6
@@ -79,8 +79,8 @@ module DutyFree
79
79
 
80
80
  # With an array of incoming data, the first row having column names, perform the import
81
81
  def df_import(data, import_template = nil)
82
- self.instance_variable_set(:@defined_uniques, nil)
83
- self.instance_variable_set(:@valid_uniques, nil)
82
+ instance_variable_set(:@defined_uniques, nil)
83
+ instance_variable_set(:@valid_uniques, nil)
84
84
 
85
85
  import_template ||= if constants.include?(:IMPORT_TEMPLATE)
86
86
  self::IMPORT_TEMPLATE
@@ -105,6 +105,7 @@ module DutyFree
105
105
  devise_class = ''
106
106
  ret = nil
107
107
 
108
+ # Multi-tenancy gem Apartment can be used if there are separate schemas per tenant
108
109
  reference_models = if Object.const_defined?('Apartment')
109
110
  Apartment.excluded_models
110
111
  else
@@ -121,10 +122,13 @@ module DutyFree
121
122
 
122
123
  # Did they give us a filename?
123
124
  if data.is_a?(String)
124
- data = if data.length <= 4096 && data.split('\n').length == 1
125
+ # Filenames with full paths can not be longer than 4096 characters, and can not
126
+ # include newline characters
127
+ data = if data.length <= 4096 && !data.index('\n')
125
128
  File.open(data)
126
129
  else
127
- # Hope that other multi-line strings might be CSV data
130
+ # Any multi-line string is likely CSV data
131
+ # %%% Test to see if TAB characters are present on the first line, instead of commas
128
132
  CSV.new(data)
129
133
  end
130
134
  end
@@ -143,8 +147,15 @@ module DutyFree
143
147
  # Will show as just one transaction when using auditing solutions such as PaperTrail
144
148
  ActiveRecord::Base.transaction do
145
149
  # Check to see if they want to do anything before the whole import
146
- if before_import ||= (import_template[:before_import]) # || some generic before_import)
147
- before_import.call(data)
150
+ # First if defined in the import_template, then if there is a method in the class,
151
+ # and finally (not yet implemented) a generic global before_import
152
+ my_before_import = import_template[:before_import]
153
+ my_before_import ||= respond_to?(:before_import) && method(:before_import)
154
+ # my_before_import ||= some generic my_before_import
155
+ if my_before_import
156
+ last_arg_idx = my_before_import.parameters.length - 1
157
+ arguments = [data, import_template][0..last_arg_idx]
158
+ data = ret if (ret = my_before_import.call(*arguments)).is_a?(Enumerable)
148
159
  end
149
160
  col_list = nil
150
161
  data.each_with_index do |row, row_num|
@@ -178,8 +189,8 @@ module DutyFree
178
189
  # %%% Will the uniques saved into @defined_uniques here just get redefined later
179
190
  # after the next line, the map! with clean to change out the alias names? So we can't yet set
180
191
  # col_list?
181
- defined_uniques(uniques, cols, cols.join('|'), starred)
182
192
  cols.map! { |col| ::DutyFree::Util._clean_name(col, import_template[:as]) } # %%%
193
+ defined_uniques(uniques, cols, cols.join('|'), starred)
183
194
  # Make sure that at least half of them match what we know as being good column names
184
195
  template_column_objects = ::DutyFree::Extensions._recurse_def(self, import_template[:all], import_template).first
185
196
  cols.each_with_index do |col, idx|
@@ -187,9 +198,7 @@ module DutyFree
187
198
  keepers[idx] = template_column_objects.find { |col_obj| col_obj.titleize == col }
188
199
  # puts "Could not find a match for column #{idx + 1}, #{col}" if keepers[idx].nil?
189
200
  end
190
- if keepers.length < (cols.length / 2) - 1
191
- raise ::DutyFree::LessThanHalfAreMatchingColumnsError, I18n.t('import.altered_import_template_coumns')
192
- end
201
+ raise ::DutyFree::LessThanHalfAreMatchingColumnsError, I18n.t('import.altered_import_template_coumns') if keepers.length < (cols.length / 2) - 1
193
202
 
194
203
  # Returns just the first valid unique lookup set if there are multiple
195
204
  valid_unique = find_existing(uniques, cols, starred, import_template, keepers, false)
@@ -206,14 +215,11 @@ module DutyFree
206
215
  s << if v.last.is_a?(Array)
207
216
  v.last[0].where(v.last[1] => row[v.last[2]]).limit(1).pluck(MAX_ID).first.to_s
208
217
  else
209
- binding.pry if v.last.nil?
210
218
  row[v.last].to_s
211
219
  end
212
220
  end
213
221
  # Check to see if they want to preprocess anything
214
- if @before_process ||= import_template[:before_process]
215
- existing_unique = @before_process.call(valid_unique, existing_unique)
216
- end
222
+ existing_unique = @before_process.call(valid_unique, existing_unique) if @before_process ||= import_template[:before_process]
217
223
  obj = if existing.include?(existing_unique)
218
224
  find(existing[existing_unique])
219
225
  else
@@ -227,7 +233,6 @@ module DutyFree
227
233
  sub_objects = {}
228
234
  this_path = nil
229
235
  keepers.each do |key, v|
230
- klass = nil
231
236
  next if v.nil?
232
237
 
233
238
  # Not the same as the last path?
@@ -242,7 +247,6 @@ module DutyFree
242
247
  modded_obj.save if sub_obj&.valid?
243
248
  end
244
249
  elsif sub_obj&.valid?
245
- # binding.pry if sub_obj.is_a?(Employee) && sub_obj.first_name == 'Andrew'
246
250
  sub_obj.save
247
251
  end
248
252
  end
@@ -285,28 +289,20 @@ module DutyFree
285
289
  # such as importing orders and having employees come along :)
286
290
  # if sub_obj.class == klass
287
291
  # trim_prefix = ''
288
- # # binding.pry
289
292
  # end
290
293
  # %%% Maybe instead of passing in "klass" we can give the belongs_to association and build through that instead,
291
294
  # allowing us to nix the klass.new(criteria) line below.
292
295
  trim_prefix = v.titleize[0..-(v.name.length + 2)]
293
296
  trim_prefix << ' ' unless trim_prefix.blank?
294
- if klass == sub_obj.class # Self-referencing thing pointing to us?
295
- # %%% This should be more general than just for self-referencing things.
296
- sub_cols = cols.map { |c| c.start_with?(trim_prefix) ? c[trim_prefix.length..-1] : nil }
297
- # assoc
298
- sub_bt, criteria = klass.find_existing(uniques, sub_cols, starred, import_template, keepers, nil, row, klass, all, '')
299
- else
300
- sub_bt, criteria = klass.find_existing(uniques, cols, starred, import_template, keepers, nil, row, klass, all, trim_prefix)
301
- end
297
+ # if klass == sub_obj.class # Self-referencing thing pointing to us?
298
+ # %%% This should be more general than just for self-referencing things.
299
+ sub_bt, criteria = klass.find_existing(uniques, cols, starred, import_template, keepers, nil, row, klass, all, trim_prefix)
302
300
  rescue ::DutyFree::NoUniqueColumnError
303
301
  sub_unique = nil
304
302
  end
305
303
  # Self-referencing shouldn't build a new one if it couldn't find one
306
- # %%% Can criteria really ever be nil anymore?
307
- unless klass == sub_obj.class && criteria.empty?
308
- sub_bt ||= klass.new(criteria || {})
309
- end
304
+ # %%% Can criteria really ever be empty anymore?
305
+ sub_bt ||= klass.new(criteria || {}) unless klass == sub_obj.class && criteria.empty?
310
306
  sub_obj.send("#{path_part}=", sub_bt)
311
307
  sub_bt
312
308
  end
@@ -326,8 +322,6 @@ module DutyFree
326
322
  trim_prefix = v.titleize[start..-(v.name.length + 2)]
327
323
  trim_prefix << ' ' unless trim_prefix.blank?
328
324
  klass = sub_next.klass
329
- # binding.pry if klass.name == 'OrderDetail'
330
-
331
325
  # assoc.inverse_of is the belongs_to side of the has_many train we came in here on.
332
326
  sub_hm, criteria = klass.find_existing(uniques, cols, starred, import_template, keepers, assoc.inverse_of, row, sub_next, all, trim_prefix)
333
327
 
@@ -347,14 +341,11 @@ module DutyFree
347
341
  sub_objects[this_path] = sub_next if this_path.present?
348
342
  end
349
343
  end
350
- # binding.pry if sub_obj.reports_to
351
- sub_obj = sub_next #if sub_next
344
+ sub_obj = sub_next # if sub_next
352
345
  end
353
- # binding.pry if sub_obj.nil?
354
346
  next if sub_obj.nil?
355
347
 
356
- sym = "#{v.name}=".to_sym
357
- next unless sub_obj.respond_to?(sym)
348
+ next unless sub_obj.respond_to?(sym = "#{v.name}=".to_sym)
358
349
 
359
350
  col_type = (sub_class = sub_obj.class).columns_hash[v.name.to_s]&.type
360
351
  if col_type.nil? && (virtual_columns = import_template[:virtual_columns]) &&
@@ -364,16 +355,15 @@ module DutyFree
364
355
  if col_type == :boolean
365
356
  if row[key].nil?
366
357
  # Do nothing when it's nil
367
- elsif %w[yes y].include?(row[key]&.downcase) # Used to cover 'true', 't', 'on'
358
+ elsif %w[true t yes y].include?(row[key]&.downcase) # Used to cover 'on'
368
359
  row[key] = true
369
- elsif %w[no n].include?(row[key]&.downcase) # Used to cover 'false', 'f', 'off'
360
+ elsif %w[false f no n].include?(row[key]&.downcase) # Used to cover 'off'
370
361
  row[key] = false
371
362
  else
372
363
  row_errors[v.name] ||= []
373
364
  row_errors[v.name] << "Boolean value \"#{row[key]}\" in column #{key + 1} not recognized"
374
365
  end
375
366
  end
376
- # binding.pry if v.name.to_s == 'first_name' && sub_obj.first_name == 'Nancy'
377
367
  sub_obj.send(sym, row[key])
378
368
  # else
379
369
  # puts " #{sub_class.name} doesn't respond to #{sym}"
@@ -423,10 +413,18 @@ module DutyFree
423
413
  s += v.last[1..-1].map { |line_num| { line_num => v.first } } if v.last.count > 1
424
414
  s
425
415
  end
426
- # Check to see if they want to do anything after the import
427
416
  ret = { inserted: inserts, updated: updates, duplicates: duplicates, errors: errors }
428
- if @after_import ||= (import_template[:after_import]) # || some generic after_import)
429
- ret = ret2 if (ret2 = @after_import.call(ret)).is_a?(Hash)
417
+
418
+ # Check to see if they want to do anything after the import
419
+ # First if defined in the import_template, then if there is a method in the class,
420
+ # and finally (not yet implemented) a generic global after_import
421
+ my_after_import = import_template[:after_import]
422
+ my_after_import ||= respond_to?(:after_import) && method(:after_import)
423
+ # my_after_import ||= some generic my_after_import
424
+ if my_after_import
425
+ last_arg_idx = my_after_import.parameters.length - 1
426
+ arguments = [ret][0..last_arg_idx]
427
+ ret = ret2 if (ret2 = my_after_import.call(*arguments)).is_a?(Hash)
430
428
  end
431
429
  end
432
430
  ret
@@ -435,89 +433,46 @@ module DutyFree
435
433
  # For use with importing, based on the provided column list calculate all valid combinations
436
434
  # of unique columns. If there is no valid combination, throws an error.
437
435
  # Returns an object found by this means.
438
- def find_existing(uniques, cols, starred, import_template, keepers, train_we_came_in_here_on, row = nil, obj = nil, all = nil, trim_prefix = '')
439
- col_name_offset = trim_prefix.length
440
- @valid_uniques ||= {} # Fancy memoisation
441
- col_list = cols.join('|')
442
- unless (vus = @valid_uniques[col_list])
443
- # Find all unique combinations that are available based on incoming columns, and
444
- # pair them up with column number mappings.
445
- template_column_objects = ::DutyFree::Extensions._recurse_def(self, all || import_template[:all], import_template).first
446
- available = if trim_prefix.blank?
447
- template_column_objects.select { |col| col.pre_prefix.blank? && col.prefix.blank? }
448
- else
449
- trim_prefix_snake = trim_prefix.downcase.tr(' ', '_')
450
- template_column_objects.select do |col|
451
- this_prefix = ::DutyFree::Util._prefix_join([col.pre_prefix, col.prefix], '_').tr('.', '_')
452
- trim_prefix_snake == "#{this_prefix}_"
453
- end
454
- end.map { |avail| avail.name.to_s.titleize }
455
- vus = defined_uniques(uniques, cols, nil, starred).select do |k, _v|
456
- is_good = true
457
- k.each do |k_col|
458
- unless k_col.start_with?(trim_prefix) && available.include?(k_col[col_name_offset..-1])
459
- is_good = false
460
- break
461
- end
462
- end
463
- is_good
464
- end
465
- @valid_uniques[col_list] = vus
466
- end
467
-
468
- # Make sure they have at least one unique combination to take cues from
469
- ret = {}
470
- unless vus.empty? # raise NoUniqueColumnError.new(I18n.t('import.no_unique_column_error'))
471
- # Convert the first entry to a simplified hash, such as:
472
- # {[:investigator_institutions_name, :investigator_institutions_email] => [8, 9], ...}
473
- # to {:name => 8, :email => 9}
474
- key, val = vus.first
475
- key.each_with_index do |k, idx|
476
- ret[k[col_name_offset..-1].downcase.tr(' ', '_').to_sym] = val[idx] if k.start_with?(trim_prefix)
436
+ def find_existing(uniques, cols, starred, import_template, keepers, train_we_came_in_here_on,
437
+ row = nil, klass_or_collection = nil, all = nil, trim_prefix = '')
438
+ unless trim_prefix.blank?
439
+ cols = cols.map { |c| c.start_with?(trim_prefix) ? c[trim_prefix.length..-1] : nil }
440
+ starred = starred.each_with_object([]) do |v, s|
441
+ s << v[trim_prefix.length..-1] if v.start_with?(trim_prefix)
442
+ s
477
443
  end
478
444
  end
479
445
 
480
- # %%% If uniqueness is based on something else hanging out on a belongs_to then we're pretty hosed.
481
- # (Case in point, importing Order with related Order Detail and Product, and then Product needs to
482
- # be found or built first before OrderDetail.)
483
- # Might have to do a deferred save kind of thing, and also make sure the product stuff came first
484
- # before the other stuff
485
-
486
- # Find by all corresponding columns
487
-
488
- # Add in any foreign key stuff we can find from other belongs_to associations
489
- # %%% This is starting to look like the other BelongsToAssociation code above around line
490
- # 697, so it really needs to be turned into something recursive instead of this two-layer
491
- # thick thing at best.
492
-
493
- # First check the belongs_tos
446
+ # First add in foreign key stuff we can find from belongs_to associations (other than the
447
+ # one we might have arrived here upon).
494
448
  criteria = {}
495
449
  bt_criteria = {}
496
450
  bt_criteria_all_nil = true
497
451
  bt_col_indexes = []
452
+ available_bts = []
498
453
  only_valid_uniques = (train_we_came_in_here_on == false)
454
+ uniq_lookups = {}
455
+ # %%% Ultimately may consider making this recursive
499
456
  bts = reflect_on_all_associations.each_with_object([]) do |sn_assoc, s|
500
457
  if sn_assoc.is_a?(ActiveRecord::Reflection::BelongsToReflection) &&
501
- (!train_we_came_in_here_on || sn_assoc != train_we_came_in_here_on) # &&
502
- # sn_assoc.klass != self # Omit stuff pointing to us (like self-referencing stuff)
458
+ (!train_we_came_in_here_on || sn_assoc != train_we_came_in_here_on)
503
459
  # %%% Make sure there's a starred column we know about from this one
504
- ret[sn_assoc.foreign_key] = nil if only_valid_uniques
460
+ uniq_lookups[sn_assoc.foreign_key] = nil if only_valid_uniques
505
461
  s << sn_assoc
506
462
  end
507
463
  s
508
464
  end
509
465
  bts.each do |sn_bt|
510
466
  # This search prefix becomes something like "Order Details Product "
511
- # binding.pry
512
467
  cols.each_with_index do |bt_col, idx|
513
468
  next if bt_col_indexes.include?(idx) ||
514
469
  !bt_col&.start_with?(trim_prefix + "#{sn_bt.name.to_s.underscore.tr('_', ' ').titleize} ")
515
470
 
471
+ available_bts << bt_col
516
472
  fk_id = if row
517
473
  # Max ID so if there are multiple, only the most recent one is picked.
518
474
  # %%% Need to stack these up in case there are multiple
519
475
  # (like first_name, last_name on a referenced employee)
520
- # binding.pry
521
476
  sn_bt.klass.where(keepers[idx].name => row[idx]).limit(1).pluck(MAX_ID).first
522
477
  else
523
478
  [sn_bt.klass, keepers[idx].name, idx]
@@ -529,46 +484,93 @@ module DutyFree
529
484
  bt_criteria[(fk_name = sn_bt.foreign_key)] = fk_id
530
485
  # Add to our criteria if this belongs_to is required
531
486
  # %%% Rails older than 5.0 handles this stuff differently!
532
- unless sn_bt.options[:optional] || !sn_bt.klass.belongs_to_required_by_default
487
+ if sn_bt.options[:optional] || !sn_bt.klass.belongs_to_required_by_default
488
+ # Should not have this fk as a requirement
489
+ uniq_lookups.delete(fk_name) if only_valid_uniques && uniq_lookups.include?(fk_name)
490
+ else # Add to the criteria
533
491
  criteria[fk_name] = fk_id
534
- else # Should not have this fk as a requirement
535
- ret.delete(fk_name) if only_valid_uniques && ret.include?(fk_name)
536
492
  end
537
493
  end
538
494
  end
539
495
 
496
+ @valid_uniques ||= {} # Fancy memoisation
497
+ col_list = cols.join('|')
498
+ unless (vus = @valid_uniques[col_list])
499
+ # Find all unique combinations that are available based on incoming columns, and
500
+ # pair them up with column number mappings.
501
+ template_column_objects = ::DutyFree::Extensions._recurse_def(self, all || import_template[:all], import_template).first
502
+ available = if trim_prefix.blank?
503
+ template_column_objects.select { |col| col.pre_prefix.blank? && col.prefix.blank? }
504
+ else
505
+ trim_prefix_snake = trim_prefix.downcase.tr(' ', '_')
506
+ template_column_objects.select do |col|
507
+ this_prefix = ::DutyFree::Util._prefix_join([col.pre_prefix, col.prefix], '_').tr('.', '_')
508
+ trim_prefix_snake == "#{this_prefix}_"
509
+ end
510
+ end.map { |avail| avail.name.to_s.titleize }
511
+ vus = defined_uniques(uniques, cols, nil, starred).select do |k, _v|
512
+ is_good = true
513
+ k.each do |k_col|
514
+ unless available.include?(k_col) || available_bts.include?(k_col)
515
+ is_good = false
516
+ break
517
+ end
518
+ end
519
+ is_good
520
+ end
521
+ @valid_uniques[col_list] = vus
522
+ end
523
+
524
+ # Make sure they have at least one unique combination to take cues from
525
+ unless vus.empty? # raise NoUniqueColumnError.new(I18n.t('import.no_unique_column_error'))
526
+ # Convert the first entry to a simplified hash, such as:
527
+ # {[:investigator_institutions_name, :investigator_institutions_email] => [8, 9], ...}
528
+ # to {:name => 8, :email => 9}
529
+ key, val = vus.first
530
+ key.each_with_index do |k, idx|
531
+ next if available_bts.include?(k) # These will be provided in criteria, and not uniq_lookups
532
+
533
+ # uniq_lookups[k[trim_prefix.length..-1].downcase.tr(' ', '_').to_sym] = val[idx] if k.start_with?(trim_prefix)
534
+ uniq_lookups[k.downcase.tr(' ', '_').to_sym] = val[idx]
535
+ end
536
+ end
537
+
538
+ # Find by all corresponding columns
539
+
540
540
  new_criteria_all_nil = bt_criteria_all_nil
541
- if train_we_came_in_here_on != false
542
- criteria = ret.each_with_object({}) do |v, s|
543
- next if bt_col_indexes.include?(v.last)
541
+ unless only_valid_uniques
542
+ uniq_lookups.each do |k, v|
543
+ next if bt_col_indexes.include?(v)
544
544
 
545
- new_criteria_all_nil = false if (s[v.first.to_sym] = row[v.last])
546
- s
545
+ if (row_value = row[v])
546
+ new_criteria_all_nil = false
547
+ criteria[k.to_sym] = row_value
548
+ end
547
549
  end
548
550
  end
549
551
 
550
552
  # Short-circuiting this to only get back the valid_uniques?
551
- return ret.merge(criteria) if only_valid_uniques
553
+ return uniq_lookups.merge(criteria) if only_valid_uniques
552
554
 
553
- # binding.pry if obj.is_a?(Order)
554
555
  # If there's nothing to match upon then we're out
555
556
  return [nil, {}] if new_criteria_all_nil
556
557
 
557
558
  # With this criteria, find any matching has_many row we can so we can update it
558
- sub_hm = obj.find do |hm_obj|
559
+ # First try looking it up through ActiveRecord
560
+ found_object = klass_or_collection.find_by(criteria)
561
+ # If not successful, such as when fields are exposed via helper methods instead of being
562
+ # real columns in the database tables, try this more intensive routine.
563
+ found_object ||= klass_or_collection.find do |obj|
559
564
  is_good = true
560
565
  criteria.each do |k, v|
561
- if hm_obj.send(k).to_s != v.to_s
566
+ if obj.send(k).to_s != v.to_s
562
567
  is_good = false
563
568
  break
564
569
  end
565
570
  end
566
571
  is_good
567
572
  end
568
- # Try looking it up through ActiveRecord
569
- # %%% Should we perhaps do this first before the more intensive find routine above?
570
- sub_hm = obj.find_by(criteria) if sub_hm.nil?
571
- [sub_hm, criteria.merge(bt_criteria)]
573
+ [found_object, criteria.merge(bt_criteria)]
572
574
  end
573
575
 
574
576
  private
@@ -601,7 +603,10 @@ module DutyFree
601
603
  end
602
604
  end
603
605
  end
604
- (starred - utilised.keys).each { |star| defined_uniq[[star]] = [cols.index(star)] }
606
+ if defined_uniq.empty?
607
+ (starred - utilised.keys).each { |star| defined_uniq[[star]] = [cols.index(star)] }
608
+ # %%% puts "Tried to establish #{defined_uniq.inspect}"
609
+ end
605
610
  @defined_uniques[col_list] = defined_uniq
606
611
  end
607
612
  defined_uniq
@@ -53,13 +53,9 @@ module DutyFree
53
53
  # Always process belongs_to, and also process has_one and has_many if do_has_many is chosen.
54
54
  # Skip any HMT or HABTM. (Maybe break out HABTM into a combo HM and BT in the future.)
55
55
  if is_habtm
56
- unless ActiveRecord::Base.connection.table_exists?(assoc.join_table)
57
- puts "* In the #{this_klass.name} model there's a problem with: \"has_and_belongs_to_many :#{assoc.name}\" because join table \"#{assoc.join_table}\" does not exist. You can create it with a create_join_table migration."
58
- end
56
+ puts "* In the #{this_klass.name} model there's a problem with: \"has_and_belongs_to_many :#{assoc.name}\" because join table \"#{assoc.join_table}\" does not exist. You can create it with a create_join_table migration." unless ActiveRecord::Base.connection.table_exists?(assoc.join_table)
59
57
  # %%% Search for other associative candidates to use instead of this HABTM contraption
60
- if assoc.options.include?(:through)
61
- puts "* In the #{this_klass.name} model there's a problem with: \"has_and_belongs_to_many :#{assoc.name}\" because it includes \"through: #{assoc.options[:through].inspect}\" which is pointless and should be removed."
62
- end
58
+ puts "* In the #{this_klass.name} model there's a problem with: \"has_and_belongs_to_many :#{assoc.name}\" because it includes \"through: #{assoc.options[:through].inspect}\" which is pointless and should be removed." if assoc.options.include?(:through)
63
59
  end
64
60
  if (is_through = assoc.is_a?(ActiveRecord::Reflection::ThroughReflection)) && assoc.options.include?(:as)
65
61
  puts "* In the #{this_klass.name} model there's a problem with: \"has_many :#{assoc.name} through: #{assoc.options[:through].inspect}\" because it also includes \"as: #{assoc.options[:as].inspect}\", so please choose either for this line to be a \"has_many :#{assoc.name} through:\" or to be a polymorphic \"has_many :#{assoc.name} as:\". It can't be both."
@@ -203,9 +199,7 @@ module DutyFree
203
199
 
204
200
  # Requireds takes its cues from all attributes having a presence validator
205
201
  requireds = _find_requireds(klass)
206
- if priority_excluded_columns
207
- klass_columns = klass_columns.reject { |col| priority_excluded_columns.include?(col.name) }
208
- end
202
+ klass_columns = klass_columns.reject { |col| priority_excluded_columns.include?(col.name) } if priority_excluded_columns
209
203
  excluded_columns = %w[created_at updated_at deleted_at]
210
204
  unique = [(
211
205
  # Find the first text field of a required if one exists
@@ -240,8 +234,14 @@ module DutyFree
240
234
  if klass.columns.map(&:name).include?(attrib)
241
235
  s << attrib
242
236
  else
243
- puts "* In the #{klass.name} model \"validates_presence_of :#{attrib}\" should be removed as it does not refer to any existing column."
244
- errored_columns << klass_col
237
+ bt_names = klass.reflect_on_all_associations.each_with_object([]) do |assoc, names|
238
+ names << assoc.name.to_s if assoc.is_a?(ActiveRecord::Reflection::BelongsToReflection)
239
+ names
240
+ end
241
+ unless bt_names.include?(attrib)
242
+ puts "* In the #{klass.name} model \"validates_presence_of :#{attrib}\" should be removed as it does not refer to any existing column."
243
+ errored_columns << klass_col
244
+ end
245
245
  end
246
246
  end
247
247
  end
@@ -5,7 +5,7 @@ module DutyFree
5
5
  module VERSION
6
6
  MAJOR = 1
7
7
  MINOR = 0
8
- TINY = 3
8
+ TINY = 4
9
9
 
10
10
  # PRE is nil unless it's a pre-release (beta, RC, etc.)
11
11
  PRE = nil
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: duty_free
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 1.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lorin Thwaits
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-11-06 00:00:00.000000000 Z
11
+ date: 2020-11-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord