tanish-kr's learning log

Learning output log

Ruby

クラスとモジュール

クラス定義

classキーワードを用いて、先頭大文字でクラスを定義します。

class Foo < Object
  DEFINE_NAME = "hoge"
  def initialize
  end
end

Foo::DEFINE_NAME
foo = Foo.new
  • class内に定義した定数は::演算子で参照可能
  • newメソッドでインスタンスを生成
  • initializeメソッドでクラスの初期化処理を定義することが出来る

インスタンスメソッド

インスタンスメソッドを定義するには、クラス定義の中でメソッドを定義します。
メソッドの末尾には疑問符や感嘆符を使うことが出来ます。オブジェクトの状態を真偽値で返す場合は疑問符を、破壊的なメソッドを表す場合は感嘆符を使用するという慣習があります。

class Foo
  def call
  end
end
foo = Foo.new
foo.call

アクセサメソッド

attr_accessor, attr_reader, attr_writer,キーワードを使用することでアクセサメソッドを定義することが可能です。

class Foo
  attr_accessor :name  # getter/setterがそれぞれ定義される
  attr_reader :sex   # getterが定義される
  attr_writer :age   # setterが定義される
end

foo = Foo.new
foo.name = "hoge"
foo.name
foo.sex
foo.age = 10

クラスメソッド

Rubyではクラスもオブジェクトの1つであり、クラスに対してメソッドを呼び出すことが出来ます。selfキーワードがついているメソッドがクラスメソッドとして定義されます。

class Foo

  # 個別でクラスメソッドを定義
  def self.hoge
    "hoge"
  end

  # 複数まとめてクラスメソッドを定義
  class << self

    def bar
      "bar"
    end

  end
end

Foo.hoge  # hoge
Foo.bar   # bar

メソッドの呼び出し制限

  • public : 制限なし
  • private : レシーバを省略するかたちでしか呼び出せない
  • protected : そのメソッドが定義されたのと同じクラス、またはサブクラスのインスタンスからしか呼び出せない

protectedメソッドは他の言語のprotectedとは異なり、同じクラスに属しているインスタンスのメソッドの中であれば、異なるインスタンスのprotectedなメソッドを呼び出すことが出来ます。
protectedのような呼び出し制限が必要となる場合はほとんどないため、基本privateメソッドを用いる方が主流です。

クラスの継承

Rubyは単一継承をサポートしており、クラスを定義する時に1つだけスーパークラスを指定することが出来ます。クラスを定義する時にスーパークラスを指定しなければ、自動的にObjectクラスが継承されます。

class Parent
  def greet
    "Hi"
  end
end

class Child < Parent
end

Child.superclass # Parent
child = Child.new
child.greet # Hi

サブクラスはスーパークラスのインスタンスメソッド、クラスメソッドを継承します。しかし、サブクラスはインスタンス変数についての情報は継承しません。クラスはインスタンス変数に関する情報は持っておらず、インスタンス変数はインスタンスメソッドの中で定義されるからです。

メソッドのオーバライド

スーパークラスにあるメソッドと同名のメソッドをサブクラスで再定義することを、メソッドのオーバライドといいます。これにより、機能をそのサブクラスに適したものに変更したり、拡張することが出来ます。
メソッドの中でsuperを呼び出すことで、スーパークラスに定義されている同名のメソッドを呼び出すことが出来ます。スーパークラスのメソッドには、サブクラスのメソッド呼び出しで受け取った引数が自動的に渡されます。

class Parent
  def greet
    "Hi"
  end
end

class Child < Parent
  def greet(name)
    super + "\t" + name
  end
end

特異メソッド

オブジェクトは、クラスに定義されたメソッドの他に、そのオブジェクト児湯のメソッドを持つことが出来ます。これを特異メソッドと呼びます。

class Foo
end

foo = Foo.new

def foo.test
  print "this test"
end

foo.test

クラス定義のネスト

クラス定義の中にネストしてクラスを定義することが可能です。

class My
  class SweetClass
  end
end

My.new
My::SweetClass.new

モジュール

module Foo
  def test
  end
end

モジュールの特徴

  • インスタンスを生成することはできない
  • 継承することは出来ない

モジュールの用途

  • 名前空間を作る
  • モジュールのメソッドをあるクラスのインスタンスメソッドとして取り込む
  • モジュールのメソッドをあるオブジェクトの特異メソッド(クラスメソッド)として取り込む
  • モジュール関数を定義して使う

メソッドをクラスのインスタンスメソッドとして取り込む

