SlideShare a Scribd company logo
1 of 55
Download to read offline
Rails in the
                          Enterprise: Off the
                          Beaten Track
                          Alex Rothenberg
                          http://alexrothenberg.com
                          @alexrothenberg

                          Pat Shaughnessy
                          http://patshaughnessy.net
                          @patshaughnessy2



Thursday, July 22, 2010
Thursday, July 22, 2010
Thursday, July 22, 2010
Thursday, July 22, 2010
Thursday, July 22, 2010
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
Problem 1:

                          Sharing a
                          development
                          database


Thursday, July 22, 2010
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
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
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
~ 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
Problem 2:

                          There was no
                          documented
                          way to create
                          the database
Thursday, July 22, 2010
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
my_app pat$ rake db:migrate
(in /Users/pat/my_app)

my_app pat$




Thursday, July 22, 2010
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
class MigrationZero < ActiveRecord::Migration
  def self.up
  end

  def self.down
  end
end




Thursday, July 22, 2010
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
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
Problem 3:

                          Our existing
                          database was
                          not built with
                          Rails in mind
Thursday, July 22, 2010
class Person < ActiveRecord::Base
end




Thursday, July 22, 2010
class Person < ActiveRecord::Base
  set_table_name  :psn_person
  set_primary_key :personid
end




Thursday, July 22, 2010
class Person < ActiveRecord::Base
  set_table_name  :psn_person
  set_primary_key :personid

  has_many :addresses, :foreign_key => :personid
end




Thursday, July 22, 2010
Thursday, July 22, 2010
Thursday, July 22, 2010
Legacy Data Gem

  Install:
  gem install legacy_data



  Source:
  http://github.com/alexrothenberg/legacy_data




Thursday, July 22, 2010
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
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
• User friendly error messages

   • Accessible to most of development team

   • Easier to write tests


Thursday, July 22, 2010
Problem 4:

                          The database
                          schema was
                          hard to work
                          with
Thursday, July 22, 2010
SELECT
  personid, first_name, last_name, ...
FROM
  psn_person




Thursday, July 22, 2010
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
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
Thursday, July 22, 2010
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
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
class Person < ActiveRecord::Base
  default_scope :readonly=>true
end




Thursday, July 22, 2010
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
Thursday, July 22, 2010
Problem 5:

                          We found
                          legacy code in
                          our database


Thursday, July 22, 2010
Thursday, July 22, 2010
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
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
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
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
Thursday, July 22, 2010
Thursday, July 22, 2010
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
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
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
Oracle Enhanced Adapter

  Install:
       gem install activerecord-oracle_enhanced-adapter



  Source:
  http://github.com/rsim/oracle-enhanced




Thursday, July 22, 2010
Merge Job

                          6357 lines         322 lines

                            0 tests         lots of tests

                          33% failure       0.1% failure




Thursday, July 22, 2010
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
Use Rails best practices
         even when confronted with
         legacy problems



Thursday, July 22, 2010
Thank you!


                          Alex Rothenberg
                          http://alexrothenberg.com
                          @alexrothenberg

                          Pat Shaughnessy
                          http://patshaughnessy.net
                          @patshaughnessy2



Thursday, July 22, 2010
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

More Related Content

What's hot

MongoDB Advanced Topics
MongoDB Advanced TopicsMongoDB Advanced Topics
MongoDB Advanced TopicsCésar Rodas
 
Schema design short
Schema design shortSchema design short
Schema design shortMongoDB
 
Symfony2 y MongoDB - deSymfony 2012
Symfony2 y MongoDB - deSymfony 2012Symfony2 y MongoDB - deSymfony 2012
Symfony2 y MongoDB - deSymfony 2012Pablo Godel
 
MongoDB Europe 2016 - ETL for Pros – Getting Data Into MongoDB The Right Way
MongoDB Europe 2016 - ETL for Pros – Getting Data Into MongoDB The Right WayMongoDB Europe 2016 - ETL for Pros – Getting Data Into MongoDB The Right Way
MongoDB Europe 2016 - ETL for Pros – Getting Data Into MongoDB The Right WayMongoDB
 
