貼code

2017年2月21日 星期二

用production mode 來部署網站


    一般情況下,直接在專案底下輸入rails server會使用development mode來啟動rails app。但在真正要上線,必須改成使用production mode。這樣一來,你的rails app跑起來才不會出現紅色的error頁面,再來,rails 在production mode會先將一些css js等放在assets裡的東西預先編譯(pre-compile)好來增加網站的效能。要改成production mode,請先完成以下事項。

    首先修改config/secret.yml,設定production mode的secret_key_base。rails 預設已經幫你把develop mode 跟 test mode的給寫好了,但要自己新增production mode中的。原本的secret.yml如下
# Be sure to restart your server when you modify this file.

# Your secret key is used for verifying the integrity of signed cookies.
# If you change this key, all old signed cookies will become invalid!

# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
# You can use `rails secret` to generate a secure secret key.

# Make sure the secrets in this file are kept private
# if you're sharing your code publicly.

development:
  secret_key_base: aed39a79230a55e79be1e261947e976f66c9830a5b9f438e7b570b3ebbca7b71cbdebbfb7eaef86a06f9a35fc6b3ed257bae12af510dcd23914da0ee32afd157

test:
  secret_key_base: 589c27fe53513bcbcb0a9ec8e6d050fb0661e073e08b8d6220ccbb4eb305e0b73602f6e5fe8b93f08f2fc49a26baf468c1a22dde3a27f6b87f8b761a0fa3dc7e

# Do not keep production secrets in the repository,
# instead read values from the environment.
production:

secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

在終端機底下輸入
rake secret
把生出來的一串東西,取代production: 底下的<%= ENV["SECRET_KEY_BASE"] %>

存擋後,再來在production mode地下的db需要建好。請先把database.yml設定好,看production mode想用哪種sql(通常建議mysql 或 postgresql)
接著在終端機輸入
RAILS_ENV=production bundle exec rake db:migrate

接著要先讓rails 把assets編好,所以一樣在終端機輸入
RAILS_ENV=production bundle exec rake assets:precompile

最後雖然compile了,但production模式預設是不會讓使用者讀取/public/assets的檔案。所以要在config/environments/production.rb裡加上
config.public_file_server.enabled = true

請注意:rails 4以前的版本可能會能夠將config.serve_static_assets = false這行改成true,但在rails 5已經不支援這樣寫了。另外,在網路上有查到
config.assets.serve_static_files = true 這樣的寫法,根據官方文件,這樣寫在rails 5的新一版會被踢除,請改成上面那樣的寫法。

照這樣設定完,就可以在終端機輸入以下來將rails app以production mode跑起來摟!
rails s -e production


參考資料:http://easonchang.logdown.com/posts/2016/08/30/start-your-rails-project-with-production-mode

2017年2月15日 星期三

Rails 5的外部鍵問題

Rails 5中有個更新,就是以往外部鍵的驗證要自己加,也就是說外部鍵可以是nil。但在Rails 5中,這個設定被取消了。也就是說,所有的外部鍵都必須要存在。而最近的案子中好死不死就需要讓一個order可以是非會員所下的。也就是order的user_id要為nil。要怎麼辦呢?

提供以下兩種做法:

  1. 直接在belongs_to: xxx 後面加上 optional: true
  2. 在controller裡加入(套用在整個application)

Rails.application.config.active_record.belongs_to_required_by_default = false
參考資料:http://blog.bigbinary.com/2016/02/15/rails-5-makes-belong-to-association-required-by-default.html

2017年2月12日 星期日

將Carrierwave與Administrate 整合

    Carrierwave 是 Rails 裡常用的照片上傳的一個gem , 而Administrate是一個較新推出的admin管理gem 。今天要來記錄如何將Carrierwave當成一個Administrate的custom field

