Jex’s Note

Rails Basic

Rails 指令

  • rails new my_app : 新增 my_app 專案
  • rails s : 啟動 server
  • rails s RAILS_ENV=development : 啟動 development 環境的 server
  • rails s -b 0.0.0.0 : 開放外部, 預設只允許本機 (127.0.0.1:3000)
  • rvmsudo rails s -p 80 : 使用 80 port, 使用 rvmsudo 是因為 rails 起 80 port 一定要用 sudo 身份
  • rails s -p 5000 : 換 port, 預設是 3000
  • rails c : rails console, 可以直接操作 ActiveRecord
  • rails c --sandbox : 沙盒的 console, 在這期間改的 DB 內容在離開時都會還原
  • rails db : 進入 db console 根據 config/database.yml. ex: 如果是 sqlite, 就會進到 sqlite 的 console
  • rake stats : 統計 code 寫了幾行
  • rake tmp:clear : 清除 cache

console :

  • Rails.cache.clear : 清除 cache

Custom helper

  • View 在不需要 controller include 可以直接用 helper 定義的 method
  • controller 要用 定義的 helper 一定要 include

app/helpers/event_helper.rb

module EventsHelper
  def do_something
  end
end

controller :

class BadgeController < ApplicationController
  include EventHelper

  ....
end

Mailer

SMTP 設定

config/application.rb

config.action_mailer.default_url_options = { host: $settings[:host] }
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
    address: "smtp.mailgun.org",
    port: 587,
    user_name: "postmaster@example.com",
    password: "9******************************d",
}

寄信範例

app/mailers/application_mailer.rb

class ApplicationMailer < ActionMailer::Base
  # 如果有設定的話, 在寄信時就不用特別指定
  default from: 'contact@gmail.com'
  default to: 'jxxxlin@gmail.com'

  layout 'mailer'
end

app/mailers/my_mailer.rb

class MyMailer < ApplicationMailer
  def welcome(user)
    @user = user

    mail(to: @user.email, subject: 'Welcome')
  end
end

寄給多位 User : to: 'aaa@gmail.com,bbb@gmail.com'

app/views/layouts/mailer.text.erb

<%= @user.nickname %> 您好~

<%= yield %>

如果有任何疑問,歡迎隨時聯絡我們

謝謝您選擇我們,期待與您的合作。

app/views/my_mailer/welcome.text.erb

Welcome to example.com, <%= @user.name %>

app/controllers/user_controller.rb

MyMailer.welcome(current_user).deliver_later

寫完一封 mail 可以在 console 下執行, 測試信件是否發送出去

Send to multiple recipients

emails = @recipients.collect(&:email).join(",")
mail(to: emails, subject: "A replacement clerk has been requested")

cc & bcc

cc 看到的所有副本的收件人 email, bcc 則看不到

mail(to: recipient.email_address_with_name, bcc: ["bcc@example.com", "Order Watcher <watcher@example.com>"])

reply-to

當收件人按下回覆,預設回覆是給寄件人,但寄件人的 email 往往是客服的信箱,所以可以透過指定 reply-to 去指定收件人

mail(to: user_email, reply_to: [email_1, email_2])

.length vs .count vs .size差異

這三個共同點都是算一個集合的數量,

如果對像是 Array 這兩者的行為無差別,

有差別的是有時候我們會利用這兩者去判斷從 DB 撈出來的筆數如果 >0 再做相關的處理,

從 DB 撈出來的是 ActiveRecord::Relation

a = User.all
User Load (115.4ms)  SELECT "users".* FROM "users"

使用 .length, .size 會直接算出數量

a.length
 => 203

a.size
 => 203

使用 .count 就會使用 SQL count, 成本相對是比較高的

a.count
(2.5ms)  SELECT COUNT(*) FROM "users"<F6>
 => 203

結論 : 如果只是需要單純判斷目前 DB 資料的筆數用 .count, 如果不是或不確定用哪個就用 .length.size

區分環境變數

config/settings.yml

:development:
  :host: '127.0.0.1'

:production:
  :host: 'example.com'

config/application.rb

# 載入 settings.yml
require 'yaml'
$settings = YAML.load(File.open("#{__dir__}/settings.yml"))[Rails.env.to_sym]

要記得先重啟 rails server 才能讀取新的 config,在程式裡使用,會依照你的環境讀取相對的設定

$settings[:host]

其他

.gitignore 新增

*.swp
db/schema.rb
*.DS_Store
Gemfile.lock
config/database.yml

# Personal :
config/settings.yml

只顯示 date 就好

如果網站上常使用 date 顯示, 一般輸出 created_at 都要用 created_at.strftime('%F %T') 很不方便

可以建一個 config/initializers/time_format.rb 或直接寫在 application.rb

