Какво ще се случи при изпълнение на следния код (и защо):
class Proc
def ===(testable)
call testable
end
end
case 42
when String then 'This is a string'
when :odd?.to_proc then 'This is an odd number'
when :even?.to_proc then 'This is an even number'
else 'I have no f*cking idea what this is.'
end
case
използва "оператора" ===
, за да оценява различните случаи.
Всеки when
се оценява така: condition === testable
.
Какъв ще бъде резултатът от изпълнението на следния код:
class Sequence
def generate
yield
yield 'hello'
yield [1, 2]
end
end
Sequence.new.to_enum.count # => ?
Грешка – недефиниран метод each
.
Object#to_enum
е еквивалентно на Object#enum_for(:each)
.
Правилният код тук би бил Sequence.new.enum_for(:generate).count
. Щеше да върне 3
.
Какъв ще бъде резултатът от изпълнението на следния код:
numbers = {one: 'eins', two: 'zwei'}
numbers.each.with_index.to_a.flatten.size # => ?
6
.
След to_a
ще имаме списък с два елемента: [[[:one, "eins"], 0], [[:two, "zwei"], 1]]
.
Какъв ще бъде резултатът от изпълнението на следния код:
numbers = {one: 'eins', two: 'zwei'}.freeze
numbers[:one].upcase!
Няма да има грешка.
Хешът numbers
реално не се променя. Мутира се само обектът, към който сочи ключа :one
.
За да се предпазим от това, би могло да напишем numbers.each { |_, number| number.freeze }
.
Какъв ще бъде резултатът от изпълнението на следния код:
x = 0
0.upto(100).lazy.map { x += 1 }.map { x += 1 }.take(2)
x # => ?
0
. Причината е, че lazy последователността все още не е оценена.
Може да я оценим с to_a
след take()
. Тогава x
щеше да е 4.
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
Object +-- Exception +-- NoMemoryError +-- ScriptError | +-- SyntaxError | +-- LoadError +-- SecurityError +-- StandardError +-- ArgumentError +-- IndexError | +-- KeyError | +-- StopIteration +-- NameError | +-- NoMethodError +-- RuntimeError +-- TypeError
Припомняне 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
puts 'show me the money'
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 # => "nyckel"
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
. Защо?
Имате ли въпроси по механизма за изключения в Ruby?
В Ruby, код от други файлове се импортира с require
.
Например:
require 'bigdecimal'
require 'bigdecimal/util'
require 'foo'
търси файл foo.rb
в "пътя за зареждане"
require 'foo/bar'
търси директория foo
с файл bar.rb
.rb
отзад не е задължително да присъства
require './foo'
търси foo.rb
в текущата директория
require '/home/skanev/foo.rb'
Всеки процес си има "текуща директория". По подразбиране наследява това от процеса, който го стартира и може да се променя.
require './foo'
не винаги ще зареди това, което очаквате.require './foo'
require_relative
require_relative 'foo'
зарежда 'foo' спрямо директорията на изпълняващия се файл
require './foo'
зарежда спрямо текущата директория на изпълняващия процес$LOAD_PATH
$:
$:.unshift(path)
require
-а. Почти
main
обектът е същият
require
-и не правят нищо
require
може да зарежда .so
и .dll
файлове$LOAD_PATH
. ├── README.rdoc ├── Rakefile ├── bin │ └── skeptic ├── features ├── lib │ ├── skeptic │ │ ├── rules.rb │ │ └── scope.rb │ └── skeptic.rb ├── skeptic.gemspec └── spec
lib/
обикновено съдържа foo.rb
и lib/foo/
foo.rb
обикновено е единственото нещо в lib/
lib/foo
lib/
се добавя в load path
require 'foo'
или require 'foo/something'
require
областта
require
и $LOAD_PATH
и вижте какво се случва
load
е много сходен с require
, но има няколко разлики
load 'foo.rb'
load
-ове изпълняват файла
load
не може да зарежда .so
/.dll
библиотеки
load
има опционален параметър, с който може да обвие файла в анонимен модул