Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Reescrevendo em Rails
  uma grande aplicação
         legada
Um relato sobre as dificuldades encontradas

               Cá...
http://surgeworks.com
cassiomarques.wordpress.com

     @cassiomarques

   cassiommc@gmail.com
Mundo ideal...
http://www.flickr.com/photos/venteco
http://www.flickr.com/photos/epleitez/
Migração de uma
 grande aplicação
desktop para Rails
Aplicação para
Controle de Serviços
  de Hemoterapia
• Desktop
• C++ (Qt 3)
• PostgreSQL
• +150k loc em C++
• +60k loc em PL/PGSQL
Porque migrar?
• Complexidade da linguagem
• Dificuldade de integração com outras
  aplicações.
• Dificuldade para acessar r...
Rails? Ruby? WTF?

• Quem programa com isso?
• Não haverá quem dê manutenção
• Não há uma empresa “por trás” do Ruby
O que eles não sabiam...

• Já existiam 4 outras aplicações menores em
  produção escritas em Ruby
• A dificuldade de encon...
O maior vilão de toda
   essa história...
Banco de Dados.
ActiveRecord
Ótimo se você seguir
  as convenções...
... mas ainda
interessante mesmo
que você não as siga.
1   class Product < ActiveRecord::Base
2     set_table_name 'produto'
3     set_primary_key 'codigo'
4   end
Bancos legados podem
usar nomes de colunas
      estranhos...
estoquequantidade
  fornecedorcnpj
  requisicaoficha
 cobrancacodigo
Ou ainda mais
 estranhos...
PRD_CDG
       STT_USR
    codigoproduto
 codigoproduto_novo
codigoproduto_novo2
1 class Product < ActiveRecord::Base
2   alias_attribute 'description', 'produtodescricao'
3 end
Não funciona para
    queries...
 2   alias_attribute 'expiration_date', 'validade'
 3   named_scope :expired,
 4     :conditions => ['expiration_date <= ?...
Associações
1   has_many :donations,
2            :class_name => 'Donation',
3            :foreign_key => 'doador_codigo'
4
5   belong...
Views no banco?
+200 tabelas c/ média
 de 15 campos cada
VIEW MAPPING HELL
Três opções:
Conviver com as
  diferenças
Criar um novo banco e
migrar os dados depois
Alterar o nome das
 tabelas e colunas
Criar base
development
RAILS_ENV=production rake db:schema:dump

           rake db:schema:load
Duas bases para
desenvolvimento
1) development “normal”

2) dump da base de produção (com dados)
Para alterar nomes...
 1   class RenameDonationTableAndColumns < ActiveRecord::Migration
 2     def self.up
 3       rename_table :doacao, :dona...
Redefinir tipos dos
    campos
 1   class ChangeDonorsColumns < ActiveRecord::Migration
 2     def self.up
 3       change_table :donors do |t|
 4       ...
Crie seus próprios
métodos para migrations
 1   module ActiveRecord
 2     module ConnectionAdapters
 3       class PostgreSQLAdapter < AbstractAdapter
 4         de...
58 change_sn_to_boolean :donors, :send_sms
chaves estrangeiras
foreign_key_migrations
composite_primary_keys
Alterar dados com
    migrations
Errado
 1   class PopulateAddressPrefixes < ActiveRecord::Migration
 2     def self.up
 3       Address.all.each do |address| 
 4...
Melhor
1   class PopulateAddressPrefixes < ActiveRecord::Migration
2     def self.up
3       prefix = AddressPrefix.find_by_code ...
Enumerações no banco
foobar=# select * from education_level;
   id     |  description  
  --------+---------------
   1      | Nao Cursou  
   ...
1 donor.education_level.description # => join no banco!
ok, vamos tirar as
enumerações do
     banco...
problema com FK’s (tabelas
 com enumerações ainda
      referenciadas)
1 donor.education_level = 5 # WTF?
enumerate_it
 github.com/cassiomarques/enumerate_it




