All talk but no code...

makes lulalala a bluff boy

Settei - 又一個讀取設定的 Gem

| Comments

五年前,本部落格第一篇文章就是在講使用 Settingslogic 作設定。經過了漫長的歲月,終於想到了怎樣還能作的更好,最後作出了一個新的 gem 叫做 Settei 。這是一個使用 yaml,但是又能符合 12-factor app 的設定方式。

https://github.com/lulalala/settei

緣起

12-factor app 是一套讓部屬更容易的規則。其中第三點指到,要把設定跟程式分開,並把設定存在環境(變數)中。

但是使用環境變數有很多缺點,要是你的程式有30項大大小小的設定,光是命名變數名稱就會很麻煩:

# 傳統使用 ENV 就得寫一長串超累:
BOARD_PAGINATION_PER_PAGE=5
BOARD_PAGINATION_MAX_PAGE=10
BOARD_REPLY_OMIT_CONDITION_N_RECENT_ONLY=5
BOARD_REPLY_OMIT_CONDITION_AVOID_ONLY_N_HIDDEN=2

使用YAML 就簡單很多

board:

  pagination:

    per_page: 5

    max_page: 10

  reply_omit_condition:

    n_recent_only: 5

    avoid_only_n_hidden: 2

但是要怎樣結合 YAML 的優勢跟 ENV VAR 的優勢呢?

我的想法是:把 YAML 給 serialize 成一串文字,就能當 env var 傳到遠端了。

本機開發跟遠端部屬的兩個流程如下:

安裝

用 Gemfile 安裝以後:

gem 'settei'

在 rails 專案下執行以下 rake task 繼續安裝:

$ rake settei:install:rails

使用方法

要是 config/environments/default.yml 內容是這樣的話:

the_answer_to_life_the_universe_and_everything: 42

google:

  api: foo

就能這樣取得設定

Setting.dig(:the_answer_to_life_the_universe_and_everything)
Setting.dig(:google, :api)

部署

要是你是使用 capistrano 或是 mina 的話,應該自動會有效。我塞了一個設定,所以deploy時,你的production.yml會直接變成Env var隨著遠端的 Rails server 啟動。於是遠端的 server 也就拿到了設定。

要是你是使用 heroku 的話,使用 rake settei:heroku:config:set app=[app_name] 來把 production.yml 上傳到指定的app中。

更詳盡的使用方法請閱讀 github,也歡迎指教~~

Gem development inside Rails app

| Comments

Often during app development, it's a good idea to extract some functionality into a gem. The simple way to do this is to open a new git repository, do a bundle gem foobar, publish it, install said gem inside Rails app, use it and test some more.

How about updates? We have to change the gem, guessing how it can be used inside Rails. Then release a version, install it inside your app, and finally do some testing. This is a lot of friction. This can be especially bad if your gem is closely coupled with the app, or gets updated a lot.

How about this?

  1. Create an empty gem (e.g. bundle gem foobar) (without doing any release)
  2. Push it onto Github.
  3. Put the gem in your Rails app as a submodule: git submodule add https://github.com/foo/foobar.git vendor/foobar
  4. Install the empty gem in your app's Gemfile: gem 'settei', path:'./vendor/foobar'
  5. ...
  6. Profit!

