Dynamiczne ładowanie plików z formularza w Ruby on Rails

23 marca 2008, 00:38:32

Poziom: 0 | Kategoria: Komputerowo-internetowo, Ruby, Ruby on Rails, Techblog. | 3 komentarze

Wstęp

Wyobraźmy sobie, że chcemy utworzyć bazę ogłoszeń, do których chcemy w łatwy i przyjemny sposób dodawać zdjęcia. W tej notce pokażę jak w dosyć prosty sposób wykorzystać Railsy do stworzenia prostego panelu do zarządzania takimi ogłoszeniami. Stworzymy formularz dodawania ogłoszeń, w którym oprócz możliwości dodawania ogłoszeń umieścimy prosty formularz, który umożliwi załadowanie zdjęcia na serwer i podpięcie go pod dodawane ogłoszenie. Oczywiście udostępnimy możliwość wyboru zdjęć już obecnych na serwerze, dla uproszczenia zakładamy, że zdjęcie może należeć tylko do jednego ogłoszenia.

Wymagania

Wszystko, czego potrzebujemy to:

Zaczynamy

Zacznijmy od stworzenia nowego projektu. Nie będę skupiał się na podstawach, na potrzeby tego opisu wykorzystam domyślną konfigurację railsów z bazą SQLite. W linii poleceń tworzymy nowy projekt:

$ rails demo
$ cd demo

Od razu stworzymy bazę danych, aby potem nie trzeba było do tego wracać:

$ rake db:create

Mając przygotowany projekt, zaopatrzmy się w odpowiednie pluginy. W tym celu odpalamy następujące polecenia:

$ script/plugin install http://svn.techno-weenie.net/projects/plugins/attachment_fu/
$ script/plugin install http://responds-to-parent.googlecode.com/svn/trunk

Środowisko mamy już przygotowane. Pora na utworzenie modeli. Ograniczymy się do najbardziej podstawowych rzeczy. Utworzymy model Advertisement, który będzie miał tytuł oraz opis. Potrzebny będzie również model Photo, który będzie miał atrybuty wymagane przez plugin attachment_fu. Wklepujemy zatem w konsoli:

$ script/generate model Advertisement name:string description:text

W przypadku modelu Photo nie będziemy podawać żadnych parametrów dla generatora:

$ script/generate model Photo

Po wygenerowaniu modeli, zanim zmigrujemy bazę danych, wyedytujemy plik migracji db/migrate/002_create_photos.rb, wypełniając go jak poniżej:

class CreatePhotos < ActiveRecord::Migration
  def self.up
    create_table :photos do |t|
      t.column :parent_id,  :integer
      t.column :content_type, :string
      t.column :filename, :string    
      t.column :thumbnail, :string 
      t.column :size, :integer
      t.column :width, :integer
      t.column :height, :integer
      t.column :advertisement_id, :integer
    end
  end

  def self.down
    drop_table :photos
  end
end

W tej chwili możemy już zmigrować bazę danych

$ rake db:migrate

Konfiguracja modeli

Pora na skonfigurowanie modeli, utworzenie relacji jeden-wiele między ogłoszeniami i zdjęciami oraz przygotowanie modelu Photo do pracy z pluginem attachment_fu (wykorzystanie relacji wiele-wiele pozostawiam jako ćwiczenie). W tym celu edytujemy pliki modeli jak poniżej

# app/models/advertisement.rb
class Advertisement < ActiveRecord::Base
  has_many :photos
end
# app/models/photo.rb
class Photo < ActiveRecord::Base
  belongs_to :advertisement
  has_attachment :content_type => :image,   # zalacznikiem jest obrazek
                 :storage => :file_system,  # trzymamy go w systemie plikow
                 :max_size => 2.megabytes,  # ograniczamy rozmiar do 2 MB
                 :resize_to => '1200x800>', # zmniejszamy plik wejsciowy
                 :thumbnails => {:thumb => '200x200>'} # tworzymy jedna miniature
  validates_as_attachment # walidujemy plik po zaladowaniu na serwer, przed zapisem do bazy danych
  # z powyzsza walidacja mialem problemy pod Windowsem, posiadaczom tego systemu radze poki co zakomentowac
  # powyzsza linijke
end

W tej chwili mamy już skonfigurowaną relację oraz przygotowany model pod umieszczanie na serwerze plików. Pora na następną część

Konfiguracja kontrolerów

