4 months ago

We have a controller action which has a error handling state.

  if error
    flash[:error] = render_to_string(partial: "foo_error").html_safe
    render :error # renders error.js.erb
  end

However eventhough the js is rendered, the response content-type is still "text/html". (The request Accept is set to
*/*;q=0.5, text/javascript, application/javascript, application/ecmascript, application/x-ecmascript
) 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 text/javascript. Somewhere in the Rails internal must have set the type when render_to_string is called, eventhough it is not directly used by the response rendering.

Using respond_to would also solve this issue, forcing Rails to return text/javascript as the content type.

  if error
    flash[:error] = render_to_string(partial: "foo_error").html_safe
    respond_to do |format|
      format.js { render :error }
    end
  end

I guess we should always specify respond_to, so the content-type can be deterministic.

 
8 months ago

參加了去年年底的 Ruby Kaigi 2015 ,所有演講的錄影都已經放在網路上了 http://rubykaigi.org/2015/schedule

想推薦沒能去的朋友一些我很喜歡的演講。

本次是雙軌議程,我的策略為「要是是日文場就先聽日文場,除非真的沒有興趣」,因為錄影都會放網路上,可是英文即時口譯不會收錄,所以英文就留給以後網路上聽。不過本次還是有點慚愧,常常打瞌睡,有時不專心,只吸收了一半。


本次大會由 Matz 開場,他提出了「Ruby 3x3」的口號,也就是希望 Ruby 3 的速度想變成 Ruby 2 的三倍。而響應這個期許,許多演講也圍繞著這個主題探討,有時候一個技術在不同的主題或抽象層被提到,讓我印象加深不少。

VM技術

第一天第二場就是 IBM 的 Experiments in sharing Java VM technology with CRuby。主題是它開發的 OMR 技術,近日快要開源。這計畫主要是想把 IBM 自己的 JVM 引擎 J9 裡面一些跟語言實做特別常見的模組開源,並希望其他語言能夠納入自己的實做。這樣子的好處是許多語言不用重複開發輪子,共享技術。這些模組改進時,語言也就免費得到的改進。第三天 IBM 更進一步介紹了 OMR 的 GC 原理 ,以及其他 Ruby 可能最佳化的方式。可見 IBM 真的很大力推廣這塊。

關於 OMR 的一些臆測,這裡有篇有趣的中文文章可以讀讀

同時在 JRuby ,也有 Oracle 介紹 Truffle 是怎樣讓元編程也有良好的速度

(果真這種大會議才會看到各個重量級公司來參一腳)

最後一天最後一場則是呼應之前幾場,由 Rubinius 的開發者 Evan Phoenix 所寫的 Key note Ruby 2020。以往像是我這樣的小開發者,都覺的要是 Ruby 很慢的話,把核心功能改用 C 寫,就是最能加速的方法。但是這會讓近年來 JIT 各種加速的技術無用武之地,因為 C 程式本身會像是黑盒子般無法探究內側邏輯,而加速技術很依賴知道程式接下來會作什麼來優化。把 Ruby 的核心庫重新用 Ruby 寫一遍是個解法,但是這樣工程太浩大所以不太現實。

所以 Evan 想提議,把 C 寫的核心庫採用 LLVM 作處理,最後 Ruby 跟 C 會落在同樣的層面,這樣就有如 JIT 之類技術運作的空間了。當然這還需要更多研究,不過作者本身覺的是可行的,讓外行人如我覺的十分興奮。這個演講說得很簡單,讓對 VM 一竅不通的我也能理解發生了什麼,十分推薦大家去聽聽看。

High Performance Template Engine: Guide to optimize your Ruby code

兩位同一家公司的開發者各自開發了自己的 Haml 引擎,想要加快 Haml 的速度。他們探討了模板引擎的原理,以及 Haml 的瓶頸還有克服的方法。適合已經對 Ruby 駕輕就熟的人讀讀,有趣好懂。

Charming Robots

使用 Ruby 控制跳舞墊控制無人機~要說最有趣的 demo 就屬於這個了。請看影片

Plugin-based software design with Ruby and RubyGems

寫開源程式常常會希望自己寫出來的東西能夠有良好的擴展性,比如像是 Firefox 能夠安裝插件一樣。這篇提到 Treasure Data 如何嘗試有插件的架構。成果是插件本身是 gem ,有相依性的管理等等。希望有一天能花點時間弄懂。

Turbo Rails with Rust

介紹了 Rust 語言,以及 Ruby 能怎樣引用它的程式。算是推坑成功,有點想找時間學學看。最有印象的是他提到 static dispatch 很重要,因為這是其他最佳化的前提。

TRICK

使用各種奇淫絕技寫出令人意想不到的程式,舉例來說現場最具視覺效果的就是這個猜球在哪個杯子的遊戲(14:24):

雖然是日文,但是頒獎時介紹每個得獎作品的簡報是英文且有詳細說明,大家可以讀讀拜見天馬行空的程式:

TRICK2015 results from mametter

技術外的感想

食物與 Party

主辦人松田明提到,這次辦在築地,就是因為 2016 年築地市場就要關了,所以他希望大家能趁這機會去試試看各式壽司或海產(這也是本次大會是使用壽司當作圖像意象的發想的原因)。當然就是 2000¥ 3000¥ 一直丟囉,好吃!

每天都有 Party ,除了主辦單位有,各家公司也自己都有舉辦,聽說從會議前的週二就開始,我參加了週四的 Heroku Pre-Party ,第一個聊到天的人就是 Metaprogramming Ruby 的作者 Paolo Perrotta ,十分友善。

有一天是卡啦 OK,在銀座裡面,因為很高檔所以要 5000¥ (倒)比較可憐的是有些外國人,因為現場都是唱卡通歌,而且沒有輪唱的概念,所以他們點不到也唱不到什麼歌。這種卡啦OK就是應該要規定每個人最多點一首歌才對呀~不過,看到 JRuby 的 Charles Nutter 大大拿著手機螢幕看羅馬拼音唱棋靈王的主題曲便是無價~


Ruby Kaigi 2016

下次的 Ruby Kaigi 已經確定要在京都舉辦,9/8 ~ 9/10 ,標在年曆上期待中~

 
11 months ago

Naming your helper methods in Rails can be very difficult. What do you feel when you see this:

auction_title(@auction)
board_title(@board)
category_title(@category)

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:

  1. easily know where to put a helper method
  2. 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.

One school of thought uses decorators to append functionalities on top the ActiveModel, draper, display_case and active_decorator being the three most well-known solutions.

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).

Usage

First install by putting this in the Gemfile:

gem 'lulalala_presenter'

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 LulalalaPresenter::Base:

# app/presenters/auction_presenter.rb
module AuctionPresenter < LulalalaPresenter::Base
  def title
    h.truncate(model.body, length: 40)
  end
end

And then in the view I call it like this

<%= @auction.presenter.title %>

Less magic

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:

delegate :body, to: model

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 method_missing.

Conclusion

At the end I quite like my results. For the top example, now I have

@auction.presenter.title
@board.presenter.title
@category.presenter.title

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.

 
about 1 year ago

Rails developers often use to_param to add more information to the url. Taking this very page as an example:

http://logdown.com/account/posts/290439-seo-and-to-param

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:

http://logdown.com/account/posts/290439-seo-and-to-param/edit

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_url and post_path like this:

# Override default generated Rails route helpers.
module CustomUrlHelper
  def post_path(target, options = nil)
    target = "#{post.id}-#{post.title[0..60]}" if target.respond_to?(:title)
    super(target, options)
  end
end
Rails.application.routes.url_helpers.send(:include, CustomUrlHelper)

This means only show/delete/update paths will be SEO friendly. Other paths (especially custom actions) will be unaffected.

 
about 1 year ago

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.

user.association(:profile).loaded? # false
FactoryGirl.create(:profile, user: user)
user.association(:profile).loaded? # false

Prior to my upgrade, the profile association will not be loaded before/after FactoryGirl.create call.

However after Rails 4.1

user.association(:profile).loaded? # false
FactoryGirl.create(:profile, user: user)
user.association(:profile).loaded? # true

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.

 
about 1 year ago

這個週末嘗試把 sqlite 的資料轉移到 postgresql 上,結果遇到許多坑。

sqlite 的 data dump 其實需要更改不少地方,才能用在其他資料庫。這部份很容易犯錯,所以我想找其他方法。

嘗試過的方法

我先是在 RailsCast 上看到 taps 這個 gem ,能夠透過另開 server 作到讀取資料再匯入的功能。可惜這個 gem 已經沒有維護了,自己嘗試修問題都是以失敗告終。

接下來我使用 pgloader ,這似乎能直接匯入資料進入 pg ,可是最近的版本似乎有 UTF8 的問題,我就算直接把 master 抓下來手動編譯,還是無法成功解決這問題,故放棄。

心灰意冷時,我突然想到,以 SQL 不行,但是既然 ActiveRecord 能夠支援各種資料庫,那麼只要把舊的資料以 yaml 格式備份出來,然後在新的資料庫給塞回去,就可以了。找了一下發現 active-dump 這款 gem ,最後作了一些調整,終於成功的轉移資料庫。

注意的地方

  • 有使用 foreign key 的話,記得依照 key 的 dependency 把匯入的順序調整,被 Key 指向的 table 移到前面。
  • 匯出前也要先把 orphan record 先刪除(就是有 foreign key 指向已經不存在的資料時)
  • 匯入前,先轉換資料庫然後跑 migration
  • 匯入後,每個 table 的 id 並不會自動設成從目前最大的 id 開始繼續遞增。必須跑類似 ALTER SEQUENCE product_id_seq RESTART WITH 1453; 來對每個 table 一一作設定,不然的話可能新增的 record 會從前面沒有的 id 開始建立。
  • 以前在 MySQL 或是 Sqlite 時似乎沒有指定 order 就會以 primary key 自動作排序。但是在 Postgresql 我發現丟回來的東西順序不是這樣(我看到的是相反的 id desc)。這代表 query 都得手動加入 order(:id) 才能確保跟以前一樣以 id desc 排序。

後記

資料轉移完以後,我才偶然發現 sequel gem 似乎也有提供類似的轉移功能,因為這款 gem 很有名,所以很有可能比以上我使用的方法更可靠好用。這就等其他人來試試看報告一下了。

 
over 1 year ago

最近想寫一個 email domain 的檢查器,想要提供 wrapper ,讓使用者能包一層 cache 或是給 testing 用的 dummy 。這個時候我就想到了 Rack middleware 的那種模式。

只不過我想說這麼簡單的程式庫,做出 middleware DSL 來是否太過浪費,所以就想說不要做成 middleware 模式那樣剝洋蔥的感覺。所以就打算平面化,用 array 把 wrapper 存起來,每個 wrapper 都有一個 valid? ,依序呼叫後有 true 就提前 return ,false就呼叫下一個。

實做後,發現我忘了應該要 cache miss 跟真正的檢查後,再把東西存起來。要這麼作就只能真正執行檢查後,再迴圈跑每個 wrapper 的另一個方法去存起來。這個時候才發現 middleware 的精妙之處。首先同一個方法就會同時負責 input 跟 output 。然後測試也比較簡單,各個 wrapper 可以獨立寫測試。用我的土炮方法,就得把 wrapper 套在 core class 上進行 integration test,並且確認裡層的東西在 cache hit 時就不會呼叫。

感覺自己因為想減少 method 呼叫層次,似乎只是一種 premature optimization 囧。

 
almost 2 years ago

When I am integrating offsite payments in ActiveMerchant, I always need to provide an endpoint to receive payment notificaiton from gateway providers. After a couple of projects, I have come up with a best-practice to do it, and I thought this might be useful for others.

I have a PaymentNotification class solely to record notifications. This is because I want to decouple the persistence of notification from the order status change. If something goes wrong when I change Order, the notification will still be safely persisted in the database for later analysis.

As you can see, the controller action is pretty light. It only saves the notification. The notification request is setup so it contains order_id and payment method.

The interesting part is the PaymentNotification model. It has a few columns for persisting different information (mostly serialized). It is also linked to Order, for easier analysis.

The process method is to check the notification's validity and trigger order status update. It is to be called separately (maybe via a scheduled job). A few kinds of custom exceptions may be raised during process, because they are handled differently. Either way, errors will be recorded in the errors column.

Any improvement is welcomed :D

 
about 2 years ago

以下轉載自我朋友的文


Re: 新增素人選項 醫師李宏信明登記參選北市長

他不是來抓神豬的票,是來抓柯的票的

剛好無聊,來 post 之前看賽局理論看到跟選舉有關的兩個模型 XD
第一個叫做「中間選民定理
讓我們先設定兩個假設:

1. 投票人的政治屬性只有一個維度(以台灣的例子來說,就是藍/綠)
2. 投票人會投給跟自己立場**最接近**的候選人

也就是說,以圖形來看,長這樣

中間的每一個方格,都是一單位的票,
圖形呈現鐘形分佈,是有一個多餘的假設,
就是中間選民比較多,極端選民比較少,這個假設在台灣不一定成立,
但沒關係,我們先專注在這個假設上,等一下再討論台灣的狀況。

現在,候選人準備要提出自己的政見了!我們就用連柯當名字好了 XD

這表示連的政見的立場,很偏向藍這端,柯的政見的立場則是中央篇綠
實際上他們的政見是不是這樣先不管,這只是假設
這樣他們能吸到的票,就是下面這樣

因為投票人會投給跟自己立場相近的!

因為柯的政見比較靠近中間,所以會拿到比較多的票,
對於連來說,這樣對他很不力,所以他也會想要去抓中間的票,
所以他就會修正他的立場,提出更靠近中央的政見。

因為投票人會投給跟自己立場相近的!

下面不畫了,總之,對柯來說,他也會想要去修正自己的政見,
然後連勝文也會想要去修正自己的立場,讓自己更靠近中央,
總之,因為能掌握到中間選民的人,就會贏得選舉,
演變到最後的結果就是候選人的政見會「盡量靠近中央,但跟對方不一樣」
(歷史上也有最後選擇「跟對方完全一樣的政見」的例子,還選贏了!)
這就是中間選民定理告訴我們的事情。

現在看到台北市,
因為台北市的狀況不是這樣中央分佈,而是藍色的比較多,
這也是為什麼連會選擇很保守的偏藍立場,現在就可以理解了,
對他來說,不太需要往中間靠,因為圖形分佈在左邊很多人,就是基本盤,
他要盡量的把握住左邊一大票人,所以繼續堅定深藍的立場就好。
(但其他縣市就不一定了,其他縣市的候選人可能會想要淡化自己的藍色)
在這個同時,連也盡可能的把柯打成綠色,因為柯越綠,對他越有益。

而柯實際上也不能站到太右邊,太右邊的話,他就會失去中間選民,
所以他不會去提台獨,這是極右,提了保證立馬輸掉這場選舉,
他不該、也不需要去提,因為他是選市長,不是選總統,這不干他的事,
實際上那些因為柯不支持台獨而在那邊靠腰的人,我們也不需要去在乎他們,
因為他們如果有腦,最終還是要投給柯,總不可能投給連勝文嘛。
除非這時候跳出一個支持台獨的候選人,那才有可能拉到柯的票,
下一個模型就會解釋為什麼這樣的候選人不太可能出現。

投票者/參選者模型(Voter-Candidate Model)

現在讓我們指出上述模型一個不太合理的地方,就是「候選人可以選定自己的立場」
實際上候選人提出的政見有侷限,什麼意思?
假設連勝文表示他支持台灣獨立,你會相信他嗎?不會嘛。
因為我們根據過去他的言行舉止,知道他不是這樣的人。
所以實際上,他的政見無法改變人們對他已經有定見的部份,只能有些微影響。
不過柯這個問題非常輕微,因為柯過去沒有政治足跡,主要的角色是知識份子。
所以人們對他的立場並沒有定見,所以會去看他的政見來訂定他的立場。

反正,現在模型變成這樣,假設:

1. 有一群人,分佈在一維的政治立場上
2. 每個人無法自由選擇自己的立場,因為立場由過去的言行定案
3. 每個人能決定自己要不要參選
4. 每個人會投票給跟自己立場最接近的人

首先,先看看目前的狀況,大概類似醬吧,我亂估的

北市是藍色居多,連就很藍,柯偏綠。
算了一下格數 33 : 39,柯領先 18%,跟實際民調的 15% 差不多。

現在有一個綠吱吱在那邊,請問,他該不該跳出來參選?
他覺得柯不夠綠,他認為自己的路線更正確,可以代表深綠民眾站出來。
如果他真的站出來會發生什麼事情?
會這樣

33 : 32 : 7 連勝文贏了,
所以為什麼上面說「那個深綠的人不太可能跳出來」,因為跳出來會搶票。
如果柯文哲贏,跟他路線有點差異,但還可以接受,
如果連勝文贏,反而是國民黨整碗捧走,吃屎了。

所以民進黨不派候選人,是一個很有政治智慧的考量,
柯的民調比較高,代表他過去的言行所立起的立場就是能吸到比較多的票,
民進黨與其派一個姚文智出來搶票,讓國民黨端走,不如全力支持柯文哲。
這個錯誤國民黨在 2000 跟 2004 年都犯了一次,
國民黨裡面的野心份子太高估國民黨跟自己的力量,
分裂了出來,結果贏變成剛剛好輸。
民進黨可沒有國民黨那種本錢玩分裂,
所以放棄參選直接支持柯是最正確的選擇,老實說沒有其他選項。
因為對民進黨來說「參選」並不重要,「勝選」也不重要,
「國民黨落選」才是民進黨最重要的事情,成功不必在我。

之前民進黨想要請柯入黨,但破局,破局以後民調落後,就不派候選人,
可以說是先納賢,納賢不成,則讓賢,他媽的民進黨這個決定的肚量不簡單。
所以那陣子我對呂秀蓮女士感到非常度爛,操他媽的整個就是來亂。
深綠跟深藍一樣的愚蠢,這不一定,但愚蠢的深綠就是愚蠢,幹!

那現在國民黨民調還落後 15%,怎麼辦?
「台北市選民還有沉默的多數沒講話,民調不準啦!」
不管是藍的綠的,都還有些人這種想法。不準?我不知道準不準,
但如果你是連勝文,你敢不敢堵看看這民調準還是不準 XD 絕對不敢賭麻 XDDD
所以如果你是國民黨,你現在要怎麼辦?很簡單……

民進黨不派深綠的出來選!
國民黨就派一個中間的出來選!
然後這個中間的就走跟柯完全一樣的路線!
所以這次他們故意挑了一個過去政治足跡比較淡、一樣是知識份子角色的人,
他會在數線上站在跟柯完全一樣的位置,然後盡可能的嘗試去拉柯文哲的票。
手段就是這樣,很聰明,不知道是哪個王八蛋想的?可能是幹總統的那個吧。
如果我猜的沒錯,接下來媒體就會主打這個人清新跟知識份子的部份,
然後盡量淡化他的顏色,讓他盡可能的靠近柯的點。
不過這手段被識破的話,效果就沒了 XD 只是不知道多少人會看破

 
about 2 years ago

歐付寶有個機制檢查 POST 內容是否可信,先是把表單所有的資料串起來,然後以 url encode 以後,再用 MD5 跟一組共同秘密加密起來。要是你產生的值跟他伺服器產生的一樣,就算認證通過。

最近發現如果表單有值是 & 或是 < 的話,認證就會失敗。

因為之前也有發生括號符號我們有轉成 %28 但是他們那端沒有轉,導致認證失敗的錯誤。所以我自己也測試了兩種情況:把 & 轉成 %26 或是不轉。兩種情況都認證失敗。

所以就寫信問客服並附了以下的 form 當參考:

<input id="TradeDesc" name="TradeDesc" type="hidden" value="&lt;&amp;" />
<input id="CheckMacValue" name="CheckMacValue" type="hidden" value="4A573F3F559226072079E3758B3D8E1A" />

結果來回了幾封信確認後,客服給了我以下答覆:

<input id="TradeDesc" name="TradeDesc" type="hidden" value="&lt;&amp;" />
一般來說POST資料時不會post到html編碼
請勿使用html encode,
另外,TradeDesc 及 ItemName 請避免使用特殊符號等字元以及html tag 等符號
請您再試試
THX.

當下就有點怒的回信,因為 html 裡面把 & 都轉成 &amp; 是基本知識了,瀏覽器 POST 資料出去時應該會自動轉回 & ,金流端不會接收到 &amp; 這種 escape 字串。而沒有對 < 作 escape 的話,瀏覽器能不能運作都是問題呢。

API 文件並沒有標明不能特別限制特殊符號,經過測試其他特殊符號都可以使用。但是就只有 <& 不行。是說這兩個符號也沒有特殊到哪裡去,要是英文商店用個 & 字元也是很正常的吧。

所以就回了信,說明 &amp; 這 html encode 不會對你們公司 server 收到的資料有所影響,並附上我產生認證碼的每個步驟,希望對方工程師能發現是哪個步驟開始相異。

結果對方客服就給我一個跳針回應:

TradeDesc 及 ItemName 請避免使用特殊符號等字元
THX.

明明是可以解決的問題(只修改我方程式),但是卻直接用這種方式迴避。到底客服有沒有把我的提報轉給工程師呢,或者工程師因為什麼原因不想研究這個問題呢?我原本只是想改善開源的 active_merchant_allpay 串接介面,但是這樣的回應~還是不要熱臉貼冷屁股好了。