activerecord-oracle_enhanced-adapter 1.1.2 → 1.1.3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord-oracle_enhanced-adapter might be problematic. Click here for more details.

data/History.txt CHANGED
@@ -1,3 +1,9 @@
1
+ == 1.1.3 2008-07-10
2
+
3
+ * Enhancements:
4
+ * Added support for custom create, update and delete methods when working with legacy databases where
5
+ PL/SQL API should be used for create, update and delete operations
6
+
1
7
  == 1.1.2 2008-07-08
2
8
 
3
9
  * Bug fixes:
data/README.txt CHANGED
@@ -31,16 +31,39 @@ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.emulate_integers_by_colu
31
31
  should be emulated as booleans (and do not use NUMBER(1) as type for booleans which is default)
32
32
  ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.emulate_booleans_from_strings = true
33
33
 
34
- The following model class definitions are available:
34
+ The following model class methods are available:
35
35
  * specify which table columns should be ignored by ActiveRecord
36
36
  ignore_table_columns :column1, :column2, :column3
37
37
 
38
+ * specify custom create, update and delete methods which should be used instead of Rails generated INSERT, UPDATE and DELETE statements
39
+ # should return ID of new record
40
+ set_create_method do
41
+ plsql.employees_pkg.create_employee(
42
+ :p_first_name => first_name,
43
+ :p_last_name => last_name,
44
+ :p_employee_id => nil
45
+ )[:p_employee_id]
46
+ end
47
+ set_update_method do
48
+ plsql.employees_pkg.update_employee(
49
+ :p_employee_id => id,
50
+ :p_first_name => first_name,
51
+ :p_last_name => last_name
52
+ )
53
+ end
54
+ set_delete_method do
55
+ plsql.employees_pkg.delete_employee(
56
+ :p_employee_id => id
57
+ )
58
+ end
59
+
38
60
  See History.txt for other enhancements to original Oracle adapter.
39
61
 
40
62
  == REQUIREMENTS:
41
63
 
42
64
  * Works with ActiveRecord version 2.0 and 2.1 (which is included in Rails 2.0 and 2.1)
43
65
  * Requires ruby-oci8 library to connect to Oracle
66
+ * Requires ruby-plsql gem to support custom create, update and delete methods
44
67
 
45
68
  == INSTALL:
46
69
 
@@ -847,3 +847,14 @@ if defined?(CGI::Session::ActiveRecordStore::Session)
847
847
  end
848
848
  end
849
849
  end