Do prawidłowej pracy będziemy potrzebować dwa kontrolery. Jeden z nich, nazwijmy go advertisements będzie odpowiedzialny za zarządzanie ogłoszeniami, drugi — photos — będzie zajmował się zdjęciami. Wykorzystamy mechanizm REST. W tym celu tworzymy kontrolery z linii poleceń:

$ script/generate controller Advertisements index show new edit create update destroy
$ script/generate controller Photos index new edit create destroy

Aby skorzystać z REST musimy jeszcze dodać odpowiednie wpisy w pliku config/routes.rb:

ActionController::Routing::Routes.draw do |map|
  map.resources :advertisements
  map.resources :photos
  
  # ponizsze wpisy pozostawiamy
  map.connect ':controller/:action/:id'
  map.connect ':controller/:action/:id.:format'
end

Nadeszła pora na zaimplementowanie akcji potrzebnych do zarządzania naszymi zasobami. Są to standardowe akcje, pokażę zatem jedynie zawartość kontrolerów:

# app/controllers/advertisements_controller.rb
class AdvertisementsController < ApplicationController

  def index
    @advertisements = Advertisement.find(:all)
    respond_to do |format|
      format.html {}
      format.xml  { render :xml => @advertisements.to_xml }
    end
  end

  def show
    @advertisement = Advertisement.find(params[:id])
    respond_to do |format|
      format.html {}
      format.xml  { render :xml => @advertisement.to_xml }
    end
  end

  def new
    @advertisement = Advertisement.new
  end

  def edit
    @advertisement = Advertisement.find(params[:id])
  end

  def create
    @advertisement = Advertisement.new(params[:advertisement])
    respond_to do |format|
      if @advertisement.save
        format.html { redirect_to advertisement_url(@advertisement) }
        format.xml  { render :xml => @advertisement.to_xml }
      else
        format.html { render :action => 'new'}
        format.xml  { render :xml => @advertisement.errors.to_xml }
      end
    end
  end

  def update
    @advertisement = Advertisement.find(params[:id])
    respond_to do |format|
      if @advertisement.update_attributes(params[:advertisement])
        format.html { redirect_to advertisement_url(@advertisement) }
        format.xml  { render :xml => @advertisement.to_xml }
      else
        format.html { redirect_to edit_advertisement_url(@advertisement) }
        format.xml  { render :xml => @advertisement.errors.to_xml }
      end
    end
  end

  def destroy
    @advertisement = Advertisement.find(params[:id])
    respond_to do |format|
      if @advertisement.destroy
        format.html { redirect_to advertisements_url }
        format.xml  { head :ok }
      else
        format.html { redirect_to advertisement_url(@advertisement) }
        format.xml  { redirect_to @advertisement.errors.to_xml }
      end
    end
  end
end
# app/controllers/photos_controller.rb
class PhotosController < ApplicationController

  def index
    @photos = Photo.find(:all, :conditions => 'parent_id IS NULL') # wybieramy tylko zdjecia w oryginalnych rozmiarach
    respond_to do |format|
      format.html {}
      format.xml  { render :xml => @photos.to_xml }
    end
  end

  def new
    @photo = Photo.new
    @advertisements = Advertisement.find(:all)
  end

  def create
    @photo = Photo.new(params[:photo])
    respond_to do |format|
      if @photo.save
        format.html { redirect_to photos_url }
        format.xml  { render :xml => @photo.to_xml }
      else
        format.html { render :action => 'new' }
        format.xml  { render :xml => @photo.errors.to_xml }
      end
    end
  end

  def destroy
    @photo = Photo.find(params[:id])
    respond_to do |format|
      if @photo.destroy
        flash[:notice] = 'Zdjęcie zostało usunięte'
        format.html { redirect_to photos_url }
        format.xml  { head :ok }
      else
        flash[:error] = 'Wystąpił błąd podczas usuwania zdjęcia'
        format.html { redirect_to photos_url }
        format.xml  { render :xml => @photo.errors.to_xml }
      end
    end
  end
end

Warstwa modeli i kontrolerów jest już prawie gotowa. Pozostało dodanie widoków, za pomocą których będziemy mogli odczuć, czy to, co stworzyliśmy w ogóle działa.

Pora na widoki

W warstwie widoków ograniczę się do najprostrzego wyświetlania elementów, bez żadnych upiększaczy (przynajmniej na razie). Poniżej wklejam najprostsze możliwe widoki, które nie oferują nic poza podstawowymi opcjami. Widoki dla kontrolera advertisements_controller:

# app/views/advertisements/index.html.erb
<h1>Ogłoszenia</h1>
<ul>
<% for advertisement in @advertisements %>
  <li>
    <p><%= link_to h(advertisement.name), advertisement_url(advertisement) %></p>
    <p><%= truncate(h(advertisement.description), 20) %></p>
    <p><%= link_to 'Edytuj', edit_advertisement_url(advertisement) %></p>
    <p><%= link_to 'Usuń', advertisement_url(advertisement), :method => :delete, :confirm => 'Czy jesteś pewien?' %></p>
  </li>
<% end %>
</ul>
<p><%= link_to 'Dodaj nowe', new_advertisement_url %></p>

# app/views/advertisements/new.html.erb
<h1>Nowe ogłoszenie</h1>
<%= error_messages_for :advertisement %>

<% form_for @advertisement do |f| %>
  <p><label for="advertisement_name">Nazwa:</label></p>
  <p><%= f.text_field :name %></p>
  <p><label for="advertisement_description">Opis:</label></p>
  <p><%= f.text_area :description, :cols => 60, :rows => 20 %></p>
  <p><%= submit_tag 'Zapisz', :disable_with => "Proszę czekać..." %></p>
<% end %>
<p><%= link_to 'Powrót', advertisements_url %></p>

# app/views/advertisements/show.html.erb
<h1><%=h @advertisement.name %></h1>
<p><%=h @advertisement.description %></p>
<p><%= link_to 'Edytuj', edit_advertisement_url(@advertisement) %></p>
<p><%= link_to 'Usuń', advertisement_url(@advertisement), :method => :delete, :confirm => 'Czy jesteś pewien?' %></p>
<p><%= link_to 'Powrót', advertisements_url %></p>
<hr/>
<% unless @advertisement.photos.empty? %>
<p>Zdjęcia:</p>
<ul>
<% for photo in @advertisement.photos %>
  <li><%= link_to image_tag(photo.public_filename(:thumb)), photo.public_filename %></li>
<% end %>
</ul>
<% end %>

# app/views/advertisements/edit.html.erb
<h1>Edycja ogłoszenia</h1>
<%= error_messages_for :advertisement %>

<% form_for @advertisement do |f| %>
  <p><label for="advertisement_name">Nazwa:</label></p>
  <p><%= f.text_field :name %></p>
  <p><label for="advertisement_description">Opis:</label></p>
  <p><%= f.text_area :description, :cols => 60, :rows => 20 %></p>
  <p><%= submit_tag 'Zapisz', :disable_with => "Proszę czekać..." %></p>
<% end %>
<p><%= link_to 'Powrót', advertisements_url %></p>

Widoki dla kontrolera photos_controller:

# app/views/photos/index.html.erb
<h1>Zdjęcia</h1>
<ul>
<% for photo in @photos %>
  <li>
    <p><%= link_to image_tag(photo.public_filename(:thumb)), photo.public_filename %></p>
    <p><%= link_to 'Usuń', photo_url(photo), :method => :delete, :confirm => 'Czy jesteś pewien?' %></p>
  </li>
<% end %>
</ul>
<p><%= link_to 'Dodaj nowe', new_photo_url %></p>

# app/views/photos/new.html.erb
<h1>Nowe zdjęcie</h1>
<%= error_messages_for :photo %>

<% form_for @photo, :html => {:multipart => true} do |f| %>
  <p><label for="photo_uploaded_data">Plik ze zdjęciem:</label></p>
  <p><%= f.file_field :uploaded_data %></p>
  <p><label for="photo_advertisement_id">Ogłoszenie:</label></p>
  <p><%= f.collection_select :advertisement_id, @advertisements, :id, :name, {:prompt => true} %></p>
  <p><%= f.submit "Załaduj" %></p>
<% end %>
<p><%= link_to 'Powrót', photos_url %></p>

W tej chwili możemy już zobaczyć, jak to wszystko działa. Wystarczy odpalić serwer:

$ script/server

Po odpaleniu serwera wpisując w adresie przeglądarki adres http://localhost:3000/advertisements możemy administrować ogłoszeniami, natomiast pod adresem http://localhost:3000/photos możemy zarządzać zdjęciami.

No dobrze, ale na początku pisałem o dynamicznym ładowaniu zdjęć podczas pisania ogłoszenia oraz o możliwości wybierania zdjęć. Na pierwszy rzut pójdzie możliwość wyboru zdjęć spośród załadowanych i nie dołączonych do innych ogłoszeń

Wybór zdjęć przy dodawaniu ogłoszenia

