Ruby & Rails勉強会

赤松 祐希(@ukstudio)

人は初めにブロックに恋をする[要出展]

ブロック


# 繰り返し
10.times{|i| puts i }
[1,2,3,4,5].map{|i| i*2}

# 簡易Strategy
%w(ruby python php javascript).sort{|a,b| a.length <=> b.length}

# 終了処理の保証
File.open('log/development.log') do |f|
  f.puts 'ERROR'
end

# DSL
describe User do
  it { should be_new_record }
end
    

高階関数とブロック(1)


function foo(func) {
  print('before')
  func.call()
  print('after')
}
foo(function(){ print('in function')})
    

ブロックは高階関数と本質は一緒。関数の代わりにコードの塊をブロックとしてメソッドに渡す。


def foo
  puts 'before'
  yield # ブロックが実行される
  puts 'after'
end

foo { puts 'in block' }
    

高階関数とブロック(2)

ブロックは高階関数と違って複数わたせない。どうしてもって時はlambdaかprocを使う。


def foo(proc1,proc2)
  puts 'before'
  proc1.call
  proc2.call
  puts 'after'
end

foo(lambda{ puts 'proc1'}, lambda{ puts 'proc2' })
    

Mix-in


class Person
  include ActiveModel::Validations
  attr_accessor :name
  validates :name, :presence =< true
end

Person.new.valid? #=> false
    

Duck Typing

ガァガァうるさいならそいつはアヒルだ

二郎を食べるならそいつは(ry


class Logger
  def self.write(out, log)
    out.write log
  end
end

File.open("hoge", "w") do |f|
  Logger.write(f, "log") # ファイルに書き込み
end

Logger.write($stdout, "log")
    

メタプログラミング

Deviseの例

Q. current_userなんでメソッドどこにも定義してないんだけど

A.


class_eval <<-METHODS, __FILE__, __LINE__ + 1
  def authenticate_#{mapping}!(opts={})
    opts[:scope] = :#{mapping}
    warden.authenticate!(opts) if !devise_controller? || opts.delete(:force)
  end

  def #{mapping}_signed_in?
    !!current_#{mapping}
  end

  def current_#{mapping}
    @current_#{mapping} ||= warden.authenticate(:scope => :#{mapping})
  end

  def #{mapping}_session
    current_#{mapping} &s;&s; warden.session(:#{mapping})
  end
  METHODS
    

自分で書かないにしても

RubyのライブラリやRailsとRailsのプラグインではちょこちょことメタプログラミングが使われている。なんだかんだで、ライブラリのコードを読まないとどうしようもない時がある。バグとか。そういう時にある程度読み解くだけの理解は必要。


class Foo
end

Foo.class_eval <<-METHODS
  def hoge
    puts 'hoge'
  end
METHODS

Foo.new.hoge #=> hoge
    

クイズ


class Foo; end
class Bar; end

Foo.class_eval <<-METHODS
  def foo
    puts 'foo'
  end

  define_method :foo_2 do
    puts 'foo 2'
  end
METHODS

Bar.instance_eval <<-METHODS
  def bar
    puts 'bar'
  end

  define_method :bar_2 do
    puts 'bar 2'
  end
METHODS

Foo.new.foo
Foo.new.foo_2
# Bar.new.bar #=> undefined method
Bar.bar
# Bar.bar_2 #=> undefined method
Bar.new.bar_2
    

特異メソッド


class Hoge; end
obj = Hoge.new
class << obj
  def singleton_method
    'singleton'
  end
end
    

これが定義されてる場所はどこだろう

特異クラス

特異メソッドは特異クラスにいる


class Object
  def eigenclass
    class << self; self end
  end
end
obj = Hoge.new
obj.eigenclass # 特異クラス
    

クラスメソッドも特異メソッド


class Hoge
  class << self
    def class_method
      puts "class_method"
    end
  end
end
Hoge.class_method #=> class_method
    

クイズの答え

まとめるとeval族やmethod_missingなどの挙動とRubyのオブジェクトモデルを把握しておくと捗るよ

正直、メタプログラミングRubyを読めばOKです

やっとRailsのお話

とりあえず情報源から

情報源

お作法の話

レールに乗る

Railsの標準の枠組みに沿って設計・実装することをレールに乗ると表現したりする。代表的なものに命名規約がある。テーブル名を単数形にしたりするとちょっと面倒。

例:モデルの命名規約

テーブル名 line_items
クラス名 LineItem

とは言え、2系のころよりはレールから外れても平気になった。例えばActiveRecord以外を使うとか。

レールはどこにあるのか

基本はRailsの規約に従うこと、提供されている機能を素直に使うことが重要。

大体のことはRailsGuidesやAWDwR、APIDocに書いてある。最終手段はコードを読む。Railsのリポジトリをcloneしておこう!

REST

resources :users
Verb Path Action
GET /users UsersController#index
GET /users/new UsersController#new
POST /users UsersController#create
GET /users/:id UsersController#show
GET /users/:id/edit UsersController#edit
PUT /users/:id UsersController#update
DELETE /users/:id UsersController#destroy

form_forなどもこれを基準に設計されている。よほどの理由がない限り、RESTfulにしよう。

この辺はRails云々よりRESTの理解が重要なのでWebを支える技術を読もう。

Scaffold

とりあえずScaffoldが生成するコードを読んでみよう。あれがRailsで一番シンプルなコード。

ルーティングがどうなっているのか、createやupdateの時、どういうパラメータがコントローラに渡ってきているのか(log/development.logにでてる)、form_forがどう使われているのか、色々と得るものが多い。

テストを書く文化

オススメ構成

ユニットテストはRSpecで書く。主にモデル。コントローラはあまり書かない。コントローラにロジックはあまり書かないため。

エンドツーエンドテストはRSpec requestとcapybara-webkitでやる。

JavaScriptのテストは?

基本はcapybara-webkitとかで頑張る。JSの単体テストは色々検討中だけど、JasmineかJsTestDriverあたりかなぁという気がしている。

ライブラリ・プラグイン

RubyのライブラリやRailsのプラグインは移り変わりが速い。

例: jeweler使えばいいとか思ってたのに、bundlerのせいでオワコン扱いされてた

パターン

例えば、@user.saveの時に同時になにか処理を行なわせたい時にsaveメソッドをオーバーライドせずにbefore/after_saveを使う。


class User > ActiveRecord::Base
  def save(*args)
    puts 'before'
    super(*args)
  end

  def before_puts
    puts 'before'
  end
  before__save :before_puts
end
    

個人的に割とこういうのは感覚的に身についてしまっているので、文書化しようとしてもそもそも意識にあがってこないし、数も多い。

なのでRailsに詳しい人とペアプロ、レビューできる環境に身を置くのが手っ取りばやい。

コードを読むにしても、アプリケーションのコードあまり公開されてないので厳しいかも。

身も蓋もないまとめ

結局はMVC、RESTfulアプリケーション、オブジェクト指向の原則に従って実装しようって感じ。テストも重要。

既にあるRailsの機能はRailsレシピブックから大体逆引きできるのでそこから探そう。あとはAPIDocかコードを読むしかない。

エッヂな機能はCHANGELOGやgitのlogをみよう。運がよければ誰かがブログに書いてくれたりもする。