Rails in the enterprise

4,392 views

Published on

Slides from OSCON 2010 talk "Off the Beaten Path: Using Rails in the Enterprise" http://bit.ly/9I9zlV

0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
4,392
On SlideShare
0
From Embeds
0
Number of Embeds
18
Actions
Shares
0
Downloads
21
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Rails in the enterprise

  1. Rails in the Enterprise: Off the Beaten Track Alex Rothenberg http://alexrothenberg.com @alexrothenberg Pat Shaughnessy http://patshaughnessy.net @patshaughnessy2 Thursday, July 22, 2010
  2. Thursday, July 22, 2010
  3. Thursday, July 22, 2010
  4. Thursday, July 22, 2010
  5. Thursday, July 22, 2010
  6. Using Rails When ... sharing a development database there was no documented way to create a new database our existing database was not built with Rails in mind the database schema is hard to work with when you find application code in the database Thursday, July 22, 2010
  7. Problem 1: Sharing a development database Thursday, July 22, 2010
  8. From:    ... To:      All Developers Cc:      ... Date:    07/06/2010 09:47 AM Subject: Dev/QA Databases unavailable. Hi All, This is to inform you that all the databases on Dev/QA servers are down due to issues in UNIX file systems. Please find the following list of servers are impacted         - oradbdev-ux01         - oradbdev-ux02         - oradbdev-ux03         - oradbqa-ux01         - oradbqa-ux02 Thursday, July 22, 2010
  9. Great tutorials for installing Oracle on a Mac: Mac OS X Leopard: http://blog.rayapps.com/2009/04/12/how-to-install-oracle- database-10g-on-mac-os-x-intel/ Mac OS X Snow Leopard: http://blog.rayapps.com/2009/09/14/how-to-install-oracle- database-10g-on-mac-os-x-snow-leopard/ Thursday, July 22, 2010
  10. Oracle install kits Oracle 11g: http://www.oracle.com/technology/software/products/ database/index.html Oracle XE (for Linux & Windows only) http://www.oracle.com/technology/software/products/ database/xe/index.html Thursday, July 22, 2010
  11. ~ pat$ sqlplus dev/dev@orcl SQL*Plus: Release 10.2.0.4.0 - Production on Wed Jul 14 22:12:06 2010 SQL> exit ~ pat$ sqlplus test/test@orcl SQL*Plus: Release 10.2.0.4.0 - Production on Wed Jul 14 22:12:06 2010 SQL> Thursday, July 22, 2010
  12. Problem 2: There was no documented way to create the database Thursday, July 22, 2010
  13. class CreatePeople < ActiveRecord::Migration   def self.up     create_table :people do |t|       t.string :name       t.timestamps     end   end   def self.down     drop_table :people   end end Thursday, July 22, 2010
  14. my_app pat$ rake db:migrate (in /Users/pat/my_app) my_app pat$ Thursday, July 22, 2010
  15. ActiveRecord::Schema.define(:version => ...) do   create_table "people", :force => true do |t|     t.string   "name"     t.datetime "created_at"     t.datetime "updated_at"   end # Lots more Enterprise crap deleted here... end Thursday, July 22, 2010
  16. class MigrationZero < ActiveRecord::Migration   def self.up   end   def self.down   end end Thursday, July 22, 2010
  17. class MigrationZero < ActiveRecord::Migration   def self.up     create_table "people", :force => true do |t|       t.string   "name"       t.datetime "created_at"       t.datetime "updated_at"     end # Lots more Enterprise crap pasted here...   end Thursday, July 22, 2010
  18. my_app pat$ rake db:populate (in /Users/pat/my_app) Deleting existing people... Loading new people... 43 people added. etc... Thursday, July 22, 2010
  19. Problem 3: Our existing database was not built with Rails in mind Thursday, July 22, 2010
  20. class Person < ActiveRecord::Base end Thursday, July 22, 2010
  21. class Person < ActiveRecord::Base set_table_name  :psn_person   set_primary_key :personid end Thursday, July 22, 2010
  22. class Person < ActiveRecord::Base set_table_name  :psn_person   set_primary_key :personid   has_many :addresses, :foreign_key => :personid end Thursday, July 22, 2010
  23. Thursday, July 22, 2010
  24. Thursday, July 22, 2010
  25. Legacy Data Gem Install: gem install legacy_data Source: http://github.com/alexrothenberg/legacy_data Thursday, July 22, 2010
  26. my_app pat$ script/generate models_from_tables analyzing psn_perdiem => PsnPerdiem analyzing psn_person => PsnPerson analyzing psn_personal_info => PsnPersonalInfo etc... Thursday, July 22, 2010
  27. class Person < ActiveRecord::Base set_table_name  :psn_person   set_primary_key :personid   has_many :addresses, :foreign_key => :personid   validates_presence_of   :first_name   validates_uniqueness_of :employee_id end Thursday, July 22, 2010
  28. • User friendly error messages • Accessible to most of development team • Easier to write tests Thursday, July 22, 2010
  29. Problem 4: The database schema was hard to work with Thursday, July 22, 2010
  30. SELECT   personid, first_name, last_name, ... FROM   psn_person Thursday, July 22, 2010
  31. SELECT   personid, first_name, last_name FROM   psn_person INNER JOIN psn_person_extra ON psn_person... INNER JOIN psn_person_career ON psn_person... INNER JOIN psn_person_email  ON psn_person... INNER JOIN pik_position      ON psn_person... INNER JOIN pik_yes_no        ON psn_person... ...and many more... Thursday, July 22, 2010
  32. class Person < ActiveRecord::Base   has_one :person_extra   has_one :person_career   has_one :person_email   belongs_to :pik_position   belongs_to :pik_yes_no # And many other associations too end Thursday, July 22, 2010
  33. Thursday, July 22, 2010
  34. SELECT   personid, first_name, last_name FROM   psn_person INNER JOIN psn_person_extra ON psn_person... INNER JOIN psn_person_career ON psn_person... INNER JOIN psn_person_email  ON psn_person... INNER JOIN pik_position      ON psn_person... INNER JOIN pik_yes_no        ON psn_person... SELECT id, first_name, last_name, ... FROM   people Thursday, July 22, 2010
  35. class CreatePeopleView < ActiveRecord::Migration   def self.up     execute <<-END_SQL     CREATE VIEW people AS SELECT personid AS id, ... FROM psn_person INNER JOIN ...       ...     END_SQL   end   def self.down     execute "DROP VIEW people"   end end Thursday, July 22, 2010
  36. class Person < ActiveRecord::Base   default_scope :readonly=>true end Thursday, July 22, 2010
  37. require 'spec_helper' describe Person do   it "should ..." do     Factory :person, :name => 'hilda'     Factory :person, :name => 'fredo'     # Do something with people # and assert on result ...   end end Thursday, July 22, 2010
  38. Thursday, July 22, 2010
  39. Problem 5: We found legacy code in our database Thursday, July 22, 2010
  40. Thursday, July 22, 2010
  41. PROCEDURE MergeAddress ( pCorrectPersonID IN pkgGlobal.tyID, pDuplicatePersonId IN pkgGlobal.tyId ) IS type tyAddressTable IS TABLE OF pkgAddressBase.tyData INDEX BY binary_integer; IDTable tyIDTable; EmptyTable tyAddressTable; CorrectTable tyAddressTable; DuplicateTable tyAddressTable; i INTEGER := 0; primary_count INTEGER := 0; primary_count_for_delete INTEGER := 0; CID pkgGlobal.tyID; SQLStr pkgGlobal.tyData; -- PROCEDURE FillTable ( pDuplicatePersonId IN pkgGlobal.tyId, pTable OUT tyAddressTable ) IS aToken pkgGlobal.tyId; CURSOR c1 IS SELECT ADDRESSID, PERSONID, ADDRESSTYPEID, ISPRIMARY, STREET1, STREET2, STREET3, STREET4, CITY, STATEPROVINCEID, POSTALCODE, COUNTRYID, SYSTEMNOTE, ADDRESSNOTE FROM tbAddress WHERE PersonID = pDuplicatePersonID; BEGIN FOR r IN c1 LOOP pTable(r.AddressId).AddressId := r.AddressID; pTable(r.AddressId).PersonID := r.PersonID; pTable(r.AddressId).AddressTypeId := r.AddressTypeId; pTable(r.AddressId).IsPrimary := r.IsPrimary; pTable(r.AddressId).Street1 := r.Street1; pTable(r.AddressId).Street2 := r.Street2; pTable(r.AddressId).Street3 := r.Street3; pTable(r.AddressId).Street4 := r.Street4; pTable(r.AddressId).city := r.city; pTable(r.AddressId).StateProvinceId := r.StateProvinceId; pTable(r.AddressId).postalCode := r.postalCode; pTable(r.Addressid).countryId := r.countryId; pTable(r.Addressid).AddressNote := r.addressNote; pTable(r.Addressid).systemNote := r.systemNote; END LOOP; END; Thursday, July 22, 2010
  42. PROCEDURE MergeAddress FUNCTION CompareValues ( ( pCorrectPersonID IN pkgGlobal.tyID, pCorrectTable IN OUT tyAddressTable, pDuplicatePersonId IN pkgGlobal.tyId pDuplicateTable IN OUT tyAddressTable, ) pRow IN INTEGER) IS RETURN INTEGER type tyAddressTable IS IS i INTEGER := 0; TABLE OF pkgAddressBase.tyData INDEX BY binary_integer; 0; m INTEGER := IDTable tyIDTable; Ret INTEGER := 0; EmptyTable tyAddressTable; master_city tbAddress.city%type; CorrectTable tyAddressTable; master_street1 tbAddress.street1%type; DuplicateTable tyAddressTable; pDuplicate_city tbAddress.city%type; i INTEGER := 0; pDuplicate_street1 tbAddress.street1%type; primary_count INTEGER := 0; master_countryId pkgGlobal.TyId; primary_count_for_delete INTEGER := 0; shouldModify INTEGER; CID pkgGlobal.tyID; BEGIN SQLStr pkgGlobal.tyData; m := pCorrectTable.First; -- FOR i IN 1..pCorrectTable.Count PROCEDURE FillTable LOOP ( SELECT DECODE(pCorrectTable(m).city,'See Address Note','CITY1',pCorrectTable(m).city) pDuplicatePersonId IN pkgGlobal.tyId, INTO master_city pTable OUT tyAddressTable FROM dual; ) SELECT DECODE(pDuplicateTable(pRow).city,'See Address Note','CITY2',pDuplicateTable(pRow).city) IS INTO pDuplicate_city aToken pkgGlobal.tyId; FROM dual; CURSOR c1 SELECT DECODE(pCorrectTable(m).street1,'See Address Note','STREET1',pCorrectTable(m).Street1) IS INTO master_street1 SELECT ADDRESSID, PERSONID, ADDRESSTYPEID, ISPRIMARY, STREET1, STREET2, STREET3, STREET4, CITY, STATEPROVINCEID, FROM dual; POSTALCODE, COUNTRYID, SYSTEMNOTE, ADDRESSNOTE SELECT DECODE(pDuplicateTable(pRow).street1,'See Address Note','STREET1111',pDuplicateTable(pRow).street1) FROM tbAddress INTO pDuplicate_street1 WHERE PersonID = pDuplicatePersonID; FROM dual; BEGIN SELECT DECODE(pCorrectTable(m).countryId,-100,100,pCorrectTable(m).countryId) FOR r IN c1 INTO master_countryId LOOP FROM dual; pTable(r.AddressId).AddressId := r.AddressID; IF master_countryId = pDuplicateTable( pRow ).countryId AND removeSpecial(master_city) = pTable(r.AddressId).PersonID := r.PersonID; removeSpecial(pDuplicate_city) AND removeSpecial(master_Street1) = removeSpecial(pDuplicate_Street1) AND pTable(r.AddressId).AddressTypeId := r.AddressTypeId; removeSpecial(NVL(pCorrectTable(m).street2,'STREET2')) = removeSpecial(NVL(pDuplicateTable(pRow).street2,'STREET2')) AND pTable(r.AddressId).IsPrimary := r.IsPrimary; removeSpecial(NVL(pCorrectTable(m).street3,'STREET3')) = removeSpecial(NVL(pDuplicateTable(pRow).street3,'STREET3')) AND pTable(r.AddressId).Street1 := r.Street1; removeSpecial(NVL(pCorrectTable(m).street4,'STREET4')) = removeSpecial(NVL(pDuplicateTable(pRow).street4,'STREET4')) AND pTable(r.AddressId).Street2 := r.Street2; NVL(pCorrectTable(m).StateProvinceId,999999999999) = NVL(pDuplicateTable( pRow ).StateProvinceId,999999999999) AND pTable(r.AddressId).Street3 := r.Street3; NVL(pCorrectTable(m).postalCode,'999999999999') = NVL(pDuplicateTable( pRow ).postalcode,'999999999999') THEN pTable(r.AddressId).Street4 := r.Street4;Ret := pkgGlobal.gTrue; pTable(r.AddressId).city := r.city; IF pDuplicateTable(pRow).AddressNote IS NOT NULL THEN pTable(r.AddressId).StateProvinceId := r.StateProvinceId; pCorrectTable(m).AddressNote := pCorrectTable(m).AddressNote||CHR(10)||'Note from pDuplicate:'|| pTable(r.AddressId).postalCode := r.postalCode; pDuplicateTable(pRow).AddressNote ; pTable(r.Addressid).countryId := r.countryId; dbms_output.put_line('Moving Column addressNote:'||substr(pCorrectTable(m).addressNote,1,200)||':'||' from Duplicate -- pTable(r.Addressid).AddressNote := r.addressNote; personId:'||pDuplicateTable(prow).personId||' to master PersonId:'||pCorrectTable(m).PersonId||' record for addressTypeId:'|| pTable(r.Addressid).systemNote := r.systemNote; pCorrectTable(m).addressTypeId); END LOOP; shouldModify := pkgGlobal.gTrue; END; END IF; Thursday, July 22, 2010
  43. PROCEDURE MergeAddress FUNCTION CompareValues IF pDuplicateTable(pRow).systemNote IS NOT NULL THEN ( ( pCorrectTable(m).systemNote := pCorrectTable(m).SystemNote||CHR(10)||'Note from D pCorrectPersonID IN pkgGlobal.tyID, pDuplicateTable(pRow).systemNote; pCorrectTable IN OUT tyAddressTable, pDuplicatePersonId IN pkgGlobal.tyId shouldModify pDuplicateTable IN OUT tyAddressTable, := pkgGlobal.gTrue; ) pRow IN INTEGER) END IF; IS RETURN INTEGER IF shouldModify = pkgGlobal.gTrue THEN type tyAddressTable IS -- Clobber added 10/16/03 IS i INTEGER := 0; pCorrectTable(m).clobber := pkgGlobal.gFalse; TABLE OF pkgAddressBase.tyData INDEX BY binary_integer; 0; m INTEGER := pkgAddressBase.modify( pCorrectTable(m), pSecure, pCommit ); IDTable tyIDTable; Ret INTEGER := 0; -- dbms_output.put_line('Moving columns from pDuplicate PersonId:'||pDuplicateTable(pRow EmptyTable tyAddressTable; pCorrectTable(m).PersonId||' where data is present in pDuplicate and null in master '); master_city tbAddress.city%type; CorrectTable tyAddressTable; END IF; master_street1 tbAddress.street1%type; DuplicateTable tyAddressTable; -- Print pDuplicate Record which is to be deleted pDuplicate_city tbAddress.city%type; i INTEGER := 0; -- dbms_output.put_line('pDuplicate Record which is to be deleted...'); pDuplicate_street1 tbAddress.street1%type; primary_count INTEGER := 0; master_countryId pkgGlobal.TyId; dbms_output.put_line('ADDRESSID -- :'||to_char(pDuplicateTable(pRow).addressId)); primary_count_for_delete INTEGER := 0; shouldModify INTEGER; -- dbms_output.put_line('PERSONID :'||to_char(pDuplicateTable(pRow).personId)); CID pkgGlobal.tyID; BEGIN -- dbms_output.put_line('ADDRESSTYPEID :'||to_char(pDuplicateTable(pRow).AddressTypeId SQLStr pkgGlobal.tyData; m := pCorrectTable.First; -- dbms_output.put_line('CITY :'||pDuplicateTable(pRow).city); -- FOR i IN 1..pCorrectTable.Count -- dbms_output.put_line('STATEPROVINCEID:'||to_char(pDuplicateTable(pRow).stateProvince PROCEDURE FillTable LOOP -- dbms_output.put_line('POSTALCODE :'||pDuplicateTable(pRow).PostalCode); ( -- dbms_output.put_line('COUNTRYID :'||to_char(pDuplicateTable(pRow).countryId)); SELECT DECODE(pCorrectTable(m).city,'See Address Note','CITY1',pCorrectTable(m).city) pDuplicatePersonId IN pkgGlobal.tyId, INTO master_city -- dbms_output.put_line('SYSTEMNOTE :'||substr(pDuplicateTable(pRow).systemNote,1,23 pTable OUT tyAddressTable FROM dual; -- dbms_output.put_line('ADDRESSNOTE :'||substr(pDuplicateTable(pRow).addressNote,1,2 ) SELECT DECODE(pDuplicateTable(pRow).city,'See Address Note','CITY2',pDuplicateTable(pRow).city) isPrimary, then move the -- Check if Primary Email exists in Master, if no and the pDuplicate is IS INTO pDuplicate_city BEGIN aToken pkgGlobal.tyId; FROM dual; SELECT COUNT(*) CURSOR c1 INTO primary_count_for_delete SELECT DECODE(pCorrectTable(m).street1,'See Address Note','STREET1',pCorrectTable(m).Street1) IS INTO master_street1 FROM tbAddress WHERE personId = pCorrectPersonId SELECT ADDRESSID, PERSONID, ADDRESSTYPEID, ISPRIMARY, STREET1, STREET2, STREET3, STREET4, CITY, STATEPROVINCEID, FROM dual; POSTALCODE, COUNTRYID, SYSTEMNOTE, ADDRESSNOTE AND IsPrimary = 1; SELECT DECODE(pDuplicateTable(pRow).street1,'See Address Note','STREET1111',pDuplicateTable(pRow).street1) FROM tbAddress INTO pDuplicate_street1 IF primary_count_for_delete = 0 AND pDuplicateTable(pRow).IsPrimary = 1 THEN WHERE PersonID = pDuplicatePersonID; FROM dual; pCorrectTable(m).IsPrimary := 1; BEGIN pCorrectTable(m).clobber := pkgGlobal.gFalse; SELECT DECODE(pCorrectTable(m).countryId,-100,100,pCorrectTable(m).countryId) FOR r IN c1 INTO master_countryId pkgAddressBase.Modify( pCorrectTable(m), pSecure, pCommit ); LOOP FROM dual; END IF; pTable(r.AddressId).AddressId := r.AddressID; IF master_countryId EXCEPTION = pDuplicateTable( pRow ).countryId AND removeSpecial(master_city) = pTable(r.AddressId).PersonID := r.PersonID; WHEN no_data_found THEN removeSpecial(pDuplicate_city) AND removeSpecial(master_Street1) = removeSpecial(pDuplicate_Street1) AND pTable(r.AddressId).AddressTypeId := r.AddressTypeId; NULL; removeSpecial(NVL(pCorrectTable(m).street2,'STREET2')) = removeSpecial(NVL(pDuplicateTable(pRow).street2,'STREET2')) AND pTable(r.AddressId).IsPrimary := r.IsPrimary; END; removeSpecial(NVL(pCorrectTable(m).street3,'STREET3')) = removeSpecial(NVL(pDuplicateTable(pRow).street3,'STREET3')) AND pTable(r.AddressId).Street1 := r.Street1; EXIT; removeSpecial(NVL(pCorrectTable(m).street4,'STREET4')) = removeSpecial(NVL(pDuplicateTable(pRow).street4,'STREET4')) AND pTable(r.AddressId).Street2 := r.Street2; ELSE NVL(pCorrectTable(m).StateProvinceId,999999999999) = NVL(pDuplicateTable( pRow ).StateProvinceId,999999999999) AND pTable(r.AddressId).Street3 := r.Street3; Ret := pkgGlobal.gFalse; NVL(pCorrectTable(m).postalCode,'999999999999') = NVL(pDuplicateTable( pRow ).postalcode,'999999999999') THEN pTable(r.AddressId).Street4 := r.Street4;Ret END IF; := pkgGlobal.gTrue; pTable(r.AddressId).city := r.city; IF pDuplicateTable(pRow).AddressNote IS NOT NULL THEN ); m := pCorrectTable.Next( m pCorrectTable(m).AddressNote LOOP; pCorrectTable(m).AddressNote||CHR(10)||'Note from pDuplicate:'|| pTable(r.AddressId).StateProvinceId := r.StateProvinceId; END := pTable(r.AddressId).postalCode := r.postalCode; RETURN Ret; pDuplicateTable(pRow).AddressNote ; pTable(r.Addressid).countryId EXCEPTION := r.countryId; dbms_output.put_line('Moving Column addressNote:'||substr(pCorrectTable(m).addressNote,1,200)||':'||' from Duplicate -- pTable(r.Addressid).AddressNote personId:'||pDuplicateTable(prow).personId||' THEN := r.addressNote; WHEN OTHERS to master PersonId:'||pCorrectTable(m).PersonId||' record for addressTypeId:'|| pTable(r.Addressid).systemNote := r.systemNote; pCorrectTable(m).addressTypeId); pkgException.RaiseOther (SQLCODE, sqlerrm); END LOOP; shouldModify := pkgGlobal.gTrue; Ret; RETURN END; END IF; NULL; END; Thursday, July 22, 2010
  44. PROCEDURE MergeAddress FUNCTION CompareValues IF pDuplicateTable(pRow).systemNote IS NOT NULL THEN ( ( pCorrectTable(m).systemNote := pCorrectTable(m).SystemNote||CHR(10)||'Note from D pCorrectPersonID IN pkgGlobal.tyID, pDuplicateTable(pRow).systemNote; pCorrectTable IN OUT tyAddressTable, pDuplicatePersonId IN pkgGlobal.tyId shouldModify pDuplicateTable IN OUT tyAddressTable, := pkgGlobal.gTrue; ) pRow IN INTEGER) END IF; IS RETURN INTEGER IF shouldModify = pkgGlobal.gTrue THEN type tyAddressTable IS -- Clobber added 10/16/03 IS i INTEGER := 0; pCorrectTable(m).clobber := pkgGlobal.gFalse; TABLE OF pkgAddressBase.tyData INDEX BY binary_integer; 0; m INTEGER := pkgAddressBase.modify( pCorrectTable(m), pSecure, pCommit ); BEGIN IDTable tyIDTable; Ret INTEGER := 0; -- dbms_output.put_line('Moving columns from pDuplicate PersonId:'||pDuplicateTable(pRow EmptyTable tyAddressTable; pCorrectTable(m).PersonId||' where data is present in pDuplicate and null in master '); master_city tbAddress.city%type; CorrectTable tyAddressTable; END IF; master_street1 tbAddress.street1%type; i ret := upper(REPLACE(REPLACE(REPLACE(REPLACE( DuplicateTable tyAddressTable; INTEGER := 0; -- Print pDuplicate Record which is to be deleted pDuplicate_city tbAddress.city%type; -- dbms_output.put_line('pDuplicate Record which is to be deleted...'); pDuplicate_street1 tbAddress.street1%type; primary_count INTEGER := 0; master_countryId pkgGlobal.TyId; dbms_output.put_line('ADDRESSID -- :'||to_char(pDuplicateTable(pRow).addressId)); REPLACE(REPLACE(REPLACE(REPLACE( primary_count_for_delete INTEGER := 0; CID pkgGlobal.tyID; shouldModify INTEGER; BEGIN -- dbms_output.put_line('PERSONID :'||to_char(pDuplicateTable(pRow).personId)); -- dbms_output.put_line('ADDRESSTYPEID :'||to_char(pDuplicateTable(pRow).AddressTypeId SQLStr pkgGlobal.tyData; m := pCorrectTable.First; -- dbms_output.put_line('CITY :'||pDuplicateTable(pRow).city); -- REPLACE(REPLACE(REPLACE(REPLACE( PROCEDURE FillTable FOR i IN 1..pCorrectTable.Count LOOP -- dbms_output.put_line('STATEPROVINCEID:'||to_char(pDuplicateTable(pRow).stateProvince -- dbms_output.put_line('POSTALCODE :'||pDuplicateTable(pRow).PostalCode); ( -- dbms_output.put_line('COUNTRYID :'||to_char(pDuplicateTable(pRow).countryId)); SELECT DECODE(pCorrectTable(m).city,'See Address Note','CITY1',pCorrectTable(m).city) REPLACE(REPLACE(REPLACE(REPLACE( pDuplicatePersonId IN pkgGlobal.tyId, pTable OUT tyAddressTable INTO master_city FROM dual; -- dbms_output.put_line('SYSTEMNOTE :'||substr(pDuplicateTable(pRow).systemNote,1,23 -- dbms_output.put_line('ADDRESSNOTE :'||substr(pDuplicateTable(pRow).addressNote,1,2 SELECT DECODE(pDuplicateTable(pRow).city,'See Address Note','CITY2',pDuplicateTable(pRow).city) isPrimary, then move the -- Check if Primary Email exists in Master, if no and the pDuplicate is REPLACE(REPLACE( ) IS INTO pDuplicate_city BEGIN aToken pkgGlobal.tyId; FROM dual; SELECT COUNT(*) string1,'!',''),'@',''),'#',''),'$',''), CURSOR c1 INTO primary_count_for_delete SELECT DECODE(pCorrectTable(m).street1,'See Address Note','STREET1',pCorrectTable(m).Street1) IS INTO master_street1 FROM tbAddress WHERE personId = pCorrectPersonId SELECT ADDRESSID, PERSONID, ADDRESSTYPEID, ISPRIMARY, STREET1, STREET2, STREET3, STREET4, CITY, STATEPROVINCEID, FROM dual; '%',''),'^',''),'&',''),'*',''), POSTALCODE, COUNTRYID, SYSTEMNOTE, ADDRESSNOTE AND IsPrimary = 1; SELECT DECODE(pDuplicateTable(pRow).street1,'See Address Note','STREET1111',pDuplicateTable(pRow).street1) FROM tbAddress INTO pDuplicate_street1 IF primary_count_for_delete = 0 AND pDuplicateTable(pRow).IsPrimary = 1 THEN WHERE PersonID = pDuplicatePersonID; FROM dual; pCorrectTable(m).IsPrimary := 1; BEGIN FOR r IN c1 '-', ''),'+',''),'',''),'(',''), pCorrectTable(m).clobber := pkgGlobal.gFalse; SELECT DECODE(pCorrectTable(m).countryId,-100,100,pCorrectTable(m).countryId) INTO master_countryId pkgAddressBase.Modify( pCorrectTable(m), pSecure, pCommit ); LOOP FROM dual; END IF; ')',''),'?',''),',',''),'.',''), pTable(r.AddressId).AddressId pTable(r.AddressId).PersonID := r.AddressID; IF master_countryId := r.PersonID; EXCEPTION = pDuplicateTable( pRow ).countryId AND removeSpecial(master_city) = WHEN no_data_found THEN removeSpecial(pDuplicate_city) AND removeSpecial(master_Street1) = removeSpecial(pDuplicate_Street1) AND pTable(r.AddressId).AddressTypeId := r.AddressTypeId; NULL; removeSpecial(NVL(pCorrectTable(m).street2,'STREET2')) = removeSpecial(NVL(pDuplicateTable(pRow).street2,'STREET2')) AND '/',''),'=','')); pTable(r.AddressId).IsPrimary pTable(r.AddressId).Street1 := r.IsPrimary; END; removeSpecial(NVL(pCorrectTable(m).street3,'STREET3')) = removeSpecial(NVL(pDuplicateTable(pRow).street3,'STREET3')) AND := r.Street1; EXIT; removeSpecial(NVL(pCorrectTable(m).street4,'STREET4')) = removeSpecial(NVL(pDuplicateTable(pRow).street4,'STREET4')) AND ELSE RETURN ret; pTable(r.AddressId).Street2 := r.Street2; NVL(pCorrectTable(m).StateProvinceId,999999999999) = NVL(pDuplicateTable( pRow ).StateProvinceId,999999999999) AND pTable(r.AddressId).Street3 := r.Street3; Ret := pkgGlobal.gFalse; NVL(pCorrectTable(m).postalCode,'999999999999') = NVL(pDuplicateTable( pRow ).postalcode,'999999999999') THEN pTable(r.AddressId).Street4 := r.Street4;Ret END IF; := pkgGlobal.gTrue; END; pTable(r.AddressId).city := r.city; IF pDuplicateTable(pRow).AddressNote IS NOT NULL THEN ); m := pCorrectTable.Next( m pCorrectTable(m).AddressNote LOOP; pCorrectTable(m).AddressNote||CHR(10)||'Note from pDuplicate:'|| pTable(r.AddressId).StateProvinceId := r.StateProvinceId; END := pTable(r.AddressId).postalCode := r.postalCode; RETURN Ret; pDuplicateTable(pRow).AddressNote ; pTable(r.Addressid).countryId EXCEPTION := r.countryId; dbms_output.put_line('Moving Column addressNote:'||substr(pCorrectTable(m).addressNote,1,200)||':'||' from Duplicate -- pTable(r.Addressid).AddressNote personId:'||pDuplicateTable(prow).personId||' THEN := r.addressNote; WHEN OTHERS to master PersonId:'||pCorrectTable(m).PersonId||' record for addressTypeId:'|| pTable(r.Addressid).systemNote := r.systemNote; pCorrectTable(m).addressTypeId); pkgException.RaiseOther (SQLCODE, sqlerrm); END LOOP; shouldModify := pkgGlobal.gTrue; Ret; RETURN END; END IF; NULL; END; Thursday, July 22, 2010
  45. Thursday, July 22, 2010
  46. Thursday, July 22, 2010
  47. require 'spec_helper' describe Person do   it "should merge two duplicates" do     Factory :person, :email => 'alex@alex.com'     Factory :person, :email => 'alex@alex.com'     Person.merge_duplicates     Person.count.should == 1   end end Thursday, July 22, 2010
  48. my_app pat$ rake spec (in /Users/pat/my_app) ................................................. .F............................................... ...................................... 1) RuntimeError in 'Should merge two duplicates' OCIError: ORA-04092:cannot ROLLBACK in a trigger ... ORA-01403: no data found ORA-06512: at "HRTEST.PKGAPPBASE", line 1775 ORA-04088: error during execution of trigger UPDATE psn_address SET personid = 10543035        WHERE applicationid = 10594482 Thursday, July 22, 2010
  49. PROCEDURE updateops   # ...lots of PL/SQL...   vQryStr := 'SELECT ... WHERE personid = ' || ppersonid;   dbms_output.put_line(vQryStr);   # ...lots more PL/SQL... END; Thursday, July 22, 2010
  50. Oracle Enhanced Adapter Install: gem install activerecord-oracle_enhanced-adapter Source: http://github.com/rsim/oracle-enhanced Thursday, July 22, 2010
  51. Merge Job 6357 lines 322 lines 0 tests lots of tests 33% failure 0.1% failure Thursday, July 22, 2010
  52. Using Rails When ... sharing a development database there was no documented way to create a new database our existing database was not built with Rails in mind the database schema is hard to work with when you find application code in the database Thursday, July 22, 2010
  53. Use Rails best practices even when confronted with legacy problems Thursday, July 22, 2010
  54. Thank you! Alex Rothenberg http://alexrothenberg.com @alexrothenberg Pat Shaughnessy http://patshaughnessy.net @patshaughnessy2 Thursday, July 22, 2010
  55. Photo Credits http://media.photobucket.com/image/lipstick%20on%20a%20pig/046664/LipstickPig-C.jpg?o=11 http://www.flickr.com/photos/piccadillywilson/1366479417/ Thursday, July 22, 2010

×