There's an echo in my head

日々のメモ。

chromedriverをインストールしてくれるchromedriver_helperをGoで書いた

a2ikm/chromedriver_helper

やることはおおまかに2つで、

  • $ chromedriver_helper installで最新のバージョンを~/.chromedriver-helper/chromedriverにインストールする
  • chromedriver_helper.BinaryPath()でインストールされているバイナリのパスを返す

詳しくはREADMEを参照。

もともとchromedriver-helperというgemがあったんだけど、Windowsで動かそうとするとzipやwgetが必要だったりと手間だったのをrubyひとつで動くようにikm-chromedriver-helperとしてforkし、そもそもrubyを入れるのが手間になったのでGoで書いてみた、というような流れ。

初めてGoでまともなものを書いたのでコーディング規約とかがよくわからなかった。おおかたvim-goのおかげだと思う。

参考にした記事

polymorphic_pathを使って定型のリンクを手軽に作る

管理画面にTwitter Bootstrapを使ってボタンっぽいリンクを作っていると、

link_to t(:new), new_user_path, class: "btn btn-success"

みたいなコードがビューにあふれてきてつらい。

なので、

new_link_to User

としたら同じリンクを生成するようなヘルパを作る*1

module LinksHelper
  def new_link_to(model, &block)
    model         = model.class unless model.is_a?(Class)
    options       = polymorphic_path(model, action: :new)
    html_options  = { class: "btn btn-success" }
    link_to t(:new), options, html_options, &block
  end
end

ポイントはpolymorphic_path*2で、これはurl_forみたいにモデルからルーティングのヘルパメソッドを呼び出してそれっぽいURLを作ってくれる。上の例だとnew_user_pathが呼び出される。

url_forと違うのは、url_forはモデルのインスタンスひとつか:action:controllerオプションをすべて渡すかのどちらかなのに対して*3、こちらはインスタンスと同時に:actionオプションを渡せて楽。

:action:new:edit以外にもヘルパメソッドが定義されていれば指定できて、例えばsleep_user_pathが定義されているなら、

polymorphic_path(user, action: :sleep)

みたいに指定できる。

scaffoldするときのコントローラのテンプレートをカスタマイズする

[小ネタ]RailsのScaffoldテンプレートを上書きするススメの勝手に追補的な記事。

コントローラのテンプレートはrailties/lib/rails/generators/rails/scaffold_controller/templates/controller.rbあたりから#{Rails.root}/lib/templates/rails/scaffold_controller/controller.rbにコピーしてくる。

このへんのどこからどこへコピーしてくるみたいなのはこのへんに書いてある(もしかしてRails Guideに公式のものがあるのかな?)。

以上。

ちなみにsaveとかupdateのコードはRails::Generators::ActiveModel経由で出力しているんだけど、そこにはsave!update!destroy!がない。これについては次のようなモンキーパッチを当てれば動く。config/application.rbからrequireするとか。

if Rails.const_defined?(:Generators)
  module Rails
    module Generators
      class ActiveModel
        def destroy!
          "#{name}.destroy!"
        end

        def save!
          "#{name}.save!"
        end

        def update!(params=nil)
          "#{name}.update!(#{params})"
        end
      end
    end
  end
end

素のオブジェクトをfields_forに使う

例えばモデル的にGraph has_many Entityだけど、GraphはActiveRecordを使う一方でEntityには素のオブジェクトを使いたい場合。データストアが異なるとか。

class Graph < ActiveRecord::Base
  after_save :save_entities

  def entities
    @entities ||= [] # TODO: entitiesを読み込む
  end

  def entities_attributes=(attributes)
    # attributes = { "0" => { "name" => "foo", "body" => "bar" }, ... }
    @entities = attributes.map do |i, (attrs)|
      Entity.new(attrs)
    end
  end

  private

    def save_entities
      # TODO: entitiesを保存する処理
    end
end

class Entity
  include ActiveModel::Model

  attr_accessor :name, :body
end

class GraphsController < ActionController::Base
  def new
    @graph = Graph.new
    # 初回表示用にEntityをいくつか初期化
    @graph.entities << Entity.new
    @graph.entities << Entity.new
  end

  def create
    @graph = Graph.new(graph_params)
    @graph.save!
  end

  private

    def graph_params
      params.require(:graph).permit(
        :title,
        entities_attributes: [:name, :body]
      )
    end
end
# new.html.slim
= form_for @graph do |f|
  = f.text_field :title

  = f.fields_for :entities do |g|
    = g.text_field :name
    = g.text_area :body

  = f.submit

キモはentities_attributes=が定義してあること。これがあるかどうかでfields_forの挙動が変わる。

こんな雰囲気。端書きなので間違ってるかも。

reactのチュートリアルを触ってみた

ここ最近ほとんどフロントエンドを触ってなかったのでFacebookのreactのチュートリアルを試しに触ってみた。

a2ikm/react-tutorial

  • コンポーネントってそういうことかー
  • JSX、最初は違和感あったけど、記述量が減るし慣れれば気にならなくなる
  • 規模が大きくなるとわからないけど、この程度なら見通しよさそう
    • propsで深く値を引き回すようだと大変になりそう

sorceryを使ったログインのテストをするときはupdate_attributesを使うこと

sorcery v0.9.0からソルトの生成とパスワードの暗号化の処理がbefore_saveからbefore_validationに移された。この影響で、テストのためにログインパスワードを上書きするときにupdate_attributeを使っているとログインできずにテストが落ちるようになった。

これはupdate_attributeがバリデーションをスキップするためにbefore_validationのフックが発火されないためだ。

対策としてはupdate_attributesもしくはupdate_attributes!を使えばいい。

module AuthenticationForFeatureRequest
  def login user, password = 'login'
    user.update_attributes password: password

    page.driver.post sessions_url, {email: user.email, password: password}
    visit root_url
  end
end

wikiのほうも更新しておいた

Ruby 2.2.0でsafe_yamlを使うときはv1.0.4以上が必要

Ruby 2.2.0を使っていて次のようなエラーが起きたらsafe_yaml(webmockなどが依存)がv1.0.4未満になっていないか確認、なっていたらv1.0.4以上に上げる。

NoMethodError: undefined method `tagged_classes' for Psych:Module

このブログに出てくるコードスニペッツは、引用あるいは断りがない限りMITライセンスです。