Now develop the gem entirely inside app's submodule. It's probably possible to autoload it (though I don't recommend it). This will also work in production too.

Rails' many default_url_options

| Comments

I have read so many different ways to set default_url_options. But at least in Rails 5.1.4, only some of them worked. The thing is, often one works for console but not for controller, or the opposite happens:

# in development.rb
config.action_controller.default_url_options({:protocol => 'https'})
config.action_controller.default_url_options(:protocol => 'https')
# Does not work

# in development.rb, outside config block
Rails.application.routes.default_url_options[:protocol] = 'https'
# Does not work, but works under console

# in routes.rb
Rails.application.routes.draw do
  default_url_options protocol: :https
# Does not work, but works under console

# in ApplicationController
def default_url_options(options={})
  { secure: true }
end
# Does not work

# in ApplicationController
def default_url_options
  { protocol: :https }
end
# Works in browser, but does not work under console

# in development.rb
config.action_controller.default_url_options= {:protocol => 'https'}
# Works in browser, but does not work under console

This means we probably want to set the options at two different places for it to work always. I think this caused many stackoverflow questions, and deserve to have a Rails repo issue.

Pundit and controller based authorization

| Comments

If we have an Order class, pundit gem will figure out to use the OrderPolicy for authorization. But what if we have multiple domains using the same item?

For example an online auction site, there will be a Seller::OrdersController and a Buyer::OrdersController. They will manage the two sides of the same order. The idea is that, buyer should be allowed to only update via Buyer::OrdersController but not Seller::OrdersController. And vice versa for seller. Obviously we would want to have two sets of policies.

However in Pundit namespaced policy require us to call authorze [:seller, item], in order for it to use the Seller::ItemPolicy. This can become repetitive.

For me, there is less room for error if we have a 1:1 relationship between controller and policy, and a controller can be assumed to use the same policy. So I patched authorize call so policy can be set on the controller level.

Solution

Put the following under ApplicationController as private methods.

  class_attribute :pundit_policy_class

  def self.set_pundit_policy_class(klass)
    self.pundit_policy_class = klass
  end

  def authorize(record, query: nil, policy_class: nil)
    query ||= params[:action].to_s + "?"

    @_pundit_policy_authorized = true

    if policy_class
      policy = policy_class.new(pundit_user, record)
    elsif self.pundit_policy_class
      policy = self.pundit_policy_class.new(pundit_user, record)
    else
      policy = policy(record)
    end

    unless policy.public_send(query)
      raise NotAuthorizedError, query: query, record: record, policy: policy
    end

    record
  end

In your controller, you can then do this to set policy:

class Seller::OrdersController < ApplicationController
  set_pundit_policy_class Seller::OrderPolicy

This means all actions under this controller will use Seller::OrderPolicy by default when you call authorize.

If we want to override this, we can also pass in policy_class in authorize:

   authorize @order, policy_class: FooPolicy

Note that I made some changes to authorize's method signature: query is a keyword argument now. It also returns the record object (as planned for its 1.2 release).

《深入理解運算原理》的翻譯慘不忍睹

| Comments

《Understand Computation》這本書原本我很期待的,聽 Ruby Rouge 的介紹,似乎可以讓讀者理解許多有趣的計算機科學原理,像是正規表示式等等。原本都快要去買英文版了,想不到12月初碁峰資訊代理的中文版竟然上市了,於是我就很高興的買了一本回家看。

結果越讀越不對勁,很多地方不大理解,於是去找了英文版來對照,才發現翻譯實在不行,大概每四到五頁就會出現巨大的錯誤。沒有對照就看不懂。這邊列出最明顯的四個例子:

漏了一個 No 意思差很多

原文中這句話被這樣翻譯:

That all works as expected, but it would be nice if we could support conditional state-
ments with no « else » clause, like « if (x) { y = 1 } »

所有工作都一如預期,但我們若能以 else 子句支援條件陳述式,就像if (x) { y = 1 },那就更棒了。(40頁)

少了「no」結果意思變反了。這個問題在 31 頁也出現過。

例2

小步語意使得我們必須從諸如 3 的不可約運算式辨識出像是 1 + 2 的可化簡運算式。

當初看到這句的時候愣了一下,為何能從前者辨識出後者,所以去找了原文:

With small-step semantics we had to distinguish reducible expressions like « 1 + 2 » from irreducible expressions like « 3 »

才知道意思只是兩者參雜的情況下分辨是哪一種而已。要翻的話應該是這樣:

在小步語意我們必須分辨可化簡運算式(如 1 + 2 )與不可化簡運算式(如 3 )

很棘手

看你能不能找到這段錯誤的地方:

當我們在大型程式呼叫 #reduce 的時候,如果訊息往下經過抽象語法樹,然後一直到它抵達準備好化簡的程式碼片段,可能就會造成棘手的巢狀 #reduce 呼叫。

應該很難,但是比對原文以後:

when we call #reduce on a large program, that might cause a handful of nested #reduce calls as the message travels down the abstract syntax tree until it reaches the piece of code that is ready to reduce.

我們就會發覺正面的句子被翻譯成負面的了。「handful」被譯為「棘手的」,其實作者想表達這只會造成「一些」呼叫而已。

58 59 頁編輯的失誤連續 Combo

我們在前面看到,操作語意是藉由設計語言的直譯器來解釋語言的意義。相較之下,操作語意語言對語言的翻譯就像編譯器。(第58頁)

是不是不懂為何要「相較」同一個東西?嗯,因為第二句話寫錯了,應該是對「指稱語意(denotational semantics)」

接下來也有點不懂:

這些語意風格無一必能說明如何有效實做語言的直譯器或編譯器…(第58頁)

無一必能 是什麼呢?翻了一下英文翻譯

None of these styles of semantics necessarily says anything about how to efficiently implement an interpreter or compiler for a language, but...

喔,原來多加了一個「必」字。

指稱語意的優點是操作語意更為抽象(第59頁)

這次則是漏了一個「比」字。

最後是一個理解錯誤:

指稱只能如其含意;尤其若指稱語言具有某些操作含意,那麼指稱語意就只能讓我們更接近實際執行的程式…(59頁)

A denotation is only as good as its meaning; in particular, a denotational semantics only gets us closer to being able to actually execute a program if the denotation language has some operational meaning

「只能」放錯位置了。意思應該是「指稱語意只有在指稱用語言本身有操作含意時,才讓我們更接近實際執行程式」

結論

怕被認為雞蛋挑骨頭,所以比較小的問題放在這裡:https://goo.gl/VVWzMb

上次買了《Ruby 物件導向設計實踐-敏捷入門》,一樣也是有一些小問題,但是因為是代理中國出版社翻譯的書,所以想說反應問題也沒用。

這次的翻譯是賴榮樞,之前已經翻譯了十本以上的資訊書,應該算是很有經驗的譯者,所以我原本比較有信心。可是這麼多問題似乎代表編輯似乎沒有在做任何事情,糟蹋這本好書實在讓我很失望。

AdequateErrors - Overcoming limitation of Rails model errors API

| Comments

Over the years I encountered many issues related to ActiveModel::Errors API. After looking at the Rails source, I realized the original design was the root cause. errors was originally just a hash of array of String, which worked for simple requirements, but not for more complex ones.

In April I started collecting use cases, and study Rails source. Last month I finally put my hands on implementing a solution: a gem to apply object-oriented design principles to make each error an object, and provide new set of APIs to access these objects. I call it AdequateErrors.

AdequateErrors can be accessed by calling model.errors.adequate. It co-exists with existng Rails API, so nothing will break. But what issues does it solve? Let me list them one by one:

Query on errors using where

Imagine we need to access the empty error on any attributes:

model.errors.details.each {|attribute, errors|
  errors.find {|h|
    h[:error] == :empty
  }
}

AdequateErrors provides a where method. Now we can stop using loops, and write complex queries:

model.errors.adequate.where(type: :empty)
model.errors.adequate.where(attribute: :title, type: :empty)
model.errors.adequate.where(attribute: :title, type: :empty, count: 3)

This returns an array of Error objects. Simple.

Access both the message and details of one particular error

If one attribute has two foo_error and one bar_error, e.g.:

# model.errors.details

{:name=>[{error: :foo_error, count: 1}, {error: :bar_error}, {error: :foo_error, count: 3}]}

How do you access the message on the third particular error? With the current implementation, we have to resort to using array indexes:

model.errors.messages[:name][2]

Or we can call generate_message to recreate a message from the details, but that's also tedious.

With AdequateErrors, we won't have this problem. Error is represented as an object, message and details are its attributes, so accessing those are straightforward:

e = model.errors.adequate.where(attribute: :title, type: :foo_error).first
e.message # full message

e.options # similar to details, where meta informations such as `:count` is stored.

Lazily evaluating message for internationalization

@morgoth mentioned this issue that when you're adding error, it's translated right away.

# actual:

I18n.with_locale(:pl) { user.error.full_messages } # => outputs EN errors always


# expecting:

I18n.with_locale(:pl) { user.error.full_messages } # => outputs PL errors

I18n.with_locale(:pt) { user.error.full_messages } # => outputs PT errors

Taking this into consideration, AdequateErrors lazily evaluates messages only when message is called.

Error message attribute prefix

Not all error messages start with the attribute name, but Rails forces you to do this. People have developed hacks to bypass this. Others simply assigned errors to :base instead of the actual attribute. This is ugly.

Here is AdequateErrors' solution. It has its own namespace in the locale file, and instead of the global default format "%{attribute} %{message}", the prefix is moved into each individual entries:

en:

  adequate_errors:

    messages:

      invalid: "%{attribute} is invalid"

      inclusion: "%{attribute} is not included in the list"

      exclusion: "%{attribute} is reserved"

All built-in error types have been converted into this. If one wishes to have prefix-less error, simply have its entry in locale file without the %{attribute}.

Just less error prune code

I remember when I first learned about Object-Oriented design principle in uni, there was this example of payroll system. In the system, one array stores account name and another array stores account number. Whenever we need to delete an account, we need to manipulate both arrays. Further more, if we need to add a new attribute, we need to add a third array. It is very clear that objectifying this system can make it simpler and less error-prone.

This is what the current Rails errors implementation looks like:

def copy!(other) # :nodoc:
  @messages = other.messages.dup
  @details  = other.details.dup
end

def clear
  messages.clear
  details.clear
end

def delete(key)
  attribute = key.to_sym
  details.delete(attribute)
  messages.delete(attribute)
end

This being similar to the case I mentioned above, really can benefit from an object-oriented approach.

Conclusion

If you are a long-time Rails developer, chances are you have met similar issue before, please try this gem. If you have other usecases that you wish to improve on, I would like to know and see if it can be added into the gem. Happy hacking!

Magics that Decorator/Presenter Gems Do to Make Your Type Less

| Comments

Some of you Rails developers probably have used a 'decorator' or 'presenter' library. These libraries aim to bridge between Rails model and view layers. If I am to define it, a presenter allows developers to group helper methods related to a model to be under a namespace related to that model, instead of the current global space.

But would you believe it? There are actually a dozen or more decorator/presenter gems out there. Why do we reinvent the wheels? The first reason is that there is really a demand, because keeping large amount of helper methods under the same namespace is just unrealistic. The second reason is that, these gem owners have different views on this philosophical question: should the interface be implicit (things are done for the user under the hood) or explicit (user has to type more).

As a fun exercise I will compare 6 of these gems and explain how the general concept works. I have not used some of the gems, but only read the readme and some of the code, so if there are mistakes please let me know.

*Disclaimer, I am the owner of LulalalaPresenter gem. And if you have never used a presenter/decorator before, read this post to know why it is useful.

The following table represents the spectrum of these gems. On the left end, we see gems favoring implicity more. On the right end, gems are more explicit in nature.


Active
Decorator
Draper Oprah Display-case Lulalala
Presenter
RailsCast
link link link link link link
Decorate (Quack like a model) Y optional Y Y
Decorates Association Y optional optional
Directly call helper method within decorator Y optional
Globally accessible view context Y Y Y
Automatically decorates Y
Presenter/Model mapping 1:1 N:1 N:1 N:1 1:1 1:1
Lines of code 281 718 130 375 110 38
Lines of test 473 3037 354 1556 294 n/a

(I find many more gems after writing this. A complete list can be found here. If you want to add your gem to it, let me know~)

"I hate magic" camp

The conservative rubyists prefer to avoid magic. They don't like to override too much things, and they write PORO instead of meta-programming.

The most simple example can be seen from Ryan Bate's RailsCast: "Presenters from Scratch". In the video he explains step by step how to make a simple presenter.

The only meta-programming used is how it infers the presenter class from the model. All other interfaces are simple object passing method calls. Only 38 lines are required to achieve this.

Since by default it does not act like a model, Ryan calls it a presenter instead of decorator. You can still delegate calls to model if you wish, but the readme indicates that presenter object should not be mixed up with model object.

I'll talk about LulalalaPresenter later after ActiveDecorator, because it is its fork.

Decorator

According to Design Patterns in Ruby, the decorator pattern is a wrapper that "supports the same core interface, but adds its own twist on that interface." In this case we add view related functionality around the model. A decorator can act as if it is the model, which means less view changes are required.

ActiveDecorator, oprah and display-case all position themselves as decorators. Draper on the other hand gives the user freedom to choose if the wrapper should become a decorator or not.

One association further

Associations are part of the ActiveModel interface too, and some gems can decorate associations for you to save some key strokes (example). This is no small task, as there are multiple ways to trigger associations. It is therefore more possible to break across Rails versions.

Directly call helper methods within decorator

Normally for a model decorator to call helper methods within it, it needs to call via view_context (often alias as h), e.g. h.url_for(). Both ActiveDecorator and Draper offers a way to save key strokes so you can call url_for directly. This is done by a simple trick: if the model does not support a method, we retry it on view context again (example).

The implications are: 1. this is a wee bit slower because method_missing is utilized. 2. if same method name exists on model and view_context, model's method takes precedence. This is usually not a problem.

Globally accessible view_context

Draper, ActiveDecorator and LulalalaPresenter all keeps the view_context in a globally accessible place (example). This is done for two reasons:

  1. Give an OO-esque feel to the decoration method:

    Draper decorates by calling model.decorate
    LulalalaPresenter presents by calling model.presenter
    The design saves you from passing view_context, otherwise one will need to do model.decorate(view_context) all the time.

  2. To allow automatic decoration (see below).

The Holy Grail of Implicity

We have reached the end of implicity. To automatically decorate things, ActiveDecorator hooks into the render call, and decorates instance variables when applicable (example).

The gem walked the extra miles for you, so you don't have to do anything beside writing the decorator class. In some way, this feels like Rails philosophy, where we just write the controller/model/view, and things will just hook up perfectly without you knowing what's happening under the hood.

My Own Presenter and Conclusion

As you can see from the table, most are decorators. They behave like models, delegating calls to the inside. However if we treat presenter as a separate object, we can reduce a lot of the delegation complexities.

I was looking for a presenter which does not involve decorating ActiveModel, but I couldn't find one. Ryan's solution was the closest I could find. So I thought I can make my own.

I personally hate typing parenthesis, because my left little finger aches when holding shift key. Instead of present(model).foo, I prefer model.present.foo. If we are to do this, how can presenter get hold of view_context object? We can pass it in everytime like this model.present.foo(vc), but that's definitely too much too type. In the end, I found out ActiveDecorator's globally accessible view_context, so I used it to make my own. Hopefully this does not offend any one, I feel guilty but I want to please my little finger.

Do we need presenters/decorators pattern? Some may argue this is an offense to the MVC architecture, and some may think it introduces extra complexity. Again we will probably never get a consensus on this, but I use it because it made coding Rails more pleasant. So if you have large number of helper methods in your codebase, I recommend you to just pick a gem and try it out :)