(關於Carrierwave的設定可參考 https://medium.com/@mauddev/rails-5-and-carrierwave-53960ec20c4b#.d9sdv7r1w)

再新增完一個uploader(假設叫ImageUplader),並在想加入照片的model(假設是Post)裡加入一個欄位(假設叫 :img, :string)之後,在administrate的post_dashboard裡
ATTRIBUTE_TYPE中加入
image: PostImageField
PostImageField可以自己換成其他名字,然後在其他地方加入image的這個欄位
再來終端機上輸入
rails generate administrate:field post_image
然後就會發現在controller/field 裡出現一個post_image_controller
在裡面新增一個method
  def to_s
    data.url
  end
接著,會在view/fields中看到三個partial ,_form代表在admin頁面中的編輯頁面如何顯示
所以將它改成可以吃一個檔案
<div class="field-unit__label">
  <%= f.label field.attribute %>
</div>
<div class="field-unit__field">
  <%= f.file_field field.attribute %>
</div>

然後_index 跟 _show裡加上
 
<%= image_tag field.to_s %>

這樣就行摟~
下次再更新如何加入thumb 跟 small的照片部分吧!!

參考資料:
https://administrate-prototype.herokuapp.com/adding_custom_field_types

2017年2月6日 星期一

Rails 購物車 cart的model部份

Rails 購物車cart 的部分。
有幾點值得注意:
  1. 功能非常基本 , 有update的部分需要修改
  2. 購物車的基本概念是,使用者在購物時會新增一個session,這個session是一個hash,命名為@product,裡頭會以各個product的id作為key,以product的amount作為value。
  3. 當確定要結帳時,再將cart送到訂單,訂單部分再接Active Record再連到資料庫。如此一來可以避免產生許多未完成的購物資料(沒真正要買)。 
code 部分:
class Cart
  def initialize(session_cart)
    if session_cart
      @products = session_cart.map{|key, val| [key.to_i, val]}.to_h
    else
      @products = Hash.new
    end
  end

  def update(product_id, amount)
    if amount == 0
      @products[product_id] = nil
    else
      @products[product_id] = amount
    end
  end

  def add(product_id, amount = 1)
    if @products[product_id]
      @products[product_id] += amount
    else
      @products[product_id] = amount
    end
  end

  def to_h
    @products
  end

  def count
    @products.count
  end

  def each
    return enum_for(:each) unless block_given?

    @products.each do |product_id, amount|
      product = Product.find_by_id(product_id)
      if product
        yield product, amount
      end
    end
  end

  def total
    @products.reduce(0) do |total, (product_id, amount)|
      product = Product.find_by_id(product_id)
      if product
        total + product.price * amount
      else
        total
      end
    end
  end
end

解釋:

  1. initialize的部分,要先確定瀏覽器裡是否已經有session_cart了,如果有,則將session_cart 裡的每個key變成interger。但map完會變成一個array,所以用hash內建的method把array變回hash再塞回@product
  2. update部分 清空一個key value的部份需修改。否則的話,@product就會以product_id為key ,amount為value來修改這對key value。
  3. add概念類似第二點
  4.  to_h為cart這個類別新稱一個method,使一個cart類別下的物件可以用.to_h得到@product這個hash變數
  5.  count目的為了算出有幾件商品,.count為hash變數的方法,算出有幾個key value pair
  6. total目的是為了算出總共多少錢,reduce(0)代表total從0開始。傳入@product裡的product_id 及 amount。設一個product變數去連接到資料庫去確定有沒有id 為 product_id的這件商品,有的話total就加上amount*product.price,沒有的話就維持

rails devise 使用者編輯資料時免當前密碼

    在開發rails 網站時,常常會用到devise這個gem來負責處理使用者的部分。
而devise預設的edit功能是需要輸入當前密碼的。有些情況下不希望如此,就可以利用以下方法(客製化devise 的controller)

首先 在 config/routes.rb 裡,加入
devise_for :users, controllers: { registrations: 'users/registrations' }
(如果devise:controller 的scope 是 users的話,即rails g devise:controllers users )

再來在users/registrations_controller.rb裡,加上
protected
def update_resource(resource, params)
    params.delete :current_password
    resource.update_without_password(params)
end

最後把edit.html.erb頁面裡的current password欄位拿掉就可以摟!!

===2017.2.12更新
後來發現這樣做的話,密碼就不能改了OAO
所以把registration_controller裡改一下
變成current_password的欄位還是show出來
然後判斷,如果current欄位跟new_password都是空的
那就不用current_password 就可以改其他資料
protected
  
def update_resource(resource, params)
  if params[:current_password].blank? && params[:password].blank?
    resource.update_without_password(params.except(:current_password))
  else
    super
  end
end


參考資料
https://github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-edit-their-account-without-providing-a-password

2017年2月5日 星期日

隨手記上線摟!

最近開始練習一些Ruby on Rails 的專案,遇到了不少的問題。但隨著出現及解決的問題越來越多,很多時候都會忘了當初解決問題的方法。於是乎想來設個網誌,紀錄一下遇到的問題以及後來解決的方法。所以...請多多指教拉