Springok's Blog

Posts match “ rails ” tag:

Rails for Zombies學習筆記

| Comments

當初看到這個標題,還不知道這標題到底想表達什麼? 學了內容原來是CodeSchool作者群利用簡化版的Twitter來說明Rails的框架運作,而且發Twitter的不是人而是Zombie~XD,個人覺得這份教材非常有趣,學起來很有幹勁,簡單整理自己的一些心得~

  1. 作者群將Rails中的最重要的組成都用了相稱的比喻圖像,有效地幫助記憶與熟悉。
  2. 有些練習可能要稍微想一下,但看一下hint基本上都可以解的出來,錯誤也有提示,真的解不出來網路上也google的到~對我而言,盡量不回頭看教材,才知道自己哪種語法不熟,題目都練到可以不看提示,只看需求然後寫對。
  3. 不熟的地方就重複多看幾次,徹底了解,這份教材很多概念都是Rails最最基本的章節。

簡單把此份教材中一些學習重點或對新手(小弟本人XD)不易理解的內容整理如下,作為自身後續回憶複習的參考~

CURD

  1. Hash--Series of key value pairs
    Recipe:
    variable = {key: value}  # 新增或更新變數
    variable[:key]  # Read the value
    
    更多的應用參考Ruby Hash API(2.2.2)
  2. CURD--Create, Update, Read, Delete為資料庫操作的基本四種型態,包括資料讀取、新增、更新與刪除,而在Rails中,由Model扮演與資料庫溝通的角色,講義15頁有相關基本語法可供作參考範例。
  3. Rails的Model命名慣例為單字首字大寫。

Model

  1. Rails_app下的Model檔案內容裡的名稱慣例為單字首字大寫(例如:class TodoList < ActiveRecord::Base),但Model檔案名稱為小寫(例如todo_list.rb),而Model與資料庫表格之間的對應關係(Mapping)可參考講義27頁。
  2. 在Model也會放入資料的正確性驗證(Valiadtion),若驗證失敗,資料則無法存進資料庫,相關驗證方式與基本語法可參考講義31、32頁與Guide
  3. 資料之間的關聯性宣告也屬於Model的管轄範圍,如belongs_to、has_many,基本語法可參考講義37、38頁與Guide

View

  1. Rails_app下的View中存在.erb檔(例如 index.html.erb),erb為Embedded Ruby的簡寫,Ruby inside HTML,<% %>中為Ruby語法,<%= %>則代表將其中Ruby語法的執行結果(Return)顯示(Print)出來。
  2. application.html.erb為HTML與共用區塊的模板(Template),如顯示notice,而其中 <%= yield %> 所在則是顯示個別頁面內容。
  3. 建立連結語法link_to參考講義61、64頁與Guide

Controller

  1. Controller在Rails中扮演的角色(Guide),簡單來說,Controller中定義各種方法(Method)或稱行動(Action),當接收route傳送來的要求(Request)後便與Model溝通要求所需資料,得到資料後再丟給View來做網頁呈現,基本語法參考講義79頁
  2. 在Rails中定義Instance variable如@tweet(But why?),與Accepting parameters如/tweets/1,其params[:id]就是1,相關參考講義81~84頁與Guide。 3.Strong Parameter,則是一種安全機制,詳細可以參考SDLong大的解說Guide
  3. 簡單的權限設定(Authorization),例如用session抓id加上條件判斷,限定該使用者可執行的Action,參考講義94、95頁。

Route

1.Route在Rails中扮演的角色(Guide實戰聖經),url傳至後端,再由route分配到對應的controller#action,講義中也介紹多種變化如Named Routes、Redirect…等

Exercise Refresher

Lab 4, Exercise 3 strong parameter


Lab 4, Exercise 4

2015Rails實戰秋季班上課心得

| Comments

自身背景...

我本身是機械背景,今年五月開始自學WebDevelopment,之前也買了XDite寫的Land Dream Rails Job,照著書中的Check List,完成一些Command Line、Git、HTML&CSS的課程,此外也訂閱了Treehouse線上課程來作為輔助,到參加上課前則是在實作Rails 101s的教材。

參加課程動機...

自己的設定目標是在今年年底前能找到一份Rails開發的工作,來上課除了想培養自身Rails開發能力之外,也能近距離與具業界實戰的前輩學習一些常用的工作策略與習慣,縮短自身摸索的陣痛期,此外,自已對目前現有的一些開源專案與眾人協作的理念滿認同的,未來想參與相關社群,盡一份心力。

關於課堂使用輔助工具...

課程輔助工具方面,XDite是用Slack跟Hackpad來作為聯繫同學們與助教的工具,也用來發佈一些最新的課堂消息,上課也使用Quip作為資料分享的平台,課程講義、每週的共筆等等,作業繳交則是用Github來做,這樣的做法,對於沒有軟體開發經驗的我,一方面覺得很新鮮,另一方面也覺得很方便又即時,也多學到了新的協作平台工具,相信未來在這個產業中工作,這種工作模式應該也是稀鬆平常的,也算是另類的職前預習吧~