Symfony2 and MongoDB
Symfony2 and MongoDBSymfony2 and MongoDB
Symfony2 and MongoDBPablo Godel
 
ElasticSearch with Tire
ElasticSearch with TireElasticSearch with Tire
ElasticSearch with TireDavid Yun
 
20th.陈晓鸣 百度海量日志分析架构及处理经验分享
20th.陈晓鸣 百度海量日志分析架构及处理经验分享20th.陈晓鸣 百度海量日志分析架构及处理经验分享
20th.陈晓鸣 百度海量日志分析架构及处理经验分享elevenma
 
MongoDB Europe 2016 - Advanced MongoDB Aggregation Pipelines
MongoDB Europe 2016 - Advanced MongoDB Aggregation PipelinesMongoDB Europe 2016 - Advanced MongoDB Aggregation Pipelines
MongoDB Europe 2016 - Advanced MongoDB Aggregation PipelinesMongoDB
 
Databases for Beginners SQLite
Databases for Beginners SQLiteDatabases for Beginners SQLite
Databases for Beginners SQLiteChristopher Wimble
 
Distributed Identities with OpenID
Distributed Identities with OpenIDDistributed Identities with OpenID
Distributed Identities with OpenIDBastian Hofmann
 
Webinar: Strongly Typed Languages and Flexible Schemas
Webinar: Strongly Typed Languages and Flexible SchemasWebinar: Strongly Typed Languages and Flexible Schemas
Webinar: Strongly Typed Languages and Flexible SchemasMongoDB
 
Strongly Typed Languages and Flexible Schemas
Strongly Typed Languages and Flexible SchemasStrongly Typed Languages and Flexible Schemas
Strongly Typed Languages and Flexible SchemasNorberto Leite
 

What's hot (12)

MongoDB Advanced Topics
MongoDB Advanced TopicsMongoDB Advanced Topics
MongoDB Advanced Topics
 
Schema design short
Schema design shortSchema design short
Schema design short
 
Symfony2 y MongoDB - deSymfony 2012
Symfony2 y MongoDB - deSymfony 2012Symfony2 y MongoDB - deSymfony 2012
Symfony2 y MongoDB - deSymfony 2012
 
MongoDB Europe 2016 - ETL for Pros – Getting Data Into MongoDB The Right Way
MongoDB Europe 2016 - ETL for Pros – Getting Data Into MongoDB The Right WayMongoDB Europe 2016 - ETL for Pros – Getting Data Into MongoDB The Right Way
MongoDB Europe 2016 - ETL for Pros – Getting Data Into MongoDB The Right Way
 
Symfony2 and MongoDB
Symfony2 and MongoDBSymfony2 and MongoDB
Symfony2 and MongoDB
 
ElasticSearch with Tire
ElasticSearch with TireElasticSearch with Tire
ElasticSearch with Tire
 
20th.陈晓鸣 百度海量日志分析架构及处理经验分享
20th.陈晓鸣 百度海量日志分析架构及处理经验分享20th.陈晓鸣 百度海量日志分析架构及处理经验分享
20th.陈晓鸣 百度海量日志分析架构及处理经验分享
 
MongoDB Europe 2016 - Advanced MongoDB Aggregation Pipelines
MongoDB Europe 2016 - Advanced MongoDB Aggregation PipelinesMongoDB Europe 2016 - Advanced MongoDB Aggregation Pipelines
MongoDB Europe 2016 - Advanced MongoDB Aggregation Pipelines
 
Databases for Beginners SQLite
Databases for Beginners SQLiteDatabases for Beginners SQLite
Databases for Beginners SQLite
 
Distributed Identities with OpenID
Distributed Identities with OpenIDDistributed Identities with OpenID
Distributed Identities with OpenID
 