I wrote an address tokenizer using machine learning

| Comments

A few years ago, I was assigned the task to extract the city/suburb names from our crawler results. I wrote a parser, using a bunch of if/else statements and regular expressions. It worked mostly, except in some extreme cases. In order to parse those extreme cases, I added more if statements and more obscure regular expressions. At the end I feel the code was very unreadable.

But was I an incompetent programmer? A few months ago I read a blog post about using machine learning to do address parsing, and I realized my old approach of creating rules, is not how our brains work. A lot of cases really requires us thinking in terms of possibility ("if there are more than three characters followed by this, it is probably a street"). These are fuzzy logics, but my if/ else regular expressions are discrete logics operating on a boolean level.

So as a pet project, I decided to implement an address parser in Ruby. In the Python community they already have Parserator. So why not in Rubyland? I am from Taiwan, so I also want to try applying that to addresses here.

I used the Conditional Random Fields model, though reading the Wikipedia article fried my brain:

I don't understand any of these. However I still keep my hopes that I can just copy & paste something and it would work out eventually. Though we don't know how to create a lego block, we can still build things using it without all the background knowledge right?

The first step is to gather the training data. My friend said that these are confidential, and can cost money. So I looked elsewhere. Eventually I found out that there are people adding address entries on this site called OpenStreetMap. Regional data can be downloaded at this site called Gisgraphy. The file is in .pbf, which stands for Protocolbuffer Binary Format. So I used pbf_parser gem to access the data inside. Not all data are for addresses, some are bus routes and some are geometry data. I wrote a parser to extract addresses into the a SQL database. There were around 15000 records.

