Migracje w Ruby on Rails

napisane przez wiktor, 23:37 05-31-2007

Ostatnio część czasu w pracy poświęcam na rozwijaniu projektu RubyTime. Jest to open-source’owa aplikacja napisana w Ruby on Rails do zliczania czasu pracy w małych firmach (ang. time tracker albo time-sheets) i następnego rozliczania pracowników i wystawiania faktur na tej podstawie klientom. Przy dodawaniu nowej funkcjonalności pierwszy raz skorzystałem z mechanizmu migracji, które oferuje Ruby on Rails. Muszę przyznać, że jestem pod wrażeniem prostoty tego rozwiązania.

Przede wszystkim zaskoczyło mnie inne podejście do projektowania baz danych. Oczywiście zastosowane podejście jest pochodną częstego stosowania w rozwijaniu aplikacji RoR metodyk lekkich/zwinnych (XP, Scrum). W nich z założenia przyjmuje się omylność przy projektowaniu, a krótkie iteracje służą do poprawiania/refaktoryzacji aplikacji. Dobre projektowanie baz danych nigdy nie było łatwym zajęciem, a pomyłki były częste. Nie raz zdarzało mi się, że zaprojektowana BD musiała zostać trochę zmieniona ze względu na zbędą redundancję albo efektywność albo czegoś zabrakło.

Ruby on Rails poprzez dostarczenie mechanizmu migracji pozwala na szybkie rozwijanie aplikacji i następnie poprawianie usterek. Trzeba podkreślić, że dodawanie nowej funkcjonalności staje się równie miłe i przyjemne.

Zobaczmy to na przykładzie :).

Powiedzmy, że mamy już istniejącą aplikację. Naglę się zorientowaliśmy, że zapomnieliśmy wprowadzić systemu logowania dla użytkowników :). Czyli mamy model User, a w bazie danych ponad 1000 rekordów. Coś trzeba zrobić. Zacznijmy od wygenerowania nowej migracji:

$ ruby script/generate migration AddLoginAndPassword
exists db/migrate
create db/migrate/002_add_login_and_password.rb

Teraz zabierzmy sie do edytowania pliku 002_add_login_and_password.rb.

#
# Migracja zmienia model użytkownika i dodaje do niego 2 kolumny:
#   - login (domyślna wartość to pierwsze słowo nazwy użytkownika)
#   - password (domyślna wartość to login użytkownika)
#
class AddLoginAndPassword < ActiveRecord::Migration
    def self.up
        add_column :users, :login, :string, :null => false
        add_column :users, :password, :string, :null => false
        User.find(:all).each { |user|
            user.login = user.name.scan(/\w+/)[0].strip.downcase
            user.password = Digest::SHA1.hexdigest(user.user)
            user.save
        }
    end

    def self.down
        remove_column :users, :login
        remove_column :users, :password
    end
end

Powyższy kod jest bardzo czytelny i widać od razu, co on robi. Wartym podkreślenia jest fakt, że migracje pozwalają nie tylko zmieniać strukturę bazy danych, ale także wypełniać ją jakimiś danymi. Jest to bardzo przydatne, gdy wprowadzamy nową funkcjonalność. Teraz zostaje nam tylko dodać do modelu więzy spójności i odpalić:

rake db:migrate VERSION=2

Voilà! :)

Komentarzy (3)

  1. Radarek napisał(a):

    Migracje są świetne, ale dlatego, że praktycznie wszystko w Railsach jest przemyślane. Sam w jednym z projektów w php przeportowałem ten mechanizm i zmiany w bazie były wykonywane za pomocą migracji :).

  2. rowery napisał(a):

    Migracje są OK.Polecam wszystkim.
    Pozdrawiam autora.

  3. Marek Kirejczyk napisał(a):

    Istnieje przy takim migrownaniu pewne niebezpieczeństwo. Zauważmy, że używasz tu modelu User i jego metody find. W niektórych przypadkach może zaistnieć pokusa by użyć innych metod modelu, ale w zdefinowanych przez programistę. Wyobraźmy sobie co się stanie z taką migracją po zmianie nazwy takiej metody...
    Migracje przestaną działać, ponieważ model na którym była oparta stara migracja już się zmienił.
    Dlatego stosuje się takie rozwiązanie:

    RUBY:
    1. class AddLoginAndPassword <ActiveRecord::Migration
    2.  class User <ActiveRecord::Base
    3.  
    4.  end
    5.  
    6.  ....
    7. end

    Pozwala to na używanie w migracji tylko metod zdefiniowanych przez ActiveRecord i zabezpiecza przed przyszłymi błędami.

Zostaw komentarz

Możesz używać znaczników do formatowania kodu takich jak: <b>...</b>, <code>...</code> lub dla konkretnych języków programowania: [java]...[/java], [ruby]...[/ruby] itd.


Wiktor Gworek Nazywam się Wiktor Gworek i jestem gospodarzem tego bloga.
Przeczytaj więcej o mnie »