oracle_enhanced 1.2.5

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.
Files changed (38) hide show
  1. data/.gitignore +10 -0
  2. data/History.txt +182 -0
  3. data/License.txt +20 -0
  4. data/Manifest.txt +32 -0
  5. data/README.rdoc +77 -0
  6. data/Rakefile +49 -0
  7. data/VERSION +1 -0
  8. data/lib/active_record/connection_adapters/emulation/oracle_adapter.rb +5 -0
  9. data/lib/active_record/connection_adapters/oracle_enhanced.rake +51 -0
  10. data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +1661 -0
  11. data/lib/active_record/connection_adapters/oracle_enhanced_connection.rb +121 -0
  12. data/lib/active_record/connection_adapters/oracle_enhanced_core_ext.rb +64 -0
  13. data/lib/active_record/connection_adapters/oracle_enhanced_cpk.rb +21 -0
  14. data/lib/active_record/connection_adapters/oracle_enhanced_dirty.rb +39 -0
  15. data/lib/active_record/connection_adapters/oracle_enhanced_jdbc_connection.rb +393 -0
  16. data/lib/active_record/connection_adapters/oracle_enhanced_oci_connection.rb +389 -0
  17. data/lib/active_record/connection_adapters/oracle_enhanced_procedures.rb +163 -0
  18. data/lib/active_record/connection_adapters/oracle_enhanced_reserved_words.rb +126 -0
  19. data/lib/active_record/connection_adapters/oracle_enhanced_schema_definitions.rb +168 -0
  20. data/lib/active_record/connection_adapters/oracle_enhanced_schema_dumper.rb +213 -0
  21. data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements_ext.rb +224 -0
  22. data/lib/active_record/connection_adapters/oracle_enhanced_tasks.rb +11 -0
  23. data/lib/active_record/connection_adapters/oracle_enhanced_version.rb +1 -0
  24. data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +477 -0
  25. data/spec/active_record/connection_adapters/oracle_enhanced_adapter_structure_dumper_spec.rb +267 -0
  26. data/spec/active_record/connection_adapters/oracle_enhanced_connection_spec.rb +206 -0
  27. data/spec/active_record/connection_adapters/oracle_enhanced_core_ext_spec.rb +40 -0
  28. data/spec/active_record/connection_adapters/oracle_enhanced_cpk_spec.rb +107 -0
  29. data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +984 -0
  30. data/spec/active_record/connection_adapters/oracle_enhanced_dbms_output_spec.rb +67 -0
  31. data/spec/active_record/connection_adapters/oracle_enhanced_dirty_spec.rb +93 -0
  32. data/spec/active_record/connection_adapters/oracle_enhanced_emulate_oracle_adapter_spec.rb +25 -0
  33. data/spec/active_record/connection_adapters/oracle_enhanced_procedures_spec.rb +370 -0
  34. data/spec/active_record/connection_adapters/oracle_enhanced_schema_dump_spec.rb +203 -0
  35. data/spec/active_record/connection_adapters/oracle_enhanced_schema_spec.rb +784 -0
  36. data/spec/spec.opts +6 -0
  37. data/spec/spec_helper.rb +114 -0
  38. metadata +140 -0
