实现评论功能

Published on:

step1 :rails g model review product_id:integer user_id:integer body:text

rake db:migrate

step2: 修改model

user.rb / product.rb :
has_many :reviews

review.rb
belongs_to :user
belongs_to :product
validates :body, presence: true

step3: 增加routes

resources :products do
resources :reviews
end

step4: rails g controller reviews

class ReviewsController < ApplicationController
  before_action :authenticate_user!, only: [:create, :destroy]
  def create
    @product = Product.find(params[:product_id])
    @review = @product.reviews.new(review_params)
    @review.user = current_user

   if @review.save
  redirect_to product_path(@product), notice: 'Review was successfully created.'
else
  redirect_to product_path(@product), notice: 'You have to write some words.'
end
  end

  def destroy
    @product = Product.find(params[:product_id])
    @review = Review.find(params[:id])
    @review.destroy
    redirect_to product_path(@product), alert: "You have deleted the review successfully"
  end

  private
  def review_params
    params.require(:review).permit(:body)
  end
end

step5 : 产品 show 页面添加:

 <div class="row review">
  <div class="col-sm-10 col-sm-offset-1">
    <h3 class="reviews_title">
      <%= @product.reviews.count %> Reviews
    </h3>
    <hr>
    <div id="reivews">
      <%= render @product.reviews%>
      <hr>
      <%= render "reviews/form"%>
    </div>
  </div>
</div>

step6. touch app/views/reviews/_review.html.erb

 <div class="reviews_wrapper clearfix">
    <div class="pull-left">
        <p class="lead"><%= review.body %></p>
        <p>
            <small>Submitted
                <strong><%= time_ago_in_words(review.created_at) %>
                    ago</strong>
                by
                <%= review.user.email %></small>
        </p>
    </div>
    <div>
<% if review.user == current_user %>
   <%= link_to("Delete", product_review_path(@product, review), method: :delete, data: { confirm: "Are you sure?"}, class: "btn btn-sm pull-right") %>
<% end %>
  </div>
</div>

step7 . touch app/views/reviews/_form.html.erb

<%= simple_form_for([@product, @product.reviews.build]) do |f|%>

  <div class="field">
      <%= f.text_area :body, class: "form-control" %>
  </div>


  <%= f.submit "Add a Review", class: "btn btn-primary" %>
<% end %>

站内信

Published on:

Ruby on Rails 實戰聖經 - ActionMailer - E-mail 發送

建立一個Mailer寄信程式

和Controller一樣,Rails也用generate指令產生Mailer類別,此類別中的一個方法就對應一個Email樣板。以下是一個產生Mailer的範例:

rails generate mailer UserMailer notify_comment
如此便會產生app/mailers/user_mailer.rb檔案,並包含一個notify_comment的Action,其template在app/views/user_mailer/notify_comment.text.erb(純文字格式)和notify_comment.html.erb(HTML格式)。如果兩種格式的樣板檔案都有,那麼Rails會合併成一封Multiple Content Types的Email。

讓我們看看 user_mailer.rb 的程式:

class UserMailer < ActionMailer::Base
default :from => "寄件人名字 noreply@example.org"

def notify_comment(user, comment)
    @comment = comment
    mail(:to => user.email, :subject => "New Comment")
end

end
其中default方法可以設定預設的寄件人。而 mail 方法可以設定收件人和郵件主旨。和View一樣,@user物件變數可以在app/views/user_mailer/notify_comment.text.erb或app/views/user_mailer/notify_comment.html.erb或樣板中存取到。而mail方法則還可以接受其他參數包括cc、bcc。

我們可以在rails console中測試,執行UserMailer.notify_comment(user, comment).deliver_now!就會寄信出去。(這裡我們假設存在一個user和comment物件代表使用者和新留言,例如user = User.first和comment = Comment.last)

實務上,我們會在controller之中,例如使用者張貼留言之後寄發信件:

def create
comment = Comment.new(comment_params)
if comment.save
UserMailer.notify_comment(current_user, comment).deliver_later!
redirect_to comments_path
else
render :action => :new
end
end

如果只需要純文字版,就砍掉app/views/user_mailer/notify_comment.html.erb這個檔案,然後在app/views/user_mailer/notify_comment.text.erb純文字格式中,可以加入以下文字跟網址:

有新留言在 <%= comments_url %>
另外,因為寄信這個動作比較耗時,通常我們也會搭配使用非同步的機制,因此上述用法分成了deliver_now!和deliver_later!兩種,而後者就會搭配ActiveJob進行非同步的寄送,我們在非同步一章會詳細介紹如何設定。

