Малко въпроси
Какво правят alias
и alias_method
? Каква е разликата между двете?
alias
е ключова дума, alias_method
е метод
alias_method
може да се използва с променливиКакъв ще е резултатът от кода по-долу? Защо?
answer = 42
universe = proc do
def say_answer
puts "The answer is: #{answer}"
end
say_answer
end
Proc
-а universe
(trolololo)
answer
е недефинирано в say_answer
def
, class
и module
са scope gate-ове
Какво ще изведе на екрана следният код:
FOOD = 'muffin'
module Cake
FOOD = 'fruit'
end
module Dessert
FOOD = ::Cake
module Cake
FOOD = 'chocolate'
end
end
| puts FOOD
module Dessert
puts Cake::FOOD
module Cake
puts Cake::FOOD
puts ::Cake::FOOD
puts Dessert::FOOD
end
puts FOOD
end
puts Dessert::FOOD::FOOD
|
Отговорът
FOOD = 'muffin'
module Cake
FOOD = 'fruit'
end
module Dessert
FOOD = ::Cake
module Cake
FOOD = 'chocolate'
end
end
| puts FOOD #=> muffin
module Dessert
puts Cake::FOOD #=> chocolate
module Cake
puts Cake::FOOD #=> chocolate
puts ::Cake::FOOD #=> fruit
puts Dessert::FOOD #=> Cake
end
puts FOOD #=> Cake
end
puts Dessert::FOOD::FOOD #=> fruit
|
Какво ще съдържат променливите след присвояването:
foo, (bar,), (larodi, *qux, answer), *others = 1, [2, 3], [1, 2, 3, 42]
foo
#=> 1
bar
#=> 2
larodi
#=> 1
qux
#=> [2, 3]
answer
#=> 42
others
#=> []::
Object
Object::FOO
и ::FOO
в (почти) всички случаи са едно и също
::Object::FOO
и ::FOO
са едно и също винагиObject
Можете да видите тази "таблица" през constants
:
module MyLibrary
class Object
end
end
MyLibrary.constants # => [:Object]
MyLibrary::Object == Object # => false
class Foo::Bar
# In the Bar scope, but not in the Foo scope
end
class
и module
В този пример:
class Foo::Bar
Larodi
end
Bar
и я записваме в таблицата с константи на Foo
Foo
трябва да съществува предварително; може да бъде или модул, или клас
Larodi
ще се търси в таблицата на Bar
и след това в root scope-а
Foo
module A::B
module C::D
Foo # Where does Ruby look for Foo?
end
end
D
B
Object
)Задайте ги сега!
Следните два реда са (почти) еквивалентни:
name = proc { |object| object.name }
name = :name.to_proc
Когато подавате блок на метод с &block
, Ruby извиква
#to_proc
, ако block
не е ламбда или proc.
Съответно, следните два реда са еквивалентни
%w(foo plugh larodi).map { |s| s.length } # => [3, 5, 6]
%w(foo plugh larodi).map(&:length) # => [3, 5, 6]
Всъщност, малко по-сложно е:
block = proc { |obj, *args| obj.method_name *args }
block = :method_name.to_proc
Това значи, че може да направите така:
[{a: 1}, {b: 2}, {c: 3}].reduce { |a, b| a.merge b } # => {:a=>1, :b=>2, :c=>3}
[{a: 1}, {b: 2}, {c: 3}].reduce(&:merge) # => {:a=>1, :b=>2, :c=>3}
Или дори:
[1, 2, 3, 4].reduce { |sum, b| sum + b } # => 10
[1, 2, 3, 4].reduce(&:+) # => 10
['Foo', :bar, 3].map(&:to_s).map(&:upcase)
class Symbol
def to_proc
# ...?
end
end
send
send
, за да викате произволни методи на този обект
private
или protected
такива
public_send
, ако искате да не прекрачвате този праг
3.send :+, 4 # => 7
3.public_send :+, 4 # => 7
3.send :puts, 'Ruby magic!' # Ruby magic!
3.public_send :puts, 'wow' # => error: NoMethodError
class Symbol
def to_proc
proc { |object, *args| object.public_send self, *args }
end
end
???
String.methods
)
String.class
) и той е Class
Долното работи:
a_hash_class = Hash
a_hash_class.new # => {}
Class.new
Module.new
ще създаде инстанция на анонимен модулanonymous_class = Class.new
anonymous_class.new # => #<#<Class:0x002b4f24590b20>:0x002b4f24590aa8>
Class.new.new # => #<#<Class:0x002b4f24590530>:0x002b4f245904e0>
Module.new # => #<Module:0x002b4f245900f8>
Можем да подадем блок на Class.new
:
duck = Class.new do
def quack_to(creature_name)
"Quack, #{creature_name}, quack!"
end
end
duck.new.quack_to("swan") # => "Quack, swan, quack!"
class Person
end
class
е просто синтаксис, който прави обект от тип "клас"
class
е scope gate, докато блоковете не са
class
го присвоява и на константа
Когато присвоим анонимен клас на константа, попадаме в специален случай:
Person = Class.new
Class.new # => #<Class:0x002ba7326dbe10>
Person = Class.new # => Person
Module.new # => #<Module:0x002ba7326db578>
Larodi = Module.new # => Larodi
?!?
Неканоничният и праволинеен начин за дефиниране на класов метод:
module Snake
class Level
def Level.load(level_data)
# Code
end
end
end
Този начин е еквивалентен на предишния слайд:
module Snake
class Level
def self.load(level_data)
# Code
end
end
end
Това работи, понеже:
self
self
и Snake::Level
в тялото на класа реферират към един и същ обектИзползва се при нужда да се дефинират няколко класови метода:
module Snake
class Level
class << self
def load(level_data)
# Code
end
end
end
end
def ClassName.method_name() end
)
Преди да продължим със следващата тема.
В Ruby има "switch". Казва се case
.
def quote(name)
case name
when 'Yoda'
puts 'Do or do not. There is no try.'
when 'Darth Vader'
puts 'The Force is strong with this one.'
when 'R2-D2'
puts 'Beep. Beep. Beep.'
else
puts 'Dunno what to say'
end
end
Забележете индентацията.
break
when
не мине, изпълнява се else
when
не мине и няма else
, не става нищо
case
е израз, което значи, че връща стойностcase operation
when :& then puts 'And?'
when :| then puts 'Or...'
when :! then puts 'Not!'
else puts 'WAT?'
end
На какво ще се оцени следният код?
case 'Wat?'
when 'watnot' then puts "I'm on a horse."
end
Ако няма else
и никой when
не match-не, се връща nil
.
case
не сравнява с ==
. Може да напишете следното:
def qualify(age)
case age
when 0..12
'still very young'
when 13..19
'a teenager! oh no!'
when 33
'the age of jesus'
when 90..200
'wow. that is old!'
else
'not very interesting'
end
end
case
сравнява с ===
. Няколко класа го имплементират:
Range
Regexp
Class
==
def qualify(thing)
case thing
when Integer then 'this is a number'
when String then 'it is a string'
when Array then thing.map { |item| qualify item }
else 'huh?'
end
end
case hours_of_sleep
when 8..10 then 'I feel fine.'
when 6...8 then 'I am a little sleepy.'
when 0..3 then 'OUT OF MY WAY! I HAVE PLACES TO BE AND PEOPLE TO SEE!'
end
def parse_date(date_string)
case date_string
when /(\d{4})-(\d\d)-(\d\d)/
Date.new $1.to_i, $2.to_i, $3.to_i
when /(\d\d)\/(\d\d)\/(\d{4})/
Date.new $3.to_i, $1.to_i, $2.to_i
end
end
Ruby позволява да обедините проверката за няколко възможни стойности в един when
, дори да не са от един и същи тип.
case number
when (42+0i) then 'This is too complex for me!'
when 42, 42.0 then 'This is more like it!'
end
when
case
, например:thing = 42
case
when thing == 1 then 1
else 'no_idea'
end
if
-овеСега е моментът.
Object#freeze
(Мутира обекта!)
Object#frozen?
dup
)
module Entities
ENTITIES = {
'&' => '&',
'"' => '"',
'<' => '<',
'>' => '>',
}.freeze
ENTITY_PATTERN = /#{ENTITIES.keys.join('|')}/.freeze
def escape(text)
text.gsub ENTITY_PATTERN, ENTITIES
end
end
Тъй като низовете в Ruby са mutable, всяко срещане на низ = нов обект:
''.object_id # => 23775293466780
''.object_id # => 23775293466240
''.object_id == ''.object_id # => false
class HTTP
attr_reader :method
def post?
method == 'POST' # New string object on each call
end
end
class HTTP
attr_reader :method
METHOD_POST = 'POST'
def post?
method == METHOD_POST
end
end
Това е досадно. Затова в Ruby 2.1 има и по-добро решение.
''.freeze.object_id # => 23589015467900
''.freeze.object_id # => 23589015467900
''.freeze.object_id == ''.freeze.object_id # => true
freeze
-нати низове и символи се размива в последните версии на Ruby
"Chunky bacon".freeze
се оптимизира от Ruby и връща един и същи обект
'text'.freeze.object_id # => 23534545930840
foo = 'text'
foo.freeze.object_id # => 23534545928760
foo.freeze.object_id == 'text'.freeze.object_id # => false
rb
файл
# frozen_string_literal: true
require 'fiddle'
class Object
def unfreeze
Fiddle::Pointer.new(object_id * 2)[1] &= ~(1 << 3)
end
end
script = 'Do you want to build a castle of ice?'.freeze
script.frozen? # => true
script.unfreeze
script[/castle of ice/] = 'snowman'
script.frozen? # => false
script # => "Do you want to build a snowman?"