關於課程...

課程規劃方面,XDite之前也專文討論過了,恩的確如此啊~已從事開發工作的角度,最怕就是你不知道自己到底在做些什麼,為什麼而做,像我以前做機構開發,尺寸變動、材料、零件選用都是有相當的考量,影響的是爾後產品的易製性、使用環境、使用者操作、甚至到後續的維護更換,整個產品的生命週期都要考慮。

我在參與課程時,自己則是以這樣的方式將機構開發聯想到軟體開發上,幫助記憶,第一步你就是需要做軟體規劃,軟體規劃當然很多方式可供使用,這課程是用User Story,相較於單純讓客戶開天馬行空的規格,透過User Story來做溝通討論,讓客戶得知具體的目標,同時也讓開發者有技術上的發揮空間。

User Story有了,再來就是技術層面的執行,我也相信條條大路通羅馬,同一個User story在程式寫法一定有很多種,課程教的其實是有實際開發經驗的工程師寫法,知道程式中哪些是重點,哪些小細節值得琢磨,哪些關係在早期建立起來對後續開發有幫助,看書、看網路的部落格當然也可以慢慢累積,但是站在巨人的肩膀當然是最有效率的方式,把找資料的時間,拿來實作,對新手而言絕對是有相當大的助益。

另外,我自己也喜歡在開發軟體上,有個概念叫做Design later(應該吧?不確定,XDite第一堂課分享提到的),就是你可以把一些現階段還不知道怎麼做的功能先保留下來,無須浪費時間琢磨,先做其他的功能,把大架構完成,等之後有靈感、或確定作法了,再來實作,例如:實作部落格,你可能還沒想好要怎麼做文章編輯的功能,就先放著,或是電子購物網站,你可能還沒決定後續的串接金流廠商,就先做一個假的,讓整個User story能動,不會因為要等待一些決策,讓你的開發工作停滯不前。

接著,有一個專案原型可以動,就可以來做一些程式碼上的整理,這個我原本很陌生,因為一開始跟著教材練,根本不知道為什麼要這樣整理,但課程中會教一套整理邏輯還有一些開發實作經驗上的豆知識,覺得卡卡的就問,漸漸就會熟悉一些Rails上的慣例跟核心的開發思想,然後知道可能有哪些工具,可以幫助你的開發工作更順利,而如果你未來立志成為一個可獨立作業的Rails資深工程師,這個課程是一個相當不錯的起點,而之後就像XDite說的找份工作,天天寫~這也是我現階段努力的目標。

透過這樣的有前後連貫的課程規劃下來,收穫很多,除了快速入門一些必要的開發技能,對於想透過Rails實現自己內心Idea的準老闆們,XDite也提供了一些不錯的方向,甚至是實作方法,剩下的就是要靠自己多加琢磨,朝心中偉大的航道邁進(?)

談談每次上課時的感受心得...

其實每次都還滿期待上課的,因為覺得又可以學到新東西,然後覺得三個小時的時間一下就過去了,課堂時間其實還滿緊湊,講完一小節,馬上實作功能,尤其是第二週,搞得很緊張,手忙腳亂,一下切Quip看講義、一下切Subl打code、一下切iTerm下指令、一下切chrome看localhost:3000結果、一下切Evernote簡單做個筆記、一下抬頭看投影片、一下低頭吃便當,好險助教們人很Nice,解決疑難雜症的速度又快~而且其實我每次都還滿想留下來聽各位同學問的問題,參與討論,但一下課都要去搭客運回新竹,實在有點可惜啊~

自身寫作業時的心得...

自己在家做作業的時候,除了複習課堂上的東西,還要寫作業、共筆,卡關還要找資料重新釐清自己的觀念,其實需要滿多時間消化吸收,但漸漸你也會感受自己的成長,反覆練習真的很重要~累了就到DeRoot找同學、助教、前輩大大聊聊天、討論問題,或督促自己來作作業,都是幫助自己持續往前的好方式。

對於XDite在解答同學問題時...

我先承認其實我都沒有問什麼問題XD,但毫無疑問,XDite是個樂於分享知識的人,對於問題有問必答,也無所保留,每次也都會開放Q&A時間,像昨天的課程上,甚至用自己在執行的一些案子來解說,也做了簡單的Demo,也分享許多在開發專案上的一些經驗或使用過的工具,為的就只是解答同學們的疑問,個人覺得還滿佩服的,願意把自己的所學分享出來,不怕你學,對我來說也是個難得的學習經驗。

順帶一提,我也在Rails meetup或新手村接觸很多前輩大大,也都有相同的傾向,讓我感受到Ruby/Rails這個生態圈,真的很樂於分享所學。