Helper 的使用

在 email 樣本中,預設是不會載入 app/helpers 裡面的 Helper 方法的,如果你要使用的話,可以在該 Mailer 類別中宣告如下:

class UserMailer < ApplicationMailer
helper :application # 這樣會載入 app/helpers/application_helper.rb
helper :users # 這樣會載入 app/helpers/users_helper.rb
# ...
end
開發預覽
開發期間我們需要常常測試預覽寄出的Email內容,但是實際寄送出去又很沒效率。我們可以安裝letter_opener這個gem,修改Gemfile加入:

gem "letter_opener", :group => :development
然後將config.action_mailer.delivery_method改成:letter_opener

這樣在開發模式下,就會開瀏覽器進行預覽,而不會真的寄信出去。

第三方寄信服務

由於Gmail是個人用途使用,用量有限,並不適合開站做生意使用。我們實務上我們會使用第三方服務來確保Email遞送的可靠性,例如:

mailgun
SendGrid
Postmark
MailChimp
AWS SES
大量寄送 Email 會是一門學問,請參考 如何正確發送(大量) Email 信件 這篇文章
Email CSS 處理
Email 會被各種奇形怪狀的閱讀器所瀏覽,這些環境中的 CSS 支援非常受限:
https://blog.othree.net/log/2016/08/25/modern-html-email-develop/
https://www.campaignmonitor.com/css/
http://templates.mailchimp.com/development/css/
解法: 需要將 CSS inline 內嵌到 HTML 裡面
安裝 https://github.com/fphilipe/premailer-rails
我們也可以套現成的 Email Responsive Template 樣板,例如:

http://blog.mailgun.com/transactional-html-email-templates/
https://github.com/leemunroe/responsive-html-email-template
https://htmlemail.io/ $49
收信
Active Mailer也可以辦到收信,但是你需要自行架設郵件伺服器。因此需要這個功能的話,也會使用第三方服務,例如mailgun和MailChimp都有提供收信的Webhook服務:信寄到第三方服務,然後第三方再呼叫網站的HTTP API,這樣就省去你自己架設郵件伺服器的困難。

周记小结

Published on:

完成了购物商城3次,购物车设计和订单生成以及乱码加入确实挺难的,容易搞不清楚逻辑。只字不差的阅读教材,把自己定义成新手重复练习吧!

先说说本周最大的坑

第2遍的时候 rails g model order 去修改参数手残弄错执行了rake db:migrate 以后很多步都没发现错误,后来报错检查的时候发现是数据库出错,而我又忘了做多少步了。就只能rake db:drop了。惨的要死,因为我当时直接的反应是 git add .
git commit -m "手残弄错数据"然后git checkout stor4git branch -D story5。结果这样不行,是数据库出错。又来了一遍.... rake db:drop rails g model order rake db:migrate 好了。就当重复练习吧。

再说说本周最大的收获

我找了很多手册,html和css的手册。手册相当于新华字典。等你以后要用到的时候,可以查到用到.包括rails的手册都可以。

blank?是什么

Published on:

Rails中会常用到的blank? 方法
blank?方法是Rails而非Ruby中的方法. 基本上它实现了nil? empty?的作用. 下面是来自Rails API文档中的介绍:
“An object is blank if it‘s false, empty, or a whitespace string.

For example, "", " ", nil, [], and {} are blank.”
举例来说, 我经常会在模型中赋值的场景调用blank?方法. 回想之前的例子-- dog哈希表, 我们可以这样来简化检验值

[ruby] view plain copy
script/console >>

dog = {:name => "Beauregard"}
puts "What kind?" if dog[:breed].blank?
=> What kind?

dog = {:name => "Beauregard", :breed => ""}
puts "What kind?" if dog[:breed].blank?
=> What kind?

可以看到 .blank? 的结果 相当于 .ni? + .empty?

routes 中的collection 和 member

Published on:

member,new,collection的区别:

:member 是对单个实体进行操作,创建路由格式是: /:controller/:id/:your_method
:collection 是对实体集合进行操作,创建路由格式是: /:controller/:your_method
:new 是新建一个实体,创建路由格式是: /:controller/:your_method/new

举例如下:

map.resources :users, :collection => { :rss => :get }
map.resources :users, :member => { :profile => :get }
map.resources :users, :new => { :draft => :get }
第一行创建的路由是:/users/rss
第二行创建的路由是:/users/1/profile
“1”就是user_id,我们需要知道用户ID才能得到用户的profile.
第三行创建的路由是:/users/new/draft

helper_method是什么意思