Time::DATE_FORMATS[:default] = "%Y-%m-%d %H:%M:%S"

將物件儲存成字串

Marshal

a = {qq: 'xxx', ff: {cc: 'ccc', dd: 'ddddd'}}

# 轉成字串
Marshal.dump(a)
 => "\x04\b{\a:\aqqI\"\bxxx\x06:\x06ET:\aff{\a:\accI\"\bccc\x06;\x06T:\addI\"\nddddd\x06;\x06T"

# 轉回成物件
Marshal.load("\x04\b{\a:\aqqI\"\bxxx\x06:\x06ET:\aff{\a:\accI\"\bccc\x06;\x06T:\addI\"\nddddd\x06;\x06T")
 => {:qq=>"xxx", :ff=>{:cc=>"ccc", :dd=>"ddddd"}}

JSON

# 轉成 JSON
a.to_json
 => "{\"qq\":\"xxx\",\"ff\":{\"cc\":\"ccc\",\"dd\":\"ddddd\"}}"

# 轉成 json ( hash key)
JSON.parse("{\"qq\":\"xxx\",\"ff\":{\"cc\":\"ccc\",\"dd\":\"ddddd\"}}")
 => {"qq"=>"xxx", "ff"=>{"cc"=>"ccc", "dd"=>"ddddd"}}

# 轉成 <F10>
JSON.parse("{\"qq\":\"xxx\",\"ff\":{\"cc\":\"ccc\",\"dd\":\"ddddd\"}}", symbolize_names: true)
 => {:qq=>"xxx", :ff=>{:cc=>"ccc", :dd=>"ddddd"}}

Marshal 會佔比較多的字符 55 : 43

解開 session

Marshal.load(Base64.decode64(cookie_token.split("--")[0]))

devise 登入使用記住我,cookie_token 才能解

Time zone - Taipei 時間

application.rb

config.time_zone = 'Taipei'

簡化判斷 + 迴圈的寫法

View 迴圈

如果要使用 each 前需要先判斷他是否為 nil 或 empty, 否則物件空的可能會噴 Error

此寫法只適合傳回空值是 empty 的, 因為可以將 if 及 each 寫在同一行, 就不需要兩層了(if 一層, each 一層),

但在寫法上也比較麻煩一些, 得先知道他是 empty, 還是 nil

取多筆時, 如果不存在, 返回 empty 的話, 如下 :
>   @posts = Post.where(user_id: 3333)
  Post Load (0.1ms)  SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = ?  [["user_id", 3333]]
 => #<ActiveRecord::Relation []>

因為返回的是 empty, 不是 nil 所以它會是 ture, 以下寫法可以將 ifeach 寫成同一行

<% if @posts.each do |post| %>
  <%= post.title %>
<% end.empty? %>
  You have no posts.
<% end %>

不顯示錯誤訊息的話 :

<% if @posts.each do |post| %>
  <%= post.title %>
<% end.empty?; end %>

對於不顯示錯誤訊息的寫法我覺得 end 那邊有點稍亂

希望能繼續找到更好的寫法

簡化 console 指令

~/.irbrc

class T
  def self.mail(mail)
    User.where(email: mail)
  end
end

Rails console :

T.mail('me@gmail.com)

console 下自動 include (好像沒用.. at 2015/9/3)

config/application.rb

config.console do
  include Rails.application.routes.url_helpers
end

找出 Gem 的真實路徑

In console

 > Gem.loaded_specs['rails'].full_gem_path
 => "/usr/local/rvm/gems/ruby-2.2.1/gems/rails-4.2.3"

Rails lib 真實路徑

ex: upload
/usr/local/rvm/gems/ruby-2.2.1/gems/actionpack-4.2.3/lib/action_dispatch/http/upload.rb

為每條 log 前加上 user_id

Started GET "/" for 127.0.0.1 at 2016-03-31 14:12:52 +0800

加上後 :

[user_id:2] Started GET "/" for 127.0.0.1 at 2016-03-31 14:12:52 +0800

如何加上 :

config/application.rb

config.middleware.delete(ActionDispatch::Cookies)
config.middleware.delete(ActionDispatch::Session::CookieStore)
config.middleware.insert_before(Rails::Rack::Logger, ActionDispatch::Session::CookieStore)
config.middleware.insert_before(ActionDispatch::Session::CookieStore, ActionDispatch::Cookies)

config/initializers/logging.rb

Rails.configuration.log_tags = [
  proc do |req|
    if req.session["warden.user.user.key"].nil?             # 這個 key 是 devise 的
      "Anonym"
    else
      "user_id:#{req.session["warden.user.user.key"][0][0]}"
    end
  end
]

ref : http://stackoverflow.com/questions/10811393/how-to-log-user-name-in-rails

Comments