AASM 應該算是 rails project 很多人使用的一支 gem 了,在狀態管理可以省下不少時間,不過當網站有國際語系的要求時,我們希望可以直接在表現層依照不同的語系顯示不同的狀態。
看了 AASM 的 wiki , 確實有支持 i18n,使用 job.aasm.human_state
就可以直接顯示相應的狀態了
en:
activerecord:
attributes:
job:
aasm_state:
cleaning: Cleaning Up
running: Working
sleeping: Asleep
我在 CSV 輸出時,插入每一個 row 都希望能夠顯示相對應語系的狀態,結果我發現超級慢… 經過交叉比對後我懷疑是 aasm 的這個 method 在慢,於是我對他做了 benchmark
使用 aasm.human_state 做翻譯
Development [14] rocket(main)> n = 10000
10000
Development [15] rocket(main)> Benchmark.bm do |x|
Development [15] rocket(main)* x.report { n.times do; order.aasm.human_state end }
Development [15] rocket(main)* end
user system total real
177.270000 0.930000 178.200000 (187.869913)
[
[0] #<Benchmark::Tms:0x00007fcbead4d6c8 @label="", @real=187.86991300003137, @cstime=0.0, @cutime=0.0, @stime=0.9300000000000002, @utime=177.27, @total=178.20000000000002>
]
使用 I18n.t(“activerecord.attributes.order.aasm_state.#{order.aasm_state}“) 做翻譯
Development [14] rocket(main)> n = 10000
10000
Development [13] rocket(main)> Benchmark.bm do |x|
Development [13] rocket(main)* x.report { n.times do; I18n.t("activerecord.attributes.order.aasm_state.#{order.aasm_state}") end }
Development [13] rocket(main)* end
user system total real
0.170000 0.000000 0.170000 ( 0.171148)
[
[0] #<Benchmark::Tms:0x00007fcbed0ba778 @label="", @real=0.1711479999939911, @cstime=0.0, @cutime=0.0, @stime=0.0, @utime=0.1699999999999875, @total=0.1699999999999875>
]
看輸出結果就不必多說了,整個慢到不行,如果不是大量輸出 csv row 還真的不會注意到,猜測是呼叫太多層導致,不如直接拿 i18n 來的乾脆。
查了一下 aasm.human_state 是如何被呼叫的?
Development [9] rocket(main)> $ Order.last.aasm.human_state
From: /Users/Nic/.rvm/gems/ruby-2.4.4/gems/aasm-4.11.0/lib/aasm/instance_base.rb @ line 32:
Owner: AASM::InstanceBase
Visibility: public
Number of lines: 3
def human_state
AASM::Localizer.new.human_state_name(@instance.class, state_object_for_name(current_state))
end
是透過 AASM::Localizer
去呼叫的,那我們查一下這邊牽扯到的地方。
Development [14] rocket(main)> AASM::Localizer.ancestors
[
[ 0] AASM::Localizer < Object,
[ 1] ActiveSupport::ToJsonWithActiveSupportEncoder,
[ 2] Object < BasicObject,
[ 3] ActionDispatch::TestProcess,
[ 4] ActionDispatch::TestProcess::FixtureFile,
[ 5] FriendlyId::ObjectUtils,
[ 6] PP::ObjectMixin,
[ 7] ActiveSupport::Dependencies::Loadable,
[ 8] JSON::Ext::Generator::GeneratorMethods::Object,
[ 9] ActiveSupport::Tryable,
[10] Kernel,
[11] BasicObject
]
在看一下直接用 I18n 的部分。
Development [15] rocket(main)> I18n.ancestors
[
[0] I18n
]
嗯,跟猜測的一樣,有太多層相依,不如直接 I18n 才是最乾脆的。
當然,如果你只是少部分使用,可以忽略不計,但如果輸出 5 萬條 CSV,然後每次都要呼叫 aasm.human_state
,那就有罪受了。