(jabá mode on)
 1   class EducationLevel < EnumerateIt::Base
 2     associate_values(
 3       :none                 => ['1', 'Não Cursou...
1 class Donor < ActiveRecord::Base
2   has_enumeration_for :education_level, :with => EducationLevel
3 end
1 donor.education_level = EducationLevel::COMPLETE_3RD_GRADE
2 donor.education_level_humanize # 3º Grau Completo (sem join)
1 <% form_for @donor do |f| %>
2   <%= f.select :education_level, EducationLevel.to_a %>
3 <% end -%>
schema.rb => banco de
        testes
... mas se a sua PK for
        varchar...
1   create_table "donors", :id => false, :force => true do |t|
3     t.string  "id", :limit => 6, :null => false
4     # ....
1   class CreateAndAssignDonorsIdSequence < ActiveRecord::Migration
2     def self.up
3       suppress_messages do
4      ...
1 # spec/spec_helper.rb e/ou features/support/schema_fixes.rb
2 require 'migrations/schema_fixes'
3
4 [FirstMigration, Sec...
Stored Procedures,
  functions, etc...
• Dificeis de testar
• Side effects
• Código procedural
• Lógica separada da app
?
OBRIGADO!
Relato Sobre a Migração de uma Aplicação Legada para Rails
Relato Sobre a Migração de uma Aplicação Legada para Rails
Relato Sobre a Migração de uma Aplicação Legada para Rails
Upcoming SlideShare
Loading in …5
×

Relato Sobre a Migração de uma Aplicação Legada para Rails

3,293 views

Published on

Palestra apresentada no dia 10/04/2010 no 8o Encontro do Grupo de Usuários Ruby de São Paulo, na Caelum.

Published in: Technology

Relato Sobre a Migração de uma Aplicação Legada para Rails

  1. 1. Reescrevendo em Rails uma grande aplicação legada Um relato sobre as dificuldades encontradas Cássio Marques
  2. 2. http://surgeworks.com
  3. 3. cassiomarques.wordpress.com @cassiomarques cassiommc@gmail.com
  4. 4. Mundo ideal...
  5. 5. http://www.flickr.com/photos/venteco
  6. 6. http://www.flickr.com/photos/epleitez/
  7. 7. Migração de uma grande aplicação desktop para Rails
  8. 8. Aplicação para Controle de Serviços de Hemoterapia
  9. 9. • Desktop • C++ (Qt 3) • PostgreSQL • +150k loc em C++ • +60k loc em PL/PGSQL
  10. 10. Porque migrar? • Complexidade da linguagem • Dificuldade de integração com outras aplicações. • Dificuldade para acessar remotamente • Dificuldade para manutenção: nada de testes, +5 min para compilar, SPs, etc...
  11. 11. Rails? Ruby? WTF? • Quem programa com isso? • Não haverá quem dê manutenção • Não há uma empresa “por trás” do Ruby
  12. 12. O que eles não sabiam... • Já existiam 4 outras aplicações menores em produção escritas em Ruby • A dificuldade de encontrar um profissional bom em Java/C#/Whatever é a mesma de encontrar um bom em Ruby • Produtividade! • Comunidade open-source
  13. 13. O maior vilão de toda essa história...
  14. 14. Banco de Dados.
  15. 15. ActiveRecord
  16. 16. Ótimo se você seguir as convenções...
  17. 17. ... mas ainda interessante mesmo que você não as siga.
  18. 18. 1 class Product < ActiveRecord::Base 2   set_table_name 'produto' 3   set_primary_key 'codigo' 4 end
  19. 19. Bancos legados podem usar nomes de colunas estranhos...
  20. 20. estoquequantidade fornecedorcnpj requisicaoficha cobrancacodigo
  21. 21. Ou ainda mais estranhos...
  22. 22. PRD_CDG STT_USR codigoproduto codigoproduto_novo codigoproduto_novo2
  23. 23. 1 class Product < ActiveRecord::Base 2   alias_attribute 'description', 'produtodescricao' 3 end
  24. 24. Não funciona para queries...
  25. 25.  2 alias_attribute 'expiration_date', 'validade'  3 named_scope :expired,  4   :conditions => ['expiration_date <= ?', Time.now]  5 # PGError: ERROR: column "expiration_date" does not exist  6  7  8 class Hemocomponent < ActiveRecord::Base  9   alias_attribute 'acronym', 'sigla' 10 end 11 Hemocomponent.find_by_acronym 'CHE' 12 # undefined method `find_by_acronym' for #<Class:0x1033f2380>
  26. 26. Associações
  27. 27. 1 has_many :donations, 2          :class_name => 'Donation', 3          :foreign_key => 'doador_codigo' 4 5 belongs_to :donor, 6            :class_name => 'Donor', 7            :foreign_key => 'doador_codigo'
  28. 28. Views no banco?
  29. 29. +200 tabelas c/ média de 15 campos cada
  30. 30. VIEW MAPPING HELL
  31. 31. Três opções:
  32. 32. Conviver com as diferenças
  33. 33. Criar um novo banco e migrar os dados depois
  34. 34. Alterar o nome das tabelas e colunas
  35. 35. Criar base development
  36. 36. RAILS_ENV=production rake db:schema:dump rake db:schema:load
  37. 37. Duas bases para desenvolvimento
  38. 38. 1) development “normal” 2) dump da base de produção (com dados)
  39. 39. Para alterar nomes...
  40. 40.  1 class RenameDonationTableAndColumns < ActiveRecord::Migration  2   def self.up  3     rename_table :doacao, :donations  4     change_table :donations do |t|  5       t.rename :codigo, :id  6       t.rename :doador, :donor_id  7       t.rename :data, :date  8       t.rename :tipo_sanguineo, :blood_type  9       # ... 10     end 11   end 12 13   def self.down 14     raise IrreversibleMigration 15   end 16 end
  41. 41. Redefinir tipos dos campos
  42. 42.  1 class ChangeDonorsColumns < ActiveRecord::Migration  2   def self.up  3     change_table :donors do |t|  4       t.change :document_type, :string, :size => 15  5       t.change :document_number, :string, :size => 15  6     end  7   end  8  9   def self.down 10     raise ActiveRecord::IrreversibleMigration 11   end 12 end
  43. 43. Crie seus próprios métodos para migrations
  44. 44.  1 module ActiveRecord  2   module ConnectionAdapters  3     class PostgreSQLAdapter < AbstractAdapter  4       def change_sn_to_boolean(table_name, column_name)  5         execute %Q{  6           ALTER TABLE #{quote_table_name(table_name)}   7           ALTER COLUMN #{quote_column_name(column_name)}   8           type boolean  9           using (case when #{quote_column_name(column_name)} = 'S' 10           then true else false end) 11         } 12       end 13     end 14   end 15 end
  45. 45. 58 change_sn_to_boolean :donors, :send_sms
  46. 46. chaves estrangeiras
  47. 47. foreign_key_migrations composite_primary_keys
  48. 48. Alterar dados com migrations
  49. 49. Errado
  50. 50.  1 class PopulateAddressPrefixes < ActiveRecord::Migration  2   def self.up  3     Address.all.each do |address|   4       if address.prefix.blank?  5         address.update_attributes(  6           :prefix => AddressPrefix.find_by_code('R')  7         )  8       end  9     end 10   end 11 end
  51. 51. Melhor
  52. 52. 1 class PopulateAddressPrefixes < ActiveRecord::Migration 2   def self.up 3     prefix = AddressPrefix.find_by_code 'R' 4     execute <<-SQL 5       update addresses set prefix_id = E'#{prefix.id}' 6       where prefix_id is null 7     SQL 8   end 9 end
  53. 53. Enumerações no banco
  54. 54. foobar=# select * from education_level;    id     |  description     --------+---------------    1      | Nao Cursou      2      | 1o Incompleto    3      | 1o Completo      4      | 2o Incompleto    5      | 2o Completo      6      | 3o Incompleto  7      | 3o Completo  
  55. 55. 1 donor.education_level.description # => join no banco!
  56. 56. ok, vamos tirar as enumerações do banco...
  57. 57. problema com FK’s (tabelas com enumerações ainda referenciadas)
  58. 58. 1 donor.education_level = 5 # WTF?
  59. 59. enumerate_it github.com/cassiomarques/enumerate_it (jabá mode on)
  60. 60.  1 class EducationLevel < EnumerateIt::Base  2   associate_values(  3     :none                 => ['1', 'Não Cursou']   ,  4     :incomplete_1st_grade => ['2', '1º Grau Incompleto'],  5     :complete_1st_grade   => ['3', '1º Grau Completo'],  6     :incomplete_2nd_grade => ['4', '2º Incompleto'],  7     :complete_2nd_grade   => ['5', '2º Completo'],  8     :incomplete_3rd_grade => ['6', '3º Grau Incompleto'],  9     :complete_3rd_grade   => ['7', '3º Grau Completo'] 10   ) 11 end
  61. 61. 1 class Donor < ActiveRecord::Base 2   has_enumeration_for :education_level, :with => EducationLevel 3 end
  62. 62. 1 donor.education_level = EducationLevel::COMPLETE_3RD_GRADE 2 donor.education_level_humanize # 3º Grau Completo (sem join)
  63. 63. 1 <% form_for @donor do |f| %> 2   <%= f.select :education_level, EducationLevel.to_a %> 3 <% end -%>
  64. 64. schema.rb => banco de testes
  65. 65. ... mas se a sua PK for varchar...
  66. 66. 1 create_table "donors", :id => false, :force => true do |t| 3   t.string  "id", :limit => 6, :null => false 4   # ... 5 end
  67. 67. 1 class CreateAndAssignDonorsIdSequence < ActiveRecord::Migration 2   def self.up 3     suppress_messages do 4       execute 'create sequence donor_id_seq' rescue nil 5       execute "alter table donors alter column id set default nextval('donors_id_seq')" 6     end 7   end 8 end
  68. 68. 1 # spec/spec_helper.rb e/ou features/support/schema_fixes.rb 2 require 'migrations/schema_fixes' 3 4 [FirstMigration, SecondMigration].each { |m| m.migrate :up }
  69. 69. Stored Procedures, functions, etc...
  70. 70. • Dificeis de testar • Side effects • Código procedural • Lógica separada da app
  71. 71. ?
  72. 72. OBRIGADO!

×