activerecord-oracle_enhanced-adapter 1.1.2 → 1.1.3

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.

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