[v] xdite rails 第一週 hw

⽤ Bootstrap 做⼀個「有 Nav bar 」的 layout

  • //= require bootstrap/dropdown 的 bootstrap/dropdown 從哪來?bootstrap 外還有其他來源可以 require 進來嗎?
  • 新增 partial: navbar的部分,有段內容如下,為什麼 <%= yield %> 包在 < div class="container"> 裡面?
+  < div class="container"> 
+    <%= render "common/navbar" %>
+    <%= yield %>
+  </div>
  • 有些路徑長得像 products_path, 但有些則像 ”common/navbar”, “groups#index” 之類的, 這些是必須記憶起來的慣例嗎?

是慣例,要記起來。
products_path 用在controller(這裡是用在index action) 或是 view, 比如說 link_to 後面
“common/navbar” 用在 view
“groups#index” 用在 route

  • 經測試,redirect_to “/“ 可以寫成 redirect_to root_path, 那為什麼 redirect_to account_orders_path 不可以寫成 redirect_to “account/orders#index”?

[不確定]
就是不可以,不爽不要用。
除了寫路徑 helper 外,也可以用相對路徑的方式寫。
用 routes helper 的好處是,如果將來 routes 那邊有更動,routes helper 會跟著一起改。

  • [提問] 怎麼個一起改法?
  • <%= yield %> 是什麼?

像是一個標記,告訴程式該在這裡放進 view, 至於放哪個 view 則看你開了哪個網頁而會自動調整。Rails 很聰明的。
http://guides.rubyonrails.org/layouts_and_rendering.html 3.2 Understanding yield

  • rails g controller products touch app/views/products/index.html.erb resources :products

這時跑 localhost:3000/products,如果沒有 products_controller, 是不是會直接去找 products/index.html.erb?

使用 simple_form 換掉 form

https://github.com/plataformatec/simple_form

  • <%= simple_form_for [:admin, @product] do |f| %> 拆解。

:admin 做為 namespace, 指 admin 下的 product。

  • simple_form 如何知道 input 屬性?
rails form helper
<%= form_for [:admin, @product] do |f| %>
  <div class="group">
    <%= f.label("標題") %>
    <%= f.text_field :title %>
  </div>
<% end %>
simple form
<%= simple_form_for [:admin, @product] do |f| %>
  <div class="group">
    <%= f.input :title %>
  </div>
<% end %>

直接從 db >> table >> column屬性 來判斷。
https://github.com/plataformatec/simple_form

加入產品修改功能

  • @product.update(product_params) 為什麼是 pass in product_params?

問錯問題了,create, update 後面本來就可以接參數,如果接的是 strong parameters, 那就是照 strong parameters 的規定去跑。所以該問的是(下方提問):

  • [提問] .create 和 .update 後面可以接什麼?

實作圖片上傳 carrierwave

  • rails g model photo product_id:integer image:string 中,image 的屬性為什麼是 string?
  • 拆解 mount_uploader :image, ImageUploader
  • uploader是什麼情況?

carrierwave 自動產生的東西
https://github.com/carrierwaveuploader/carrierwave/blob/master/lib/generators/uploader_generator.rb

  • 看API也有寫到 mount_uploader :avatar, AvatarUploader, 是說 carrierwave 有固定的幾類 uploaders, 還是說 :avatar, AvatarUploader是自由命名的?

可以自由命名!另外,carrierwave 什麼檔案都可以傳,不限於圖片,這是我原本誤解的地方。

  • accepts_nested_attributes_for :photo 的用法

當你把 photo 的資料跟 product 資料 一起送出 時,建立 product 後才會立刻跟著建立關聯的 photo 資料
http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
http://archives.ryandaigle.com/articles/2009/2/1/what-s-new-in-edge-rails-nested-attributes

  • 問題:上面 accepts_nested_attributes_for 的用法,和 resources 包在另一個 resources 裡面有什麼差別?