850
+
851
+ # RSI: load custom create, update, delete methods functionality
852
+ # rescue LoadError if ruby-plsql gem cannot be loaded
853
+ begin
854
+ require 'active_record/connection_adapters/oracle_enhanced_procedures'
855
+ rescue LoadError
856
+ if defined?(RAILS_DEFAULT_LOGGER)
857
+ RAILS_DEFAULT_LOGGER.info "INFO: ActiveRecord oracle_enhanced adapter could not load ruby-plsql gem. "+
858
+ "Custom create, update and delete methods will not be available."
859
+ end
860
+ end
@@ -0,0 +1,105 @@
1
+ require 'ruby_plsql'
2
+ require 'activesupport'
3
+
4
+ module ActiveRecord #:nodoc:
5
+ module ConnectionAdapters #:nodoc:
6
+ module OracleEnhancedProcedures #:nodoc:
7
+
8
+ module ClassMethods
9
+ def set_create_method(&block)
10
+ include_with_custom_methods
11
+ self.custom_create_method = block
12
+ end
13
+
14
+ def set_update_method(&block)
15
+ include_with_custom_methods
16
+ self.custom_update_method = block
17
+ end
18
+
19
+ def set_delete_method(&block)
20
+ include_with_custom_methods
21
+ self.custom_delete_method = block
22
+ end
23
+
24
+ private
25
+ def include_with_custom_methods
26
+ unless included_modules.include? InstanceMethods
27
+ class_inheritable_accessor :custom_create_method, :custom_update_method, :custom_delete_method
28
+ include InstanceMethods
29
+ end
30
+ end
31
+ end
32
+
33
+ module InstanceMethods
34
+ def self.included(base)
35
+ base.instance_eval do
36
+ alias_method_chain :create, :custom_method
37
+ # insert after dirty checking in Rails 2.1
38
+ if private_instance_methods.include?('update_without_dirty')
39
+ alias_method :update_without_custom_method, :update_without_dirty
40
+ alias_method :update_without_dirty, :update_with_custom_method
41
+ else
42
+ alias_method_chain :update, :custom_method
43
+ end
44
+ private :create, :update
45
+ alias_method_chain :destroy, :custom_method
46
+ public :destroy
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ # Creates a record with custom create method
53
+ # and returns its id.
54
+ def create_with_custom_method
55
+ # check if class has custom create method
56
+ return create_without_custom_method unless self.class.custom_create_method
57
+ self.class.connection.log_custom_method("custom create method", "#{self.class.name} Create") do
58
+ self.id = self.class.custom_create_method.bind(self).call
59
+ end
60
+ @new_record = false
61
+ id
62
+ end
63
+
64
+ # Updates the associated record with custom update method
65
+ # Returns the number of affected rows.
66
+ def update_with_custom_method(attribute_names = @attributes.keys)
67
+ # check if class has custom create method
68
+ return update_without_custom_method unless self.class.custom_update_method
69
+ return 0 if attribute_names.empty?
70
+ self.class.connection.log_custom_method("custom update method with #{self.class.primary_key}=#{self.id}", "#{self.class.name} Update") do
71
+ self.class.custom_update_method.bind(self).call
72
+ end
73
+ 1
74
+ end
75
+
76
+ # Deletes the record in the database with custom delete method
77
+ # and freezes this instance to reflect that no changes should
78
+ # be made (since they can't be persisted).
79
+ def destroy_with_custom_method
80
+ # check if class has custom create method
81
+ return destroy_without_custom_method unless self.class.custom_delete_method
82
+ unless new_record?
83
+ self.class.connection.log_custom_method("custom delete method with #{self.class.primary_key}=#{self.id}", "#{self.class.name} Destroy") do
84
+ self.class.custom_delete_method.bind(self).call
85
+ end
86
+ end
87
+
88
+ freeze
89
+ end
90
+
91
+ end
92
+
93
+ end
94
+ end
95
+ end
96
+
97
+ ActiveRecord::Base.class_eval do
98
+ extend ActiveRecord::ConnectionAdapters::OracleEnhancedProcedures::ClassMethods
99
+ end
100
+
101
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.class_eval do
102
+ # public alias to log method which could be used from other objects
103
+ alias_method :log_custom_method, :log
104
+ public :log_custom_method
105
+ end
@@ -3,7 +3,7 @@ module ActiveRecord #:nodoc:
3
3
  module OracleEnhancedVersion #:nodoc:
4
4
  MAJOR = 1
5
5
  MINOR = 1
6
- TINY = 2
6
+ TINY = 3
7
7
 
8
8
  STRING = [MAJOR, MINOR, TINY].join('.')
9
9
  end
@@ -91,6 +91,7 @@ describe "OracleEnhancedAdapter database session store" do
91
91
  end
92
92
 
93
93
  it "should change session data when partial updates enabled" do
94
+ return pending("Not in this ActiveRecord version") unless CGI::Session::ActiveRecordStore::Session.respond_to?(:partial_updates=)
94
95
  CGI::Session::ActiveRecordStore::Session.partial_updates = true
95
96
  @session = CGI::Session::ActiveRecordStore::Session.new :session_id => "222222", :data => "something" #, :updated_at => Time.now
96
97
  @session.save!
@@ -102,6 +103,7 @@ describe "OracleEnhancedAdapter database session store" do
102
103
  end
103
104
 
104
105
  it "should have one enhanced_write_lobs callback" do
106
+ return pending("Not in this ActiveRecord version") unless CGI::Session::ActiveRecordStore::Session.respond_to?(:after_save_callback_chain)
105
107
  CGI::Session::ActiveRecordStore::Session.after_save_callback_chain.select{|cb| cb.method == :enhanced_write_lobs}.should have(1).record
