読者です 読者をやめる 読者になる 読者になる

気軽に楽しくプログラムと遊ぶ

自分が興味があってためになるかもって思う情報を提供しています。

メタプログラミング Ruby 第1章 オブジェクトモデル

rubyのclass、moduleの違い。クラスオブジェクトとインスタンスオブジェクトについてなど わかっていそうでわかっていない点をメタプログラミングの本を用いて理解していく。

オープンクラス

文字列からアルファベットとスペース以外を取り除くメソッド

def to_alphanumeric(s)
  s.gsub /[^\w\s]/, ''
end

オープンクラスを使った書き方

class String
  def to_alphanumeric
    gsub /[^\w\s]/, ''
  end
end

標準のStringクラスへメソッドを追加。
標準のクラス書き換えていいんですか。。初心者の想い。。
普通の開発現場とかでもあり得る対応なのかなぁ。
現場のコーディングルールとかRubyのベストプラクティスを読んでみたい。

今回のメソッドは汎用的なメソッドのため、許容できる。らしい。
ただし、Stringなどの重要なクラスは慎重に考える必要はある。

クラス定義の中身

3.times do
  class C
    puts "Hello"
  end
end

Hello
Hello
Hello
=> 3

クラス定義時にputsが呼ばれている。
3つ定義しているわけではなく、クラスの再オープンで定義している。

あと、do〜end内にクラス定義!?
Java屋さんとしてはでっかいクエスチョンマークがつきました。
ブロック内での定義は特に縛りないのかな。

メソッド定義して呼び出すのも試してみる。

3.times do
  def hello
    puts "Hello"
  end
  hello
end
Hello
Hello
Hello
=> 3

特に問題ないみたい。ブロック内での処理記述はクラス内での
記述と変わらないのかな

オープンクラスの例

Moneyオブジェクトに数値をMoneyオブジェクトへ変換するメソッドNumeric#to_money()がある。
標準クラスNumericには元々ない。

price = 100.to_money()

標準クラスNumericを再オープンして書き換えている。

class Numeric
  def to_money
    Money.new(self * 100)
  end
end

Money gemなどgemを取り込むとこのような書き換えが裏で発生したりしているんですね。

オブジェクトの中身

インスタンス変数
インスタンス変数はメソッドが呼ばれた時点で生成される

class MyClass
  def my_method
   @v = 1
  end
end
=> nil

o = MyClass.new
=> #<MyClass:0x007fbe91102398>

# この時点では@vは存在しない
o.instance_variables
=> []

o.my_method
=> 1

o.instance_variables
=> [:@v]

インスタンスメソッドメソッド
用語の違いを正しく理解する。
以下のようなインストロペクション(オブジェクト自身の内容調査する)で確認できる。

# クラスのインスタンスメソッドとオブジェクトのメソッドは同一
String.instance_methods == "abc".methods
=> true
# クラスのメソッドとオブジェクトのメソッドは同一でない
String.methods == "abc".methods
=> false

インスタンスメソッドというと、クラスが定義しているメソッドを呼び出しているという意味になるそうだ。まだ、よくわからない。。

クラスオブジェクト

# Stringのインスタンスが"hello"
"hello".class
=> String

# ClassのインスタンスがString
String.class
=> Class

# 継承メソッドを除いたClassで定義されたメソッドは以下
Class.instance_methods(false)
=> [:allocate, :new, :superclass]

# Objectを継承してStringが実装されている
String.superclass
=> Object
# BasicObjectを継承してObjectが実装されている
Object.superclass
=> BasicObject
# BasicObjectがトップレベルオブジェクト
BasicObject.superclass
=> nil

# Moduleを継承してClassが実装されている
Class.superclass
=> Module
# Objectを継承してModuleが実装されている
Module.superclass
=> Object

ClassはModuleに左記メソッドを追加したにすぎない [:allocate, :new, :superclass]
ほとんど同一のもの。

ClassとModuleの使い分け
Class:インスタンス生成、継承を行う
Module:インスタンスメソッドを適用させたい場合、またはネームスペースとして用いる。 継承できない。

moduleをネームスペースとして利用する

module Rake
  class Task

Taskクラスの名称は「Rake::Task」となる
他のクラスにTaskクラスが存在しても衝突を起こすことがなくなる。
Rake::としているのはクラス名Taskが定数だから。

Rakeのような定数をまとめるだけのモジュールのことをネームスペースと呼ぶらしい。

モジュールとメソッドの探索

モジュールのインクルードを行うと、クラスとスーパークラスの間に
無名クラスを作成し、継承関係が作成される。

module M
  def my_method
    'M#method()'
  end
end
=> nil

class C
  include M
end
=> C

class D < C; end
=> nil

D.new.my_method()
=> "M#method()"

D.ancestors
=> [D, C, M, Object, Kernel, BasicObject]
# モジュールMは表示されるが、裏で生成された無名クラスは意識させないため、隠れている

Rubyの達人になるための道 selfを理解する

irb起動時のself

self
# mainはトップレベルコンテキストと呼ばれる。
=> main

self.class
=> Object

クラス定義とself

class MyClass
  self
end
=> MyClass

privateキーワードとself
privateメソッドのルール:「明示的なレシーバーをつけて呼び出せない」
下記は明示的なselfをつけているのでエラーとなる。削除すると動作するようになる。

class C
  def public_method
    self.private_method
  end

  private
  def private_method; end
end
=> nil

C.new.public_method
NoMethodError: private method `private_method' 

privateメソッド呼び出しについて補足
スーパークラスから継承したprivateメソッドは継承先クラスで呼び出せる。 ・インスタンスメソッド内からしか呼び出せない。クラスには紐付かないメソッド

第1章完了。。

次はメソッド
メタプログラミング Ruby 第2章 メソッド - 気軽に楽しくプログラムと遊ぶ