Aby umożliwić dołączanie zdjęć podczas dodawania czy edycji ogłoszenia, skorzystamy z możliwości, jakie dostarcza metoda has_many w modelu ogłoszeń, a dokładnie z settera photo_ids. W tym celu należy dodać kilka rzeczy do kontrolera ogłoszeń i zmodyfikować odpowiednio widoki.

W metodach new oraz edit dodajemy następującą linijkę (oczywiście przeczymy tutaj zasadzie DRY, jednak chodzi tutaj o jak najprostsze pokazanie, o co chodzi):

@photos = Photo.find(:all, :conditions => 'parent_id IS NULL')

W tej chwili możemy już pokazać w formularzu dodawania/edycji ogłoszeń dostępne zdjęcia wraz z check boxami do ich zaznaczania. W tym celu dodajemy do formularza w plikach app/views/advertisements/{new,edit} gdzieś w bloku form_for, najlepiej po text area:

<div id="photos">
<% for photo in @photos %>
  <p><%= image_tag photo.public_filename(:thumb) %></p>
  <p><%= check_box_tag 'advertisement[photo_ids][]', photo.id, @advertisement.photos.include?(photo) %>
<% end %>
</div>

Od tej pory będziemy mogli zaznaczać zdjęcia, które będziemy chcieli dodać do ogłoszenia (pamiętajmy o tym, że mamy relację jeden-wiele, więc jeżeli tworząc lub edytując jakieś ogłoszenie zaznaczymy zdjęcie wcześniej zajęte, zostanie ono przypisane tylko do nowo tworzonego/edytowanego ogłoszenia — aby to zmienić należy zamienić relację z jeden-wiele na wiele-wiele). Zostaje tylko jeden problem. Kiedy będziemy chcieli usunąć z jakiegoś ogłoszenia wszystkie zdjęcia i odznaczymy wszystkie dostępne w formularzu, po kliknięciu 'Dodaj' lub 'Edytuj' nic się nie stanie. Dzieje się tak dlatego, że w tym przypadku Railsy nie przekażą do kontrolera zawartości parametru advertisement[photo_ids][]. Jednak jest bardzo prosty sposób, by to zmienić. Do kontrolera advertisements_controller w akcji update dodajemy na początku następującą linijkę:

params[:advertisement][:photo_ids] ||= []

Spowoduje ona, że w przypadku, gdy odznaczymy wszystkie zdjęcia podczas edycji ogłoszenia, zostaną rzeczywiście usunięte z relacji.

Dynamiczne dodawanie plików + a pinch of AJAX

Do zrobienia została nam jeszcze jedna część — dynamiczne ładowanie zdjęcia podczas dodawania lub edycji ogłoszenia i uaktualnianie formularza nowym zdjęciem. Nie da się tego zrobić przy pomocy samego AJAX-a, ponieważ ze względów bezpieczeństwa AJAX nie pozwala na przesyłanie lokalnych plików. Istnieje jednak prosty sposób, aby tę niedogodność ominąć. W tym celu utworzymy na stronie niewodoczny iframe i ustawimy na niego atrybut target formularza ładującego zdjęcie. Następnie, wykorzystując plugin responds_to_parent, uaktualnimy listę zdjęć w formularzu dotyczącym ogłoszenia.

Na początku zdefiniujmy sobie prosty layout oraz dołączmy potrzebne skrypty JavaScript. W tym celu tworzymy plik app/views/layouts/application.erb o nastepującej zawartości:

<html>
  <head>
    <meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
    <title>Demonstracja dynamicznego ładowania plików, &copy; 2008 GhandaL</title>
    <%= javascript_include_tag :defaults %>
  </head>
  <body>
    <% if flash[:notice] %>
      <div style="border: 1px green"><%= flash[:notice] %></div>
    <% end %>
    <% if flash[:error] %>
      <div style="border: 1px red"><%= flash[:error] %></div>
    <% end %>
    <%= yield :layout %>
  </body>
</html>

W celu ładowania pliku posłużymy się fragmentami kodu widoków, tzw. partials. Zanim jednak dopracujemy widoki, zmodyfikujemy odpowiednio kontroler photos_controller, aby uaktualniał stronę, która wywołała akcję create. W tym celu modyfikujemy akcję create w pliku app/controllers/photos_controller.rb tak, by wyglądała jak poniżej:

  def create
    @photo = Photo.new(params[:photo])
    respond_to do |format|
      if @photo.save
        format.html { redirect_to photos_url }
        format.xml  { render :xml => @photo.to_xml }
        format.js do
          responds_to_parent do
            render :update do |page|
              page.insert_html :bottom, "photos", :partial => 'photo', :object => @photo
            end
          end
        end
      else
        format.html { render :action => 'new' }
        format.xml  { render :xml => @photo.errors.to_xml }
        format.js do
          responds_to_parent do
            render :update do |page|
              # jakis kod uaktualniajacy, np. bledem, strone
            end
          end
        end
      end
    end
  end

