06. Синоними на методи. Именовани пространства. Класови методи. Паралелно присвояване.

06. Синоними на методи. Именовани пространства. Класови методи. Паралелно присвояване.

06. Синоними на методи. Именовани пространства. Класови методи. Паралелно присвояване.

31 октомври 2016

Днес

OpenFest

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

Въпрос 1

Какво ще има в ancestors chain-a?

module Foo; end
module Bar; end
module Baz; end

module Qux
  include Foo
  include Bar
end

class Garply
  include Foo
  include Qux
end

class Waldo < Garply
  include Foo
  include Baz
  include Bar
end

Waldo.ancestors # =>

[Waldo, Baz, Garply, Qux, Bar, Foo, Object, Kernel, BasicObject]

Основните класове в Ruby

"Всичко наследява от Object"

Class.ancestors # => [Class, Module, Object, Kernel, BasicObject]

Основните класове в Ruby

Class.ancestors # => [Class, Module, Object, Kernel, BasicObject]

protected

Само обекти от същия клас могат да викат protected методи

class Vector
  def initialize(x, y) @x, @y = x, y          end
  def inspect()        "Vector.new(#@x, #@y)" end

  def +(other)
    Vector.new(*coords.zip(other.coords).map { |a, b| a + b })
  end

  protected
  def coords() [@x, @y] end
end

vector = Vector.new(1, 2) + Vector.new(3, 4)
vector        # => Vector.new(4, 6)
vector.coords # => error: NoMethodError

private и protected

още известни като Private, Public и General Specific

Клас-макросите private и protected

Понеже private и protected са методи:

class Person
  def name() end
  def age()  end

  private :name, :age
end

Или дори:

class String
  private :upcase!, :downcase!
end

"Foo".upcase! # => error: NoMethodError

Клас-макросите private и protected (2)

Помните ли, че дефиницията на метод връща името на метод като символ?

def foo() end # :foo

Значи може да направите така:

class Person
  private def name
    "My name is a secret."
  end

  private def age
    "I'm feeling feminine, so my age is a secret, too."
  end
end

Синоними на методи в Ruby

Синтаксис за синоними на методи

Семантика на синоними на методи

Пример с alias

Въпреки името си, alias прави копие на метод.

class Array
  alias old_reduce reduce

  def reduce(*args, &block)
    puts "I see you are using #reduce. Let me help!"
    old_reduce(*args, &block) * 0.01
  end
end

[1, 2, 3, 4, 5, 6].reduce { |a, b| a + b } # => 0.21

Синоними на методи

Пример за реална употреба:

def to_s
  to_html
end

По-добре да се запише така:

alias_method :to_s, :to_html

Или така:

alias to_s to_html

Разлики между alias и alias_method

Разлики между alias и alias_method

създаване на синоними по време на изпълнение

class Array
  [:size, :count, :length].each do |method_name|
    alias_method "old_#{method_name}", :size
  end

  def size
    0
  end
end

[1, 2, 3].size     # => 0
[1, 2, 3].old_size # => 3

Забележки относно синоними на методи

Именувани пространства

Класовете и модулите могат да служат като именувани пространства.

module Useless
  class Thing
  end
end

class Grandfather
  class StraightRazor
  end
end

Useless::Thing.new             # => #<Useless::Thing:0x002b3e0ec017b0>
Grandfather::StraightRazor.new # => #<Grandfather::StraightRazor:0x002b3e0ec01328>

Именувани пространства (2)

Ако се намирате в модул, няма нужда да ползвате пълния път до константите:

module Useless
  class Thing
  end

  Thing.new         # => #<Useless::Thing:0x002b563b230958>
end

Useless::Thing.new  # => #<Useless::Thing:0x002b563b2304f8>
Thing.new           # => error: NameError

Търсене на променливи в Ruby

bacon = 2

def foo
  chunky = 10

  1.times do
    chunky       # 10
    chunky = 44
  end

  chunky         # 44
  bacon          # error: NameError
end

foo()

Правила за търсене на константи

Малък пример

PLACE = 'root'
module Outer
  PLACE = 'intermediate'
  module Inner
    PLACE = 'deep'
  end
end

PLACE               # => "root"
Outer::Inner::PLACE # => "deep"
module Outer
  module Inner
    PLACE           # => "deep"
    ::PLACE         # => "root"
  end
  PLACE             # => "intermediate"
  Inner::PLACE      # => "deep"
end

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

Може да дефинирате класови методи така:

class Something
  def Something.answer
    42
  end
end

Something.answer   # => 42

Класови методи (2)

Не може да ги викате неквалифицирано от инстанцията:

class Something
  def Something.answer
    42
  end

  def do_stuff
    answer             # => error: NameError
    Something.answer   # => 42
  end
end

thing = Something.new
thing.answer           # => error: NoMethodError
Something.answer       # => 42

thing.do_stuff

Класови методи (3)

Достъпни са в наследниците:

class Base
  def Base.answer() 42 end
end

class Derived < Base
  def Derived.say_answer
    answer         # => 42
    Base.answer    # => 42
  end
end

Derived.answer     # => 42
Base.answer        # => 42

Derived.say_answer

Класови методи (4)

Има и други начини за дефиниция на класови методи:

Класови методи (5)

Присвояване

Паралелно присвояване

прост пример

a, b = 1, 2
a              # => 1
b              # => 2

