Przegląd po zmianach w Rails 2.0

Wysłane przez Marek Tenus (~marcus) dnia 04.11.2007

Jak wiecie prace nad Rails 2.0 wciąż trwają. Zanim jednak pojawi się nowa wersja frameworka powinniśmy przyjrzeć się bliżej zmianom jakie w nim nastąpią w sosunku do wersji 1.2.X. Pewien czas temu pojawił się na weblogu serwisu rubyonrails.org przegląd po zmianach jakie wprowadzono do RoR. Autorzy zastrzegają sobie jadnak, że niektóre z opisanych elementów mogą ulec zmianie, lecz mogą to być najwyżej jakieś pomniejsze fixy lub ulepszenia.

Action Pack: Resources

Z pewnością większość z was używa Resources zwłaszcza pomocnych przy tworzeniu RESTowych API. Zmiany:
  • Usunięto średnik dla metod zdefiniowanych w kontrolerze przez developera i zastąpiono go slashem
  • Przed:

    /people/1;edit 
    

    Po

    /people/1/edit 
    
  • Dodano możliwość namespacesowania deklarowanych zasobów
  • map.namespace(:admin) do |admin|
        admin.resources :products,
       :collection => { :inventory => :get },
       :member     => { :duplicate => :post },
       :has_many   => [ :tags, :images, :variants ]
    end
    

    Tak zdefiniowany zasób stworzy nam następująco nazwane marszruty (ang. routes)

    inventory_admin_products_url
    admin_product_tags_url
    

Poza tym dodano nowe zadanie do rake'a "routes”, dzięki któremu możemy listować wszystkie nazwane marszruty zdefiniowane w routes.rb.