Though in OSM people enters address in different sections such as city and suburb, in reality it is not strictly followed as to which field represents what. This is especially true in Eastern countries. there are a few distinct levels which does not have an English counterpart. People also puts the full address in the street field and the like. So I have to write scripts to boldly move the data around the columns, add new columns to match Taiwanese address rules. I feel I have touched more than 2/3 of the addresses. I call this part cleaning.

Once cleaning is done, all we have to do is to feed those data in to train the model. Sylvester Keil wrote two Rubygems to do CRF training, one of which is called wapiti. It is a wrapper to a C library of the same name. He was very kind and helped me when I wanted to know how to use the gem.

Eventually I was able to feed my data into wapiti and create a model file. Some East-Asian languages have the property that pharaes are not separated by space characters, I have to chop the address into individual characters, and then feed them in. On the other end, when the model determines the result, I then have to combine neighbouring characters of the same label back into a phrase.

The result was much better than I expected, it can parse common addresses just fine. All of these are me writing no rules at all. I created a website for people to try out http://addresstokenizer.lulalala.com/, so I can also gather some new data.

People do inform me extreme cases where the tokenization fails. As my first time writing something using Machine Learning, the feeling is quite different, as something like this:

if result.wrong?
  say "Not me! It's its fault!
       The machine is too stupid to learn~~"
  shrug
  guilt = 0 # do not feel guilty at all~
else
  say "Hehe"
  feels "complimented"
  happiness += 100
end

I provided a gem (https://github.com/lulalala/lulalala_address_tokenizer) and provided a model file. The gem is intended for East Asian addresses (Chinese, Japanese and Korean), so if you are in these region, please try create your own model. Once you plug it in, it should just work. Once I have time, I plan to put my training data online for others to make correction on.

Always use respond_to

| Comments

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.

RubyKaigi 2015 感想

| Comments

參加了去年年底的 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):

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

技術外的感想

食物與 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 ,標在年曆上期待中~