Кажете всичко, което знаете за 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
е същото, но може да подава аргументи на блока си.
Можем ли да направим така, че вместо изключения, несъществуващи локални променливи или методи да резултират в nil
? Как?
foo # => nil
foo.bar.baz # => nil
Можем, като предефинираме метода method_missing
в BasicObject
.
class BasicObject
def method_missing(*) end
end
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]]
self
), има и текущ клас
def
kind_of?(Module) ? name : self.class.name
class
и module
го променятdef foo() end # Тук е Object
class Something
def bar() end # Тук е Something
class OrOther
def baz() end # Тук е Something::OrOther
end
end
class Something
def foo
def bar
6 * 9
end
bar - 12
end
end
something = Something.new
something.foo # => 42
something.bar # => 54
class_eval
променя self
, и текущия клас:
def monkey_patch_string
String.class_eval do
self # => String
def answer
42
end
end
end
"abc".respond_to? :answer # => false
monkey_patch_string
"abc".respond_to? :answer # => true
Module#module_eval
е синоним на Module#class_eval
.
Очакват ни define_method
, чистото зло – eval
и binding.
send
– извикване на метод, ако имате името му в променлива
method_missing
– "отговаряне" на несъществуващи методи
define_method
– динамично създаване на методиclass Something
define_method :foo do |arg|
"!#{arg}! :)"
end
end
Something.new.foo('a') # => "!a! :)"
class Something
METASYNTACTIC = %w[foo bar baz]
METASYNTACTIC.each do |name|
define_method name do |arg|
"!#{arg}! :)"
end
end
end
Something.new.bar('a') # => "!a! :)"
Something.new.baz('a') # => "!a! :)"
Като `define_method`, но прави класов метод, не инстанционен.
class Something
define_singleton_method :foo do |arg|
"!#{arg}! :)"
end
end
Something.foo('a') # => "!a! :)"
Something.new.foo('a') # => error: NoMethodError
eval(text)
изпълнява код в низ
things = []
eval 'things << 42'
things # => [42]
eval
?
x = 1_024
vars = binding
vars # => #<Binding:0x002b3a00685848>
vars.eval('x') # => 1024
x = 1_024
def foo
y = 42
binding
end
vars = foo
vars.eval('y') # 42
vars.eval('x') # error: NameError
local_variable_[gs]et
module
, class
и def
секват binding-а
top_level = 1
module Something
in_module = 2
class Other
in_class = 3
def larodi
top_level # error: NameError
in_module # error: NameError
in_class # error: NameError
end
end
end
Something::Other.new.larodi
Scope gate-овете могат да се заобиколят с define_method
, Class.new
и Module.new
.
top_level = 1
module Something
in_module = 2
class Other
in_class = 3
define_method :larodi do
top_level # error: NameError
in_module # error: NameError
in_class # 3
end
end
end
Something::Other.new.larodi
top_level = 1
module Something
in_module = 2
Other = Class.new do
in_class = 3
define_method :larodi do
top_level # error: NameError
in_module # 2
in_class # 3
end
end
end
Something::Other.new.larodi
top_level = 1
Something = Module.new do
in_module = 2
Other = Class.new do
in_class = 3
define_method :larodi do
top_level # 1
in_module # 2
in_class # 3
end
end
end
Other.new.larodi
Обърнете внимание, че тук Other
се дефинира в root namespace-а.
Може да (пре)дефинирате метод в конкретен обект.
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:0x002b0e4453ef58>>
things.singleton_class.instance_methods(false)
# => [:answer]
[].singleton_class.instance_methods(false)
# => []
Целите числа и символите нямат singleton класове. Това е заради специалното им вътрешно представяне в 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:0x002b38b23a1e00>>
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 Module
def define_singleton_method(*args, &block)
singleton_class.instance_eval do
define_method(*args, &block)
end
end
end
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
дефинират методи върху самия класdefine_singleton_method
използваме, когато сме вътре в класа
class_eval
използваме, когато define_singleton_method
не би ни свършил удобна работаПомните ли 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]
class Cow
def self.moo(say)
puts "#{say} moo"
end
end
class Foo < Cow
moo 'Hello'
end
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
∎