106
108
  end
107
109
 
@@ -0,0 +1,268 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper.rb'
2
+
3
+ describe "OracleEnhancedAdapter custom methods for create, update and destroy" do
4
+ def log_to(stream)
5
+ ActiveRecord::Base.logger = Logger.new(stream)
6
+ ActiveRecord::Base.clear_active_connections!
7
+ ActiveRecord::Base.colorize_logging = false
8
+ ActiveRecord::Base.logger.level = Logger::DEBUG
9
+ end
10
+
11
+ before(:all) do
12
+ ActiveRecord::Base.establish_connection(:adapter => "oracle_enhanced",
13
+ :database => "xe",
14
+ :username => "hr",
15
+ :password => "hr")
16
+ @conn = ActiveRecord::Base.connection
17
+ plsql.connection = @conn.raw_connection
18
+ @conn.execute("DROP TABLE test_employees") rescue nil
19
+ @conn.execute <<-SQL
20
+ CREATE TABLE test_employees (
21
+ employee_id NUMBER(6,0),
22
+ first_name VARCHAR2(20),
23
+ last_name VARCHAR2(25),
24
+ hire_date DATE,
25
+ salary NUMBER(8,2),
26
+ version NUMBER(15,0),
27
+ create_time DATE,
28
+ update_time DATE
29
+ )
30
+ SQL
31
+ @conn.execute("DROP SEQUENCE test_employees_s") rescue nil
32
+ @conn.execute <<-SQL
33
+ CREATE SEQUENCE test_employees_s MINVALUE 1
34
+ INCREMENT BY 1 CACHE 20 NOORDER NOCYCLE
35
+ SQL
36
+ @conn.execute <<-SQL
37
+ CREATE OR REPLACE PACKAGE test_employees_pkg IS
38
+ PROCEDURE create_employee(
39
+ p_first_name VARCHAR2,
40
+ p_last_name VARCHAR2,
41
+ p_hire_date DATE,
42
+ p_salary NUMBER,
43
+ p_employee_id OUT NUMBER);
44
+ PROCEDURE update_employee(
45
+ p_employee_id NUMBER,
46
+ p_first_name VARCHAR2,
47
+ p_last_name VARCHAR2,
48
+ p_hire_date DATE,
49
+ p_salary NUMBER);
50
+ PROCEDURE delete_employee(
51
+ p_employee_id NUMBER);
52
+ END;
53
+ SQL
54
+ @conn.execute <<-SQL
55
+ CREATE OR REPLACE PACKAGE BODY test_employees_pkg IS
56
+ PROCEDURE create_employee(
57
+ p_first_name VARCHAR2,
58
+ p_last_name VARCHAR2,
59
+ p_hire_date DATE,
60
+ p_salary NUMBER,
61
+ p_employee_id OUT NUMBER)
62
+ IS
63
+ BEGIN
64
+ SELECT test_employees_s.NEXTVAL INTO p_employee_id FROM dual;
65
+ INSERT INTO test_employees (employee_id, first_name, last_name, hire_date, salary,
66
+ version, create_time, update_time)
67
+ VALUES (p_employee_id, p_first_name, p_last_name, p_hire_date, p_salary,
68
+ 1, SYSDATE, SYSDATE);
69
+ END create_employee;
70
+
71
+ PROCEDURE update_employee(
72
+ p_employee_id NUMBER,
73
+ p_first_name VARCHAR2,
74
+ p_last_name VARCHAR2,
75
+ p_hire_date DATE,
76
+ p_salary NUMBER)
77
+ IS
78
+ v_version NUMBER;
79
+ BEGIN
80
+ SELECT version INTO v_version FROM test_employees WHERE employee_id = p_employee_id FOR UPDATE;
81
+ UPDATE test_employees
82
+ SET employee_id = p_employee_id, first_name = p_first_name, last_name = p_last_name,
83
+ hire_date = p_hire_date, salary = p_salary,
84
+ version = v_version + 1, update_time = SYSDATE;
85
+ END update_employee;
86
+
87
+ PROCEDURE delete_employee(
88
+ p_employee_id NUMBER)
89
+ IS
90
+ BEGIN
91
+ DELETE FROM test_employees WHERE employee_id = p_employee_id;
92
+ END delete_employee;
93
+ END;
94
+ SQL
95
+
96
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.emulate_dates_by_column_name = true
97
+
98
+ class TestEmployee < ActiveRecord::Base
99
+ set_primary_key :employee_id
100
+
101
+ validates_presence_of :first_name, :last_name, :hire_date
102
+
103
+ # should return ID of new record
104
+ set_create_method do
105
+ plsql.test_employees_pkg.create_employee(
106
+ :p_first_name => first_name,
107
+ :p_last_name => last_name,
108
+ :p_hire_date => hire_date,
109
+ :p_salary => salary,
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
+ )
123
+ end
124
+
125
+ # return value is ignored
126
+ set_delete_method do
127
+ plsql.test_employees_pkg.delete_employee(
128
+ :p_employee_id => id
129
+ )
130
+ end
131
+
132
+ end
133
+ end
134
+
135
+ after(:all) do
136
+ Object.send(:remove_const, "TestEmployee")
137
+ @conn.execute "DROP TABLE test_employees"
138
+ @conn.execute "DROP SEQUENCE test_employees_s"
139
+ @conn.execute "DROP PACKAGE test_employees_pkg"
140
+ end
141
+
142
+ before(:each) do
143
+ @today = Date.new(2008,6,28)
144
+ @buffer = StringIO.new
145
+ end
146
+
147
+ it "should create record" do
148
+ @employee = TestEmployee.create(
149
+ :first_name => "First",
150
+ :last_name => "Last",
151
+ :hire_date => @today
152
+ )
153
+ @employee.reload
154
+ @employee.first_name.should == "First"
155
+ @employee.last_name.should == "Last"
156
+ @employee.hire_date.should == @today
157
+ @employee.create_time.should_not be_nil
158
+ @employee.update_time.should_not be_nil
159
+ end
160
+
161
+ it "should update record" do
162
+ @employee = TestEmployee.create(
163
+ :first_name => "First",
164
+ :last_name => "Last",
165
+ :hire_date => @today
166
+ )
167
+ @employee.reload
168
+ @employee.first_name = "Second"
169
+ @employee.save!
170
+ @employee.reload
171
+ @employee.first_name.should == "Second"
172
+ end
173
+
174
+ it "should not update record if nothing is changed and partial updates are enabled" do
175
+ return pending("Not in this ActiveRecord version") unless TestEmployee.respond_to?(:partial_updates=)
176
+ TestEmployee.partial_updates = true
177
+ @employee = TestEmployee.create(
178
+ :first_name => "First",
179
+ :last_name => "Last",
180
+ :hire_date => @today
181
+ )
182
+ @employee.reload
183
+ @employee.save!
184
+ @employee.reload
185
+ @employee.version.should == 1
186
+ end
187
+
188
+ it "should update record if nothing is changed and partial updates are disabled" do
189
+ return pending("Not in this ActiveRecord version") unless TestEmployee.respond_to?(:partial_updates=)
190
+ TestEmployee.partial_updates = false
191
+ @employee = TestEmployee.create(
192
+ :first_name => "First",
193
+ :last_name => "Last",
194
+ :hire_date => @today
195
+ )
196
+ @employee.reload
197
+ @employee.save!
198
+ @employee.reload
199
+ @employee.version.should == 2
200
+ end
201
+
202
+ it "should delete record" do
203
+ @employee = TestEmployee.create(
204
+ :first_name => "First",
205
+ :last_name => "Last",
206
+ :hire_date => @today
207
+ )
208
+ @employee.reload
209
+ empl_id = @employee.id
210
+ @employee.destroy
211
+ @employee.should be_frozen
212
+ TestEmployee.find_by_employee_id(empl_id).should be_nil
213
+ end
214
+
215
+ it "should log create record" do
216
+ log_to @buffer
217
+ @employee = TestEmployee.create(
218
+ :first_name => "First",
219
+ :last_name => "Last",
220
+ :hire_date => @today
221
+ )
222
+ @buffer.string.should match(/^TestEmployee Create \(\d+\.\d+\) custom create method$/)
223
+ end
224
+
225
+ it "should log update record" do
226
+ (TestEmployee.partial_updates = false) rescue nil
227
+ @employee = TestEmployee.create(
228
+ :first_name => "First",
229
+ :last_name => "Last",
230
+ :hire_date => @today
231
+ )
232
+ log_to @buffer
233
+ @employee.save!
234
+ @buffer.string.should match(/^TestEmployee Update \(\d+\.\d+\) custom update method with employee_id=#{@employee.id}$/)
235
+ end
236
+
237
+ it "should log delete record" do
238
+ @employee = TestEmployee.create(
239
+ :first_name => "First",
240
+ :last_name => "Last",
241
+ :hire_date => @today
242
+ )
243
+ log_to @buffer
244
+ @employee.destroy
245
+ @buffer.string.should match(/^TestEmployee Destroy \(\d+\.\d+\) custom delete method with employee_id=#{@employee.id}$/)
246
+ end
247
+
248
+ it "should validate new record before creation" do
249
+ @employee = TestEmployee.new(
250
+ :last_name => "Last",
251
+ :hire_date => @today
252
+ )
253
+ @employee.save.should be_false
254
+ @employee.errors.on(:first_name).should_not be_nil
255
+ end
256
+
257
+ it "should validate existing record before update" do
258
+ @employee = TestEmployee.create(
259
+ :first_name => "First",
260
+ :last_name => "Last",
261
+ :hire_date => @today
262
+ )
263
+ @employee.first_name = nil
264
+ @employee.save.should be_false
265
+ @employee.errors.on(:first_name).should_not be_nil
266
+ end
267
+
268
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,12 +1,14 @@
1
- # begin
2
- # require 'spec'
3
- # rescue LoadError
4
- require 'rubygems'
5
- gem 'rspec'
6
- require 'spec'
7
- # end
1
+ require 'rubygems'
2
+ gem 'rspec'
3
+ require 'spec'
8
4
 
9
5
  $:.unshift(File.dirname(__FILE__) + '/../lib')
6
+ # gem 'activerecord', '=2.0.2'
7
+ # gem 'actionpack', '=2.0.2'
8
+ # gem 'activesupport', '=2.0.2'
9
+ gem 'activerecord', '=2.1.0'
10
+ gem 'actionpack', '=2.1.0'
11
+ gem 'activesupport', '=2.1.0'
10
12
  require 'activerecord'
11
13
  require 'actionpack'
12
14
  require 'action_controller/session/active_record_store'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-oracle_enhanced-adapter
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.2
4
+ version: 1.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Raimonds Simanovskis
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-07-08 00:00:00 +03:00
12
+ date: 2008-07-10 00:00:00 +03:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -28,11 +28,13 @@ files:
28
28
  - History.txt
29
29
  - License.txt
30
30
  - README.txt
31
+ - lib/active_record/connection_adapters/oracle_enhanced.rake
31
32
  - lib/active_record/connection_adapters/oracle_enhanced_adapter.rb
32
- - lib/active_record/connection_adapters/oracle_enhanced_version.rb
33
+ - lib/active_record/connection_adapters/oracle_enhanced_procedures.rb
33
34
  - lib/active_record/connection_adapters/oracle_enhanced_tasks.rb
34
- - lib/active_record/connection_adapters/oracle_enhanced.rake
35
+ - lib/active_record/connection_adapters/oracle_enhanced_version.rb
35
36
  - spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb
37
+ - spec/active_record/connection_adapters/oracle_enhanced_procedures_spec.rb
36
38
  - spec/spec.opts
37
39
  - spec/spec_helper.rb
38
40
  has_rdoc: true