class BasicObject

要約

特殊な用途のために意図的にほとんど何も定義されていないクラスです。 Objectクラスの親にあたります。Ruby 1.9 以降で導入されました。

性質

BasicObject クラスは Object クラスからほとんどのメソッドを取り除いたクラスです。

Object クラスは様々な便利なメソッドや Kernel から受け継いだ関数的メソッドを多数有しています。これに対して、 BasicObject クラスはオブジェクトの同一性を識別したりメソッドを呼んだりする最低限の機能の他は一切の機能を持っていません。

用途

基本的にはほぼすべてのクラスの親は Object と考えて差し支えありません。しかし、ある種のクラスを定義する際には Object クラスは持っているメソッドが多すぎる場合があります。

例えば、 BasicObject#method_missingを利用して Proxy パターンを実装する場合にはObject クラスに定義済みのメソッドはプロクシできないという問題が発生します。このような場合に Object ではなく BasicObject から派生して問題を解決できます。

注意

通常のクラスは Object またはその他の適切なクラスから派生すべきです。真に必要な場合にだけ BasicObject から派生してください。

class Proxy < BasicObject
  def initialize(target)
    @target = target
  end

  def method_missing(message, *args)
    @target.__send__(message, *args)
  end
end

proxy = Proxy.new("1")
proxy.to_i #=> 1

目次

インスタンスメソッド
privateメソッド

インスタンスメソッド

! self -> bool[permalink][rdoc]

オブジェクトを真偽値として評価し、その論理否定を返します。

このメソッドは self が nil または false であれば真を、さもなくば偽を返します。主に論理式の評価に伴って副作用を引き起こすことを目的に再定義するものと想定されています。

このメソッドを再定義しても Ruby の制御式において nil や false 以外が偽として扱われることはありません。

[RETURN]
オブジェクトが偽であれば真、さもなくば偽

class NegationRecorder < BasicObject
  def initialize
    @count = 0
  end
  attr_reader :count

  def !
    @count += 1
    super
  end
end

recorder = NegationRecorder.new
!recorder
!!!!!!!recorder
puts 'hoge' if !recorder

puts recorder.count #=> 3

class AnotherFalse < BasicObject
  def !
    true
  end
end
another_false = AnotherFalse.new

# another_falseは*真*
puts "another false is a truth" if another_false
  #=> "another false is a truth"
self != other -> bool[permalink][rdoc]

オブジェクトが other と等しくないことを判定します。

デフォルトでは self == other を評価した後に結果を論理否定して返します。このため、サブクラスで BasicObject#== を再定義しても != とは自動的に整合性がとれるようになっています。

ただし、 BasicObject#!= 自身や BasicObject#! を再定義した際には、ユーザーの責任で整合性を保たなくてはなりません。

このメソッドは主に論理式の評価に伴って副作用を引き起こすことを目的に再定義するものと想定されています。

[PARAM] other:
比較対象となるオブジェクト

[SEE_ALSO] BasicObject#==, BasicObject#!

class NonequalityRecorder < BasicObject
  def initialize
    @count = 0
  end
  attr_reader :count

  def !=(other)
    @count += 1
    super
  end
end
recorder = NonequalityRecorder.new

recorder != 1
puts 'hoge' if recorder != "str"

p recorder.count #=> 2
self == other -> bool[permalink][rdoc]

オブジェクトが other と等しければ真を、さもなくば偽を返します。

このメソッドは各クラスの性質に合わせて、サブクラスで再定義するべきです。多くの場合、オブジェクトの内容が等しければ真を返すように (同値性を判定するように) 再定義することが期待されています。

デフォルトでは Object#equal? と同じオブジェクトの同一性になっています。

[PARAM] other:
比較対象となるオブジェクト
[RETURN]
other が self と同値であれば真、さもなくば偽

例:

class Person < BasicObject
  def initialize(name, age)
    @name = name
    @age = age
  end
end

tanaka1 = Person.new("tanaka", 24)
tanaka2 = Person.new("tanaka", 24)

tanaka1 == tanaka1    #=> true
tanaka1 == tanaka2    #=> false

[SEE_ALSO] BasicObject#equal?, Object#==, Object#equal?, Object#eql?

__id__ -> Integer[permalink][rdoc]

各オブジェクトに対して一意な整数を返します。あるオブジェクトに対してどのような整数が割り当てられるかは不定です。