Webinar: Strongly Typed Languages and Flexible Schemas
Webinar: Strongly Typed Languages and Flexible SchemasWebinar: Strongly Typed Languages and Flexible Schemas
Webinar: Strongly Typed Languages and Flexible Schemas
 
Strongly Typed Languages and Flexible Schemas
Strongly Typed Languages and Flexible SchemasStrongly Typed Languages and Flexible Schemas
Strongly Typed Languages and Flexible Schemas
 

Similar to Rails in the enterprise

Google Devfest Singapore - OpenSocial
Google Devfest Singapore - OpenSocialGoogle Devfest Singapore - OpenSocial
Google Devfest Singapore - OpenSocialPatrick Chanezon
 
R getting spatial
R getting spatialR getting spatial
R getting spatialFAO
 
iOS 개발자의 Flutter 체험기
iOS 개발자의 Flutter 체험기iOS 개발자의 Flutter 체험기
iOS 개발자의 Flutter 체험기Wanbok Choi
 
DataDay 2023 Presentation
DataDay 2023 PresentationDataDay 2023 Presentation
DataDay 2023 PresentationMax De Marzi
 
Teaching Programming Online
Teaching Programming OnlineTeaching Programming Online
Teaching Programming OnlinePamela Fox
 
Vavr Java User Group Rheinland
Vavr Java User Group RheinlandVavr Java User Group Rheinland
Vavr Java User Group RheinlandDavid Schmitz
 
Jaoo - Open Social A Standard For The Social Web
Jaoo - Open Social A Standard For The Social WebJaoo - Open Social A Standard For The Social Web
Jaoo - Open Social A Standard For The Social WebPatrick Chanezon
 
Helping Data Teams with Puppet / Puppet Camp London - Apr 13, 2015
Helping Data Teams with Puppet / Puppet Camp London - Apr 13, 2015Helping Data Teams with Puppet / Puppet Camp London - Apr 13, 2015
Helping Data Teams with Puppet / Puppet Camp London - Apr 13, 2015Sergii Khomenko
 
Puppet Camp London 2015 - Helping Data Teams with Puppet
Puppet Camp London 2015 - Helping Data Teams with PuppetPuppet Camp London 2015 - Helping Data Teams with Puppet
Puppet Camp London 2015 - Helping Data Teams with PuppetPuppet
 
Constance et qualité du code dans une équipe - Rémi Prévost
Constance et qualité du code dans une équipe - Rémi PrévostConstance et qualité du code dans une équipe - Rémi Prévost
Constance et qualité du code dans une équipe - Rémi PrévostWeb à Québec
 
Lighting talk neo4j fosdem 2011
Lighting talk neo4j fosdem 2011Lighting talk neo4j fosdem 2011
Lighting talk neo4j fosdem 2011Jordi Valverde
 
AndroidからWebサービスを使う
AndroidからWebサービスを使うAndroidからWebサービスを使う
AndroidからWebサービスを使うMasafumi Terazono
 
PHP 8: Process & Fixing Insanity
PHP 8: Process & Fixing InsanityPHP 8: Process & Fixing Insanity
PHP 8: Process & Fixing InsanityGeorgePeterBanyard
 
GraphQL, l'avenir du REST par François ZANINOTTO
GraphQL, l'avenir du REST par François ZANINOTTOGraphQL, l'avenir du REST par François ZANINOTTO
GraphQL, l'avenir du REST par François ZANINOTTOLa Cuisine du Web
 
Leap Ahead with Redis 6.2
Leap Ahead with Redis 6.2Leap Ahead with Redis 6.2
Leap Ahead with Redis 6.2VMware Tanzu
 
Micro-ORM Introduction - Don't overcomplicate
Micro-ORM Introduction - Don't overcomplicateMicro-ORM Introduction - Don't overcomplicate
Micro-ORM Introduction - Don't overcomplicateKiev ALT.NET
 
Tuga IT 2018 Summer Edition - The Future of C#
Tuga IT 2018 Summer Edition - The Future of C#Tuga IT 2018 Summer Edition - The Future of C#
Tuga IT 2018 Summer Edition - The Future of C#Paulo Morgado
 