Published on:

Helper是什麼

Helper是一種輔助方法,目的是將Ruby code帶入view當中,而不是單純的平舖直敘。這點也是Rails分工的一環,model和controller的method不能在view當中直接使用,只有helper method才行。

Helper和Helper method差在哪裡?

Helper是一個概念,而在Rails架構中helper自己有一個資料夾,我們可以在裡面定義helper method,定義完以後就可以在view當中使用。Rails當中有提供很多內建的helper,只要是.erb結尾的檔案,都可以使用helper,包括html.erb、js.erb等等。

當然,helper這個詞並不會使用於controller和model當中。

find与find_by的区别

Published on:

find

根据id进行查询,像Product.find(3),查询语句是Product Load (0.1ms) SELECT "products".* FROM "products" WHERE "products"."id" = ? LIMIT ? [["id", 3], ["LIMIT", 1]],也可以直接传一个Product的对象,像

product = Product.first
Product.find(product)

find会把传过去的model对象的id进行查询。甚至可以这样:

user = User.last
Product.find(user)

find会把user的id的值进行查询。
find没有查询到结果,会抛出一个ActiveRecord::RecordNotFound异常。

find_by

需要传递一个hash作为参数。像Product.find_by(id:3),查询语句是Product Load (0.1ms) SELECT "products".* FROM "products" WHERE "products"."id" = ? LIMIT ? [["id", 3], ["LIMIT", 1]]。当然也可以查询其它的字段,像Product.find_by(title: 'the yellow book',查询语句是Product Load (0.1ms) SELECT "products".* FROM "products" WHERE "products"."title" = ? LIMIT ? [["title", "the yellow book"], ["LIMIT", 1]]
find_by没有查询到结果,会返回nil

where

where返回的是一个ActiveRecord_Relation集合,并不是一个model的对象,像product = Product.where("id = 1"),查询后,这样使用product.id是不行的,需要这样prodcut.first.id或者product.take.id

session是什么以及使用方法

Published on:

Session是什么

在计算机中,尤其是在网络应用中,称为“会话控制”。Session 对象存储特定用户会话所需的属性及配置信息。这样,当用户在应用程序的 Web 页之间跳转时,存储在 Session 对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。当用户请求来自应用程序的 Web 页时,如果该用户还没有会话,则 Web 服务器将自动创建一个 Session 对象。当会话过期或被放弃后,服务器将终止该会话。Session 对象最常见的一个用法就是存储用户的首选项。例如,如果用户指明不喜欢查看图形,就可以将该信息存储在 Session 对象中。有关使用 Session 对象的详细信息,请参阅“ASP 应用程序”部分的“管理会话”。注意 会话状态仅在支持 cookie 的浏览器中保留。

使用方法

Session 是 用于保持状态的基于 Web服务器的方法。Session 允许通过将对象存储在 Web服务器的内存中在整个用户会话过程中保持任何对象。
Session 通常用于执行以下操作
存储需要在整个用户会话过程中保持其状态的信息,例如登录信息或用户浏览 Web应用程序时需要的其它信息。
存储只需要在页重新加载过程中或按功能分组的一组页之间保持其状态的对象。
Session 的作用就是它在 Web服务器上保持用户的状态信息供在任何时间从任何设备上的页面进行访问。因为浏览器不需要存储任何这种信息,所以可以使用任何浏览器,即使是像 Pad 或手机这样的浏览器设备。
持久性方法的限制
随着越来越多用户登录,Session 所需要的服务器内存量也会不断增加。
访问 Web应用程序的每个用户都生成一个单独的 Session 对象。每个 Session 对象的持续时间是用户访问的时间加上不活动的时间。
如果每个 Session 中保持许多对象,并且许多用户同时使用 Web应用程序(创建许多 Session),则用于 Session 持久性的服务器内存量可能会很大,从而影响了可伸缩性。

小感悟

Published on:

看开学典礼的感悟:

  1. 6块木板就能组成木桶装水,至于装多少水那是以后的事。
    虎式坦克的确精良无比,一辆可以干掉好几辆T34,但是打群架依然输了。数量,研发成本决定了走向。先打赢再谈精良,仗打不赢什么都没了。所以木桶好看不好看,装的多不多不那么重要(至少现在对我们来说)重要的是它可以装水了(还离水源很近=。=)

  2. 保护自己的注意力。
    -- 有些东西只要你真想,真愿意花时间。你肯定比很多人强,或者说更大概率。
    -- 网络是双刃剑,互联网改变我们生活的同时也在摄取我们的注意力。太多太多的东西在影响或者消磨我们的注意力了。因为我们很多时候就根本不清楚自己最想要的到底是什么。怎么样才是自己想要的状态。
    -- 你的专注力能有多少。每天花2小时再同一件事情上,不用坚持到习惯的话,用不了几年你也是专家级别的了。你能做到吗?

  3. 阅读英文文档不仅是装逼利器,还是掌握先一手的信息和更有效的学习工具。

  4. 至少,还能当老师=。= 教才是最好的学。教会别人,升华自己。

看项目管理的感悟:

  1. ppt很关键,关键的能让人注意到你的产品和代码,不至于默默无闻。
  2. 时间的安排是门技术活。让自己和团队保持一种时间不充裕的状态,使其发挥出更强大的功效。
  3. 预留下的时间,做充分的测试和排练,确保上线不爆炸的几率。也锻炼了团队的功力(压缩时间)写出更简洁有效的代码。
  4. 再科学的安排也需要强有力的执行力才行。所以,自己本身就要有这样良好的习惯和经验。

Git 基本指令

Published on:

Git基本指令

git init在資料夾內安裝Git環境。
git add (filename)選擇要給Git監控的檔案,可以搭配Linux的指令收尋多個檔案,我會使用的方式是用git add .監控所有檔案,再搭配.gitignore檔案去Ban掉不需要監控的檔案。
git -m commit "(commit name)"為一個段落提交一個紀錄點,雙引號內的commit名稱需要多練習唷!很多時候commit一多而名稱嚇得不清楚的話後面維護會很麻煩。

以上為正常流程最常使用的一套指令,再來介紹版本控制最大的魅力,當開發到一半發現專案做亂掉了怎麼辦?

git reset HEAD^可以把所有檔案回到上一次commit的狀態,所以特別建議新手每做一點小修改就下一個commit,讓自己多掌握自己作品的控管。
git checkout .這是蠻特別的用法,效果會跟上面一樣,小細節上就先不談。

但如果像上述說的下了一堆COMMIT,但我有一個想拿掉的功能已經下了數次COMMIT該怎麼恢復?(意思就是重回一次以上的COMMIT點)

git log查看所有commit記錄,這時候commit名稱下得清不清楚的好處就在這裡顯現囉!查看完之後可以按q離開。

git reset HEAD~(number)查完想回到的第幾個commit之後數字是回到前幾次版本。
例如git reset HEAD~2是回到前兩次,git reset HEAD~3則回到前三次,以此類推。

reset絕對要小心使用!

在我們開發專案中,其實最好保持MASTER(主支)的乾淨,所以要做任何新的功能最好開新的BRANCH(分支)開發。

git branch列出目前有多哪些branch(只在本機端的)
git branch -a列出所有branch(包括遠端的)
git branch (branch name)產生新的branch
git checkout (brach name)切換到指令brach,這時可以開發新功能囉!
git branch -d (brach name)刪除指定branch
git branch -D (brach name)強制刪除指定branch

如果做到一半必須先去別的分支做其他事情又不想亂下COMMIT的話我們可以用GIT的暫存(STASH)功能。

git stash把從最後一個commit到剛剛的東西暫存到暫存區裡,這時的專案狀態又會回到最後一次commit那邊
git stash list列出所有暫存區(stash)的資料
git stash pop取出最新的一筆並移除在暫存區(stash)的資料
git stash apply取出最新的一筆暫存資料但是暫存資料不移除
git stash clear把暫存區(stash)的東西都清掉

GITHUB提供的遠端平台,可以讓大家使用GIT共同開發一個專案,稍微熟悉的話對新手算是加分哦!先介紹自己管理自己的專案。

git remote add origin git@github.com:username/(repository
name).git
指定遠端的地址,使用Github創造新專案(repository
)的時候就有清楚的引導囉!
git push在設定完地址和下完commit的時候即可使用這個指令上傳到剛剛設定的Github專案上,要注意的是版本狀態必須是最新的,也就是說是下完commit之後才能上傳,一個檔案都不能有修改過。
git status查詢有哪些檔案是修改過的,可以用這個指令查詢因為修改過而不能git push的檔案。

再來我們有可能是去下載一個檔案來做開發。

git pullpush是上推檔案到遠端的話pull就是從遠端下拉檔案的意思囉,可以把別人的專案更新到最新狀態。
git merge (branch name)在別的分支開發玩的功能要合併到比較主要的分支就下這個指令,要注意如果有衝突(conflict)的話Git會提示你,請小心解決,個人會比較推薦使用Github上的功能更有可讀性。

更多的Git命令