jarorcon sp
DESCRIPTION
Jarorcon company introducing Ruby on RailsTRANSCRIPT
Mirosław WołoszynPrzemysław Kowalczyk
Jarorcon Sp. z o.o.
ul. Wałbrzyska 12
52-314 Wrocław
email: [email protected]
www.susuh.de
Użyte technologie: Ruby on Rails, Ajax, JavaScrip, XML, MySql, Apache, Mongrel, Memcache, Ferret, GetText, RCov, Test Driven Development Serwisy zintegrowane z portalem: Jajah, Twistage, Sevenload, HiClip, Google Maps Api, Ad Sense, Octazen Invite Sender & Contact Importer
Runek usług Susuh.
www.define-finance.com
Użyte technologie: Ruby on Rails, Behaviour Driven Development (RSpec), Ajax, Javascript, XML, MySql, Apache, Mongrel, Memcache, Ferret, GetText, BackgrounDRb Serwisy zintegrowane z portalem: Sevenload
Portal dla osób działających na europejskim rynku finansów i ubezpieczeń.
www.absolventa.de
Portal pracy przeznaczony dla absolwentów.
Użyte technologie: Ruby on Rails, Behaviour Driven Development (RSpec), Ajax, Javascript, XML, MySql, Apache, Mongrel, Memcache, BackgrounDRb Serwisy zintegrowane z portalem: Amazon S3
Praca, praktyka w Jarorcon Sp. z o.o.
Wrocław - praca we Wrocławskim biurze firmy Jarorcon Sp. z o.o.
Hamburg - praca w niemieckiej firmie AKRA GmbH.
Kontakt: [email protected] www.jarorcon.pl
Rubyi
Rails
Przemysław Kowalczyk
Jarorcon sp. z o.o.
O mnie
Absolwent Uniwersytetu Wr., 1999 r., kierunek: Informatyka, specjalizacja: Inżynieria Oprogramowania
Pracuję jako programista od 1997 r. Java: 6 lat,
C++: 4 lata, Ruby: prawie rok
Aplikacje WWW: prawie 7 lat
Plan prezentacji
Ruby Ruby <=> Java przykłady
Ruby on Rails wprowadzenie przykłady
ActiveRecord ActiveRecord <=> JPA przykłady
Stworzony w 1995 r. przez Yukihiro Matsumoto (a.k.a. "Matz")
“Designed for programmer productivity and fun!”
Ogólnego zastosowania
Wieloplatformowy Open source Obiektowy Elastyczny
Automatyczne zarządzanie pamięcią
Refleksja Inspiracje:
Smalltalk Perl Lisp Python
Ruby <=> Java
Kompilowany Statyczne typowanie
Wszystko jest obiektem
(oprócz typów prymitywnych, tablic, interfejsów, enumów, adnotacji) ☺
Wywoływanie metod
Interpretowany Dynamiczne typowanie
(duck-typing) Wszystko jest obiektem
(klasy, moduły)
Wysyłanie komunikatów
Ruby <=> Java Ruby > Java
Zwięzły, wyrazisty kod => szybsze tworzenie, niższe koszty utrzymania
DSL (Domain Specific Language) => myślenie pojęciami z dziedziny problemu
Otwarte klasy, metaprogramowanie => łatwość tworzenia DSL, frameworków, AOP (programowanie aspektowe)
Przyjazny dla programisty: składnia, Principle of Least Surprise
Ruby < Java Wolniejszy (ale: JRuby, wiele innych implementacji w toku)
Słabe wsparcie Unicode (tylko UTF-8, poprawione w Ruby 1.9)
Trudniejszy refactoring
Bardziej wymagający (łatwiej sobie zrobić krzywdę)
Przyjazna składnia
puts 'Hello World!'
10.times {puts 'Hello'}
array = [1, 'hi', 3.14, 1, 2, [4, 5]]
hash = {'water' => 'wet', 'fire' => 'hot'}puts hash['water']
Nawiasy opcjonalne
int też obiekt i swoje metody ma
Składnia dla tworzenia tablic
Składnia dla tworzenia tablic asocjacyjnych
Metody mogą mieć blok kodu jako parametr
Średniki opcjonalne
Przyjazna składnia
def fib n return 1 if n < 2 fib(n-1) + fib(n-2)end
n = 5
puts "#{n}-th Fibonacci number is #{fib n}"
(1..10).map {|i| i * 2}.find_all {|i| i%3 == 0}
=> [6, 12, 18]
Skrócona forma if
return zbędny na końcu metody
Interpretowane wewnątrz napisu
Tworzy obiekt klasy Range
Zwięzły kod
top5 = users. sort_by{|u| u.activity}. last(5). collect{|u| u.login}
Collections.sort(users, new Comparator<User>() {
public int compare(User u1, User u2) {
return u1.getActivity() - u2.getActivity();
}
});
List<String> top5 = new ArrayList<String>(5);
for (User user : users.subList(users.size() - 5, users.size())) {
top5.add(user.getLogin());
}
Dostępne w Rails i Ruby 1.9
top5 = users. sort_by(&:activity). last(5). collect(&:login)
Podejrzane!
Błędy!
Accidentalcomplexity!
DSL – Domain Specific Language
class User < ActiveRecord::Base
has_many :projects
belongs_to :department
validates_presence_of :name
validates_uniqueness_of :login
validate :blacklisted_login?
before_save :encrypt_password
...
Otwarte klasy
class String def blank? self.strip == '' endend
class NilClass def blank?; true; endend
"RUBY".length
"RUBY".blank?
nil.blank?
public class StringUtils { public static boolean isBlank(String str) { return str == null || str.trim().equals(""); }}
"JAVA".length();
StringUtils.isBlank("JAVA");
StringUtils.isBlank(null);
Osobna klasa!Metoda statyczna!
Brak konsekwencji!
Metaprogramowanie
xml = XmlGenerator.new
puts xml.people {
xml.person {
xml.name {"Gallagher"} +
xml.profession {"inventor"} +
xml.fictional
}
}
<people><person><name>Gallegher</name><profession>inventor</profession><fictional/></person></people>
class XmlGenerator
def method_missing m, *args
if block_given?
"<#{m}>#{yield}</#{m}>"
else
"<#{m}/>"
end
end
end
Metaprogramowanie
Person = Struct.new(:name, :phone, :email)p1 = Person.new(:name => "DHH")
#Campingclass Post < R '/post/(\d+)' def get post_id ... endend
def run_tests methods.grep /^test_/ do |m| self.send m endend
Ruby on Rails
Twórca: David Heinemeier Hansson Data opublikowania: lipiec 2004 r. Obecna wersja stabilna: 2.0.2 Stworzony przez
wydzielenie z istniejącejaplikacji (Basecamp)
Ruby on Rails – buzzwords
MVCAJAXRESTDRYConvention over Configuration
Generatory plikówplugins wsparcie dla TDD
Web development that doesn't hurt!
Optimized for programmer happiness
Does it scale?YES!
Ruby will reach 4 million programmers by 2013 (Gartner)
Ruby on Rails – enterprise ready?
amazon.com BBC CapGemini Cisco C|Net EA (Electronic Arts) IBM JP Morgan NASA Oracle
New York Times NBC Barclays LA Times Chicago Tribune Orbitz Google Turner Media Siemens Yahoo!
YES!
Convention over Configuration
MessageFoldersController(klasa kontrolera)
message_folders(tabela)
MessageFolder(klasa modelu)
app/models/message_folder.rb(plik)
MessageFolderTest(klasa testowa dla modelu)
test/unit/message_folder_test.rb(plik)
app/controllers/message_folders_controller.rb(plik)
app/views/message_folders/index.rhtmledit.rhtml
show.rhtml(widoki)
MessageFoldersControllerTest(klasa testowa dla kontrolera)
test/functional/message_folders_controller_test.rb(plik)
Ruby on Rails – model
class Campaign < ActiveRecord::Base
has_many :codes, :dependent => :destroy has_many :free_codes, :class_name => "Code", :conditions => "user_id is null" belongs_to :creator, :class_name => "User"
validates_presence_of :name, :valid_from, :valid_to, :invitations_no validates_uniqueness_of :name
def initialize *args super if @new_record valid_from ||= Time.now.tomorrow.midnight valid_to ||= 2.weeks.since(valid_from) invitations_no ||= 5 end end
def active? Time.now >= valid_from && Time.now <= valid_to endend
Operacje na czasie i datach
Nazwa klasy na podstawie nazwy relacji
Ruby on Rails – controller
class AdminCampaignsController < ApplicationController before_filter :admin_required caches_page :index, :show cache_sweeper :campaign_sweeper, :only => [:create, :update, :destroy]
def index @campaigns = Campaign.paginate :all, :page=>params[:page], :per_page=>25 end
def edit @campaign = Campaign.find(params[:id]) end
def update @campaign = Campaign.find(params[:id]) if @campaign.update_attributes(params[:campaign]) flash[:notice] = _('Campaign was successfully updated.') redirect_to admin_campaign_url(@campaign) else render :action => "edit" end end
...
Cache'owanie!
Ruby on Rails – view
<h1>New campaign</h1><%= error_messages_for :campaign %><% form_for(:campaign, :url => admin_campaigns_path) do |f| %> <%= render :partial => 'form', :locals => { :f => f } %> <p> <%= submit_tag "Create" %> </p><% end %><%= link_to 'Back', admin_campaigns_path %>
<p> <b>Name</b><br /> <%= f.text_field :name %></p>
<p> <b>Valid from</b><br /><%= f.datetime_select :valid_from %></p>
<p> <b>Valid to</b><br /> <%= f.datetime_select :valid_to %></p>
Formularz dla modelu
Wstawienie fragmentu strony
Partial '_form.rhtml'
Widok 'new.rhtml'
Ruby on Rails – test
class AdminCampaignsControllerTest < Test::Unit::TestCase
fixtures :campaigns, :campaign_codes
def test_should_get_index get :index assert_response :success assert assigns(:campaigns) end
def test_should_create_campaign assert_difference Campaign, :count, 1 do post :create, :campaign => { :name => 'test campaign' } end
assert_redirected_to admin_campaign_path(assigns(:campaign)) end
def test_shouldnt_create_campaign_without_name assert_difference Campaign, :count, 0 do post :create, :campaign => { :name => '' } end assert_not_nil assigns(:campaign).errors.on(:name) end
Fixtures – predefiniowane dane w bazie testowej
Wykonanie żądania HTTP: GET index
Blok kodu powinien zwiększyć Campaign.count o 1
Wykonanie żądania HTTP: POST create z parametrami
ActiveRecord <=> JPA
Wzorzec projektowy: ActiveRecord
Atrybuty zdefiniowane w tabeli
Relacje zdefiniowane w klasie
Mapowanie automatyczne
Finders, dynamic finders, SQL
Wzorzec projektowy: DataMapper
Atrybuty zdefiniowane w klasie
Relacje zdefiniowane w klasie lub XML
Mapowanie przez adnotacje lub XML
Criteria, HQL, SQL
ActiveRecord
find(:first, :conditions => [ "user_name = ? AND password = ?", user_name, password ])
find(:first, :conditions => { :user_name => user_name, :password => password })
find(:first, :conditions => params[:user])
find_by_user_name_and_password(user_name, password)
Tag.find_or_create_by_name("Summer")
user.manager.projects.find(project_id)
manager.projects.create(:name => 'new project')
Dynamic finder
Znajdź lub stwórz
User belongs_to :manager
Manager has_many :projects
Znajduje projekt, ale tylko wtedy, gdy ma go manager danego użytkownika
ActiveRecord (2)
Person.find(:all, :include => [ :account, :friends ])
Company.find(:first, :conditions => [ "id = :id AND name = :name AND division = :division AND created_at > :accounting_date", { :id => 3, :name => "37signals", :division => "First", :accounting_date => '2005-01-01' }]
Message.find(all, :select => "messages.*", :joins => "INNER JOIN message_folder_items mfi ON (mfi.message_id = messages.id)", :conditions => ["mfi.message_folder_id = ?", self.id], :order => 'messages.sent_at desc', :limit => 20)
Eager loading
YAML <=> XML
dates:
month names: [sty, lut, mar, kwi, ...]
day names: [Nie, Pon, Wto, Śro, ...]
date formats:
short: '%e %b'
long: '%e %B %Y'
numbers:
separator: '.'
delimiter: ','
precision: 3
<format> <dates> <month-names> <month-name>sty</month-name> <month-name>lut</month-name> <month-name>mar</month-name> <month-name>kwi</month-name> <month-name>...</month-name> </month-names> <day-names> <day-name>Nie</day-name> <day-name>Pon</day-name> <day-name>Wto</day-name> <day-name>Śro</day-name> <day-name>...</day-name> </day-names> <date-formats> <short>%e %b</short> <long>%e %B %Y</long> </date-formats> </dates> <numbers> <separator>.</separator> <delimiter>,</delimiter> <precision>3</precision> </numbers></format>
Przydatne adresy
http://www.ruby-lang.org/pl/ http://www.rubyonrails.org/ http://jarorcon.pl/ http://szeryf.wordpress.com/
Pytania?