Teraz możemy się zająć stworzeniem odpowiednich partials. Po pierwsze stworzymy kod, który wyświetli nowo załadowane zdjęcie. Tworzymy plik app/views/photos/_photo.erb o zawartości:

<p><%= image_tag photo.public_filename(:thumb) %></p>
<p><%= check_box_tag 'advertisement[photo_ids][]', photo.id, true %></p>

Do zrobienia została nam jeszcze jedynie modyfikacja widoków akcji new i edit kontrolera advertisements_controller. Gdzieś na końcu plików app/views/advertisements/{new,edit} dopisujemy następujący kod:

<iframe name="dummy" id="dummy" style="width:1px;height:1px;border:0"></iframe>
<% form_for :photo, :url => {:controller => :photos, :action => :create, :format => 'js'}, :html => {:target => 'dummy', :multipart => true} do |f| %>
  <p><label for="photo_uploaded_data">Plik ze zdjęciem:</label></p>
  <p><%= f.file_field :uploaded_data %></p>
  <p><%= f.submit 'Dodaj' %></p>
<% end %>

I to już wszystko. Od tej pory dodając i edytując ogłoszenia można już ładować zdjęcia bez potrzeby przeładowywania strony.

Konkluzja

Oczywiście powyższe rozwiązanie ma pewne wady. Przede wszystkim relacja jeden-wiele jest bardzo prymitywna w tym przypadku i zabrania dołączania jednego zdjęcia do kilku ogłoszeń. Jednak modyfikacja tego nie jest trudna, nie o tym jest również ta notka. Kod demonstracyjnej aplikacji możesz ściągnąć mojej strony. Mam nadzieję, że powyższy opis komuś się przyda. Jeśli zauważyłeś jakiś błąd lub chciałbyś wyrazić opinię o moim rozwiązaniu, zapraszam do komentowania.

Tagi:

Mój pierwszy VPS

11 marca 2008, 14:43:37

Poziom: 0 | Kategoria: Komputerowo-internetowo, Ruby, Ruby on Rails, Techblog, Web Design. | 10 komentarzy

Ponieważ zaczęły mnie denerwować timeouty w przekazywaniu żądań z Apache do mongreli na Rootnode zacząłem rozglądać się za czymś lepszym. Pod uwagę brałem VPS-y w cenie do ok. 60zł za miesiąc. Postanowiłem w najbliższych miesiącach przetestować kilka z nich. Na pierwszy ogień poszedł ProVPS. Za tym rozwiązaniem przemawia lokalizacja serwerów w Polsce, w Gdańsku, a co za tym idzie niskie pingi, w granicach 35-45ms. Jednak na tym plusy się kończą (porównując do reszty ofert branych pod uwagę). Za 49 zł netto miesięcznie dostajemy gwarantowane 128 MB RAM-u, rozszerzane do 512 MB, 5 GB powierzchni dyskowej, 25 GB transferu miesięcznego oraz 1 adres IP. Na tle konkurencji trochę blado.

Wczoraj aktywowano mi VPS i spędziłem kilka ładnych godzin na konfigurowaniu poszczególnych części systemu. Postanowiłem uruchomić minimum usług, głównie po to, by zmieścić się w drakońskich limitach pamięci. Niestety musiałem zrezygnować z mySQL, ponieważ nie znalazłem sposobu na ograniczenie konsumpcji pamięci do akceptowalnego na tym serwerze poziomu — po uruchomieniu mysqld konsumował ok. 100 MB pamięci operacyjnej pomimo przeróżnych konfiguracji znalezionych w sieci i w dokumentacji mySQL. Zaminiłem mySQL na PostgreSQL i tutaj małe zaskoczenie — domyślna instalacja postgresa zużywa ponad połowę mniej pamięci niż mySQL!

Gdy baza danych była już gotowa, nadszedł czas na serwer WWW. Wybór oczywiście padł na nginx, o wiele lżejszy od Apache'a serwer plus load balancer. Na VPS-ie będą uruchamiane jedynie statyczne pliki oraz aplikacje w Railsach i Merbie (w liczbie max. 3 — patrz pamięć). Z konfiguracją nginxa nie było żadnych problemów, świetną konfigurację dla aplikacji railsowych w obsługą cache przygotował Ezra Zygmuntowicz. Konfiguracja ta dostępna jest na jego stronie.