Object#object_id と同じですが、#object_id は BasicObject にはない事に注意してください。

例:

# frozen_string_literal: false
obj = Object.new
obj.object_id == obj.__id__              # => true
Object.new.__id__  == Object.new.__id__  # => false
(21 * 2).__id__    == (21 * 2).__id__    # => true
"hello".__id__     == "hello".__id__     # => false
"hi".freeze.__id__ == "hi".freeze.__id__ # => true

[SEE_ALSO] Object#object_id, [ruby-dev:42840]

__send__(name, *args) -> object[permalink][rdoc]
__send__(name, *args) { .... } -> object

オブジェクトのメソッド name を args を引数にして呼び出し、メソッドの結果を返します。

ブロック付きで呼ばれたときはブロックもそのまま引き渡します。

[PARAM] name:
呼び出すメソッドの名前。 Symbol または文字列で指定します。
[PARAM] args:
メソッドに渡す任意個の引数

例:

class Mail
  def delete(*args)
    "(Mail#delete) - delete " + args.join(',')
  end
  def send(name, *args)
    "(Mail#send) - #{name} #{args.join(',')}"
  end
end
mail = Mail.new
mail.send :delete, "gentle", "readers"       # => "(Mail#send) - delete gentle,readers"
mail.__send__ :delete, "gentle", "readers"   # => "(Mail#delete) - delete gentle,readers"

[SEE_ALSO] Object#__send__

equal?(other) -> bool[permalink][rdoc]

オブジェクトが other と同一であれば真を、さもなくば偽を返します。

このメソッドは2つのオブジェクトが同一のものであるかどうかを判定します。一般にはこのメソッドを決して再定義すべきでありません。ただし、 BasicObject の位置づけ上、どうしても再定義が必要な用途もあるでしょう。再定義する際には自分が何をしているのかよく理解してから実行してください。

[PARAM] other:
比較対象となるオブジェクト
[RETURN]
other が self 自身であれば真、さもなくば偽

例:

original = "a"
copied = original.dup
substituted = original

original == copied          #=> true
original == substituted     #=> true
original.equal? copied      #=> false
original.equal? substituted #=> true

[SEE_ALSO] Object#equal?, Object#==, Object#eql?

instance_eval(expr, filename = "(eval)", lineno = 1) -> object[permalink][rdoc]
instance_eval {|obj| ... } -> object

オブジェクトのコンテキストで文字列 expr またはオブジェクト自身をブロックパラメータとするブロックを評価してその結果を返します。

オブジェクトのコンテキストで評価するとは評価中の self をそのオブジェクトにして実行するということです。また、文字列 expr やブロック中でメソッドを定義すればそのオブジェクトの特異メソッドが定義されます。

ただし、ローカル変数だけは、文字列 expr の評価では instance_eval の外側のスコープと、ブロックの評価ではそのブロックの外側のスコープと、共有します。

メソッド定義の中で instance_eval でメソッドを定義した場合は、囲むメソッドが実行されたときに初めて instance_eval 内のメソッドが定義されます。これはメソッド定義のネストと同じです。 クラス/メソッドの定義/メソッド定義のネスト を参照してください。

BasicObject を継承して作ったクラス内で instance_eval する場合はトップレベルの定数や Kernel モジュールに定義されているメソッドは見えません。これは、トップレベルの定数が Object 以下に作成されるためです。

[PARAM] expr:
評価する文字列です。
[PARAM] filename:
文字列を指定します。ファイル filename に文字列 expr が書かれているかのように実行されます。スタックトレースの表示などを差し替えることができます。
[PARAM] lineno:
整数を指定します。行番号 lineno から文字列 expr が書かれているかのように実行されます。スタックトレースの表示などを差し替えることができます。

例:

class Foo
  def initialize data
    @key = data
  end
  private
  def do_fuga
    p 'secret'
  end
end

some = Foo.new 'XXX'
some.instance_eval{p @key} #=> "XXX"
some.instance_eval{do_fuga } #=> "secret" # private メソッドも呼び出せる

some.instance_eval 'raise' # ..:10: (eval):1:  (RuntimeError)
messg = 'unknown'
some.instance_eval 'raise messg','file.rb',999 # file.rb:999: unknown (RuntimeError)

例:

class Bar < BasicObject
  def call1
    instance_eval("::ENV.class")
  end
  def call2
    instance_eval("ENV.class")
  end
end

bar = Bar.new
bar.call1 # => Object
bar.call2 # raise NameError

[SEE_ALSO] Module#module_eval, Kernel.#eval, BasicObject#instance_exec

instance_exec(*args) {|*vars| ... } -> object[permalink][rdoc]

与えられたブロックをレシーバのコンテキストで実行します。

ブロック実行中は、 self がレシーバのコンテキストになるのでレシーバの持つインスタンス変数にアクセスすることができます。

[PARAM] args:
ブロックパラメータに渡す値です。
class KlassWithSecret
  def initialize
    @secret = 99
  end
end
k = KlassWithSecret.new
# 以下で x には 5 が渡される
k.instance_exec(5) {|x| @secret + x }   #=> 104

[SEE_ALSO] Module#class_exec, Module#module_exec, BasicObject#instance_eval

privateメソッド

method_missing(name, *args) -> object[permalink][rdoc]

呼びだされたメソッドが定義されていなかった時、Rubyインタプリタがこのメソッドを呼び出します。

呼び出しに失敗したメソッドの名前 (Symbol) が name にその時の引数が第二引数以降に渡されます。

デフォルトではこのメソッドは例外 NoMethodError を発生させます。

[PARAM] name:
未定義メソッドの名前(シンボル)です。
[PARAM] args:
未定義メソッドに渡された引数です。
[RETURN]
ユーザー定義の method_missing メソッドの返り値が未定義メソッドの返り値であるかのように見えます。
class Foo
  def initialize(data)
    @data = data
  end
  def method_missing(name, lang)
    if name.to_s =~ /\Afind_(\d+)_in\z/
      if @data[lang]
        p @data[lang][$1.to_i]
      else
        raise "#{lang} unknown"
      end
    else
      super
    end
  end
end

dic = Foo.new({:English => %w(zero one two), :Esperanto => %w(nulo unu du)})
dic.find_2_in :Esperanto #=> "du"

[注意] このメソッドを override する場合は対象のメソッド名に対して Object#respond_to? が真を返すようにしてください。そのためには、Object#respond_to_missing? も同様に override する必要があります。

[SEE_ALSO] Object#respond_to?, Object#respond_to_missing?

singleton_method_added(name) -> object[permalink][rdoc]

特異メソッドが追加された時にインタプリタから呼び出されます。

通常のメソッドの追加に対するフックには Module#method_addedを使います。

[PARAM] name:
追加されたメソッド名が Symbol で渡されます。
class Foo
  def singleton_method_added(name)
    puts "singleton method \"#{name}\" was added"
  end
end

obj = Foo.new
def obj.foo
end

#=> singleton method "foo" was added

[SEE_ALSO] Module#method_added,BasicObject#singleton_method_removed,BasicObject#singleton_method_undefined

singleton_method_removed(name) -> object[permalink][rdoc]

特異メソッドが Module#remove_method により削除された時にインタプリタから呼び出されます。

通常のメソッドの削除に対するフックには Module#method_removedを使います。

[PARAM] name:
削除されたメソッド名が Symbol で渡されます。
class Foo
  def singleton_method_removed(name)
    puts "singleton method \"#{name}\" was removed"
  end
end

obj = Foo.new
def obj.foo
end

class << obj
  remove_method :foo
end

#=> singleton method "foo" was removed

[SEE_ALSO] Module#method_removed,BasicObject#singleton_method_added,BasicObject#singleton_method_undefined

singleton_method_undefined(name) -> object[permalink][rdoc]

特異メソッドが Module#undef_method または undef により未定義にされた時にインタプリタから呼び出されます。

通常のメソッドの未定義に対するフックには Module#method_undefined を使います。

[PARAM] name:
未定義にされたメソッド名が Symbol で渡されます。
class Foo
  def singleton_method_undefined(name)
    puts "singleton method \"#{name}\" was undefined"
  end
end

obj = Foo.new
def obj.foo
end
def obj.bar
end

class << obj
  undef_method :foo
end
obj.instance_eval {undef bar}

#=> singleton method "foo" was undefined
#   singleton method "bar" was undefined

[SEE_ALSO] Module#method_undefined,BasicObject#singleton_method_added,BasicObject#singleton_method_removed , クラス/メソッドの定義/undef