13. Интроспекция и метапрограмиране, част 3

13. Интроспекция и метапрограмиране, част 3

13. Интроспекция и метапрограмиране, част 3

19 ноември 2014

Днес

Първи тест

Четвърта задача

Още две предизвикателства

Въпрос 1

Как Ruby пази полета и методи?

  • Полетата се пазят в обекти
  • Методите се пазят в модули
  • Обеките нямат методи (освен ако не са модули)

Въпрос 2

Кажете ми всичко, което знаете за instance променливите.

  • Пазят се в обект
  • Достъпни са в наследници
  • Не са директно достъпни извън обекта
  • Трябва да се ползват методи за достъп (напр. attr_accessor)
  • Могат да се достъпят "заобиколно" с instance_variable_get и компания

Въпрос 3

Кой е класът на "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

Въпрос 4

Какво прави instance_eval?

Изпълнява блока с променен self.

Въпрос 5

Как Ruby знае къде да постави метод, дефиниран с def?

Винаги има текущ клас, в който този метод отива. Той може да се промени с module, class и class_eval. Всъщност, дори с instance_eval, но за това — по-късно.

Въпрос 6

Можем ли да направим така, че вместо изключения, несъществуващи локални променливи или методи да резултират в nil? Как?

foo          # => nil
foo.bar.baz  # => nil

Можем, като предефинираме метода method_missing в BasicObject.

class BasicObject
  def method_missing(*) end
end

Въпрос 7

Ако имаме името на метод в променлива, например 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

Въпрос 7 (продължение)

някои алтернативи

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

Най-важните неща от миналата лекция

instance_eval

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]]

def object.method

Може да (пре)дефинирате метод в конкретен обект.

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 класове

Singleton класове

визуализация

Object#singleton_class

Собственият клас е достъпен чрез #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)     # []

Symbol и Integer

...и техните метакласове

Целите числа и символите нямат собствени класове. Това е заради оптимизация. В Ruby интерпретатора, те се представят по много различен начин от всички други обекти.

1_000.singleton_class # error: TypeError
:blah.singleton_class # error: TypeError

class << thing

алтернативен синтаксис

Можете да отворите собствения клас на обект с 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 и eigenclass

Оригиналният метод е достъпен чрез super.

super и eigenclass (2)

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 и eigenclass

OMG момент

Някой има ли идея защо super работи?

things = []

def things.answer
  42
end

things.singleton_class # #<Class:#<Array:0x42226cfc>>
things.singleton_class.superclass # Array

Да, eigenclass-ът е наследник на класа на обекта

superclass и 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

...върху клас

Помните ли extend?

module Knowledge
  def answer() 42 end
end

class Something
  extend Knowledge
end

Something.answer # 42

extend

...върху не-клас

module Knowledge
  def answer() 42 end
end

text = "fourty-two"
text.extend Knowledge

text.answer # 42

Сещате ли се как може да се имплементира?

extend

...с class <<

module Knowledge
  def answer() 42 end
end

class Something; end

class << Something
  include Knowledge
end

Something.answer # 42

extend

...чрез instance_eval и eigenclass

module Knowledge
  def answer() 42 end
end

class Something; end

Something.singleton_class.instance_eval { include Knowledge }

Something.answer # 42

extend

...чрез include

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

Класови методи и наследяване

друг OMG момент

Собственият клас на наследника наследява собствения клас на родителя:

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

Класови методи и наследяване

визуализация

Класови методи и наследяване

Класови методи и наследяване

takeaway

Метакласът на суперкласа е суперкласът на метакласа.

Grand Unified Theory

  1. Има само един вид обекти - били те обикновени или модули
  2. Има само един вид модули - били те обикновени или клас
  3. Има само един вид методи - живеят в модули, които често са класове
  4. Всеки обект има "реален клас" - бил той обикновен клас или собствен клас
  5. Всеки клас има точно един суперклас - с изключение на BasicObject
  6. Суперкласът на метакласа на обект е класът на обекта. Суперкласът на метакласа на клас е метакласът на родителя на класа.
  7. При извикване на метод, Ruby взема "реалния клас" и търси в неговия ancestor chain

Ancestor chains

Няколко примера за ancestor chains

Относно търсенето на (инстанционни) методи на обектите от тип 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]

Ancestor chains

за "класови" методи

По отношение на "класовите" методи, викани върх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]

Пето предизвикателство

Идната седмица...

Въпроси