不一樣。resources 包 resources 是在 routes 的寫法,accepts_nested_attributes_for 是做在表單的部份,要做巢狀表單的設計。order.rb 中有一行 accepts_nested_attributes_for :info,建立起 Order 和 OrderInfo 的關聯,而現在我們希望只要填一次表單,就能把兩個 models 需要 create 或是 update 的資料一次送上去,讓程式可以一次處理兩個 models 的東西。其實也可以宣告 accepts_nested_attributes_for :item, 只是我們這邊用不到所以沒寫。以上是巢狀表單在 model 的宣告。

  • resize_to_fitresize_to_fill 的差別?

https://support.cloudinary.com/hc/en-us/articles/202521222-What-is-the-difference-between-Fill-Fit-and-Limit-scaling-modes-

  • 上傳圖片前,先在上傳頁面看到縮圖而不是連結。(在教材中我們是使用carrierwave來傳圖片)

http://stackoverflow.com/questions/4459379/preview-an-image-before-it-is-uploaded

  • 拆解 <%= c.input :image , as: :file %>

在 views/admin/products/new.html.erb 中

< div class="form-group">
  <%= f.simple_fields_for :photo do |c| %>
    <%= c.input :image, as: :file %>
  <% end %>
</div>

這是巢狀表單 view 上面的宣告。Model 那邊是要做資料的過濾把關,view 這邊則是要對巢狀表單的宣告去建立。我們在 admin/products_controller 的 “new” method 裡有一行 @photo = @product.build_photo,這邊 :photo 就是在宣告這個 @photo, 如此才會顯示圖片。第二個部分,<%= c.input :image, as: :file %> 代表photo model 中 image 欄位,圖上傳的資訊存入這個欄位;as: :file 的部份,現在 image 欄位是 string,如果用 simple_form 會自動幫你變成一個 string 的欄位,一個單行表格填寫的欄位,但我們現在是要傳檔案,所以用 as: :file 來把表單屬性從 string 變成可以上傳一個檔案的選項。

那像在 views/admin/product/index.html.erb中,product.photo.image.thumb.url 能夠顯示為 url, 而不是一串 string,原因是在這個例子中,有三個尺寸的images, 所有的資訊都是存到這個 image 欄位中,只是 carrierwave 是用一整包的物件把這些資訊存到這個欄位,這一包有包含圖片資料如,圖片名稱、三種sizes、三種sizes對應的URLs......等。

如果沒有 as: :file, 就根本不能傳檔案上去,因為連 "Choose File" 的按鈕都不會有。

當選好檔案按 OK 要傳上去時,controller 還會再做一次把關的機制,通過 controller 把關之後,controller 會把資訊存進 model。photo.rb 有一行 mount_uploader :image, ImageUploader,ImageUploader是圖片上傳設定檔,去image_uploader.rb 可以看到

def store_dir
  "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end

比如說我們有做size的的設定,它會幫我們把圖裁切成不同size, 然後把圖檔存到 public/uploads 底下的資料夾,也就是下面這行:

"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}” 
  • photo 和 image 的關係是?三種 image version 的 process 是寫在 image_uploader.rb 裡面,所以一張 photo 是什麼時候透過 mini_magick 產生了三種 version 的 image? 在 admin/products_controller.rb 中,@photo = @product.photo 這行做了什麼?在 admin/products/new.html.erb 跟 edit.html.erb 中的 image_tag(@photo.image.version.url) 是不是只是選擇要上傳哪種 version 的 image, 而不是在此時才產生特定 version 的 image?
  • 假如我現在要從上傳一張照片改成可以上傳多張照片,怎麼運用 git flow 來做新功能?model 之間的關聯性以及 controller 的 new, create, edit, update 要怎麼改?

目前的想法:

  1. models 間的關聯性要改
    product has_many :photos
    
  2. controller 裡的 new, create, edit, update 要改

目前已知:

  1. carrierwave 文件有寫 multiple file uploads 的改法

