Кажете ми всичко, което знаете за instance променливите.
attr_accessor
)instance_variable_get
и компанияКой е класът на {}
? На Integer
? На Class
?
Кой е родителят на String
? На Object
? На Class
?
{}.class == Hash # => true
Integer.class == Class # => true
Class.class == Class # => true
String < Object # => true
Object < BasicObject # => true
Class < Module # => true
Какво прави instance_eval
? Ами instance_exec
?
Изпълнява блока с променен self
. instance_exec
е същото, но може да подава аргументи на блока си.
Как Ruby знае къде да постави метод, дефиниран с def
?
Винаги има текущ клас, в който този метод отива. Той може да се промени
с module
, class
и class_eval
. Всъщност,
дори с instance_eval
, но за това — по-късно.
Можем ли да направим така, че вместо изключения, несъществуващи локални променливи или методи да резултират в nil
? Как?
foo # => nil
foo.bar.baz # => nil
Можем, като предефинираме метода method_missing
в BasicObject
.
class BasicObject
def method_missing(*) end
end
Ако имаме името на метод в променлива, например method_name = 'first_name'
, бихме могли да ползваме define_method
, за да създадем динамично метод:
class MyObject
method_name = 'first_name'
implementation = -> { @data_store['first_name'] }
define_method(method_name, &implementation)
end
method_name = 'first_name'
implementation = -> { @data_store['first_name'] }
MyObject.send :define_method, method_name, &implementation
Или:
some_class = MyObject
method_name = 'first_name'
implementation = -> { @data_store['first_name'] }
some_class.class_eval do
define_method(method_name, &implementation)
end
method_missing
instance_eval
/class_eval
define_method
some_list = []
some_list.instance_eval do
push 5
push 'foo'
push :bar
push [:another, :list]
size # => 4
end
some_list # => [5, "foo", :bar, [:another, :list]]
Може да (пре)дефинирате метод в конкретен обект.
things = [22, :f, 'Sofia']
def things.size
-5
end
def things.asl
"#{self[0]}/#{self[1]}/#{self[2]}"
end
things # => [22, :f, "Sofia"]
things.size # => -5
things.length # => 3
things.asl # => "22/f/Sofia"
[].asl # => error: NoMethodError
[].size # => 0
Собственият клас е достъпен чрез #singleton_class
things = []
def things.answer
42
end
things.singleton_class
# => #<Class:#<Array:0x002b572f8bc548>>
things.singleton_class.instance_methods(false)
# => [:answer]
[].singleton_class.instance_methods(false)
# => []
Целите числа и символите нямат собствени класове. Това е заради специалното им вътрешно представяне в Ruby.
1_000.singleton_class # => error: TypeError
:blah.singleton_class # => error: TypeError
Можете да отворите собствения клас на обект с class <<
things = [22, :f, 'Sofia']
class << things
def size
-5
end
def asl
"#{self[0]}/#{self[1]}/#{self[2]}"
end
end
things.asl # => "22/f/Sofia"
things.size # => -5
Оригиналният метод е достъпен чрез super
.
things = [22, :f, 'Sofia']
class << things
def each
super
yield :P
end
end
aggregated = []
things.each do |thing|
aggregated << thing
end
aggregated # => [22, :f, "Sofia", :P]
Някой има ли идея защо super
работи?
things = []
def things.answer
42
end
things.singleton_class # => #<Class:#<Array:0x002b91ce2dc478>>
things.singleton_class.superclass # => Array
Да, singleton класът е наследник на класа на обекта.
Ще продължим с преглед отвътре на "класовите" методи.
Вероятно помните, че класови методи могат да се дефинират така:
class Something
def Something.foo() 42 end
def self.bar() 42 end
class << self
def qux() 42 end
end
end
Класовите методи се пазят в собствения клас на класа
class Something
def self.answer() 42 end
end
Something.singleton_class # => #<Class:Something>
Something.singleton_class.instance_methods(false) # => [:answer]
class Something; end
Something.instance_eval do
def say_something() 'something' end
end
Something.say_something # => "something"
Something.new.say_something # => error: NoMethodError
def Something.say_something() 'something' end
self == Something
Something
instance_eval
дефинират методи върху singleton класаclass Something; end
Something.class_eval do
def say_something() 'something' end
end
Something.say_something # => error: NoMethodError
Something.new.say_something # => "something"
class Something; def say_something() 'something' end end
self == Something
Something
class_eval
дефинират методи върху самия класПомните ли extend
?
module Knowledge
def answer() 42 end
end
class Something
extend Knowledge
end
Something.answer # => 42
module Knowledge
def answer() 42 end
end
text = "fourty-two"
text.extend Knowledge
text.answer # => 42
Сещате ли се как може да се имплементира?
module Knowledge
def answer() 42 end
end
class Something; end
class << Something
include Knowledge
end
Something.answer # => 42
module Knowledge
def answer() 42 end
end
class Something; end
Something.singleton_class.instance_eval { include Knowledge }
Something.answer # => 42
module Knowledge
def answer() 42 end
end
class Something; end
Something.singleton_class.include Knowledge
Something.answer # => 42
Изводи:
include
и extend
са просто методи, викащи се на определени обекти
obj.extend(foo)
≃ obj.singleton_class.include(foo)
Ще продължим да дълбаем в класовите методи при наследяване.
Класовите методи на родителя са достъпни в класовите методи на наследника:
class Something
def self.answer() 42 end
end
class Other < Something
def self.better_answer() answer * 2 end
end
Other.better_answer # => 84
Собственият клас на наследника наследява собствения клас на родителя:
class Something; end
class Other < Something; end
Something.singleton_class # => #<Class:Something>
Other.singleton_class.superclass # => #<Class:Something>
Something.singleton_class == Other.singleton_class.superclass # => true
Singleton класът на суперкласа е суперкласът на singleton класа.
BasicObject
и неговия singleton клас
BasicObject
наследява от Class
class Foo
end
Foo.singleton_class.ancestors
# => [#<Class:Foo>,
# #<Class:Object>,
# #<Class:BasicObject>,
# Class,
# Module,
# Object,
# PP::ObjectMixin,
# Kernel,
# BasicObject]
object
, Ruby го търси в object.singleton_class.ancestors
Относно търсенето на (инстанционни) методи на обектите от тип String
:
''.singleton_class.ancestors
# => [#<Class:#<String:0x007f8788e51bd8>>,
# String, Comparable, Object, Kernel, BasicObject]
''.class.ancestors
# => [String, Comparable, Object, Kernel, BasicObject]
String.ancestors
# => [String, Comparable, Object, Kernel, BasicObject]
По отношение на "класовите" методи, викани върхy String
:
String.singleton_class.ancestors
# => [#<Class:String>, #<Class:Object>, #<Class:BasicObject>,
# Class, Module, Object, Kernel, BasicObject]
String.class.ancestors
# => [Class, Module, Object, Kernel, BasicObject]
Class.ancestors
# => [Class, Module, Object, Kernel, BasicObject]
BasicObject
∎