table_warnings 0.0.7 → 1.0.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/.gitignore +1 -0
- data/CHANGELOG +14 -0
- data/Gemfile +1 -2
- data/README.rdoc +3 -3
- data/Rakefile +5 -10
- data/lib/table_warnings/arbitrary.rb +17 -0
- data/lib/table_warnings/blank.rb +13 -0
- data/lib/table_warnings/column.rb +70 -0
- data/lib/table_warnings/debug.rb +2 -0
- data/lib/table_warnings/exclusive.rb +31 -0
- data/lib/table_warnings/nonexistent_owner.rb +31 -0
- data/lib/table_warnings/null.rb +13 -0
- data/lib/table_warnings/range.rb +32 -0
- data/lib/table_warnings/registry.rb +38 -0
- data/lib/table_warnings/scout.rb +85 -0
- data/lib/table_warnings/size.rb +55 -0
- data/lib/table_warnings/version.rb +1 -1
- data/lib/table_warnings.rb +134 -24
- data/table_warnings.gemspec +6 -5
- data/test/helper.rb +94 -16
- data/test/test_combinations.rb +85 -0
- data/test/test_warn_if_nonexistent_owner.rb +60 -0
- data/test/test_warn_if_nonexistent_owner_except.rb +67 -0
- data/test/test_warn_if_nulls_except.rb +120 -0
- data/test/test_warn_if_nulls_in.rb +114 -0
- data/test/test_warn_unless_range.rb +18 -0
- metadata +105 -25
- data/lib/table_warnings/config.rb +0 -11
- data/lib/table_warnings/warning/arbitrary.rb +0 -13
- data/lib/table_warnings/warning/blank.rb +0 -25
- data/lib/table_warnings/warning/size.rb +0 -34
- data/lib/table_warnings/warning.rb +0 -30
- data/test/test_table_warnings.rb +0 -64
data/lib/table_warnings.rb
CHANGED
@@ -1,53 +1,163 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
1
3
|
require 'active_support'
|
2
4
|
require 'active_support/version'
|
3
|
-
require 'active_support/core_ext' if
|
5
|
+
require 'active_support/core_ext' if ActiveSupport::VERSION::MAJOR >= 3
|
4
6
|
require 'active_record'
|
5
7
|
|
6
|
-
|
7
|
-
|
8
|
-
|
8
|
+
require 'table_warnings/registry'
|
9
|
+
require 'table_warnings/exclusive'
|
10
|
+
require 'table_warnings/blank'
|
11
|
+
require 'table_warnings/size'
|
12
|
+
require 'table_warnings/arbitrary'
|
13
|
+
require 'table_warnings/null'
|
14
|
+
require 'table_warnings/column'
|
15
|
+
require 'table_warnings/scout'
|
16
|
+
require 'table_warnings/nonexistent_owner'
|
17
|
+
require 'table_warnings/range'
|
9
18
|
|
10
|
-
|
11
|
-
|
19
|
+
module TableWarnings
|
20
|
+
def TableWarnings.registry
|
21
|
+
@registry || Thread.exclusive do
|
22
|
+
@registry ||= Registry.new
|
23
|
+
end
|
12
24
|
end
|
13
25
|
|
14
|
-
#
|
26
|
+
# used to resolve columns to warnings
|
27
|
+
Disposition = Struct.new(:exclusives, :covers, :matches)
|
28
|
+
|
29
|
+
# Get current warning messages on the table.
|
15
30
|
def table_warnings
|
16
|
-
|
31
|
+
messages = []
|
32
|
+
|
33
|
+
TableWarnings.registry.nonexclusive(self).each do |warning|
|
34
|
+
messages << warning.messages
|
35
|
+
end
|
36
|
+
|
37
|
+
pool = column_names.map do |column_name|
|
38
|
+
TableWarnings::Column.new self, column_name
|
39
|
+
end
|
40
|
+
exclusive = TableWarnings.registry.exclusive(self)
|
41
|
+
|
42
|
+
assignments = {}
|
43
|
+
# pass 1 - exclusives and covers
|
44
|
+
exclusive.each do |warning|
|
45
|
+
disposition = Disposition.new
|
46
|
+
disposition.exclusives = warning.exclusives pool
|
47
|
+
disposition.covers = warning.covers pool
|
48
|
+
assignments[warning] = disposition
|
49
|
+
pool -= disposition.exclusives
|
50
|
+
end
|
51
|
+
if ENV['TABLE_WARNINGS_DEBUG'] == 'true'
|
52
|
+
$stderr.puts "pass 1"
|
53
|
+
assignments.each do |warning, disposition|
|
54
|
+
$stderr.puts " #{warning.scout.matcher} - exclusives=#{disposition.exclusives.map(&:name)} covers=#{disposition.covers.map(&:name)}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
# pass 2 - allow regexp matching, but only if somebody else didn't cover it
|
58
|
+
exclusive.each do |warning|
|
59
|
+
disposition = assignments[warning]
|
60
|
+
disposition.matches = warning.matches(pool).select do |match|
|
61
|
+
assignments.except(warning).none? { |_, other| other.covers.include?(match) }
|
62
|
+
end
|
63
|
+
pool -= disposition.matches
|
64
|
+
end
|
65
|
+
if ENV['TABLE_WARNINGS_DEBUG'] == 'true'
|
66
|
+
$stderr.puts "pass 2"
|
67
|
+
assignments.each do |warning, disposition|
|
68
|
+
$stderr.puts " #{warning.scout.matcher} - exclusives=#{disposition.exclusives.map(&:name)} covers=#{disposition.covers.map(&:name)} matches=#{disposition.matches.map(&:name)}"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
if ENV['TABLE_WARNINGS_STRICT'] == 'true'
|
72
|
+
$stderr.puts "uncovered columns"
|
73
|
+
$stderr.puts pool.join("\n")
|
74
|
+
end
|
75
|
+
|
76
|
+
# now you can generate messages
|
77
|
+
assignments.each do |warning, disposition|
|
78
|
+
messages << warning.messages(disposition.exclusives+disposition.matches)
|
79
|
+
end
|
80
|
+
|
81
|
+
messages.flatten.compact
|
82
|
+
end
|
83
|
+
|
84
|
+
def warn_unless_range(*args)
|
85
|
+
options = args.extract_options!
|
86
|
+
args.flatten.each do |matcher|
|
87
|
+
TableWarnings.registry.add_warning self, TableWarnings::Range.new(self, matcher, options)
|
88
|
+
end
|
17
89
|
end
|
18
90
|
|
19
91
|
# Warn if there are blanks in a certain column.
|
20
92
|
#
|
21
93
|
# Blank includes both NULL and "" (empty string)
|
22
|
-
def
|
23
|
-
|
24
|
-
|
94
|
+
def warn_if_blanks(*args)
|
95
|
+
options = args.extract_options!
|
96
|
+
args.flatten.each do |matcher|
|
97
|
+
TableWarnings.registry.add_warning self, TableWarnings::Blank.new(self, matcher, options)
|
98
|
+
end
|
25
99
|
end
|
26
100
|
|
101
|
+
# Warn if there are NULLs in a certain column.
|
102
|
+
def warn_if_nulls(*args)
|
103
|
+
options = args.extract_options!
|
104
|
+
args.flatten.each do |matcher|
|
105
|
+
TableWarnings.registry.add_warning self, TableWarnings::Null.new(self, matcher, options)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
27
109
|
# Warn if there are blanks in ANY column.
|
28
|
-
#
|
29
|
-
# Blank includes both NULL and "" (empty string)
|
30
110
|
def warn_if_any_blanks
|
31
|
-
|
32
|
-
|
111
|
+
TableWarnings.registry.add_warning self, TableWarnings::Blank.new(self, /.*/)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Warn if there are nulls in ANY column.
|
115
|
+
def warn_if_any_nulls
|
116
|
+
TableWarnings.registry.add_warning self, TableWarnings::Null.new(self, /.*/)
|
33
117
|
end
|
34
118
|
|
35
|
-
# Warn if the number of records falls out of an (approximate)
|
119
|
+
# Warn if the number of records falls out of an (approximate) size.
|
36
120
|
#
|
37
121
|
# Approximations: :few, :tens, :dozens, :hundreds, :thousands, :hundreds_of_thousands, :millions
|
38
122
|
# Exact: pass a Range or a Numeric
|
39
|
-
def
|
40
|
-
|
41
|
-
::TableWarnings.config.warnings[self].add warning
|
123
|
+
def warn_unless_size(approximate_size, options = {})
|
124
|
+
TableWarnings.registry.add_warning self, TableWarnings::Size.new(self, approximate_size, options)
|
42
125
|
end
|
43
126
|
|
44
127
|
# An arbitrary warning.
|
45
|
-
def
|
46
|
-
|
47
|
-
|
128
|
+
def warn_if(&blk)
|
129
|
+
TableWarnings.registry.add_warning self, TableWarnings::Arbitrary.new(self, blk)
|
130
|
+
end
|
131
|
+
|
132
|
+
def warn_if_nulls_except(*args)
|
133
|
+
options = args.extract_options!
|
134
|
+
args.flatten.each do |matcher|
|
135
|
+
TableWarnings.registry.add_warning self, TableWarnings::Null.new(self, matcher, options.merge(:negative => true))
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def warn_if_blanks_except(*args)
|
140
|
+
options = args.extract_options!
|
141
|
+
args.flatten.each do |matcher|
|
142
|
+
TableWarnings.registry.add_warning self, TableWarnings::Blank.new(self, matcher, options.merge(:negative => true))
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def warn_if_nonexistent_owner(*args)
|
147
|
+
options = args.extract_options!
|
148
|
+
args.flatten.each do |belongs_to_association_name|
|
149
|
+
TableWarnings.registry.add_warning self, TableWarnings::NonexistentOwner.new(self, belongs_to_association_name, options)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def warn_if_nonexistent_owner_except(*args)
|
154
|
+
options = args.extract_options!
|
155
|
+
args.flatten.each do |belongs_to_association_name|
|
156
|
+
TableWarnings.registry.add_warning self, TableWarnings::NonexistentOwner.new(self, belongs_to_association_name, options.merge(:negative => true))
|
157
|
+
end
|
48
158
|
end
|
49
159
|
end
|
50
160
|
|
51
|
-
unless
|
52
|
-
|
161
|
+
unless ActiveRecord::Base.method_defined? :table_warnings
|
162
|
+
ActiveRecord::Base.extend TableWarnings
|
53
163
|
end
|
data/table_warnings.gemspec
CHANGED
@@ -1,11 +1,9 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
|
3
|
-
require 'table_warnings/version'
|
2
|
+
require File.expand_path('../lib/table_warnings/version', __FILE__)
|
4
3
|
|
5
4
|
Gem::Specification.new do |s|
|
6
5
|
s.name = 'table_warnings'
|
7
6
|
s.version = TableWarnings::VERSION
|
8
|
-
s.platform = Gem::Platform::RUBY
|
9
7
|
s.authors = ['Seamus Abshere']
|
10
8
|
s.email = ['seamus@abshere.net']
|
11
9
|
s.homepage = 'https://github.com/seamusabshere/table_warnings'
|
@@ -23,7 +21,10 @@ Gem::Specification.new do |s|
|
|
23
21
|
s.add_runtime_dependency 'activesupport'
|
24
22
|
|
25
23
|
s.add_development_dependency 'fastercsv'
|
26
|
-
s.add_development_dependency '
|
27
|
-
s.add_development_dependency 'mini_record-compat'
|
24
|
+
s.add_development_dependency 'active_record_inline_schema'
|
28
25
|
s.add_development_dependency 'sqlite3'
|
26
|
+
s.add_development_dependency 'minitest'
|
27
|
+
s.add_development_dependency 'minitest-reporters'
|
28
|
+
s.add_development_dependency 'yard'
|
29
|
+
# s.add_development_dependency 'debugger'
|
29
30
|
end
|
data/test/helper.rb
CHANGED
@@ -1,25 +1,103 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
require 'bundler'
|
3
|
-
|
4
|
-
|
5
|
-
require '
|
6
|
-
|
7
|
-
require '
|
8
|
-
# thanks authlogic!
|
9
|
-
ActiveRecord::Schema.verbose = false
|
10
|
-
begin
|
11
|
-
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
|
12
|
-
rescue ArgumentError
|
13
|
-
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :dbfile => ":memory:")
|
2
|
+
require 'bundler/setup'
|
3
|
+
|
4
|
+
if ::Bundler.definition.specs['debugger'].first
|
5
|
+
require 'debugger'
|
6
|
+
elsif ::Bundler.definition.specs['ruby-debug'].first
|
7
|
+
require 'ruby-debug'
|
14
8
|
end
|
15
9
|
|
10
|
+
require 'minitest/spec'
|
11
|
+
require 'minitest/autorun'
|
12
|
+
require 'minitest/reporters'
|
13
|
+
MiniTest::Unit.runner = MiniTest::SuiteRunner.new
|
14
|
+
MiniTest::Unit.runner.reporters << MiniTest::Reporters::SpecReporter.new
|
15
|
+
|
16
|
+
require 'active_record'
|
17
|
+
require 'active_record_inline_schema'
|
18
|
+
|
19
|
+
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
|
20
|
+
|
16
21
|
# require 'logger'
|
17
22
|
# logger = Logger.new $stdout
|
18
23
|
# logger.level = Logger::DEBUG
|
19
24
|
# ActiveRecord::Base.logger = logger
|
20
25
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
26
|
+
class ActiveRecord::Base
|
27
|
+
class << self
|
28
|
+
# ignores protected attrs
|
29
|
+
def force_create!(attrs = {})
|
30
|
+
record = new
|
31
|
+
attrs.each do |k, v|
|
32
|
+
record.send "#{k}=", v
|
33
|
+
end
|
34
|
+
record.save!
|
35
|
+
record
|
36
|
+
end
|
37
|
+
end
|
25
38
|
end
|
39
|
+
|
40
|
+
class MiniTest::Spec
|
41
|
+
# start transaction
|
42
|
+
before do
|
43
|
+
# activerecord-3.2.3/lib/active_record/fixtures.rb
|
44
|
+
@fixture_connections = ActiveRecord::Base.connection_handler.connection_pools.values.map(&:connection)
|
45
|
+
@fixture_connections.each do |connection|
|
46
|
+
connection.increment_open_transactions
|
47
|
+
connection.transaction_joinable = false
|
48
|
+
connection.begin_db_transaction
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# rollback
|
53
|
+
after do
|
54
|
+
@fixture_connections.each do |connection|
|
55
|
+
if connection.open_transactions != 0
|
56
|
+
connection.rollback_db_transaction
|
57
|
+
connection.decrement_open_transactions
|
58
|
+
end
|
59
|
+
end
|
60
|
+
@fixture_connections.clear
|
61
|
+
ActiveRecord::Base.clear_active_connections!
|
62
|
+
end
|
63
|
+
|
64
|
+
def assert_warning(model, expected_warning)
|
65
|
+
hits = model.table_warnings.select { |warning| warning =~ expected_warning }
|
66
|
+
refute hits.none?, "#{model.name} unexpectedly lacked warning #{expected_warning.inspect}"
|
67
|
+
refute hits.many?, "#{model.name} had MULTIPLE warnings like #{expected_warning.inspect}: #{hits.inspect}"
|
68
|
+
end
|
69
|
+
|
70
|
+
def assert_no_warning(model, specific_unexpected_warning = nil)
|
71
|
+
warnings = model.table_warnings
|
72
|
+
if specific_unexpected_warning
|
73
|
+
refute(warnings.any? { |warning| warning =~ specific_unexpected_warning }, "#{model.name} unexpectedly had warning #{specific_unexpected_warning.inspect}")
|
74
|
+
else
|
75
|
+
refute warnings.any?, "#{model.name} unexpectedly had some warnings (#{warnings.inspect})"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def assert_causes_warning(model, expected_warnings)
|
80
|
+
expected_warnings = [expected_warnings].flatten
|
81
|
+
expected_warnings.each do |expected_warning|
|
82
|
+
assert_no_warning model, expected_warning
|
83
|
+
end
|
84
|
+
warnings_before = model.table_warnings
|
85
|
+
yield
|
86
|
+
expected_warnings.each do |expected_warning|
|
87
|
+
assert_warning model, expected_warning
|
88
|
+
end
|
89
|
+
unexpected_warnings = (model.table_warnings - warnings_before).reject do |warning|
|
90
|
+
expected_warnings.any? { |expected_warning| warning =~ expected_warning }
|
91
|
+
end
|
92
|
+
refute unexpected_warnings.any?, "#{model.name} unexpectedly ALSO got warnings #{unexpected_warnings.inspect}"
|
93
|
+
end
|
94
|
+
|
95
|
+
def assert_does_not_cause_warning(model)
|
96
|
+
assert_no_warning model
|
97
|
+
yield
|
98
|
+
assert_no_warning model
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
require 'table_warnings'
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class PetAlpha < ActiveRecord::Base
|
4
|
+
col :birthday, :type => :datetime
|
5
|
+
col :gender
|
6
|
+
col :sire
|
7
|
+
warn_if_nulls :birthday
|
8
|
+
warn_if_nulls_except :gender
|
9
|
+
end
|
10
|
+
PetAlpha.auto_upgrade!
|
11
|
+
|
12
|
+
class PetBeta < ActiveRecord::Base
|
13
|
+
col :birthday, :type => :datetime
|
14
|
+
col :gender
|
15
|
+
col :sire
|
16
|
+
warn_if_nulls :birthday
|
17
|
+
warn_if_nulls_except /ende/
|
18
|
+
end
|
19
|
+
PetBeta.auto_upgrade!
|
20
|
+
|
21
|
+
class PetGamma < ActiveRecord::Base
|
22
|
+
col :birthday, :type => :datetime
|
23
|
+
col :gender
|
24
|
+
col :sire
|
25
|
+
col :certified, :type => :boolean
|
26
|
+
warn_if_nulls /irthd/, :conditions => { :certified => true }
|
27
|
+
warn_if_nulls_except /ende/
|
28
|
+
end
|
29
|
+
PetGamma.auto_upgrade!
|
30
|
+
|
31
|
+
class PetDelta < ActiveRecord::Base
|
32
|
+
col :birthday, :type => :datetime
|
33
|
+
col :gender
|
34
|
+
col :sire
|
35
|
+
col :certified, :type => :boolean
|
36
|
+
warn_if_nulls /irthd/, :conditions => { :certified => true }
|
37
|
+
warn_if_nulls_except /ende/, :conditions => { :certified => true }
|
38
|
+
end
|
39
|
+
PetDelta.auto_upgrade!
|
40
|
+
|
41
|
+
describe TableWarnings do
|
42
|
+
describe "combinations of positive ('in') and negative ('except') rules" do
|
43
|
+
it "combines a positive column and a negative column" do
|
44
|
+
assert_causes_warning PetAlpha, [/null.*birthday/i, /null.*sire/i] do
|
45
|
+
PetAlpha.force_create!
|
46
|
+
end
|
47
|
+
end
|
48
|
+
it "combines a positive column and a negative regexp" do
|
49
|
+
assert_causes_warning PetBeta, [/null.*birthday/i, /null.*sire/i] do
|
50
|
+
PetBeta.force_create!
|
51
|
+
end
|
52
|
+
end
|
53
|
+
it "combines a positive regexp with conditions and a negative regexp" do
|
54
|
+
assert_causes_warning PetGamma, [/null.*sire/i, /null.*certified/i] do
|
55
|
+
PetGamma.force_create!
|
56
|
+
end
|
57
|
+
PetGamma.delete_all # !
|
58
|
+
assert_causes_warning PetGamma, [/null.*sire/i, /null.*birthday/i] do
|
59
|
+
PetGamma.force_create! :certified => true
|
60
|
+
end
|
61
|
+
end
|
62
|
+
it "combines a positive regexp with conditions and a negative regexp with conditions" do
|
63
|
+
assert_does_not_cause_warning PetDelta do
|
64
|
+
PetDelta.force_create!
|
65
|
+
end
|
66
|
+
PetDelta.delete_all # !
|
67
|
+
assert_causes_warning PetDelta, [/null.*sire/i, /null.*birthday/i] do
|
68
|
+
PetDelta.force_create! :certified => true
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
=begin
|
75
|
+
warn_if_nulls_except(
|
76
|
+
:alt_fuel_code,
|
77
|
+
:carline_mfr_code,
|
78
|
+
:vi_mfr_code,
|
79
|
+
:carline_code,
|
80
|
+
:carline_class_code,
|
81
|
+
:carline_class_name,
|
82
|
+
)
|
83
|
+
warn_if_nulls /alt_fuel_efficiency/, :conditions => 'alt_fuel_code IS NOT NULL'
|
84
|
+
warn_if_nulls :carline_class, :conditions => 'year < 1998'
|
85
|
+
=end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class Person < ActiveRecord::Base
|
4
|
+
col :llc_name
|
5
|
+
end
|
6
|
+
Person.auto_upgrade!
|
7
|
+
|
8
|
+
class PetRed < ActiveRecord::Base
|
9
|
+
col :handler_id, :type => :integer
|
10
|
+
belongs_to :handler, :class_name => 'Person'
|
11
|
+
warn_if_nonexistent_owner :handler
|
12
|
+
end
|
13
|
+
PetRed.auto_upgrade!
|
14
|
+
|
15
|
+
class PetBlue < ActiveRecord::Base
|
16
|
+
col :trainer_id
|
17
|
+
belongs_to :trainer, :class_name => 'Person', :primary_key => :llc_name
|
18
|
+
warn_if_nonexistent_owner :trainer
|
19
|
+
end
|
20
|
+
PetBlue.auto_upgrade!
|
21
|
+
|
22
|
+
class PetGreen < ActiveRecord::Base
|
23
|
+
col :trainer_id
|
24
|
+
belongs_to :trainer, :class_name => 'Person', :primary_key => :llc_name
|
25
|
+
warn_if_nonexistent_owner :trainer, :allow_null => true
|
26
|
+
end
|
27
|
+
PetGreen.auto_upgrade!
|
28
|
+
|
29
|
+
describe TableWarnings do
|
30
|
+
describe :warn_if_nonexistent_owner do
|
31
|
+
before do
|
32
|
+
Person.force_create! :llc_name => 'My Small Business, LLC'
|
33
|
+
end
|
34
|
+
it "takes a single belongs-to association name" do
|
35
|
+
assert_causes_warning PetRed, /nonexistent.*handler/i do
|
36
|
+
PetRed.force_create!
|
37
|
+
end
|
38
|
+
end
|
39
|
+
it "checks the value of foreign keys not just their presence" do
|
40
|
+
assert_causes_warning PetRed, /nonexistent.*handler/i do
|
41
|
+
PetRed.force_create! :handler_id => 999999
|
42
|
+
end
|
43
|
+
end
|
44
|
+
it "doesn't raise false warnings" do
|
45
|
+
assert_does_not_cause_warning PetRed do
|
46
|
+
PetRed.force_create! :handler_id => Person.first.id
|
47
|
+
end
|
48
|
+
end
|
49
|
+
it "regards nulls as nonexistent even if the association primary key column contains nulls" do
|
50
|
+
assert_causes_warning PetBlue, /nonexistent.*trainer/i do
|
51
|
+
PetBlue.force_create!
|
52
|
+
end
|
53
|
+
end
|
54
|
+
it "allows nulls if explicitly requested" do
|
55
|
+
assert_does_not_cause_warning PetGreen do
|
56
|
+
PetGreen.force_create!
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|