a, b = b, a
a              # => 2
b              # => 1

Има няколко различни случая, които ще разгледаме.

Паралелно присвояване

присвояване на една променлива

a = 1, 2, 3
a # => [1, 2, 3]

Практически същото като a = [1, 2, 3]

Паралелно присвояване

разпакетиране на дясната страна

a, b = [1, 2, 3]
a # => 1
b # => 2

a, b = 1, 2, 3
a # => 1
b # => 2

Паралелно присвояване

със splat аргументи

head, *tail = [1, 2, 3]
head   # => 1
tail   # => [2, 3]

first, *middle, last = 1, 2, 3, 4
first  # => 1
middle # => [2, 3]
last   # => 4

Паралелно присвояване

splat аргументи отдясно

first, *middle, last = 1, [2, 3, 4]
first  # => 1
middle # => []
last   # => [2, 3, 4]

first, *middle, last = 1, *[2, 3, 4]
first  # => 1
middle # => [2, 3]
last   # => 4

Вложено присвояване

head, (title, body) = [1, [2, 3]]
head   # => 1
title  # => 2
body   # => 3

Вложено присвояване и splat-ове

head, (title, *sentences) = 1, [2, 3, 4, 5, 6]
head      # => 1
title     # => 2
sentences # => [3, 4, 5, 6]

Ред на оценка

Бележка за реда на оценка при присвояване — първо отдясно, след това отляво:

x = 0
a, b, c = x, (x += 1), (x += 1)
x # => 2
a # => 0
b # => 1
c # => 2

Променливата _

Променливата _

Може да ползвате едно име само един път, когато то се среща в списък с параметри на метод, блок и прочее.

Proc.new { |a, b, a| } # SyntaxError: duplicated argument name
Proc.new { |_, b, _| } # => #<Proc:0x007f818af68de0@(irb):23>

Горното важи не само за блокове, но и за методи.

Присвояване в Ruby

Къде важат тези правила?

[[1, [2, 3]], [4, [5, 6]], [7, [8, 9]]].each do |a, (b, c)|
  puts "#{a}, #{b}, #{c}"
end
# 1, 2, 3
# 4, 5, 6
# 7, 8, 9

Присвояване в Ruby

Имате ли въпроси по тази тема?

Quine

програма, принтираща кода си

->_{_%_}["->_{_%%_}[%p]"]

Quine

to the eleven

v=0000;eval$s=%q~d=%!^Lcf<LK8,                  _@7gj*LJ=c5nM)Tp1g0%Xv.,S[<>YoP
4ZojjV)O>qIH1/n[|2yE[>:ieC       "%.#%  :::##"       97N-A&Kj_K_><wS5rtWk@*a+Y5
yH?b[F^e7C/56j|pmRe+:)B     "##%      ::##########"     O98(Zh)'Iof*nm.,$C5Nyt=
PPu01Avw^<IiQ=5$'D-y?    "##:         ###############"    g6`YT+qLw9k^ch|K'),tc
6ygIL8xI#LNz3v}T=4W    "#            #.   .####:#######"    lL27FZ0ij)7TQCI)P7u
}RT5-iJbbG5P-DHB<.   "              ##### # :############"   R,YvZ_rnv6ky-G+4U'
$*are@b4U351Q-ug5   "              #######################"   00x8RR%`Om7VDp4M5
PFixrPvl&<p[]1IJ   "              ############:####  %#####"   EGgDt8Lm#;bc4zS^
y]0`_PstfUxOC(q   "              .#############:##%   .##  ."   /,}.YOIFj(k&q_V
zcaAi?]^lCVYp!;  " %%            .################.     #.   "  ;s="v=%04o;ev"%
(;v=(v-($*+[45,  ":####:          :##############%       :   "  ])[n=0].to_i;)%
360)+"al$s=%q#{  "%######.              #########            "  ;;"%c"%126+$s<<
126}";d.gsub!(/  "##########.           #######%             "  |\s|".*"/,"");;
require"zlib"||  "###########           :######.             "  ;d=d.unpack"C*"
d.map{|c|n=(n||  ":#########:           .######: .           "  )*90+(c-2)%91};
e=["%x"%n].pack   " :#######%           :###### #:          "   &&"H*";e=Zlib::
Inflate.inflate(   "  ######%           .####% ::          "   &&e).unpack("b*"
)[0];22.times{|y|   "  ####%             %###             "   ;w=(Math.sqrt(1-(
(y*2.0-21)/22)**(;   " .###:             .#%             "   ;2))*23).floor;(w*
2-1).times{|x|u=(e+    " %##                           "    )[y*z=360,z]*2;u=u[
90*x/w+v+90,90/w];s[(    " #.                        "    ;y*80)+120-w+x]=(""<<
32<<".:%#")[4*u.count((     " .                   "     ;"0"))/u.size]}};;puts\
s+";_ The Qlobe#{" "*18+ (       "#  :#######"       ;"Copyright(C).Yusuke End\
oh, 2010")}";exit~;_ The Qlobe                  Copyright(C).Yusuke Endoh, 2010

Quine

Може да пуснете quine-а от предния слайд като го запазите във файл quine.rb и ползвате това скриптче:

#!/bin/sh

while true
do
  ruby quine.rb | tee quine_result
  mv quine_result quine.rb
  sleep 0.2
done

Constant Autoloading in Rails

🎉 bonus 🎉

Въпроси