Następną rzeczą do zrobienia była kompilacja Ruby, Rubygems, ImageMagick oraz instalacja Subversion. Oczywiście po drodze były mniejsze komplikacje — a to zabrakło biblioteki zlib, a to nie zainstalowałem libsqlite3-dev. W każdym razie po mniej więcej 5 godzinach miałem już działającego Postgresa, nginxa, SVN, Ruby+Rails i całą resztę potrzebnych do życia aplikacji.

Nadszedł czas na sprawdzenie, czy praca nie poszła na marne. Zgrałem pisaną ostatnio aplikację, zmigrowałem bazę, ustawiłem dwie instancje serwera thin i uruchomiłem je. Et voilà! Wszystko ruszyło i działa jak należy i, co najważniejsze, naprawdę dużo szybciej niż na Rootnode, gdzie aplikacja stała poprzednio.

Nieprzespana noc z konfiguracją serwera za mną, ale za miesiąc prawdopodobnie czeka mnie następna, ponieważ, jak pisałem na wstępie, mam w planie przetestowanie kilku dostawców, którzy mają mniejszy miesięczny abonament i gwarantują 2x więcej pamięci, co dla mnie jest rzeczą ważną. Zastanawiam się, czy sensowne będzie kupowanie VPS-a w USA. Na mojej liście jest Slicehost oraz SilverRack z całkiem niezłą ofertą za niewielkie pieniądze. Z drugiej strony za zachodnią granicą mamy Host Europe, również z ciekawą ofertą i niskimi pingami.

Pomimo dużo większych nakładów czasowych myślę, że nie wrócę już do dzielonego hostingu. VPS i serwer dedykowany daje tę wolność, jakiej nie ma na żadnym dzielonym hostingu (Rootnode jest bardzo liberalne, ale, np. nie mogłem doprosić się o aktualizację Ruby do wersji 1.8.6, ponieważ ma "za dużo zależności"). Pamiętać trzeba oczywiście o tym, że samemu trzeba dbać o stabliność i bezpieczeństwo danych na takim serwerze, ale to z kolei wymusza pewną pracę i naukę, która później nie zginie.

PS. Jakby ktoś z czytających miał wiedzę na temat tego, jak ograniczyć zużycie pamięci przez mySQL, proszę o kontakt.

Tagi:

Ebb — szybciej, coraz szybciej!

05 marca 2008, 09:37:26

Poziom: 0 | Kategoria: Komputerowo-internetowo, Ruby, Techblog. | 4 komentarze

Na horyzoncie pojawił się nowy gracz na rynku serwerów do obsługi frameworków w języku Ruby i Python (w przyszłości) — jest nim Ebb. Serwer ten bije konkurentów na głowę, przynajmniej w szybkości i ilości requestów na sekundę. Ja zauważyłem, że zużywa troche więcej pamięci niż np. Mongrel. W swojej małej aplikacji używałem już Mongrela oraz młodszego Thina — nadszedł czas na przetestowanie Ebb. Na pierwszy rzut oka rzeczywiście wzrost wydajności widoczny jest niemal natychmiast.

Ebb jest na razie młodym serwerem i brak mu dokumentacji, a przynajmniej ja nie stwierdziłem takowej. Jednak aby odpalić aplikację w Railsach wystarczy wykonać kilka prostych poleceń:

$ gem install ebb

Następnie przejść do katalogu aplikacji Railsowej i odpalić

$ ebb_rails start

Pełna lista opcji, jakie oferuje Ebb jest następująca:

Usage: ebb_rails [options] start|stop

Server options:
    -p, --port PORT        use PORT (default: 3000)
    -e, --env ENV          Rails environment 
                                     (default: development)
    -c, --chdir PATH       Rails root dir 
                                     (default: current dir)
    -d, --daemonize        Daemonize
    -l, --log-file FILE    File to redirect output
    -P, --pid-file FILE    File to store PID
    -t, --timeout SEC      Request or command timeout in sec
                                     (default: 60)

Common options:
    -h, --help             Show this message
    -v, --version          Show version

W przypadku uruchamiania jako demon, konieczne jest podanie ścieżki do pliku z PID procesu.

Tagi:

Wcześniejsze wpisy Nowsze wpisy