class TodoList
def TodoList.parse(todos_string)
# Solution lost.
end
end
Този начин е еквивалентен на предишния слайд:
class TodoList
def self.parse(todos_string)
# Solution lost.
end
end
Това работи, понеже:
self
self
и TodoList
в тялото на класа са едно и същоclass TodoList
class << self
def parse(todos_string)
# Solution lost.
end
end
end
def ClassName.method_name() end
)
Преди да продължим със следващата тема.
Object#freeze
Object#frozen?
module Entities
ENTITIES = {
'&' => '&',
'"' => '"',
'<' => '<',
'>' => '>',
}.freeze
ENTITY_PATTERN = /#{ENTITIES.keys.join('|')}/.freeze
def escape(text)
text.gsub ENTITY_PATTERN, ENTITIES
end
end
Тъй като низовете в Ruby са mutable, всяко срещане на низ = нов обект:
''.object_id # 548088440
''.object_id # 548088190
''.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'.freeze
def post?
method == METHOD_POST
end
end
Това е досадно. Затова в Ruby 2.1 има и по-добро решение.
"Chunky bacon".freeze
няма да работи, защото пак ще създава нов обект
"Chunky bacon"f
ще създаде замразен обект от тип низ
Преди да продължим със следващата тема.
Exception
и StandardError
.
StandardError < Exception
.Object +-- Exception +-- NoMemoryError +-- ScriptError | +-- SyntaxError | +-- LoadError +-- SecurityError +-- StandardError +-- ArgumentError +-- IndexError | +-- KeyError | +-- StopIteration +-- NameError | +-- NoMethodError +-- RuntimeError +-- TypeError
После ще видим пълната.
# Предизвиква RuntimeError
raise "'Prophet!' said I, 'Thing of evil!" # error: RuntimeError
# Като горното, но с различен текст
raise RuntimeError, 'prophet still, if bird or devil!' # error: RuntimeError
# Друг начин да предизвикаме RuntimeError
raise RuntimeError.new('Whether tempter sent, or whether...') # error: RuntimeError
begin
puts '...tempest tossed thee here ashore'
raise NameError, 'Desolate yet all undaunted'
rescue => ex
ex.message # "Desolate yet all undaunted"
ex.class # NameError
end
begin
raise KeyError, 'on this desert land enchanted'
rescue ArgumentError => ex
puts 'on this home by horror haunted'
rescue KeyError, TypeError => ex
ex.message # "on this desert land enchanted"
ex.class # KeyError
end
rescue
хваща "само" наследници на StandardError
, ако не сме указали друго:
Object +-- Exception +-- NoMemoryError +-- ScriptError +-- StandardError +-- ArgumentError +-- NameError | +-- NoMethodError +-- RuntimeError
begin
raise KeyError, 'tell me truly, I implore'
rescue IndexError => ex
puts 'IndexError'
rescue KeyError => ex
puts 'KeyError'
end
Припомняне KeyError < IndexError
$eh = 'foo'
begin
raise KeyError, 'Is there - is there balm in Gilead?'
rescue IndexError => ex
$eh = 'index'
rescue KeyError => ex
$eh = 'key'
end
$eh # "index"
Изпълнява се първия rescue
, за който изключението е kind_of?
типа.
Динамичните езици обикновено ползват прости правила
Кодът в ensure
клаузата се изпълнява винаги.
begin
raise 'tell me - tell me, I implore!' if rand(2).zero?
ensure
puts '????? ??? ?????, "?????????"'
end
rand(2).zero?
връща true
или false
, 50 на 50.else
клаузата се изпълнява когато няма възникнало изключение.
begin
launch_nukes
rescue
puts 'Uh-oh! Something went wrong :('
else
puts 'War... War never changes'
end
launch_nukes
няма удивителна.
begin
get_a_life
rescue NoFriendsError => ex
puts 'Goodbye cruel world'
rescue InsufficientVespeneGasError, NotEnoughMineralsError => ex
puts 'I think I play too much StarCraft'
rescue
puts ';('
else
puts 'Woohoo!'
ensure
puts 'rm -rf ~/.history'
end
В случай, че ползвате rescue
в метод така:
def execute
begin
potentially_dangerous
rescue SomeException => e
# Handle the error
ensure
# Ensure something always happens
end
end
По-добре е да го запишете без begin
/end
, което е еквивалентно на предното:
def execute
potentially_dangerous
rescue SomeException => e
# Handle the error
ensure
# Ensure something always happens
end
Ако възникне изключение при обработка друго, старото се игнорира и се "вдига" новото.
begin
raise KeyError
rescue
raise TypeError
puts "I'm a line of code, that's never executed ;("
end
raise
в rescue
клауза "вдига" същото
изключение, което се обработва.
begin
raise KeyError, 'But high she shoots through air and light'
rescue
puts 'Whoops'
raise
end
result = begin
raise KeyError if rand(3).zero?
raise NameError if rand(3).zero?
rescue KeyError
'nyckel'
rescue NameError
'namn'
else
'ingenting'
end
result # "ingenting"
rescue
може да се ползва като модификатор.StandardError
.[].fetch(1) rescue 'baba' # "baba"
raise type, message
всъщност извиква type.exception(message)
за да конструира изключение.
class Thing
def exception(message)
NameError.new(message)
end
end
thing = Thing.new
raise thing, 'whoops' # error: NameError
Може да разделим изключенията на два вида.
rescue
.За първите обикновено създаваме клас. За вторите обикновено ползваме raise
.
KeyError
или IndexError
. Защо?
raise
и rescue
.def iterate_pairs(hash)
hash.values.each { |array| iterate_values array }
end
def iterate_values(array)
array.each do |item|
if item == 'Nemo'
puts 'Found Nemo!'
throw :done
end
end
end
animals = {cats: %w[Simba], fish: %w[Crispy Nemo], boars: %w[Pumba]}
catch(:done) { iterate_pairs(animals) }
Този пример е доста синтетичен.
catch
приема символ и блок.
throw
със същия символ.
catch
.
catch
-а.
throw
взема допълнителен аргумент, който е върнатата стойност на catch
.
throw
, стойността на catch
е последния оценен израз.