モジュールに定義されたメソッドは、クラスのインスタンスメソッドとして取り込む事ができます。これをMix-inと言います。インスタンスメソッドとしてモジュールを取り込むにはincludeを用います。

module Greetable
  def greet_to(name)
    puts "Hello, #{name}. class is #{self.class}."
  end
end

class Alice
  include Greetable
end

alice = Alice.new
alice.greet_to("Bob") # => "Hello, Bob. class is Alice."

メソッドをオブジェクトに取り込む

モジュールに定義されたメソッドは、オブジェクトの特異メソッドとして取り込むが出来ます。オブジェクトにモジュールのメソッドを取り込むには、extendを用います。

module Greetable
  def greet_to(name)
    puts "Hello, #{name}. class is #{self.class}."
  end
end

o = Object.new
o.extend Greetable

o.greet_to("World") # "Hello, World. class is Object."

モジュール関数

サブルーチンとして利用されることを目的としたメソッドは、モジュール関数として定義されることがあります。モジュール関数とは「privateなインスタンスメソッドであると同時に、モジュールの特異メソッドでもある」メソッドのことを指します。モジュール関数としてメソッドを定義することで、そのメソッドを2つの方法で使うことが出来るようになります。

# モジュールから直接呼び出す
Math.sqrt(4)

# includeして使う
include Math
sqrt(4)

モジュールに定義したメソッドをモジュール関数にする

module MyFunctions
  def my_module_function
    puts "Called"
  end
  module_function :my_module_function
end

複数のモジュール関数を定義する

module MyFunctions
  module_function

  def my_first_functin
    "first"
  end

  def my_second_functin
    "second"
  end
end

クラスやモジュールを自動的にロードする

毎回必ずロードするとは限らない外部ファイルの読み込みには、autoloadを用いることが出来ます。

autoload :MySweets
MySeeets # ここでrequireされる

オブジェクト

ほとんどのクラスはObjectクラスから派生したサブクラスです。スーパークラスを指定しなかった場合は自動的にObjectクラスを継承します。

class MyClass
end

MyClass.superclass # => Object

オブジェクトの基本的な振る舞い

Objectくらすには、そのオブジェクトの情報を返すメソッドや比較演算子など、オブジェクトとしての基本的な機能が実装されています。

o = Object.new

# 自身についての情報を返す
o.class              # => Object
o.is_a?(Object)     # => true
o.object_id         # オブジェクト児湯のID

オブジェクトへの変更を禁止する

Object#freezeを呼び出すと、レシーバへの破壊的な操作を禁止することが出来ます。freezeされたオブジェクトに変更を加えようとするとRuntimeErrorが発生します。

オブジェクトをコピーする

オブジェクトをコピーするにはObject#dupObject#cloneを用います。どちらのメソッドも汚染状態も含めてオブジェクトをコピーします。 Object#cloneはfreezeの状態や、特異メソッドの情報も含めてコピーします

original = Object.new

original.object_id
original.freeze

copy_dup = original.dup
copy_dup.object_id
copy_dup.frozen?   # => false

copy_clone = original.clone
copy_clone.object_id
copy_clone.frozen? # => true

汚染されたオブジェクト

Rubyにはセーフレベルという、外部からの入力によって危険な操作が行われることを未然に防ぐ機能があります。

環境変数やコマンドライン引数など、外部からの入力は汚染されたオブジェクトとして扱われます。セーフレベルを適切に設定すると、汚染されたオブジェクトを用いた危険な操作が禁止されます。 オブジェクトが汚染されているかどうかは、Object#tainted?で確認できます。

$SAFE   # => 0

Object.new.tainted?  # false
ENV["HOME"].tainted? # true

rcfile = ENV["HOME"] + "/.zshrc"
rcfile.tainted? # true 汚染されたオブジェクトを元に生成されたオブジェクトも汚染されたオブジェクトになる

セーフレベル

レベル 概要
0 デフォルトのセーフレベル。IOや環境変数、コマンドライン引数から得られた文字列には汚染マークがつく
1 汚染マークがつく対象は0と同じ。汚染されたオブジェクトを引数としたファイル操作、コマンドの実行、シグナルのトラップなどが禁止される
2 汚染マークがつく対象は0と同じ。1の制限に加え、プロセスに関する操作も禁止される。
3 このレベルで生成されたでオブジェクトにはすべて汚染マークがつく。2までの制限に加え、汚染を解除することなどが禁止される。
4 汚染マークがつく対象は3と同じ。3までの制限に加え、グローバル変数や汚染されていないオブジェクトの変更、メソッドの再定義などが禁止される