結論

簡單條列目前對我自身來說收穫最多的部分(到第三週)

  • User Story導向的開發概念
  • Rails 的一些基礎概念加強
  • 程式碼整理與課程專案相關Gem的介紹
  • 共筆整理的資料收集與邏輯訓練
  • 認識一些朋友與Ruby/Rails生態圈

這課程對於想用Rails找工作的人是個很好的起點,而對於已經有一些想法進而想要實現創業,在尋找適合的開發工具的人,也提供了一個很不錯的網站原型,就我個人而言,我是屬於前者,而且覺得收穫豐盛,然而課程結束其實才是真正的挑戰開始,之後希望能將第四週的課程跟一些未來找工作的心得合併再一起做個分享~

Rails101學習筆記(1)-關於Model

| Comments

這篇是筆記型式,紀錄我在練習實作Rails101時遇到的一些問題或是觀念上卡關的地方,以供爾後複習時,快速回憶之用~

資料庫欄位名稱打錯了怎麼辦?

如果不小心在建立資料庫表格時,column名字取錯或打錯,可參考下面的方法更改:

Step1. 新增一新的migration
rails g migration FixColumnName

Step2. 在 db/migrate/xxxxxxxxxx_fix_column_name.rb中用rename_column的方式修改成正確的column name

class FixColumnName < ActiveRecord::Migration
  def change
    rename_column :table_name, :old_column, :new_column
  end
end

step3. 再跑rake db:migrate

rake db:migrate

http://stackoverflow.com/questions/1992019/how-can-i-rename-a-database-column-in-a-rails-migration
上面連結中的回答有提到兩種方式,我後來選了第二個方法,用了 rake db:rollback指令(輸入rake -T 可看相關指令),回溯資料庫後,重新產生一個新的Model覆蓋,但資料就無法保留,過程中,也會問你要不要overwrite原來的/app/models/xxxx.rb的檔案,之後有幾個選擇,可按h看進一步解說,這部分選擇就看個人了囉。

Model關聯物件的新增刪除

model之間的關聯設定完成後,就可以利用Rails提供的method來作關聯物件新增刪除操作,這邊簡單記錄一些容易搞混的method。

build vs. new vs. create

而new跟build基本上沒有差別,build只是new的alias,而create則是new完之後還會執行後續的SQL指令將資料寫入資料庫(就是save)。

若以Rails101中的程式碼說明,前兩行程式碼,group使用了build新增post,再把post的author給currentuser,而此時資料尚未存入資料庫(save動作是在按下頁面表格中Submit按鈕後才執行)。

而後續if conditional判斷式,則是作為是否成功執行save的判斷。

app/controllers/posts_controller.rb
def create
    @post = @group.posts.build(post_params)
    @post.author = current_user

  if @post.save
      redirect_to group_path(@group), notice: "新增文章成功!"
    else
      render :new
    end
end

destroy vs. delete

執行delete會直接從Model中移除該關聯物件,並將其foreign keys設為null。
而destory除移除關聯物件外,還會執行相關的Callback動作

Model關聯的設定選項(Association Reference)

model之間的關聯設定完成後,有時候做一些額外的設定,來輔助實作,簡單說明一些之前卡住的觀念題~。

關於Foreign_key設定的運用時機

在建立belongs_to關聯性後,慣例上,Rails自動會將該model的foreign_key設為關聯Model的名字加底線id,如下面例子中Order model的foreign_key就應該是叫customer_id,但需要注意的是foreign_key欄位必須由自己手動建立,也就是在Order model中加入customer_id的欄位。

class Order < ActiveRecord::Base
  belongs_to :customer
end

但有時候,如下面的例子,會使用class_name設定Patron model作為實際關聯對象。

雖然一樣可以使用customer_id,但卻不太直覺,這時候可以客製化foreign_key的名稱為patron_id,然後把Order model中customer_id欄位名稱更改為patron_id,如此一來就直覺多囉。

class Order < ActiveRecord::Base
  belongs_to :customer, class_name: "Patron",
                        foreign_key: "patron_id"
end

Counter_Cache

這個功能可以自動更新model的關聯物件數量(新增或移除物件時,會自動加一減一),常常我們需要使用這個資訊來實作功能,藉由這個方式,就不用每次跑大量的SQL count查詢。

首先必須先自己在Model中手動新增一個欄位,以下面的例子來說,依照慣例,將新增欄位命名為orders_count再加入counter_cache: true,如此一來, @customer.order.size就會變成自動去讀orders_count的值,不會傻傻地再跑一堆SQL count查詢啦。

class Order < ActiveRecord::Base
  belongs_to :customer, counter_cache: true
end
class Customer < ActiveRecord::Base
  has_many :orders
end

