Matti Paksula / Syksy 2009
Tietojenkäsittelytieteen laitos
Helsingin Yliopisto
Kurssista on mahdollista saada 4op + 1op tekemällä "27h" enemmän töitä.
Kurssin jälkeen on mahdollista työllistyä Rails-kehittäjänä
"Rails isn't playing catch-up with the new de facto web standards; it's helping define them."
taulukko = ["ruby", "python", "perl", "php"]
if taulukko.include? "php"
puts "Voi ei, otetaan PHP pois"
taulukko.delete "php"
end
unless taulukko.include? "scala"
puts "Scala puuttuu!"
taulukko << "scala"
end
class Koe
def metodi
puts("sulkuja") ; puts("ja puolipilkkujakin!")
end
end
kokeilu = Koe.new
kokeilu.metodi
10.times {
puts "Mahtava kieli!"
}
"Agility is part of the fabric of Rails"
-pr" (pure ruby) -versioita, jotka eivät sisällä natiivikoodia ja ovat täten hitaita
Varsinaisesta IDE:stä ei välttämättä ole hyötyä, hyvä tekstieditori toimii paremmin.
Laitokselle asennettu valmiiksi "hyvät oletukset"
rails applikaation_nimi
.
|-- README Geneerinen readme
|-- Rakefile "rake" -komennon oletus "Makefile"
|-- app Koko applikaation koodi tulee pääosin tänne
| |-- controllers Kaikki controllerit, elikkäs ohjaimet
| | `-- application_controller.rb Yläluokka kaikille controllereille, oletusarvoisesti kaikki periytyvät tästä
| |-- helpers Viewien apumetodit
| | `-- application_helper.rb Yläluokka helppereille
| |-- models Modelit, eli tietokantatauluja edustavat luokat
| `-- views Näkymät
| `-- layouts Näkymäwrapperit
|-- config Konfiguraatiotiedostot
| |-- boot.rb Railssin alustus
| |-- database.yml Tietokantakonfiguraatiot YAML -muodossa
| |-- environment.rb Koko applikaatiota koskevat ympäristöasetukset.
| |-- environments Eri ympäristöjen asetukset
| | |-- development.rb Kehitysympäristö
| | |-- production.rb Tuotantoympäristö
| | `-- test.rb Testausympäristö
| |-- initializers Lisää asetuksia, ajetaan kerran käynnistyksessä
| | |-- backtrace_silencers.rb kts. sisältö
| | |-- inflections.rb Sanojen taivutuksen määrittely, tarvitaan tietokantataulujen nimeämiseen.
| | |-- mime_types.rb Tuetut mime-tyypit
| | |-- new_rails_defaults.rb Rails 2.0 -> 3.0 -asioita
| | `-- session_store.rb Määrittelee missä sessiot säilytetään, oletusarvoisesti cookiessa.
| |-- locales Lokalisointi
| | `-- en.yml Kielen lokalisointitiedosto
| `-- routes.rb Määrittelee miten URL:t yhdistyvät applikaation controllereihin.
|-- db Tietokantaskeemat, SQLite -kannan oletussijainti
|-- doc Dokumentaatio tänne..
| `-- README_FOR_APP ..ehkä
|-- lib Applikaation käyttämät kirjastot, katso myös "vendor"
| `-- tasks Applikaation Rake -taskit
|-- log Eri ympäristöjen logitiedostot
| |-- development.log
| |-- production.log
| |-- server.log
| `-- test.log
|-- public Applikaation staattiset tiedostot
| |-- 404.html Oletusarvoinen 404, annetaan myös silloin kun haettua tietoa ei löydy tietokannasta
| |-- 422.html Access denied
| |-- 500.html Näytetään, kun applikaatio suorittaa poikkeuksen
| |-- favicon.ico
| |-- images Kuvat tänne
| | `-- rails.png ..
| |-- index.html Tämä pitää poistaa, jotta "/" -URL toimii
| |-- javascripts JavaScriptit
| | |-- application.js Applikaation omat JavaScriptit tänne
| | |-- controls.js script.aculo.us -kirjaston muinainen versio
| | |-- dragdrop.js ..drag'n'drop -kirjasto
| | |-- effects.js ..ja effect. Nämä voi halutessaan poistaa tai päivittää uudempiin.
| | `-- prototype.js Prototype JavaScript -kirjasto
| |-- robots.txt robots.txt
| `-- stylesheets Tyylitiedostot tänne
|-- script Railssin omat skriptit
| |-- about Kertoo tietoja ajoympäristöstä
| |-- console irb konsoli Rails-tuella
| |-- dbconsole Valitun tietokannan konsoli
| |-- destroy Poistaa generoituja tiedostoja
| |-- generate Generoi tiedostoja
| |-- performance Suorituskykytestausta
| | |-- benchmarker
| | `-- profiler
| |-- plugin Asentaa plugineja vendor/plugins -kansioon
| |-- runner Suorittaa mitä tahansa koodia
| `-- server Käynnistää paikallisen web-serverin (Webrick tai Mongrel)
|-- test Test::Unit -yksikkötestit
| |-- fixtures Valmis tietosisältö testeihin (tai kehitykseen)
| |-- functional
| |-- integration
| |-- performance
| | `-- browsing_test.rb
| |-- test_helper.rb
| `-- unit Yksikkötestit modeleille
|-- tmp Väliaikaistiedostot
| |-- cache
| |-- pids
| |-- sessions
| `-- sockets
`-- vendor
`-- plugins Pluginit
app/modelsscript/generate model Course name:stringdb/migrate/timestamp_create_courses.rbrake db:migratec = Course.new c.name = "Ruby on Rails" c.description = "Syksyn ketterin kurssi" c.save
app/controllersscript/generate controller Courses indexconfig/routes.rb määritellään miten URL:t ohjautuvat millekkin controllerillemap.connect ':controller/:action/:id' määrittelee mm. seuraavat URL:t
routes.rb tiedostoa päivitetään, tulee käynnistää palvelin uudestaanscript/serverhttp://localhost:3000/courses/indexdef index @courses = Course.find(:all) end
@courses -instanssin näkymälle<h1>Ilmo 2.0</h1> <% @courses.each do |course| %> <%= course.name %><br/> <% end %>
RAILS_ENV -ympäristömuuttuja määrittelee missä ympäristössä toimintaandevelopment -ympäristössäproduction -ympäristössä ei ladata luokkia uudestaan jokaiselle requestille ym.production -ympäristössä.Perjantai 10-12
# Leikkikää irb:ssä!
hajautustaulu = {}
hajautustaulu = Hash.new
hajautustaulu[:avain] = "symboli -avaimen arvo"
hajautustaulu["avain"] = "string -avaimen arvo"
hajautustaulu[23] = "integer -avaimen arvo"
# >> hajautustaulu
# => { :avain=>"symboli -avaimen arvo",
# 23=>"integer -avaimen arvo",
# "avain"=>"string -avaimen arvo"}
# id:n perusteella
Course.find(1)
# nimen perusteella
Course.find_by_name("rio")
# useamman kentän perusteella
Course.find_by_name_and_description("rio",
"rinnakkaisohjelmointi")
c = Course.new :name => "LaMa"
c = Course.new({:name => "LaMa"})
c.save
c = Course.find(:first) c = Course.first c.name = c.name.upcase c.save
Course.destroy(1) c = Course.last c.destroy Course.destroy_all :name => "Rails"
Course.count
Person.count(:conditions => "age > 26 \
AND salary > 60000")
rake db:seed & db/seed.rbARGV -taulukkolink_toparams -muuttujalayouts
yield (kiinnostuneille)helpers & tehdään oma link_to -toteutusroutes.rb & named routesrake routes -komentoredirect_tolog/ -hakemistoadd_column & rename_columnFlash -viestit näkymissäform_for
# Original controller code
def authenticate
if params[:login][:username] and params[:login][:password]
user = User.authenticate(params[:login][:username], params[:login][:password])
if user
# password correct
session[:user] = user
redirect_to_stored
else
# password wrong
redirect_to :action=>"index"
end
end
end
# Better implementation
def authenticate
# No need to check if params were given to controller
# if params[:login][:username] and params[:login][:password]
user = User.authenticate(params[:login][:username], params[:login][:password])
if user
# 1) Don't store entire user object in session
# 2) Wrap a method around login, so that we can change authentication behaviour if needed
# session[:user] = user
login_user(user)
redirect_to_stored # This probably was to redirect to "attempted page" (before filter) so this might be okay!
else
# If we change our page to use for login, let's use a named_route for that
# redirect_to :action=>"index"
redirect_to login_page
end
end
# refactored
def authenticate
user = User.authenticate(params[:login][:username], params[:login][:password])
if user
login_user(user)
redirect_to_stored # This probably was to redirect to "attempted page" (before filter) so this is okay.
else
redirect_to login_page
end
end
# Original controller code
def logout
Offer.unlock_all_by_user_id(session[:user].id) if session[:user]
session[:user] = nil
redirect_to :controller=>"offer", :action=>"index"
end
# Better implementation
def logout
# Wrap this to logout(user) or user.logout! -method
# Note also that I checked if session[:user] exists even if we are in logout, where it exists.
# Offer.unlock_all_by_user_id(session[:user].id) if session[:user]
Offer.unlock_by(user) # Unlock all by user
user.logout! # Logout user
# As above, use named routes to ensure readability
#redirect_to :controller=>"offer", :action=>"index"
redirect_to root_url
end
# refactored
def logout
Offer.unlock_by(user)
user.logout!
redirect_to root_url
end
# original controller code
if not @offer.locked.nil? and @offer.locked != session[:user].id
redirect_to :action=>"edit_lock", :id=>@offer.id
else
@offer.lock = session[:user].id
end
# step 1
# 1) use implement locking check at instance
# 2) use current_user instead of session
if @offer.locked? and @offer.locked != current_user.id
redirect_to :action=>"edit_lock", :id=>@offer.id
else
@offer.lock = current_user.id
end
# step 2
# 1) combine lock checking with user at instance
if @offer.locked_by?(current_user)
# use resources instead of direct classes and actions
redirect_to edit_offer_lock(@offer)
else
# Implement locking at lower level, this implementation can be changed without breaking API to controllers
@offer.lock_for(current_user)
end
# refactored:
if @offer.locked_by?(current_user)
redirect_to edit_offer_lock(@offer)
else
@offer.lock_for(current_user)
end
named_scope :by_popularity, :order => "(SELECT count(*) from registrations r left join exercise_groups eg on eg.id = r.exercise_group_id left join course_instances ci on ci.id = eg.course_instance_id where ci.course_id = courses.id group by ci.course_id) DESC"
