第55回 Ruby/Rails 勉強会@関西で初級者向けレッスンやってきた
初級者向けレッスンを担当したので、以下スライドを解説。*1 *2
クラスを作ってみよう
class Person; end obj = Person.new # => #<Person:0x10102718> obj.class # => Person Person.superclass # => Object
- Person という名前のクラスを作る。
- class から end までがクラス。
- クラス名は大文字で始める (キャメルケース)
属性を持たせてみよう
class Person def initialize name @name = name end end matz = Person.new('matz') # => #<Person:0x10138598 @name="matz">
- initialize は特別なメソッド
- new すると呼ばれる
- 引数 (name) をインスタンス変数 (@name) で記憶
- def から end がメソッド
属性にアクセスしてみよう
class Person attr_reader :name end matz.name # => "matz"
- インスタンス変数は外から見えない
- attr_writer
- セッター
- attr_reader
- ゲッター
- attr_accessor
- セッター&ゲッター
変数・定数のおさらい
- 最初の数文字を見れば区別できる
- クラス名は定数
属性を増やしてみよう
class Person def initialize name, born = nil @name, @born = name, born end attr_accessor :born end matz.methods.map(&:to_s).grep(/born/) # => ["born", "born="]
- initialize メソッドを変更
- オーバーロードではない
- 引数に既定値があれば省略できる
- attr_accessor で born メソッドと born= メソッドが生成された
属性を増やしてみよう (2)
- アクセスしてみる
matz.born = Time.local(1965, 4, 14) dhh = Person.new('dhh', Time.local(1979, 10, 15)) matz.born # => 1965-04-14 00:00:00 +0900 dhh.born # => 1979-10-15 00:00:00 +0900
- 代入のような表記で born= メソッドが呼ばれる
メソッドを作ってみよう
class Person def age (Time.now.strftime('%Y%m%d').to_i - @born.strftime('%Y%m%d').to_i) / 10000 end end matz.age # => 47 dhh.age # => 32
- メソッド名は小文字で始める (スネークケース)
- return は省略可能
- 最後に評価した式の値がメソッドの値になる
メソッドを上書きしてみよう
matz.to_s # => "#<Person:0x10138598>" class Person def to_s "#{@name}(#{age})" end end matz.to_s # => "matz(47)" dhh.to_s # => "dhh(32)"
- to_s はオブジェクトの文字列表現
- 未定義なら Object#to_s が実行される*3
インスタンスを比較してみると……
person = Marshal.load(Marshal.dump matz) person == dhh # => false person == matz # => false # おかしい
- 深いコピー
- Marshal.dump して Marshal.load する
- == はメソッド
- Person#== を定義しても良いが……
- ==, !=, >, <, >=, <= 別々に定義するの面倒
順序を決めよう
class Person include Comparable def <=> o @name <=> o.name end end person == matz # => true person == dhh # => false matz > dhh # => true
- <=> 演算子で順序を決定
- Comparable で ==, != , >, <, >=, <= を生成
Array#sort してみよう
- 順序が決まれば sort できる
people = [matz, dhh]
people.sort # => [dhh(32), matz(47)]
Array ときたら、次は ……
Hash のキーにしてみると……
値に入れてもおもしろくないので
h = {matz => "Ruby", dhh => "Rails"} h[matz] # => "Ruby" h[dhh] # => "Rails" key = Marshal.load(Marshal.dump matz) key == matz # => true h[key] # => nil # おかしい
- 深いコピーをしたオブジェクトをキーにすると
- 値が取り出せない!
hash 値を計算しよう
class Person def hash code = 17 code = 37 * code + @name.hash code = 37 * code + @born.hash end end matz.hash # => -22068619118 dhh.hash # => 14923733106
eql? を上書きしよう
- hash 値がぶつかっていないか調べる必要がある
- Hash クラスは eql? でキーが正しいか判断する
class Person def eql? o return false unless @name.eql? o.name return false unless @born.eql? o.born true end end key.eql? matz # => true key.eql? dhh # => false
- a.eql? b が真の場合 a.hash == b.hash であること
- そのように hash, eql? を定義する
Hash にアクセスしてみよう
h = {matz => "Ruby", dhh => "Rails"} h[matz] # => "Ruby" h[dhh] # => "Rails" h[key] # => "Ruby"
- 最初に作った h (Hash) は Person#hash を定義する前に作成したもの
- 格納位置がおかしいので作り直す*5
アクセス制御してみよう
オマケ
- public
- protected
- private
class Person protected :born end matz.born # ~> protected method `born' called for matz(47):Person (NoMethodError)
- protected なメソッドを呼ぶと例外が発生する