踩到的雷:

  1. 因為有用 mini_magick 在 photo 底下生成 images, 所以不知道上傳多張圖片時,到底是上傳了多張 photos 還是 images。目前的想法是,上傳時可以設定傳哪種 version 的 photo.image, 所以這個 image_uploader 就都是傳一樣 version 的 photo.image, 不能客製化。 [提問] 不設定就是上傳預設的 version 嗎?
  2. carrierwave 必須是 master branch 的版本才有 multiple file uploads 的功能。

問題:

  1. 在 admin/products/edit.html.erb 中有一個條件是是 <% if @photo.image.present? %>,說明了存的圖片是 @photo.image, 那為什麼沒有 Image model?為什麼不是 product has_one image 或是上傳多張照片後的 product has_many images?
  • @photo = @product.build_photo 中的 build_photo 來自哪裡? build_photo 後 @product 有了 photo 就可以直接 @product.photo 召喚物件,對嗎?

.build_photo 的出現是因為 photo belongs_to product。在belongs_to的關連建立起來後,Rails 自動產生數個 methods, 其中包括 build_associationhttp://guides.rubyonrails.org/association_basics.html 4.1.1

  • [提問] 這邊的 .build_photo 跟之前一個問題 ".new 與 .build 的差別" 的 .build 一樣嗎?
  • photo_attributes: [:image, :id] 是什麼?各代表什麼?

這是 strong parameters 在這部份的寫法。

  • 拆解 <%= image_tag(@photo.image.thumb.url) %>
  • 在圖片還沒儲存時,有辦法跳出縮圖而不是路徑嗎?

這是屬於前端的範疇
http://stackoverflow.com/questions/4459379/preview-an-image-before-it-is-uploaded

  • "rails 裡面的一個機制 spring 的 cache 卡住,導致讀取不到 carrierwave 的 uploader,輸入 bin/spring stop bin/spring start 即可解決。" 為什麼 spring 的 cache 會卡住?

前台 products#show, products#index 實作

  • products controller 中的 index 和 show 都不需要註明 @photo = @product.photo,是因為 accepts_nested_attributes_for 的關係嗎?

建立後台 layout

  • 為什麼這裡要安插一個 layout "admin" 的指令?

rails 的指令,用來 override default layout, which is application.html.erb. layout "admin" 是取 app/views/layouts/admin.html.erb 來使用,別忘了這裡是 admin::ProductsController, 我們正在做 admin 才能看到的後台。

建立使用者後台做權限管理

  • member 和 collection 是什麼?

https://ihower.tw/rails4/routing.html

  • post :to_admin 及 post :to_normal 又是什麼?

https://ihower.tw/rails4/routing.html

  • 跟下方 to_normal_admin_user_path(user), method: :post 又有什麼關聯?

要先設好 routes, 才會產生 to_normal_admin_user_path(user) 這樣的 helper, 然後即使已經在 routes.rb 設定了 post :to_normal, 在 <%= link_to "轉為一般使用者", to_normal_admin_user_path(user), method: :post %> 中還是需要註明 method: :post

  • .to_admin.to_normal 來自哪裡?為什麼可以用在自己的 def 中?

admin/users_controller.rb 中,def to_admin 裡面的 .to_admin 其實來自 model 的 def to_admin, 這個 method 的功能是更改 is_admin 欄位的值。

  • self 是什麼?

http://stackoverflow.com/questions/7520444/what-does-self-do-in-ruby-on-rails
http://www.rubyfleebie.com/understanding-class-methods-in-ruby/
https://www.quora.com/With-Ruby-on-Rails-how-do-you-know-when-to-use-the-self-class-or-instance-variables

Deploy 到 heroku

  • 怎麼安排 development, test, production 階段?在 local 端必是 development? push 到 heroku端會自動切換成 development?

不需要安排。上遠端 server 就必然是 production, 在 local 就必定是 development. test 階段是寫 script 去測試 app 有沒有狀況。Rails 在開發階段預設就是吃 development mode,所以很多東西是動態產生的,比如說能看到 debug 資訊,比如說記憶體不會 cache 住,每次撈一筆資料都是重新 refresh, html 也是重新產生;但是在 production 階段就會去 cache 住像 sequel query, html......等。