rake routes

  • Mapowanie jednego zasobu jako wielo-kontekstowego (czyli wiele różnie zdefiniowanych zasobów odnoszących się do tego samego kontrolera)
  • # /avatars/45 => AvatarsController#show
    map.resources :avatars
    
    # /people/5/avatar => AvatarsController#show 
    map.resources :people, :has_one => :avatar
    

    Action Pack: MultiView

    O zmianach w tym pakiecie możemy powiedzieć, że jest to dość ciekawy sposób uporządkowania dotychczasowych możliwośći tego pakietu. Pliki w View mają nową konwencję nazewnictwa:

    action.format.renderer
    

    co oznacza, że dla akcji index będziemy mogli stworzyć pliki o formatach:

    //zamiast index.rxml
    index.atom.builder
    index.html.erb
    index.csv.erb
    index.html.haml
    ...
    

    należy dodać, że wspólnym dla wszystkich formatów templatem jest plik index.erb. Dzięki wykorzystaniu respond_to możemy dość łatwo utworzyć template'y o danym formacie.

    class UserController < ApplicationController
        def index
          respond_to do |format|
            format.html   # renders index.html.erb
            format.csv    # renders index.csv.erb
          end
        end
    

    Poza tym możemy sami zadeklarować swój 'fake'owy (zafałszowany) typ mime dla wewnętrznego routingu. Dzięki czemu nie będzie wyrenderowany domyślny format template'a dla danego typu mime (dla iphone jest to haml, a poniżej zmienimy go na html).

    # rejestracja przez siebie zdefiniowanego 'fake'owego typu mime 
    # w config/initializers/mime_types.rb
      Mime.register_alias "text/html", :iphone
    
      class ApplicationController < ActionController::Base
        before_filter :adjust_format_for_iphone
    
        private
          def adjust_format_for_iphone
            if request.env["HTTP_USER_AGENT"] 
               && request.env["HTTP_USER_AGENT"][/(iPhone|iPod)/]
              request.format = :iphone
            end
          end
      end
    

    i możemy już użyć tego typu w naszym kodzie:

    respond_to do |format|
      format.iphone # index.iphone.erb # zamiast index.iphone.haml
    end
    

    bądź możemy zadeklarować swój własny typ mime:

    # rejestracja przez siebie zdefiniowanego typu mime 
    # w config/initializers/mime_types.rb
      Mime.register_alias "text/html", :yhml
    

    Action Pack: Record identification

    Poniżej przykład, który wam wszystko wyjaśni.

    redirect_to(person)
    link_to(person.name, person)
    form_for(person)
    

    Gdzie person jest tożsame z objektem Person zmapowanym w routes i do metody person_url

    Action Pack: HTTP Loving

    Nowy pakiet w Rails umożliwiający wygodną obsługę uwierzytelniania komunikacji poprzez HTTP. Poniższy przykład powinien powiedzieć wam o tym pakiecie wystarcająco dużo.

    class UserController < ApplicationController
        USER_NAME, PASSWORD = "marek", "hasło_marka" 
    
        before_filter :authenticate, :except => [ :index ]
    
        def index
          render :text => "Strefa dostępna dla każdego" 
        end
    
        def edit
          render :text => "Znasz hasło więc masz tu dostęp" 
        end
    
        private
          def authenticate
            authenticate_or_request_with_http_basic do |user_name, password| 
              user_name == USER_NAME && password == PASSWORD
            end
          end
    end
    

    Też macie wrażenie, że zaskakująco proste i bezpieczne? Więcej możliwości możecie poznać zaglądając do ActionController::HttpAuthentication. Warto wspomnieć też o obsłudze assetów. Czyli możliwości zaciągania css, js, grafiki z innych hostów (co znacznie przyśpiesza ich ściąganie a tym samym ładownie w przeglądarce).

    ActionController::Base.asset_host =assets%d.ruby-on-rails.pl”
    

    Dzięki powyższej deklaracji pliki graficzne naprzykład przy ładowaniu z użyciem image_tag (tzw. assetowych callerów) będą zaciągane z kilku źródeł/aliasów (pomimo, że pliki znajdują się nawet w tym samym katalogu). Obsuga assetów została wprowadzona zapewne po wynikach badań przeprowadzonych przez Yahoo. Szybkość ładowania takich aplikacji zwiększyła się prawie dwukrotnie. Proponuję przeczytać więcej tutaj. Zastanawiące jest tylko, dlaczego tworzone są 4 assety pomimo, że badania Yahoo Team'u pokazują, że najbardziej optymalne wydaje się użycie tylko dwóch. Widocznie team Rails ma większą wiedzę na ten temat..

    Action Pack: Security

    W tym pakiecie głównie zwiększono zabezpieczenia przed atakami CRSF i XSS. Zabezpieczniem przed CRSF ma być dodawany specjalny token do formularzy i Ajaxowych zapytań. Przed atakami XSS będzie nas chroniła ulepszona wersja metody TextHelper#sanitize. Do tej pory istniała tylko black lista dla tej metody, przez co jej użycie często było niemożliwe, dlatego też wprowadzono white list'e, w której można definiować, które tagi są dopuszczalne.

    Action Pack: Exception handling

    Zmiana w tym pakiecie bardzo mi się spodobała. Będzie możliwa prosta obsługa danego wyjątku pd danym typie z poziomu metody danego kontrolera. Nie będziecie już musieli używać rescue_action_in_public. Wystarczy skorzystać z makra rescue_from.

    class UserController < ApplicationController
        rescue_from User::NotAuthorized, :with => :deny_access
    
        protected
          def deny_access
            ...
          end
    end
    

    Action Pack: Miscellaneous

    Rozbudowano w tym pakiecie możliwości obsług formatu Atom (AtomFeedHelper). Jest ona jeszcze prostsza i wygodniejsza.

    # index.atom.builder:
      atom_feed do |feed|
        feed.title("Mój blog!")
        feed.updated((@posts.first.created_at))
        for post in @posts
          feed.entry(post) do |entry|
            entry.title(post.title)
            entry.content(post.body, :type => 'html')
    
            entry.author do |author|
              author.name("marcus")
            end
          end
        end
     end
    

    Poza tym usunięto in_place_editor i autocomplete_for i przeniesiono je do odpowiednich pluginów.

    Active Record: Performance

    Tutaj dość mała zmiana, choć czasami może się okazać przydatną w naszym projekcie. Dodano bardzo prosty Query Cache dla zapytań SQL. Nie jest to coś niesamowitego, ale zawsze lepsze coś niż nic..

    Active Record: Sexy migrations

    Zmieniono konwencję kodowania migracji (czy są one sexy sami oceńcie ;) )

    # Obecnie byśmy napisali
    create_table :people do |t|
      t.column, "account_id",  :integer
      t.column, "first_name",  :string, :null => false
      t.column, "last_name",   :string, :null => false
      t.column, "description", :text
      t.column, "created_at",  :datetime
      t.column, "updated_at",  :datetime
    end
    # a już niedługo będziemy pisali
    create_table :people do |t|
      t.integer :account_id
      t.string  :first_name, :last_name, :null => false
      t.text    :description
      t.timestamps
    end
    

    Active Record

    XML zamiast JSON'a

    person = Person.new
    # deserializacja z XML
    person.from_xml(“Marek“)
    # serializacja do JSON'a z ta samą składnią, jaka występuję przy serializacji do XML 
    person.to_json
    

    Pozbycie się pewnego balastu

    Usunięto acts_as_XYZ przenosząc je do odpowiedniego pluginu. Jeśli więc będziemy chcieli użyć act_as_list musimy przedtem zainstalować potrzebny nam plugin.

    script/plugin install acts_as_list

    Pozostawiono jedynie adaptery dla baz danych MySQL, SQLite i PostgreSQL. Jeśli będziecie chcieli użyć naprzykład bazy Oracle w swoim projekcie, będziecie musieli doinstalować sobie odpowiedni adapter (activerecord-nazwa_bazy-adapter):

    gem install activerecord-oracle-adapter

    with_scope jako metoda protected

    ActiveResource zamiast ActionWebService

    Trochę mnie ta zmiana martwi (osobiście wolę, aby SOAP i REST współistniały w Railsach razem). Wogóle nie podoba mi się tutaj podejście teamu Railsowego do SOAP. Zawsze jednak będziemy mogli użyć pluginu :) :

    gem install actionwebservice

    "Wrócił" debugger

    Whoaa.. W końcu coś ciekawego. W pełni funkcjonalny i "prawdziwy" debugger jak na możliwości "aplikacji przeglądarkowych". Możemy ustawiać breakpoint'y, śledzić każdą linię kodu.. a wystarczy jedynie zainstalować plugin ruby-debug.

    gem install ruby-debug

    Lepsza konfigurowalność aplikacji

    Pojawił się config/initializers, gdzie możemy umieszczać nasze pliki konfiguracyjne (będą one ładowane automtycznie). Nie będziemy musieli już wszystkie nasze ustawienia umieszczać w config/environment.rb lub tworzyć własnych struktur

    Prostsze porządkowanie pluginów

    Zmiana z pewnością przydatna przy plugnowości Rails 2.0. Będziemy mogli sobie definiować kolejność ładowania pluginów (w config.plugins).

    # najpierw załaduj plugin act_as_list a potem pozostałe
    config.plugins = [ :acts_as_list, :all ]
    

    Podsumowanie

    Z pewnością możemy stwierdzić, że Rails 2.0 bardziej zrobi krok w przód niż do tyłu. Co do niektórych zmian możemy mieć wątpliwości, lecz dopiero przy pisania większego projektu będziemy mogli w pełni je sprawdzić i ocenić. Osobiście uważam, że odchodzenie od SOAP w tak znaczący sposób nie dokońca mi się podoba. Z pewnością powyższe zmiany powinny wpłynąć na bezpieczeństwo i wydajności aplikacji oraz poprawić jakość naszej pracy. Pluginowość (nie modułowość!!) Railsów uważam za strzał w dziesiątkę no i ten debugger.. . Ogólnie mogę stwierdzić, że zmiany są na 4+. Prawdopodobnie prace nad Rails 2.0 skończą się w marcu/kwietniu 2008 roku.

    Warto przeczytać

    Ocena zmian w Rails 2.0
    Preparing for Rails 2.0