View
3.586
Download
0
Category
Tags:
Preview:
Citation preview
Rails Tutorial
自我介紹
http://blog.xdite.net
低調部落客
http://handlino.com
Rails Developer
bingo.handlino.com
sudomake.com
VeryXD.net
Project Using Rails
VeryXD.net
Project Using Sinatra
MrIE6.com
DRYDon’t Repeat Yourself
Agenda
• ActiveRecord
• Template / Helper
• Library
• Plugin
Agenda
• ActiveRecord
• Template / Helper
• Library
• Plugin
少寫更多的 Code
MVCModel - View - Controller
MVCModel-View-Controller
route.rbHTTP requestGET /users/1
Browser
UsersController
end
def show@user = User.find(params[:id])
respond_to do |format|format.htmlformat.xml
endend
def index......
end
Model
Database
#show.html.erb
<html> <h1>User Profile</h1> <p><%= @user.nickname %></p></html>
View
決定哪一個 Controller 和 Action
C > M現實
C > M現實
大家都愛把 Code 塞進 Controller
Refactor to ModelActiveRecord 內建 feature
ActiveRecord• validation
• callback
• counter_cache
• named_scope
• STI
• polymorphic association
validation
validation
class Post < ActiveRecord::Base validates_presence_of :subject end
validation
class Post < ActiveRecord::Base validates_presence_of :subject end
validation
validation
validates_exclusion_of :age, :in => 30..60
validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, :on => :create
validates_inclusion_of :format, :in => %w( jpg gif png )
validates_length_of :phone, :in => 7..32, :allow_blank => true
validates_uniqueness_of :teacher_id, :scope => [:semester_id, :class_id]
Callbacks
def create @post = Post.new(params[:post]) @post.content += " < #{Time.now.to_s} >" if @post.save flash[:notice] = 'Post was successfully created.' redirect_to blog_path(@post) else render :action => "new" end end
直覺想法
Callbacks
class Post < ActiveRecord::Base
before_create :insert_timestamp def insert_timestamp self.content += " < #{Time.now.to_s} >" end
end
應該使用 before_create
Callbacks
before_validationbefore_validation_on_createafter_validatinafter_validation_on_createbefore_savebefore_create
after_createafter_save
insert update
before_validationbefore_validation_on_updateafter_validatinafter_validation_on_upatebefore_savebefore_update
after_updateafter_save
before_destroy
after_destroy
delete
DestroyCreate Update
Counter Cache
計算文章留言數
Counter Cache
class Comment < ActiveRecord::Base belongs_to :post def after_save self.update_counter_cache end def after_destroy self.update_counter_cache end def update_conter_cache self.post.comments_count = self.post.comments.size slf.post.save(false) endend
直覺想法
class PostCommentsCount < ActiveRecord::Migration def self.up add_column :posts, :comments_count, :integer , :default => 0 end
def self.down remove_column :posts,:comments_count endend
Counter Cacheclass Comment < ActiveRecord::Base belongs_to :post, :counter_cache => trueend
自動解決
Named Scope
Post.publish=> SELECT * FROM `posts` WHERE (`posts`.`is_blocked` = 0)
Post.recent=> SELECT * FROM `posts` ORDER BY created_at DESC LIMIT 1
Post.publish.recent=> SELECT * FROM `posts` WHERE (`posts`.`is_blocked` = 0) ORDER BY created_at DESC LIMIT 1
class Post < ActiveRecord::Base named_scope :publish, :conditions => { :is_blocked => false } named_scope :recent, :order => "created_at DESC", :limit => 1end
v.2.1 起
STI (Single Table Inheritance )
class Post < ActiveRecord::Base has_many :comments before_create :insert_timestamp def insert_timestamp self.content += " < #{Time.now.to_s} >" end named_scope :publish, :conditions => { :is_blocked => false } named_scope :recent, :order => "created_at DESC", :limit => 1 end
class Article < Postend
Polymorphic Association
• Post has_many comments
• Photo has_many comments
• News has_many comments
post_comment
photo_comment
news_comment
直覺想法
需要這麼多張 Table 嗎?
Polymorphic Associationclass Post < ActiveRecord::Base has_many :comments, :as => :resourceend
class Comment < ActiveRecord::Base belongs_to :resource, :polymorphic => trueend
class PolymorphicComment < ActiveRecord::Migration def self.up remove_column :comments, :post_id add_column :comments, :resource_id, :integer add_column :comments, :resource_type, :string end
def self.down add_column :comments, :post_id, :integer remove_column :comments, :resource_id remove_column :comments, :resource_type endend
Polymorphic Association
MVCModel - View - Controller
V > C現實
V > C現實
大家都愛把 Code 寫在 View 裡 ....
Less code in viewRender Template & Use Helper
View 裡面的髒 code
Render Template
用 partial 包起來
Render Template
用 partial 包起來
Write Your Own Helper
自動生 <td> <tr>
Write Your Own Helper
Don’t re-invent the wheeluse the library ( lib/ ), gems
Yahoo Open Hack Day 佳作
1日開發 HTML +CSS (not flash)和多出品
lib/
rubygems
lib/
7326 gems
數不清的 gems
Rapid Web Developmentvendor/plugin
1. 認證篇authentication
restful-authentication
• 使用者註冊/登入/登出
• User model
• RESTful Session controller
• Activation mailer
./script/generate authenticated user sessions --include-activation
不需要自己撰寫認證系統
open_id_authentication
• 支援 OpenID 登入
• openid session controller
• openid migration
openid_pack
• restful_authentication + open_id_authentication同時支援用戶註冊或使用 OpenID ( multiple )
• 和多出品
2. Model 篇ActiveRecord
attachment_fu
• 將 model 當做一個檔案附件
• File system / amazon S3 / Database store
has_attachment :storage => :file_system, :path_prefix => 'public/files', :content_type => :image, :resize_to => [50,50]
restful_authenication
attachment_fu
acts_as_taggable_on_steroids
class Post < ActiveRecord::Base acts_as_taggable end
p = Post.find(:first) p.tag_list # [] p.tag_list = "Funny, Silly" p.save p.tag_list # ["Funny", "Silly"]
p.tag_list.add("Great", "Awful") p.tag_list.remove("Funny")
• 裝作有標籤 tagging 功能
標籤雲
validates_url_of
• 加上 http:// 如果資料缺少 http:// 或 https://
• 使用正規表示法檢查格式• 和多出品
class Foo < ActiveRecord::Base validates_url_of :url, :message => 'is not valid or not responding'.tend
3. Controller&View 篇ActionPack
will_paginate
@posts = Post.paginate :page => params[:page], :per_page => 50
<ol> <% for post in @posts -%> <li>Render `post` in some nice way.</li> <% end -%> </ol>
<%= will_paginate @posts %>
• 分頁必備,包含 HTML helper 及 CSS style
分頁功能
will_paginate (cont.)• 附贈 named_scope,如果你還沒升級到 Rails 2.1
class Product < ActiveRecord::Base
named_scope :cheap, :conditions => { :price => 0..5 } named_scope :recent, lambda { |*args| {:conditions => ["released_at > ?", (args.first || 2.weeks.ago)]} } named_scope :visible, :include => :category, :conditions => { 'categories.hidden' => false } end
@products = Product.recent.cheap.paginate :page => params[:page], :per_page => 50
• 人人都愛的串接寫法
jRails• 我們都愛 jQuery
gugod hlbbyebye! Prototype.js
facebox_render• Facebox is a JQuery-based lightbox
http://famspam.com/facebox/
facebox_render (cont.)
Text
class ApplicationController < ActionController::Base include FaceboxRenderend
• 無縫銜接 facebox,和多出品
facebox_render (cont.)
Text
class ApplicationController < ActionController::Base include FaceboxRenderend
<%= facebox_link_to "Login", :url => new_session_url %>
• 無縫銜接 facebox,和多出品
facebox_render (cont.)
Text
class ApplicationController < ActionController::Base include FaceboxRenderend
<%= facebox_link_to "Login", :url => new_session_url %>
• 無縫銜接 facebox,和多出品
facebox_render (cont.)
Text
class ApplicationController < ActionController::Base include FaceboxRenderend
<%= facebox_link_to "Login", :url => new_session_url %>
def new # do some thing you want respond_to do |format| format.html format.js { render_facebox } endend
• 無縫銜接 facebox,和多出品
facebox_render (cont.)
Text
class ApplicationController < ActionController::Base include FaceboxRenderend
<%= facebox_link_to "Login", :url => new_session_url %>
def new # do some thing you want respond_to do |format| format.html format.js { render_facebox } endend
• 無縫銜接 facebox,和多出品
Ajax form submit
Ajax form submit
<% form_remote_tag :url => batch_event_attendees_path(@event) do %>
Ajax form submit
def batch ... respond_to do |format| format.html format.js { render_to_facebox } endend
Text
def batch ... respond_to do |format| format.html format.js { render_to_facebox } endend
Text
def batch ... respond_to do |format| format.html format.js { render_to_facebox } endend
batch.html.erb
Text
def batch ... respond_to do |format| format.html format.js { render_to_facebox } endend
batch.html.erb
Text
stickies• 強化版的 flash[:notice] 提示訊息
• 四種不同種類的訊息提示,包含 CSS style 及 Javascript close按鈕。
error_stickie("Your account has been disabled") warning_stickie("Your account will expire in 3 days") notice_stickie("Account activated") debug_stickie("This only works when RAILS_ENV is development")
<%= render_stickies %>
4. 上線篇deployment
ar_mailer• 當有很多 E-mail 要發送的時候,需要非同步的 E-mail 發送
Class EventNotifier < ActionMailer::ARMailer
def signup_notification(user) ... end
end
UserNotifier.deliver_signup_notification(@user)
Database ar_sendmail daemon
hoptoad
• 記錄 exception 例外時(500 error)
• 包含當時的 log 記錄
New Relic• profiling your rails app
• trace code stack
• provide optimize direction
Capistrano自動化 deploy 步驟1. ssh to production server
2. svn checkout 3. run some your script (link file/copy config file/ clear cache…etc)4. restart mongrel cluster
plugin 哪裡找?
thank you.
Bonus
I18nless pain( Rails 2.2 )
使用 session 切換
yml 管理
Template & EngineUltima lazy solution ( Rails 2.3)
Template
• 開發者會不斷的進行新專案• 每次依需求安裝不等數量的 gem / plugin
• 每次依需求撰寫不同的 route / rake
• ...開始維護自己的架站包?
• ......還是要 by case 修改架站包
• ..........苦悶 =_=
Templates
• (restful / openid / twitter) _authenication
• Google App Engine
Engine
• 開發者會不斷的進行新專案• MVC 架構可以抄襲以前的 code
• ...每次還是從自己的架站包 copy
• ......還是要 by case 修改架站包
• ..........苦悶 =_=
推書籤到 Twitter
也是我寫的 XD
推書籤到 Twitter
也是我寫的 XD
• 把 Application 當做一個 Plugin
• 不需另外撰寫code整合原有程式
• Load 了就上
• 有效利用舊 code
Engine plugin
thanks again.
http://blog.xdite.net
http://twitter.com/xdite
Recommended