DSL
-и (domain specific languages)
Как Ruby пази полета и методи?
Кажете ми всичко, което знаете за instance променливите.
attr_accessor
)instance_variable_get
и компанияКой е класът на "foo"
? На Integer
? На Class
?
Кой е родителят на String
? На Object
? На Class
?
"foo".class == String # true
Integer.class == Class # true
Class.class == Class # true
String < Object # true
Object < BasicObject # true
Class < Module # true
Какво прави instance_eval
?
Изпълнява блока с променен self
.
Как 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'
, можем ли динамично да създадем такъв метод? Как?
class MyObject
method_name = 'first_name'
implementation = -> { @data_store['first_name'] }
# Dynamically define a first_name "getter"?
end
Бихме могли да използваме 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:0x412a7588>>
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 = []
for thing in things
aggregated << thing
end
aggregated # [22, :f, "Sofia", :P]
Някой има ли идея защо super
работи?
things = []
def things.answer
42
end
things.singleton_class # #<Class:#<Array:0x42226cfc>>
things.singleton_class.superclass # Array
Да, eigenclass-ът е наследник на класа на обекта
Вероятно помните, че класови методи могат да се дефинират така:
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]
Помните ли 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
са просто методи, викащи се на определени обекти.
Класовите методи на родителя са достъпни в класовите методи на наследника:
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
BasicObject
и неговия метаклас
BasicObject
наследява от Class
Метакласът на суперкласа е суперкласът на метакласа.
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]
some_collection.each(&block)
String#split(//)
vs. String#chars
– кое е по-ясно?