在之前的開發中,我們只簡單新建了資料表。
而在rails中,要去跟資料庫拿資料的就是model,
裡面能設定association、validation、callback,以及scope......等等。
今天就用post.rb來介紹一下rails的model。
舉例來說,我們知道每個po文會有作者,
但我們現在作者和po文是分開的,該怎樣把它們連起來呢?
首先先在每一則post裡加上user_id。
概念:因為每一個po文只會屬於一個user。
rails g migration add_user_id_to_posts user_id:integer
加上user_id之後我們讓rails有辦法去找到關聯,
但是我們還沒告訴它如何去找。
所以在以下post以及user的model裡面加上belongs_to、has_many:
class Post < ActiveRecord::Base
validates :title, presence: true, length: {maximum: 50}
belongs_to :user
end
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :posts
end
> 這裡的動詞和名詞複數變化,也跟程式無關,只要用一般的英文文法去想就ok了
現在進去rails console裡面,隨便找一個user:
user = User.first
再輸入user.posts
就可以找到目前屬於這個user發的po文,
目前會回傳一個空的array,不過我們可以在這裡直接新增一個post,
user.posts.create(title:"Whatever", content:"doesn't matter")
不過這樣手動測並不是一個好方法,rspec也有辦法測model間的association。
在開始之前我們得做一點小小的修正,
因為我們在contoller加上authenticate\_user!
這個before_filter,
所以現在運行post\_controller\_spec.rb
會出錯,
這裡要在spec_helper.rb裡面require 'devise',
然後加入tests_heler。
config.include Devise::TestHelpers, :type => :controller
接著我們就可以在controller spec裡面去讓假的user登入了。
let(:user) { Fabricate(:user) }
before { sign_in user }
這兩行就可以搞定所有事情。
不過改完會發現仍然有錯誤,問題很簡單,
因為我們還沒在Fabricate裡面新增假造的user。
Fabricator(:user) do
name { Faker::Name.name }
email { Faker::Internet.email }
password { Faker::Internet.password }
end
這樣舊的測試就可以跑起來了,準備開始測post的model。
首先下載一個gem,記得照著readme上面的提醒加上require: false
的選項,
不然每個檔案都require這一個gem,可能會發生神奇的錯誤。
(我猜是因為裡面的has\_many
和belongs\_to
兩個matcher會和原本rails在model中使用的衝突到)
再來記得在rails_helper.rb裡面require進來,
如果是2.x的rspec就放在spec_helper.rb裡面,__否則會出錯__。 Q_Q
再來就是測試的code啦!
看著shoulda文件很清楚,不過這裡還是簡單做一次
require 'rails_helper'
RSpec.describe User, :type => :model do
let(:user) { Fabricate(:user) }
it "should have many posts" do
expect(user).to have_many(:posts)
end
end
require 'rails_helper'
RSpec.describe Post, :type => :model do
let(:post) { Fabricate(:post) }
it "should belong to user" do
expect(post).to belong_to(:user)
end
end
通過之後我們可以在新增文章的地方加入hidden field來設定po文的作者是誰,
也記得加入strong parameter。
private
def post_params
params.require(:post).permit( :user_id,:title, :content, :note, :source)
end
<%= f.input :user_id, as: :hidden, input_html: { :value => current_user.id} %>
再來就可以在文章列表(post的index頁面)加上欄位啦!
現在看起來醜醜的,而且放太多邏輯在view裡面是不好的行為,
之後會refactor這些東西,今天就先知道這樣就好啦!
參考資料:
https://github.com/thoughtbot/shoulda-matchers