如果需要的話,你也可以客製化counter_cache對應的欄位名稱,如下:

class Order < ActiveRecord::Base
  belongs_to :customer, counter_cache: :count_of_orders
end
class Customer < ActiveRecord::Base
  has_many :orders
end

Rails101學習筆記(2)-關於View與Helper

| Comments

在Rails中,View常常搭配helper使用,來作頁面的呈現,不管是內建的或是自訂helper都可以讓我們在設計頁面上更有一些彈性,例如:可能你會在不同的頁面但想要有相同的部分區塊呈現,或是類似的頁面產生邏輯,此時就可以藉由呼叫helper幫你達成。

在Rails架構底下,在產生Controller的同時,Rails就會自動幫你生一個同名的檔案,而我們會遵循一些慣例,把自訂helper放到對應的位置,各自controller使用的template就放在該檔案下,而像是整個網站或application都會用到的,我們就放到app/helpers/application_helper.rb。

順帶一提,helper是全域的,命名也很自由,不需要對應controller的名稱。

API參考:
http://api.rubyonrails.org/classes/ActionView/Helpers/TagHelper.html#method-i-content_tag
ihower大大關於Helper的詳細說明:
https://ihower.tw/rails4/actionview-helpers.html

Content_tag與搭配自訂helper實作:

Content_tag這個Rails的helper可產生一HTML小區塊,基本的用法如下:

content_tag(:p, "Hello world!”)
      # => <p>Hello world!</p>

也可以在裡面再包content_tag

content_tag(:div, content_tag(:p, "Hello world!"), class: "strong”)
      # => <div class="strong"><p>Hello world!</p></div>

在Rails101中則是使用在notice_message這個自訂的helper中,幫助在頁面中產生相關提示,像是”成功建立文章“等等,是個不錯的應用情境,如下所示:

application_helper.rb
module ApplicationHelper

  def notice_message
    alert_types = {notice: :success, alert: :danger}

    close_button_options = {class: "close",  "data-dismiss" => "alert", "aria-hidden" => true}
    close_button = content_tag(:button, "x", close_button_options)
     #上面這行就用了content_tag,並且相關設定包成了close_button_options這個hash,一來方便調整,二來也增加易讀性。


    alerts = flash.map do |type, message|
      alert_content = close_button + message

      alert_type = alert_types[type.to_sym] || type
      alert_class = "alert alert-#{alert_type} alert-dimssable"

      content_tag(:div, alert_content, class: alert_class)
    end
     #產生alert的區塊中也用了content_tag,先產出相關設定,最後一行再產生HTML區塊。


     alerts.join("\n").html_safe
  end
end

另外關於上面這段Code,也簡單補充幾個在實作時比較轉不過來的地方。

flash.map
flash是Rails內建的Method,幫助我們做一些即時的訊息提示。
map這個method是把值丟進後續指定的程式碼執行,並return執行結果。
另外,map跟each有點差別:
each是在Array底下的method,而map在Ruby裡面是包含在enumerable的mixin底下,詳細說明則可參考map vs. each

alert_class的樣式則是使用了bootstrap提供的alert type包含success, info, warning, danger。

Simple Format

simple format可把文字轉成具簡單格式的HTML區塊呈現的helper,用法如下:

my_text = "Here is some basic text...\n...with a line break.” #範例文字設定
simple_format(my_text)
# => "<p>Here is some basic text...\n<br />...with a line break.</p>"
# \n指的是換行,而遇到換行simple_format就會自動加入<br>斷行,如下所示
simple_format(my_text, {}, wrapper_tag: "div”)
# => "<div>Here is some basic text...\n<br />...with a line break.</div>"
# 預設是用<p>包起來,也可以將客制設定改為<div>
more_text = "We want to put a paragraph...\n\n...right there."
simple_format(more_text)
# => "<p>We want to put a paragraph...</p>\n\n<p>...right there.</p>"
# 連續兩個以上的換行,會將前述文字用<p>包起來

而在Rails101中則用在下列區塊,提供自動換行:

posts_helper.rb
module PostsHelper
  def render_post_content(post)
    simple_format(post.context)
  end
end

Form_for與Simple Form

在專案中的頁面中,常常需要使用表單,Rails中也提供各式各樣的Form helpers供表單呈現與使用。

像是表格的各欄位通常會對應到model中的各個欄位,例如在頁面中個人資料表單的各欄位,對應到Person這個model中的屬性(attributes),像是姓名、出生年月日等等,而其中Form_for這個helper就可以幫我們直接把表格中各欄位跟model的各屬性綁在一起。

Form_for基本用法在Rails guideihower實戰聖經都有相當完整的解說。

而在Rails101中,使用了Simple form這個gem,這個gem讓產生頁面表單更有彈性,做更進一步的客製化,還可以整合bootstrap做一些樣式上的變化,不過因為Rails101所使用的表單與欄位設計上還沒有很複雜,暫時還感受不到他的威力,但還是可以透過其他前輩的文章感知一二,而基本用法推薦觀看Railscasts#234

時間格式的轉換

在Rails中時間格式有許多不同的變化,可透過to_s來選用自己需要的格式,用法如下:

time = Time.now          # => Thu Jan 18 06:10:17 CST 2007
time.to_s(:short)        # => "18 Jan 06:10"
time.to_s(:long)         # => "January 18, 2007 06:10"

像是create_at或是update_at都可以使用這個method

也有其他用文字敘述的呈現方式distance_of_time_in_words或是time_ago_in_words,用法如下:

from_time = Time.now
distance_of_time_in_words(from_time, from_time + 50.minutes)
# => about 1 hour
distance_of_time_in_words_to_now(Time.now - 30.second)
# => "less than a minute”

Rails101學習筆記(3)-Others

| Comments

Router相關

在Rails開發框架中的Router,主要功用有三個,其一是辨識HTTP request的URL網址,再丟到對應的Controller#action去處理,其二,在Rails中有一些慣例,Router就依據這些慣例規則產生路徑helper與URL,也免去在view直接指定URL的瑣事,最後,Router也可以開發者的需求,客製化設定一些路徑與URL,而以上這些都可以透過編輯config/routes.rb這個檔案來達成,如何正確地撰寫這些生成路徑規則與客製化設定,絕對是Rails開發中最需要把握的環節之一。

提供一常見Rails面試題目是來自5倍紅寶石,可用來檢視自己對於相關規則是否了解。

另外愛用$rake routes,可以檢查目前設定規則,包含URL helper, URL網址與對應的Controller action。

下面簡單筆記一些Rails101中用到的routes.rb設定

namespace

Namespace是Scope的一種特定應用,特別適合例如後台介面,這樣就整組controller、網址path、URL helper前置名稱都影響到:

config/routes.rb
namespace :admin do
  resources :projects
end

如此對應的controller會是Admin::ProjectsController,URL網址則如/admin/projects,而URL Helper如admin_projects_path這樣的形式。

member & collection

如果是要產生路徑對應至某個特定元素操作的自訂Action,像Rails101s中groups#quit與groups#join:

config/routes.rb
resources :groups do
    member do
      post :quit #退出討論版

      post :join #加入討論版

    end
    resources :posts
  end

就可產生網址格式:

  • groups/[:id]/quit
  • groups/[:id]/join

URL helper:

  • quit_groups_path(@group)
  • join_groups_path(@group)

而如果是要產生路徑至群集操作的自訂Action,用法如下:

config/routes.rb
resources :photos do
  collection do
    get :search  #對應到photos#search

  end
end

就可產生網址格式:

  • photos/search

URL helper:

  • search_photos_path

Gemfile

Rails專案中所需要使用的Gem都列在這個檔案中(ihower實戰聖經的寫法說明),而每次修改Gemfile後,務必記得執行bundle install,bundler就會檢查現有並安裝新增的Gem,同時產生一個 Gemfile.lock 檔案,當中會詳列出使用gem的版本,把這個檔案納入git commit的對象中,則可讓其他開發者與上線版本使用相同的gem版本了。

另外在gem後面加上 "require: false" 代表在跑 rails server 的時候不會去呼叫到這個 gem,幫助你在開發環境啟動 server 時能較快速運作,未來只要新安裝的 gem 若不會用在 server 運作上,都可以加入這段來減少無謂的 loading。

Rails如何使用Carrierwave上傳圖片

| Comments

這篇介紹如何使用Carrierwave實作圖⽚上傳功能與並以mini_magick產生不同尺寸的數張圖片。

目前carrierwave是上傳圖片時普遍選用的gem,而且有很多功能可以玩,例如可整合另一個自動裁切圖片大小的gem叫mini_magick,下面簡單記錄我實作的一些步驟與設定。

另外要使用mini_magick前,須事先安裝image magick,如果是mac環境底下,應該跑一下下面指令就可以完成安裝了。

$ brew install imagemagick

不知道自己有沒有裝,跑一下

$ convert -version

OK的話就會出現類似下面的畫面:

$ convert -version
Version: ImageMagick 6.8.9-7 Q16 x86_64 2014-09-11 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2014 ImageMagick Studio LLC
Features: DPC Modules
Delegates: bzlib fftw freetype jng jpeg lcms ltdl lzma png tiff xml zlib

情境說明:

User story:

賣方在網站上架商品時,要可以上傳實物照片。

Model:

  • Product
  • Photo

實作步驟:

  1. 安裝Gem
  2. 設定model與建立關係
  3. 在Rails console確認關係
  4. 設定上傳圖片時,同時切成各種尺寸的圖片供使用
  5. 設定.gitignore (選擇性步驟,但建議作)
  6. 修改controller與頁面
  7. 上傳看看吧~

Step1. 安裝Gem

安裝跟其他gem一樣,在Gemfile新增兩行

Gemfile
gem 'carrierwave'
gem 'mini_magick'

再執行

$ bundle install

然後新增一個Uploader,命名為image(舉例說明,可依需求命名)

$ rails generate uploader image

Step2. 設定model與建立關係

產生一個model, photo用來存放照片

$ rails g model photo product_id:integer image:string
# product_id 是foreign_key, image則是之後給mount_uploader的欄位

跑migration

$ rake db:migrate

接著Photo中加入關係與mount_uploader

photo.rb
class Photo < ActiveRecord::Base
  belongs_to :product

  mount_uploader :image, ImageUploader
end

在Product中也做相關宣告

product.rb
class Product < ActiveRecord::Base
  has_many :photos, dependent: :destroy

  accepts_nested_attributes_for :photos  
  #之後我們要做nested form,先在這邊設定接受變更Photo底下的attributes

end

Step3. 在Rails console確認關係

首先需要修正無法在 rails c 讀取到 uploader.rb 的問題:

config/appliaction.rb
module Artstore
  class Application < Rails::Application
   ()
    config.active_record.raise_in_transactional_callbacks = true
+    config.autoload_paths += %W(#{config.root}/app/uploaders)
  end
end

接下來在Rails c輸入Product.first.photos應該就可以確認結果,如下結果就是成功,但因為目前我們還沒上傳任何圖片,回傳的是[ ],而到目前為止圖片上傳功能的Model端設定已經OK。

Step4. 設定上傳圖片時,同時切成各種尺寸的圖片供使用

然後編輯你的image_uploader.rb 讓MiniMagick可以用還可將照片切成各種尺寸

app/uploaders/image_uploader.rb
class ImageUploader < CarrierWave::Uploader::Base
()

+ include CarrierWave::MiniMagick #設定使用minimagick

()

+  process resize_to_fit: [800, 800] #圖片上傳後,自動切成你要的size


+ version :thumb do #設同時切其他size的版本-thumb

+    process resize_to_fill: [200,200]
+  end

+ version :medium do #設同時切其他size的版本-medium

+    process resize_to_fill: [400,400]
+  end

end

Step5. 設定.gitignore (選擇性步驟,但建議作)

上傳處理後的圖片自動存放在public/uploads底下,而建議將public/uploaders 資料夾放入 .gitignore,因為commit這些上傳圖片的變更,事實上也不太有用。

.gitignore
   ()
+ public/uploads

Step6. 修改Controller與頁面

完成以上步驟後,接下來就只要在Controller與頁面加入對應的上傳欄位就可以啦~
以我練習的專案來說,是在新增商品的步驟做圖片上傳,所以在Product#new底下,除了原本的@product之外,多宣告一個@photo,並且在product_params的底下允許存取image這個欄位,像這樣:

 def new
    @product = Product.new
    @photo = @product.photos.new
  end

()

private
 def product_params
    params.require(:product).
    permit(:title, :description, :quantity, :price, photos_attributes: [:image]) 
    # 配合先前在Product model中的設定,使用nested_attributes的設定方式,通過驗證。


  end

然後在對應的new.html.erb,加入該上傳欄位:

new.html.erb
…(略)

  <div class="form-group">
    <%= f.simple_fields_for :photos do |c| %>
      <%= c.input :image, as: :file %>
    <% end %>
  </div>
# 這邊是用我自己在練習的專案作說明,使用simple_form,
# 然後是在已有的表單底下,增加額外的欄位,所以用的是fields_for指令,
# 而要從本地端電腦上傳圖片,所以是as: :file。

或者你也可以給定圖片所在的遠端URL,來上傳圖片,CarrierWave已經很貼心的幫我們做好整合,也有固定的命名方式,所以只要更改上傳欄位的名稱為 :remote_xxx_url,並在product_params的底下允許存取remote_xxxx_url這個欄位,就OK啦,像這樣:

new.html.erb
…(略)

  <div class="form-group">
    <%= f.simple_fields_for :photos do |c| %>
      <%= c.input :image, as: :file %> # 使用本地端上傳,手動更改type為File
      <%= c.input :remote_image_url%>  # 使用遠端url上傳,使用預設type即可(String)
    <% end %>
  </div>

產生的頁面會長這樣:

Step7. 上傳看看吧~

在頁面中選擇其中一種方式上傳後,專案底下public/uploads/photos/image應該就會有三張不同尺寸的圖片囉~

之後如果要在頁面中使用圖片路徑,像下面使用就可以指向圖片所在位置把對應圖片叫出來囉~

Photo.first.image.url        =>"/uploads/photo/image/1/xxx.jpg"        # size: 800x800

Photo.first.image.thumb.url   =>"/uploads/photo/image/1/thumb_xxx.jpg" # size: 200x200

Photo.first.image.medium.url =>"/uploads/photo/image/1/medium_xxx.jpg"  # size: 400x400

#以Photo的第一張圖片舉例。

以上簡單整理供參考,有問題也可一起討論~感謝!

參考資料

Railscast的carrierwave教學影片
https://www.youtube.com/watch?v=YpF_4uciMvg
Carrierwave裁切各種不同版本的圖片設定
https://github.com/carrierwaveuploader/carrierwave#adding-versions
Carrierwave如何設定遠端上傳圖片
https://github.com/carrierwaveuploader/carrierwave#uploading-files-from-a-remote-location

Rails的一些Refactor小技巧

| Comments

Controller中什麼時機可用before_action

before_action最常用於準備跨action共用的資料,或是使用者權限驗證等等,此動作可以接受Code block、一個Symbol方法名稱或是一個物件(Rails會呼叫此物件的filter方法)。

像是如果使用Devise這個gem,會給你的使用者權限驗證的helper- authenticate_user!,通常就會加到before_action。

另外也可以指定before_action所搭配的action,如:

before_action :require_login, only: [:create, :edit, :update, :destory]

如果需要取消從父類別繼承過來的Filter,也可以使用skip_before_action:

skip_before_action :filter_method_name

另外注意的是當有多個before_action的時候,Rails是由上往下依序執行的。

Ref:
https://ihower.tw/rails4/actioncontroller.html#filters

View中什麼時機要用 Helper, 什麼時機要用 partial

兩者都可以協助整理View程式碼,提升可讀性與可維護性。

Partial通常用在HTML Template中需要重複使用的大塊Template區域,通常超過三行以上,例如像是表/選單,nav_bar, footer。

Helper則通常用於只有三到五行左右的HTML,有點像一個小單元而又需要重複使用的情況,或是未來格式可能會變動的物件,例如像是連結、按鈕、時間、圖片等等。

Ref:
使用時機:
http://blog.xdite.net/posts/2011/12/04/misunderstanding-about-render
Partial說明 :
https://ihower.tw/rails4/actionview.html#partials
http://guides.rubyonrails.org/layouts_and_rendering.html#using-partials
Helper說明:
https://ihower.tw/rails4/actionview-helpers.html#helper
http://blog.xdite.net/posts/2011/12/09/how-to-design-helpers

什麼時機可用 Service Object

當需要執行較複雜的機械式程式腳本,可能還有跨model的action時,會考慮將相關流程包裝成Service Object,可大大增加程式碼的可讀性與可維護性。

其他像是,需要使用到外部服務時,像是到其他社群網站發表貼文,也是使用時機。

儘管service object裡面可能會處理一連串的程式執行,但慣例上,一個Service object就是執行一個商業功能邏輯,如商品下訂、開立發票、寄密碼提醒信,通常是在controller裡呼叫一些instance variable輸入service object回傳結果。

使用與應用時機可參考:
Gourmet Service Objects
Service objects in Rails will help you design clean and maintainable code. Here's how.
Service Objects: What They Are, and When to Use Them

ActionMailer與Mailgun實作

| Comments

最近練習專案中有用到ActionMailer,簡單記錄一下使用方式與連接的第三方SMTP寄信服務。

利用E-mail發送各種資訊給網站會員或電子報訂閱者,已經是各網站必備的服務功能,而Rails中的ActionMailer也幫我們做了很好的整合。

一旦我們做好網站寄送Email的相關規劃,如寄送時機、內容等等,就可以利用ActionMailer來自動發信,舉例來說,當消費者在購物網站下單後,寄送訂單確認信,或是用戶註冊後,寄送歡迎信等等。

透過下面幾個步驟就可以讓我們設定好網站寄信的功能:

  1. 建立Mailer
  2. 編輯對應樣版(Template)
  3. 設定寄信者
  4. 在本機預覽Mail與套用CSS格式
  5. 連結第三方SMTP寄信服務(Mailgun)

Step1. 建立Mailer

跟建立Controller很類似,首先我們先建立一個Mailer,而這個Mailer類別(class)中的一個方法(method)也會對應到一個樣板(template),舉例來說,產生一個OrderMailer的指令如下:

$ rails g mailer ordermailer

接著編輯ordermailer,例如新增寄發訂單確認信的方法(method):

app/mailer/order_mailer.rb
def notify_order_place(order)  # 當這個方法被呼叫時,我們希望可以得到order的資訊

     @order = order
     @user = order.user
     @order_items =@order.items
     # 從order裡萃取出等等所需要用的各個實例變數(instance variable)


     mail(to: @user.email, subject: "[ECstore] 感謝您的訂單,以下是您這次的訂單商品明細 #{order.id}")
     # 最後設定收件者與標題並寄送Email

end

Step2. 編輯對應樣版(Template)

app/views/order_mailer/notify_order_placed.html.erb
<div class="row">
  <div class="col-md-12">
    <h2>
      訂單明細 <br>
      <small>
        <%= link_to("訂單連結", order_url(@order.id)) %>
      </small>
    </h2>
    <table class="table table-bordered">
      <thead>
        <tr>
          <th width="70%">商品明細</th>
          <th>單價</th>
          <th>數量</th>
          <th>小記</th>
        </tr>
      </thead>
      <tbody>

        <% @order_items.each do |order_item| %>
          <tr>
            <td>
              <%= order_item.product_name %>
            </td>
            <td>
              <%= order_item.price %>
            </td>
            <td>
              <%= order_item.quantity %>
            </td>
            <td>
              <%= order_item.quantity * order_item.price %>
            </td>
          </tr>
        <% end %>

      </tbody>
    </table>
   </div>
</div>

Step3. 設定寄信者

app/mailer/application_mailer.rb
 default from: servicenoreply@service.com"
 # 可將預設的寄信者設定改掉,以符合使用需求

Step4. 在本機預覽Mail與套用CSS格式

接下來,我們希望能在本機開發端預覽Mail,確認Mail的內容與格式,同時也確認OrderMailer這個Actionmailer運作正常,為了達到預覽目的,需求搭配使用一個Gem叫letter_opener。
首先安裝Gem:

Gemfile
group :development do
     gem "letter_opener"
end

接下來跑bundle install,然後在開發環境的config設定:

config/environments/development.rb
()
config.action_mailer.default_url_options = { host: 'localhost:3000 } #本機端

config.action_mailer.delivery_method = :letter_opener

接下來在Rails c底下輸入:

>> OrderMailer.notify_order_placed(Order.last).deliver!
#抓出最後一筆訂單資料,並寄送訂單確認信

如此一來,瀏覽器應該會就開一個新分頁,顯示發送mail的內容如下:

但可以看出信件格式好像怪怪的,是因為Mailer的樣板並沒有套用CSS的關係,這封mail如先前編輯的是一份HTML格式的文件,如果要讓收信者能看到相關CSS的呈現,勢必也要讓其擁有相關的CSS設定,在這邊可借助Roadie這個Gem達到目的。

首先安裝Gem

Gemfile
group :development do
     gem "letter_opener"
 +   gem roadie"
end

接下來跑bundle install,在樣式底下加入

app/views/order_mailer/notify_order_placed.html.erb
+    <link rel="stylesheet" type="text/css" href="/assets/application.css" />
     # 加入CSS檔案的所在位置連結

<div class="row">
  <div class="col-md-12">
    <h2>
      訂單明細 <br>
      <small>
        <%= link_to("訂單連結", order_url(@order.id)) %>
      </small>
…(略)

再寄送一次試試:

已經套用了基本的表格了。

Step5. 連結第三方SMTP寄信服務(Mailgun)

而寄信方式的選項有:test, :sendmail, :smtp。
其中SMTP (Simple Mail Transport Protocol)又稱"簡單郵件傳輸協議" 是電子郵件傳輸的標準協定,是比較推薦的寄信方式,用Gmail就可以實作,但通常網站服務都需要大量寄信,自架mail server通常不會是個人選項,這時候就可以利用一些第三方的寄信服務,這篇則是以Mailgun說明。

首先,到官網去申請帳號,免費帳號每個月的前一萬封mail免費,註冊完之後,點擊上方Domains進入後,可看到一預設的domain,在點擊後,進入如下的顯示各資訊的畫面,利用這些資訊在Rails中做相關設定。

到Rails環境底下做mail寄送的設定:

config/environments/development
()

config.action_mailer.default_url_options = {  host: 'localhost:3000’ }
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
    address:              'smtp.mailgun.org',
    port:                  587,
    domain:               'sandboxb50419f43d7a4fe590dda7e73ea0896a.mailgun.org',
    user_name:             ENV['SMTP_USERNAME'],
    password:              ENV['SMTP_PW'],
    authentication:       'plain',
    }
()

相關帳密利用(1)環境變數設定或(2)設定在yml檔中,並將該yml檔加入.gitignore,避免帳密外洩。

這樣就設定完成了,把收件者設成自己的Email,然後寄信試試看吧~

PS:在部署環境(Production)下的設定方式也相同,而帳密利用環境變數做設定的,要記得在部署環境中加入相關變數,才讀得到值。

Ref:
ActionMailer
https://ihower.tw/rails4/actionmailer.html
http://guides.rubyonrails.org/action_mailer_basics.html
Letter_Opener
https://github.com/ryanb/letter_opener
Roadie
https://github.com/Mange/roadie
第三方SMTP寄信服務介紹
http://www.yogoeasy.com/mandrill-mailgun-guide/