10. Getting Spatial
10. Getting Spatial10. Getting Spatial
10. Getting SpatialFAO
 

Similar to Rails in the enterprise (20)

Google Devfest Singapore - OpenSocial
Google Devfest Singapore - OpenSocialGoogle Devfest Singapore - OpenSocial
Google Devfest Singapore - OpenSocial
 
R getting spatial
R getting spatialR getting spatial
R getting spatial
 
iOS 개발자의 Flutter 체험기
iOS 개발자의 Flutter 체험기iOS 개발자의 Flutter 체험기
iOS 개발자의 Flutter 체험기
 
DataDay 2023 Presentation
DataDay 2023 PresentationDataDay 2023 Presentation
DataDay 2023 Presentation
 
Teaching Programming Online
Teaching Programming OnlineTeaching Programming Online
Teaching Programming Online
 
Vavr Java User Group Rheinland
Vavr Java User Group RheinlandVavr Java User Group Rheinland
Vavr Java User Group Rheinland
 
Jaoo - Open Social A Standard For The Social Web
Jaoo - Open Social A Standard For The Social WebJaoo - Open Social A Standard For The Social Web
Jaoo - Open Social A Standard For The Social Web
 
Big data. Opportunità e rischi
Big data. Opportunità e rischiBig data. Opportunità e rischi
Big data. Opportunità e rischi
 
Helping Data Teams with Puppet / Puppet Camp London - Apr 13, 2015
Helping Data Teams with Puppet / Puppet Camp London - Apr 13, 2015Helping Data Teams with Puppet / Puppet Camp London - Apr 13, 2015
Helping Data Teams with Puppet / Puppet Camp London - Apr 13, 2015
 
Puppet Camp London 2015 - Helping Data Teams with Puppet
Puppet Camp London 2015 - Helping Data Teams with PuppetPuppet Camp London 2015 - Helping Data Teams with Puppet
Puppet Camp London 2015 - Helping Data Teams with Puppet
 
Constance et qualité du code dans une équipe - Rémi Prévost
Constance et qualité du code dans une équipe - Rémi PrévostConstance et qualité du code dans une équipe - Rémi Prévost
Constance et qualité du code dans une équipe - Rémi Prévost
 
Lighting talk neo4j fosdem 2011
Lighting talk neo4j fosdem 2011Lighting talk neo4j fosdem 2011
Lighting talk neo4j fosdem 2011
 
AndroidからWebサービスを使う
AndroidからWebサービスを使うAndroidからWebサービスを使う
AndroidからWebサービスを使う
 
PHP 8: Process & Fixing Insanity
PHP 8: Process & Fixing InsanityPHP 8: Process & Fixing Insanity
PHP 8: Process & Fixing Insanity
 
GraphQL, l'avenir du REST par François ZANINOTTO
GraphQL, l'avenir du REST par François ZANINOTTOGraphQL, l'avenir du REST par François ZANINOTTO
GraphQL, l'avenir du REST par François ZANINOTTO
 
Leap Ahead with Redis 6.2
Leap Ahead with Redis 6.2Leap Ahead with Redis 6.2
Leap Ahead with Redis 6.2
 
Micro-ORM Introduction - Don't overcomplicate
Micro-ORM Introduction - Don't overcomplicateMicro-ORM Introduction - Don't overcomplicate
Micro-ORM Introduction - Don't overcomplicate
 
10. R getting spatial
10.  R getting spatial10.  R getting spatial
10. R getting spatial
 
Tuga IT 2018 Summer Edition - The Future of C#
Tuga IT 2018 Summer Edition - The Future of C#Tuga IT 2018 Summer Edition - The Future of C#
Tuga IT 2018 Summer Edition - The Future of C#
 
10. Getting Spatial
10. Getting Spatial10. Getting Spatial
10. Getting Spatial
 

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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 39. Problem 5: We found legacy code in our database 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
  • 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