@@ -0,0 +1,67 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ describe "OracleEnhancedAdapter logging dbms_output from plsql" do
4
+ include LoggerSpecHelper
5
+
6
+ before(:all) do
7
+ @buffer = StringIO.new
8
+ ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
9
+ ActiveRecord::Base.connection.execute <<-SQL
10
+ CREATE or REPLACE
11
+ FUNCTION MORE_THAN_FIVE_CHARACTERS_LONG (some_text VARCHAR2) RETURN INTEGER
12
+ AS
13
+ longer_than_five INTEGER;
14
+ BEGIN
15
+ dbms_output.put_line('before the if -' || some_text || '-');
16
+ IF length(some_text) > 5 THEN
17
+ dbms_output.put_line('it is longer than 5');
18
+ longer_than_five := 1;
19
+ ELSE
20
+ dbms_output.put_line('it is 5 or shorter');
21
+ longer_than_five := 0;
22
+ END IF;
23
+ dbms_output.put_line('about to return: ' || longer_than_five);
24
+ RETURN longer_than_five;
25
+ END;
26
+ SQL
27
+ end
28
+
29
+ after(:all) do
30
+ ActiveRecord::Base.connection.execute "DROP FUNCTION MORE_THAN_FIVE_CHARACTERS_LONG"
31
+ end
32
+
33
+ before(:each) do
34
+ @buffer = StringIO.new
35
+ log_to @buffer
36
+ ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
37
+ @conn = ActiveRecord::Base.connection
38
+ end
39
+
40
+ it "should NOT log dbms output when dbms output is disabled" do
41
+ @conn.disable_dbms_output
42
+
43
+ @conn.select_all("select more_than_five_characters_long('hi there') is_it_long from dual").should == [{'is_it_long'=>1}]
44
+
45
+ @buffer.string.should_not match(/^DBMS_OUTPUT/)
46
+ end
47
+
48
+ it "should log dbms output lines to the rails log" do
49
+ @conn.enable_dbms_output
50
+
51
+ @conn.select_all("select more_than_five_characters_long('hi there') is_it_long from dual").should == [{'is_it_long'=>1}]
52
+
53
+ @buffer.string.should match(/^DBMS_OUTPUT: before the if -hi there-$/)
54
+ @buffer.string.should match(/^DBMS_OUTPUT: it is longer than 5$/)
55
+ @buffer.string.should match(/^DBMS_OUTPUT: about to return: 1$/)
56
+ end
57
+
58
+ it "should log dbms output lines to the rails log" do
59
+ @conn.enable_dbms_output
60
+
61
+ @conn.select_all("select more_than_five_characters_long('short') is_it_long from dual").should == [{'is_it_long'=>0}]
62
+
63
+ @buffer.string.should match(/^DBMS_OUTPUT: before the if -short-$/)
64
+ @buffer.string.should match(/^DBMS_OUTPUT: it is 5 or shorter$/)
65
+ @buffer.string.should match(/^DBMS_OUTPUT: about to return: 0$/)
66
+ end
67
+ end
@@ -0,0 +1,93 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ if ActiveRecord::Base.instance_methods.include?('changed?')
4
+
5
+ describe "OracleEnhancedAdapter dirty object tracking" do
6
+
7
+ before(:all) do
8
+ ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
9
+ @conn = ActiveRecord::Base.connection
10
+ @conn.execute <<-SQL
11
+ CREATE TABLE test_employees (
12
+ id NUMBER,
13
+ first_name VARCHAR2(20),
14
+ last_name VARCHAR2(25),
15
+ job_id NUMBER(6,0) NULL,
16
+ salary NUMBER(8,2),
17
+ comments CLOB,
18
+ hire_date DATE
19
+ )
20
+ SQL
21
+ @conn.execute <<-SQL
22
+ CREATE SEQUENCE test_employees_seq MINVALUE 1
23
+ INCREMENT BY 1 CACHE 20 NOORDER NOCYCLE
24
+ SQL
25
+ class TestEmployee < ActiveRecord::Base
26
+ end
27
+ end
28
+
29
+ after(:all) do
30
+ Object.send(:remove_const, "TestEmployee")
31
+ @conn.execute "DROP TABLE test_employees"
32
+ @conn.execute "DROP SEQUENCE test_employees_seq"
33
+ end
34
+
35
+ it "should not mark empty string (stored as NULL) as changed when reassigning it" do
36
+ @employee = TestEmployee.create!(:first_name => '')
37
+ @employee.first_name = ''
38
+ @employee.should_not be_changed
39
+ @employee.reload
40
+ @employee.first_name = ''
41
+ @employee.should_not be_changed
42
+ end
43
+
44
+ it "should not mark empty integer (stored as NULL) as changed when reassigning it" do
45
+ @employee = TestEmployee.create!(:job_id => '')
46
+ @employee.job_id = ''
47
+ @employee.should_not be_changed
48
+ @employee.reload
49
+ @employee.job_id = ''
50
+ @employee.should_not be_changed
51
+ end
52
+
53
+ it "should not mark empty decimal (stored as NULL) as changed when reassigning it" do
54
+ @employee = TestEmployee.create!(:salary => '')
55
+ @employee.salary = ''
56
+ @employee.should_not be_changed
57
+ @employee.reload
58
+ @employee.salary = ''
59
+ @employee.should_not be_changed
60
+ end
61
+
62
+ it "should not mark empty text (stored as NULL) as changed when reassigning it" do
63
+ @employee = TestEmployee.create!(:comments => '')
64
+ @employee.comments = ''
65
+ @employee.should_not be_changed
66
+ @employee.reload
67
+ @employee.comments = ''
68
+ @employee.should_not be_changed
69
+ end
70
+
71
+ it "should not mark empty date (stored as NULL) as changed when reassigning it" do
72
+ @employee = TestEmployee.create!(:hire_date => '')
73
+ @employee.hire_date = ''
74
+ @employee.should_not be_changed
75
+ @employee.reload
76
+ @employee.hire_date = ''
77
+ @employee.should_not be_changed
78
+ end
79
+
80
+ it "should not mark integer as changed when reassigning it" do
81
+ @employee = TestEmployee.new
82
+ @employee.job_id = 0
83
+ @employee.save!.should be_true
84
+
85
+ @employee.should_not be_changed
86
+
87
+ @employee.job_id = '0'
88
+ @employee.should_not be_changed
89
+ end
90
+
91
+ end
92
+
93
+ end
@@ -0,0 +1,25 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ describe "OracleEnhancedAdapter emulate OracleAdapter" do
4
+
5
+ before(:all) do
6
+ if defined?(ActiveRecord::ConnectionAdapters::OracleAdapter)
7
+ @old_oracle_adapter = ActiveRecord::ConnectionAdapters::OracleAdapter
8
+ ActiveRecord::ConnectionAdapters.send(:remove_const, :OracleAdapter)
9
+ end
10
+ end
11
+
12
+ it "should be an OracleAdapter" do
13
+ @conn = ActiveRecord::Base.establish_connection(CONNECTION_PARAMS.merge(:emulate_oracle_adapter => true))
14
+ ActiveRecord::Base.connection.should_not be_nil
15
+ ActiveRecord::Base.connection.is_a?(ActiveRecord::ConnectionAdapters::OracleAdapter).should be_true
16
+ end
17
+
18
+ after(:all) do
19
+ if @old_oracle_adapter
20
+ ActiveRecord::ConnectionAdapters.send(:remove_const, :OracleAdapter)
21
+ ActiveRecord::ConnectionAdapters::OracleAdapter = @old_oracle_adapter
22
+ end
23
+ end
24
+
25
+ end
@@ -0,0 +1,370 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ describe "OracleEnhancedAdapter custom methods for create, update and destroy" do
4
+ include LoggerSpecHelper
5
+
6
+ before(:all) do
7
+ ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
8
+ @conn = ActiveRecord::Base.connection
9
+ plsql.activerecord_class = ActiveRecord::Base
10
+ @conn.execute("DROP TABLE test_employees") rescue nil
11
+ @conn.execute <<-SQL
12
+ CREATE TABLE test_employees (
13
+ employee_id NUMBER(6,0),
14
+ first_name VARCHAR2(20),
15
+ last_name VARCHAR2(25),
16
+ hire_date DATE,
17
+ salary NUMBER(8,2),
18
+ description CLOB,
19
+ version NUMBER(15,0),
20
+ create_time DATE,
21
+ update_time DATE,
22
+ created_at DATE,
23
+ updated_at DATE
24
+ )
25
+ SQL
26
+ @conn.execute("DROP SEQUENCE test_employees_s") rescue nil
27
+ @conn.execute <<-SQL
28
+ CREATE SEQUENCE test_employees_s MINVALUE 1
29
+ INCREMENT BY 1 CACHE 20 NOORDER NOCYCLE
30
+ SQL
31
+ @conn.execute <<-SQL
32
+ CREATE OR REPLACE PACKAGE test_employees_pkg IS
33
+ PROCEDURE create_employee(
34
+ p_first_name VARCHAR2,
35
+ p_last_name VARCHAR2,
36
+ p_hire_date DATE,
37
+ p_salary NUMBER,
38
+ p_description VARCHAR2,
39
+ p_employee_id OUT NUMBER);
40
+ PROCEDURE update_employee(
41
+ p_employee_id NUMBER,
42
+ p_first_name VARCHAR2,
43
+ p_last_name VARCHAR2,
44
+ p_hire_date DATE,
45
+ p_salary NUMBER,
46
+ p_description VARCHAR2);
47
+ PROCEDURE delete_employee(
48
+ p_employee_id NUMBER);
49
+ END;
50
+ SQL
51
+ @conn.execute <<-SQL
52
+ CREATE OR REPLACE PACKAGE BODY test_employees_pkg IS
53
+ PROCEDURE create_employee(
54
+ p_first_name VARCHAR2,
55
+ p_last_name VARCHAR2,
56
+ p_hire_date DATE,
57
+ p_salary NUMBER,
58
+ p_description VARCHAR2,
59
+ p_employee_id OUT NUMBER)
60
+ IS
61
+ BEGIN
62
+ SELECT test_employees_s.NEXTVAL INTO p_employee_id FROM dual;
63
+ INSERT INTO test_employees (employee_id, first_name, last_name, hire_date, salary, description,
64
+ version, create_time, update_time)
65
+ VALUES (p_employee_id, p_first_name, p_last_name, p_hire_date, p_salary, p_description,
66
+ 1, SYSDATE, SYSDATE);
67
+ END create_employee;
68
+
69
+ PROCEDURE update_employee(
70
+ p_employee_id NUMBER,
71
+ p_first_name VARCHAR2,
72
+ p_last_name VARCHAR2,
73
+ p_hire_date DATE,
74
+ p_salary NUMBER,
75
+ p_description VARCHAR2)
76
+ IS
77
+ v_version NUMBER;
78
+ BEGIN
79
+ SELECT version INTO v_version FROM test_employees WHERE employee_id = p_employee_id FOR UPDATE;
80
+ UPDATE test_employees
81
+ SET employee_id = p_employee_id, first_name = p_first_name, last_name = p_last_name,
82
+ hire_date = p_hire_date, salary = p_salary, description = p_description,
83
+ version = v_version + 1, update_time = SYSDATE;
84
+ END update_employee;
85
+
86
+ PROCEDURE delete_employee(
87
+ p_employee_id NUMBER)
88
+ IS
89
+ BEGIN
90
+ DELETE FROM test_employees WHERE employee_id = p_employee_id;
91
+ END delete_employee;
92
+ END;
93
+ SQL
94
+
95
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.emulate_dates_by_column_name = true
96
+
97
+ class ::TestEmployee < ActiveRecord::Base
98
+ set_primary_key :employee_id
99
+
100
+ validates_presence_of :first_name, :last_name, :hire_date
101
+
102
+ # should return ID of new record
103
+ set_create_method do
104
+ plsql.test_employees_pkg.create_employee(
105
+ :p_first_name => first_name,
106
+ :p_last_name => last_name,
107
+ :p_hire_date => hire_date,
108
+ :p_salary => salary,
109
+ :p_description => "#{first_name} #{last_name}",
110
+ :p_employee_id => nil
111
+ )[:p_employee_id]
112
+ end
113
+
114
+ # return value is ignored
115
+ set_update_method do
116
+ plsql.test_employees_pkg.update_employee(
117
+ :p_employee_id => id,
118
+ :p_first_name => first_name,
119
+ :p_last_name => last_name,
120
+ :p_hire_date => hire_date,
121
+ :p_salary => salary,
122
+ :p_description => "#{first_name} #{last_name}"
123
+ )
124
+ end
125
+
126
+ # return value is ignored
127
+ set_delete_method do
128
+ plsql.test_employees_pkg.delete_employee(
129
+ :p_employee_id => id
130
+ )
131
+ end
132
+
133
+ end
134
+ end
135
+
136
+ after(:all) do
137
+ Object.send(:remove_const, "TestEmployee")
138
+ @conn = ActiveRecord::Base.connection
139
+ @conn.execute "DROP TABLE test_employees"
140
+ @conn.execute "DROP SEQUENCE test_employees_s"
141
+ @conn.execute "DROP PACKAGE test_employees_pkg"
142
+ end
143
+
144
+ before(:each) do
145
+ @today = Date.new(2008,6,28)
146
+ @buffer = StringIO.new
147
+ end
148
+
149
+ it "should create record" do
150
+ @employee = TestEmployee.create(
151
+ :first_name => "First",
152
+ :last_name => "Last",
153
+ :hire_date => @today
154
+ )
155
+ @employee.reload
156
+ @employee.first_name.should == "First"
157
+ @employee.last_name.should == "Last"
158
+ @employee.hire_date.should == @today
159
+ @employee.description.should == "First Last"
160
+ @employee.create_time.should_not be_nil
161
+ @employee.update_time.should_not be_nil
162
+ end
163
+
164
+ it "should rollback record when exception is raised in after_create callback" do
165
+ @employee = TestEmployee.new(
166
+ :first_name => "First",
167
+ :last_name => "Last",
168
+ :hire_date => @today
169
+ )
170
+ TestEmployee.class_eval { def after_create() raise "Make the transaction rollback" end }
171
+ begin
172
+ employees_count = TestEmployee.count
173
+ lambda {
174
+ @employee.save
175
+ }.should raise_error("Make the transaction rollback")
176
+ @employee.id.should == nil
177
+ TestEmployee.count.should == employees_count
178
+ ensure
179
+ TestEmployee.class_eval { remove_method :after_create }
180
+ end
181
+ end
182
+
183
+ it "should update record" do
184
+ @employee = TestEmployee.create(
185
+ :first_name => "First",
186
+ :last_name => "Last",
187
+ :hire_date => @today,
188
+ :description => "description"
189
+ )
190
+ @employee.reload
191
+ @employee.first_name = "Second"
192
+ @employee.save!
193
+ @employee.reload
194
+ @employee.description.should == "Second Last"
195
+ end
196
+
197
+ it "should rollback record when exception is raised in after_update callback" do
198
+ TestEmployee.class_eval { def after_update() raise "Make the transaction rollback" end }
199
+ begin
200
+ @employee = TestEmployee.create(
201
+ :first_name => "First",
202
+ :last_name => "Last",
203
+ :hire_date => @today,
204
+ :description => "description"
205
+ )
206
+ empl_id = @employee.id
207
+ @employee.reload
208
+ @employee.first_name = "Second"
209
+ lambda {
210
+ @employee.save
211
+ }.should raise_error("Make the transaction rollback")
212
+ @employee.reload
213
+ @employee.first_name.should == "First"
214
+ ensure
215
+ TestEmployee.class_eval { remove_method :after_update }
216
+ end
217
+ end
218
+
219
+ it "should not update record if nothing is changed and partial updates are enabled" do
220
+ return pending("Not in this ActiveRecord version") unless TestEmployee.respond_to?(:partial_updates=)
221
+ TestEmployee.partial_updates = true
222
+ @employee = TestEmployee.create(
223
+ :first_name => "First",
224
+ :last_name => "Last",
225
+ :hire_date => @today
226
+ )
227
+ @employee.reload
228
+ @employee.save!
229
+ @employee.reload
230
+ @employee.version.should == 1
231
+ end
232
+
233
+ it "should update record if nothing is changed and partial updates are disabled" do
234
+ return pending("Not in this ActiveRecord version") unless TestEmployee.respond_to?(:partial_updates=)
235
+ TestEmployee.partial_updates = false
236
+ @employee = TestEmployee.create(
237
+ :first_name => "First",
238
+ :last_name => "Last",
239
+ :hire_date => @today
240
+ )
241
+ @employee.reload
242
+ @employee.save!
243
+ @employee.reload
244
+ @employee.version.should == 2
245
+ end
246
+
247
+ it "should delete record" do
248
+ @employee = TestEmployee.create(
249
+ :first_name => "First",
250
+ :last_name => "Last",
251
+ :hire_date => @today
252
+ )
253
+ @employee.reload
254
+ empl_id = @employee.id
255
+ @employee.destroy
256
+ @employee.should be_frozen
257
+ TestEmployee.find_by_employee_id(empl_id).should be_nil
258
+ end
259
+
260
+ it "should delete record and set destroyed flag" do
261
+ return pending("Not in this ActiveRecord version (requires >= 2.3.5)") unless TestEmployee.method_defined?(:destroyed?)
262
+ @employee = TestEmployee.create(
263
+ :first_name => "First",
264
+ :last_name => "Last",
265
+ :hire_date => @today
266
+ )
267
+ @employee.reload
268
+ @employee.destroy
269
+ @employee.should be_destroyed
270
+ end
271
+
272
+ it "should rollback record when exception is raised in after_desotry callback" do
273
+ TestEmployee.class_eval { def after_destroy() raise "Make the transaction rollback" end }
274
+ begin
275
+ @employee = TestEmployee.create(
276
+ :first_name => "First",
277
+ :last_name => "Last",
278
+ :hire_date => @today
279
+ )
280
+ @employee.reload
281
+ empl_id = @employee.id
282
+ lambda {
283
+ @employee.destroy
284
+ }.should raise_error("Make the transaction rollback")
285
+ @employee.id.should == empl_id
286
+ TestEmployee.find_by_employee_id(empl_id).should_not be_nil
287
+ ensure
288
+ TestEmployee.class_eval { remove_method :after_destroy }
289
+ end
290
+ end
291
+
292
+ it "should set timestamps when creating record" do
293
+ @employee = TestEmployee.create(
294
+ :first_name => "First",
295
+ :last_name => "Last",
296
+ :hire_date => @today
297
+ )
298
+ @employee.created_at.should_not be_nil
299
+ @employee.updated_at.should_not be_nil
300
+ end
301
+
302
+ it "should set timestamps when updating record" do
303
+ @employee = TestEmployee.create(
304
+ :first_name => "First",
305
+ :last_name => "Last",
306
+ :hire_date => @today
307
+ )
308
+ @employee.reload
309
+ @employee.created_at.should be_nil
310
+ @employee.updated_at.should be_nil
311
+ @employee.first_name = "Second"
312
+ @employee.save!
313
+ @employee.created_at.should be_nil
314
+ @employee.updated_at.should_not be_nil
315
+ end
316
+
317
+ it "should log create record" do
318
+ log_to @buffer
319
+ @employee = TestEmployee.create(
320
+ :first_name => "First",
321
+ :last_name => "Last",
322
+ :hire_date => @today
323
+ )
324
+ @buffer.string.should match(/^TestEmployee Create \(\d+\.\d+(ms)?\) custom create method$/)
325
+ end
326
+
327
+ it "should log update record" do
328
+ (TestEmployee.partial_updates = false) rescue nil
329
+ @employee = TestEmployee.create(
330
+ :first_name => "First",
331
+ :last_name => "Last",
332
+ :hire_date => @today
333
+ )
334
+ log_to @buffer
335
+ @employee.save!
336
+ @buffer.string.should match(/^TestEmployee Update \(\d+\.\d+(ms)?\) custom update method with employee_id=#{@employee.id}$/)
337
+ end
338
+
339
+ it "should log delete record" do
340
+ @employee = TestEmployee.create(
341
+ :first_name => "First",
342
+ :last_name => "Last",
343
+ :hire_date => @today
344
+ )
345
+ log_to @buffer
346
+ @employee.destroy
347
+ @buffer.string.should match(/^TestEmployee Destroy \(\d+\.\d+(ms)?\) custom delete method with employee_id=#{@employee.id}$/)
348
+ end
349
+
350
+ it "should validate new record before creation" do
351
+ @employee = TestEmployee.new(
352
+ :last_name => "Last",
353
+ :hire_date => @today
354
+ )
355
+ @employee.save.should be_false
356
+ @employee.errors.on(:first_name).should_not be_nil
357
+ end
358
+
359
+ it "should validate existing record before update" do
360
+ @employee = TestEmployee.create(
361
+ :first_name => "First",
362
+ :last_name => "Last",
363
+ :hire_date => @today
364
+ )
365
+ @employee.first_name = nil
366
+ @employee.save.should be_false
367
+ @employee.errors.on(:first_name).should_not be_nil
368
+ end
369
+
370
+ end