在一個 Rails 專案，常需要使用一些設定值。最常見的就是各種 API 使用的 key。通常我們會把這些設定獨立起來放在一個 yaml 檔案裡面，避免紀錄在 git 的版本管理系統裡面。個人使用了 Settingslogic ，覺得十分合用，不過在一些細節上我想要推廣一些撇步：
Rails 的啟動是十分花時間的，在 Ruby 1.9 下面通常要等個幾十秒。有時候有些可以獨立運作的套件為了使用一個 Rails 下面的設定值，就得花時間啟動整個 Rails 才能取得設定。要避免這種情形，Settingslogic 我是這樣寫的：
首先是手動 require Settingslogic，然後其 yaml 檔案則是使用相對路徑作參照（與
require "./path/to/app_config" 讀取設定了。
app_config 放在 config 資料夾下面，然後在 config/initializers/ 下開個
app_config.rb 檔案 require 我的
我的 config/app_config.yml 長這樣：
當然這個檔案不會放進版本控制。會放進版本控制的是 config/app_config.yml.example 檔案，裡面只有放 key 沒有值，讓大家知道需要設定什麼。我也沒有用 namespace 之類的東西，反正一個地方就一個設定，依照 exmaple 照樣畫葫蘆即可。
.example.com 的設定。網域設定又會因為環境的不同而改變。 production 跟 staging 各會有不同的設定。所以我覺得把網域寫在 config 中是最恰當的。
我在 production.rb 跟 staging.rb 當然也就這樣改了：
像是 staging/development 這種地方，把可能變動的網域給寫死在 rb 檔然後存進去 git 中本來就怪怪的，所以我都用設定檔來作設定。另一個好處是，想要修改網域就只要修改一個地方，徹底貫徹DRY懶人精神！
最後，記得也要準備一份只有 key 沒有 value 的
Eager loading 是 Rails 解決 N+1 問題的方法，用
includes 方法就能在讀取資料庫資料時順便把 association 也讀進來。
不過 includes 是得在讀取之前就先下的指令，而我卻有時候卻只有一堆已經讀進來的 Active Record 物件，想要只用一次 sql query 把每個物件自己的 association 都讀進來，是該怎樣作呢？其實對一個陣列的 ActiveRecord 我們可以這樣作：
I was trying to upgrade from Rails 4.0 to Rails 4.1. Some specs broke, and I found a small behaviour change.
So my user has_one profile.
Prior to my upgrade, the profile association will not be loaded before/after FactoryGirl.create call.
However after Rails 4.1
This means I have to be careful to not have stale profile in tests, calling
reload more often.
P.S. my factory_girl gem has remain unchanged in version 4.4.0 and factory_girl_rails gem in 4.4.1. So this is solely due to Rails behavior change.
Rails developers often use
to_param to add more information to the url. Taking this very page as an example:
We can clear get an idea of what this page is about. Therefore search engines favours this kind of urls.
However, if we just override
to_param, we would also see other urls getting changed. One most notable example would be edit path:
I think this can cause issues as the seo-friend param can potentially get very long. Then it would be difficult to notice the
edit in the url. Also it may not be that useful to do SEO on action urls (rather than the content urls). We probably will never want edit page to appear on search engines.
I think instead, we should conditionally do SEO on urls that needs it. One way to do this is to have our own version of the url helpers. For example, we can define
post_path like this:
This means only show/delete/update paths will be SEO friendly. Other paths (especially custom actions) will be unaffected.
Naming your helper methods in Rails can be very difficult. What do you feel when you see this:
To avoid method name collision, we usually have to append the model name in front. Helper methods are also global, so you can access all kinds of helpers in one go.
For me personally, I want the following when I create a helper method:
- easily know where to put a helper method
- easily know how to name the method
Reinventing the wheel
People thought of ways to organize these helper methods, which are now commonly called presenters, decorators or exhibiters.
Built-in helper methods all reside within view-context objects. So for presenter to work it needs access to the view-context object. However passing it for every helper call can be tedius. I liked how draper and active_decorator grabs that view_context for you, so you won't have to keep passing it to presenter when calling it.
However I see that its effort in decorating the model object can easily leak. For example would you remember to also decorate the association objects?
I wanted something else instead, a presenter object which contains only the helper methods. I add a
presenter() method in the model object to access the presenter object. No decoration and less issues. The
presenter() object also takes care of caching the presenter object, so you only instantiate a presenter per model once.
So I made my changes on top the active_decorator, and called it lulalala_presenter (all the good names are taken already).
First install by putting this in the Gemfile:
Say you want to add a
title() helper method for auction, which takes care of truncation and html cleansing. I simply create a AuctionPresenter class. Remember to subclas it with
And then in the view I call it like this
I think it is good to start with less magic, but allow user to add magic if they want more convenience.
By default you use
h to access all the build-in helper methods, and
model to access the model object. If you are lazy you can choose to add stuff in your presenter so you can type less. For example you can delegate a few model attributes:
You can probably also automatically delegate everything to models using other delegation libraries.
For skipping typing
h, you can mix-in active_decorator's helper module so it will redirect calls to view-context using
At the end I quite like my results. For the top example, now I have
Having to type
.presenter when calling helpers isn't so bad as I first thought. This makes it very obvious that the method belongs to presenter, so you won't ever mix up a helper methods with model methods.
My two goals are also fulfilled. If I need an
og_image_tag helper for my auction, I immediately know I should put it in the
AuctionPresenter, and I can name it without worrying about prefixing model names.
We have a controller action which has a error handling state.
However eventhough the js is rendered, the response content-type is still "text/html". (The request
Accept is set to
) This is very puzzling to us, because this always worked for us.
Later we found out that the root cause was because we call
render_to_string. If we remove that call, Rails will again be able to guess the content-type as
render_to_string is called, eventhough it is not directly used by the response rendering.
respond_to would also solve this issue, forcing Rails to return
I guess we should always specify
respond_to, so the content-type can be deterministic.