Ruby Gold 認定試験の Ver 3 の勉強時に学んだことを備忘録として残しておく
[前回までの記事]
環境
- ruby: 3.1.2
キーワード引数
キーワード引数で a:
のようなデフォルト値を指定しない場合、構文エラーにはならないが、呼び出し時に指定しなかった場合に ArgumentError
になる
class KeywordArgument
def foo(a:, b: 'bbb')
puts "a: #{a}, b: #{b}"
end
end
keyword_argument = KeywordArgument.new
keyword_argument.foo(a: 'aaa', b: 'BBB')
#=> a: aaa, b: BBB
keyword_argument.foo(a: 'aaa')
#=> a: aaa, b: bbb
keyword_argument.foo
#=> ./keyword_argument.rb:2:in `foo': missing keyword: :a (ArgumentError)
#=> from ./keyword_argument.rb:8:in `<main>'
グローバル変数, クラス変数, インスタンス変数
グローバル変数
グローバル変数のため全体で同じ変数を参照
class GlobalArgument
$count = 0
def increment
$count = $count + 1
puts $count
end
def self.increment
$count = $count + 1
puts $count
end
end
class ExtendGlobalArgument < GlobalArgument
def increment
$count = $count + 2
puts $count
end
end
global_argument = GlobalArgument.new
global_argument2 = ExtendGlobalArgument.new
global_argument.increment
#=> 1
global_argument2.increment
#=> 3
global_argument.increment
#=> 4
global_argument2.increment
#=> 6
クラス変数
クラスごとに変数が管理される
class ClassVariable
@@count = 0
def increment
@@count = @@count + 1
puts @@count
end
def self.increment
@@count = @@count + 1
puts @@count
end
end
class ExtendClassVariable < ClassVariable
def increment
@@count = @@count + 2
puts @@count
end
end
class_argument = ClassVariable.new
class_argument2 = ExtendClassVariable.new
class_argument.increment
#=> 1
class_argument2.increment
#=> 3
class_argument.increment
#=> 4
class_argument2.increment
#=> 6
インスタンス変数
グローバル変数、クラス変数と同じ要領で書くとエラー
class InstanceVariable
@count = 0
def increment
@count = @count + 1
puts @count
end
def self.increment
@count = @count + 1
puts @count
end
end
class ExtendInstanceVariable < InstanceVariable
def increment
@count = @count + 2
puts @count
end
end
instance_variable = InstanceVariable.new
instance_variable2 = ExtendInstanceVariable.new
instance_variable.increment
#=> ./instance_variable.rb:5:in `increment': undefined method `+' for nil:NilClass (NoMethodError)
#=> @count = @count + 1
accessor もしくは initialize メソッドでの初期化が必要
※インスタンスごとに変数が管理される
class InstanceVariable
def initialize
@count = 0
end
def increment
@count = @count + 1
puts @count
end
def self.increment
@count = @count + 1
puts @count
end
end
class ExtendInstanceVariable < InstanceVariable
def increment
@count = @count + 2
puts @count
end
end
instance_variable = InstanceVariable.new
instance_variable2 = ExtendInstanceVariable.new
instance_variable.increment
#=> 1
instance_variable2.increment
#=> 2
instance_variable.increment
#=> 2
instance_variable2.increment
#=> 4
singleton を include した場合は同一インスタンスを返却するようになる
require 'singleton'
class SingletonInstanceVariable
include Singleton
def initialize
@count = 0
end
def increment
@count = @count + 1
puts @count
end
def self.increment
@count = @count + 1
puts @count
end
end
class ExtendSingletonInstanceVariable < SingletonInstanceVariable
def increment
@count = @count + 2
puts @count
end
end
# SingletonInstanceVariable.new は使えない => NoMethodError
instance_variable = SingletonInstanceVariable.instance
instance_variable2 = ExtendSingletonInstanceVariable.instance
instance_variable3 = SingletonInstanceVariable.instance
instance_variable.increment
#=> 1
instance_variable2.increment
#=> 2
instance_variable3.increment
#=> 2
instance_variable.increment
#=> 3
instance_variable2.increment
#=> 4
instance_variable3.increment
#=> 4
引数の異なるメソッドを継承先のクラスで定義した際の super の挙動
super 指定時に ()
を省略した場合は super と同じ指定がされたものと見なされるため Arguments Error となる
class Foo
def foo(a, b)
puts "Foo#foo a=#{a}, b=#{b}"
end
end
class Bar < Foo
def foo(a)
super
puts "Bar#foo a=#{a}"
end
end
foo = Foo.new
foo.foo('A', 'B')
#=> Foo#foo a=A, b=B
bar = Bar.new
bar.foo('aaa')
#=> ./inheritance_method.rb:2:in `foo': wrong number of arguments (given 1, expected 2) (ArgumentError)
#=> from ./inheritance_method.rb:9:in `foo'
#=> from ./inheritance_method.rb:19:in `<main>'
デフォルト値を指定してある場合は呼び出せる
class Foo
def foo(a, b = 'BBB')
puts "Foo#foo a=#{a}, b=#{b}"
end
end
class Bar < Foo
def foo(a)
super
puts "Bar#foo a=#{a}"
end
end
foo = Foo.new
foo.foo('A', 'B')
#=> Foo#foo a=A, b=B
bar = Bar.new
bar.foo('aaa')
#=> Foo#foo a=aaa, b=BBB
#=> Bar#foo a=aaa
もしくは、super に渡す引数を指定する
class Foo
def foo(a, b)
puts "Foo#foo a=#{a}, b=#{b}"
end
end
class Bar < Foo
def foo(a)
super(a, 'BB')
puts "Bar#foo a=#{a}"
end
end
foo = Foo.new
foo.foo('A', 'B')
#=> Foo#foo a=A, b=B
bar = Bar.new
bar.foo('aaa')
#=> Foo#foo a=aaa, b=BB
#=> Bar#foo a=aaa
method_missing
該当のメソッドが存在しなかった場合に呼び出されるメソッド
module M
def method_missing(id, *args)
puts "C#method_missing"
end
end
class A
include M
def method_missing(id, *args)
puts "A#method_missing"
end
end
class B < A
def method_missing(id, *args)
puts "B#method_missing"
end
end
obj = B.new
obj.test_method
#=> "B#method_missing"
Object クラスに定義したメソッドについて
クラス定義に使用する Class クラスは、 Object クラスの子孫にあたるため Object クラスに定義したメソッドは呼び出し可能
[Class クラスの継承リスト]
Class < Module < Object < Kernel < BasicObject
class Object
def foo
puts 'Object#foo'
end
end
class Foo
def foo
super
puts 'Foo#foo'
end
end
class Bar < Foo
def foo
super
puts 'Bar#foo'
end
end
bar = Bar.new
bar.foo
#=> Object#foo
#=> Foo#foo
#=> Bar#foo
定数の探索範囲
module M
CONSTANT_A = 'A'.freeze
end
class A
include M
CONSTANT_A = 'AAA'.freeze
def foo
puts M::CONSTANT_A
puts CONSTANT_A
end
end
class B < A
CONSTANT_A = 'BBB'.freeze
def foo
super
puts CONSTANT_A
end
end
class C < B
CONSTANT_C = 'CCC'.freeze
def foo
super
puts CONSTANT_A
end
class D
CONSTANT_D = 'DDD'.freeze
def foo
puts CONSTANT_C
# puts CONSTANT_A
#=> uninitialized constant C::D::CONSTANT_A (NameError)
#=> Did you mean? C::D::CONSTANT_D
#=> C::CONSTANT_C
# puts ::CONSTANT_A
#=> uninitialized constant CONSTANT_A (NameError)
super
#=> super: no superclass method `foo' for #<C::D:0x000000010502f840> (NoMethodError)
#=> Did you mean? for
puts ::A::CONSTANT_A
puts ::B::CONSTANT_A
end
end
end
a = A.new
b = B.new
c = C.new
d = C::D.new
a.foo
#=> A
#=> AAA
b.foo
#=> A
#=> AAA
#=> BBB
c.foo
#=> A
#=> AAA
#=> BBB
#=> BBB
d.foo
#=> CCC
#=> AAA
#=> BBB