nashbridges.me - Блоки в Ruby









Search Preview

Блоки в Ruby

nashbridges.me
Что общего и чем отличаются блоки и методы в Ruby, почему блок является замыканием, как используются блоки.
.me > nashbridges.me

SEO audit: Content analysis

Language Error! No language localisation is found.
Title Блоки в Ruby
Text / HTML ratio 55 %
Frame Excellent! The website does not use iFrame solutions.
Flash Excellent! The website does not have any flash contents.
Keywords cloud в и > puts end на => что не с nil блок блока это Ruby метода метод = только
Keywords consistency
Keyword Content Title Description Headings
в 77
и 63
> 57
puts 50
end 40
на 38
Headings
H1 H2 H3 H4 H5 H6
2 8 11 0 0 0
Images We found 0 images on this web page.

SEO Keywords (Single)

Keyword Occurrence Density
в 77 3.85 %
и 63 3.15 %
> 57 2.85 %
puts 50 2.50 %
end 40 2.00 %
на 38 1.90 %
=> 38 1.90 %
что 37 1.85 %
не 35 1.75 %
с 32 1.60 %
nil 26 1.30 %
блок 26 1.30 %
26 1.30 %
блока 25 1.25 %
это 21 1.05 %
Ruby 20 1.00 %
метода 19 0.95 %
метод 18 0.90 %
= 18 0.90 %
только 16 0.80 %

SEO Keywords (Two Word)

Keyword Occurrence Density
=> nil 24 1.20 %
Поклон тебе 15 0.75 %
тебе Цезарь 15 0.75 %
> puts 14 0.70 %
приветствуют тебя 14 0.70 %
смерть приветствуют 14 0.70 %
на смерть 14 0.70 %
Идущие на 14 0.70 %
> spartaksay_hello 10 0.50 %
001 > 9 0.45 %
003 > 8 0.40 %
006 > 7 0.35 %
say_hello puts 7 0.35 %
def say_hello 7 0.35 %
puts Поклон 7 0.35 %
тебя end 7 0.35 %
puts Идущие 7 0.35 %
тебя => 7 0.35 %
004 > 7 0.35 %
do end 6 0.30 %

SEO Keywords (Three Word)

Keyword Occurrence Density Possible Spam
Поклон тебе Цезарь 15 0.75 % No
Идущие на смерть 14 0.70 % No
смерть приветствуют тебя 14 0.70 % No
на смерть приветствуют 14 0.70 % No
тебя => nil 7 0.35 % No
приветствуют тебя => 7 0.35 % No
приветствуют тебя end 7 0.35 % No
003 > spartaksay_hello 7 0.35 % No
puts Идущие на 7 0.35 % No
puts Поклон тебе 7 0.35 % No
say_hello puts Поклон 7 0.35 % No
def say_hello puts 7 0.35 % No
end => nil 5 0.25 % No
variable or method 5 0.25 % No
local variable or 5 0.25 % No
006 > puts 5 0.25 % No
> spartaksay_hello do 5 0.25 % No
undefined local variable 5 0.25 % No
NameError undefined local 5 0.25 % No
будем кормить крокодила 5 0.25 % No

SEO Keywords (Four Word)

Keyword Occurrence Density Possible Spam
на смерть приветствуют тебя 14 0.70 % No
Идущие на смерть приветствуют 14 0.70 % No
смерть приветствуют тебя end 7 0.35 % No
puts Идущие на смерть 7 0.35 % No
puts Поклон тебе Цезарь 7 0.35 % No
say_hello puts Поклон тебе 7 0.35 % No
def say_hello puts Поклон 7 0.35 % No
смерть приветствуют тебя => 7 0.35 % No
приветствуют тебя => nil 7 0.35 % No
undefined local variable or 5 0.25 % No
Сейчас будем кормить крокодила 5 0.25 % No
NameError undefined local variable 5 0.25 % No
local variable or method 5 0.25 % No
Цезарь Идущие на смерть 4 0.20 % No
будем кормить крокодила Тотошку 4 0.20 % No
end Поклон тебе Цезарь 4 0.20 % No
=> nil 006 > 4 0.20 % No
003 > spartaksay_hello do 4 0.20 % No
Поклон тебе Цезарь Идущие 4 0.20 % No
тебе Цезарь Идущие на 4 0.20 % No

Internal links in - nashbridges.me

все теги
Теги, которыми отмечены статьи
зачем и для кого
О блоге
rss
Ruby и точка
Установка Ruby on Rails на Windows
Установка Ruby on Rails на Windows
Git
Статьи с тегом «Git»
Linux
Статьи с тегом «Linux»
SSH
Статьи с тегом «SSH»
Введение в объектно-ориентированный Ruby
Введение в объектно-ориентированный Ruby
класс
Статьи с тегом «класс»
модуль
Статьи с тегом «модуль»
объект
Статьи с тегом «объект»
философия
Статьи с тегом «философия»
Ruby
Статьи с тегом «Ruby»
Ресурсы и книги по Ruby и Ruby on Rails
Ресурсы и книги по Ruby и Ruby on Rails
литература
Статьи с тегом «литература»
Проки и лямбды
Проки и лямбды
блок
Статьи с тегом «блок»
замыкание
Статьи с тегом «замыкание»
DSL
Статьи с тегом «DSL»
лямбда
Статьи с тегом «лямбда»
Ruby on Rails
Статьи с тегом «Ruby on Rails»
Sinatra
Статьи с тегом «Sinatra»
yield
Статьи с тегом «yield»
Gem глазами потребителя
Gem глазами потребителя
bundler
Статьи с тегом «bundler»
джемы
Статьи с тегом «джемы»
Github
Статьи с тегом «Github»
require
Статьи с тегом «require»
Rubygems
Статьи с тегом «Rubygems»
RVM
Статьи с тегом «RVM»
Блоки в Ruby
Блоки в Ruby
итератор
Статьи с тегом «итератор»
цикл
Статьи с тегом «цикл»

Nashbridges.me Spined HTML


Блоки в Ruby Ruby и . все статьи все теги зачем и для кого rss Блоки в Ruby сложность материала: для начинающих версия Ruby: 1.9 необходимые знания: класс, его атрибуты, создание объекта, интерполяция строк, оператор if, методы puts и require, консоль irb теги: Ruby блок замыкание yield итератор цикл DSL Когда возникает необходимость в блоках? Что такое блок? Передача контроля блоку Обмен информацией с блоком Почему блок нельзя считать анонимным методом? Внешние переменные Столкновение переменных Как это использовать? Как метод и блок считают аргументы Нечто, похожее на блоки Поэтичный стиль и блоки Разница между { } и do end Блок и хеш Использование блоков в повседневном коде Итераторы Обёртывание кода DSL Конструктор объекта Послемыслия и работа над ошибками добавлен раздел о poetry mode (01.08.11) Блок является одной из ключевых конструкций языка Ruby, которую необходимо хорошо понимать и любить, поскольку использование блоков, наряду с грамотным именованием всех идентификаторов (переменных и т. д.) и вызовами методов по цепочке, делает ваш код читаемым настолько, насколько может быть читаема хорошая инструкция на английском. Когда возникает необходимость в блоках? Представим, вы создали простой класс, описывающий гладиатора: # gladiator.rb matriculation Gladiator attr :name def initialize(name) @name = name end def say_hello puts "Поклон тебе, Цезарь!" puts "Идущие на смерть приветствуют тебя!" end # остальное опустим для краткости # ... end Пример использования: :001 > require "./gladiator" :002 > spartak = Gladiator.new("Спартак") :003 > spartak.say_hello Поклон тебе, Цезарь! Идущие на смерть приветствуют тебя! => nil Точка в require говорит Ruby о том, что файл надо искать в текущем каталоге. Допустим также, что он настолько понравился коллегам-программистам, что те решили использовать его в своих проектах. Спустя некоторое время начали приходить письма с просьбами и предложениями. Возьмем, например, это, из некой страховой компании, где возникла необходимость синхронизовать свою логику с вашей вот так: def say_hello puts "Поклон тебе, Цезарь!" # процедура составления завещания # ... puts "Идущие на смерть приветствуют тебя!" end Сейчас они переписали ваш класс, добавив в say_hello вызов метода, отвечающий за завещания. Но… это же чертовски неправильно! say_hello должен, натурально, говорить «хэллоу» и больше ничего. И вообще, это не забота гладиатора, составлять завещания, его дело воевать. Пока вы думаете над проблемой, приходит еще одно письмо, на этот раз от программистов из самого Pixar! По странному стечению обстоятельств им понадобилось такое: def say_hello puts "Поклон тебе, Цезарь!" # запустить анимацию наклона персонажа вперед # ... puts "Идущие на смерть приветствуют тебя!" end Вот было бы хорошо позволить выполнение чужого кода внутри своего метода! И при этом не задумываться, за что он будет отвечать и насколько сложен. Как раз для этого в Ruby используют блоки. Что такое блок? Блок — это произвольный код, который можно передать любому методу в качестве неявного последнего аргумента. Следует понимать, что при этом блок является особой конструкцией языка и обособлен от списка явных аргументов метода, что означает следующее: методу можно передать только один блок; он всегда идет в самом конце и вынесен за скобки с аргументами (если они есть); его можно задать при вызове любого метода, независимо от того, указаны в определении метода аргументы или нет. Код может находиться внутри фигурных скобок { } или ключевых слов do end. Что именно использовать — решать вам, потому что обе конструкции описывают совершенно одинаковые по свойствам блоки. Обычно в фигурные скобки заключают код, состоящий из одной строки, при этом метод и его блок записывают на одной строке. Если код блока слишком длинный или объемный, вместо скобок на строке с вызовом метода оставляют do, а сам код пишут с новой строки с отступом и в конце «закрывают» его end'ом. Посмотрим, как это выглядит на практике: :004 > spartak.say_hello { puts "Отпустите меня домой" } Поклон тебе, Цезарь! Идущие на смерть приветствуют тебя! => nil Вас не раздражает этот nil в конце? Его возвращает метод puts. Как видим, Ruby совершенно не возражает, что мы добавили к методу блок, хотя сам метод ничего о нем не знает! При этом метод выполнился без изменений. Что, если записать выражение иначе? :005 > spartak.say_hello do :006 > puts "Отпустите меня домой" :007?> end Поклон тебе, Цезарь! Идущие на смерть приветствуют тебя! => nil Абсолютно никакой разницы. Но какой толк от блока, который не выполняется? Передача контроля блоку Существует два способа выполнить код блока, переданный методу. В этой статье рассматривается только один из них — ключевое слово yield. yield — это часть синтаксиса языка, как и объявление блока (поэтому ее нельзя переопределить). Когда в теле метода мы вызываем yield, то подразумеваем следующее: «отдай управление блоку, а когда тот закончит, верни управление этому методу». Попробуйте переписать приветствующий метод так: matriculation Gladiator def say_hello puts "Поклон тебе, Цезарь!" yield puts "Идущие на смерть приветствуют тебя!" end end и еще одна попытка передать блок: :003 > spartak.say_hello { puts "То, что мы делаем в жизни, отзывается в вечности." } Поклон тебе, Цезарь! То, что мы делаем в жизни, отзывается в вечности. Идущие на смерть приветствуют тебя! => nil Вуаля! Теперь все надоедливые товарищи могут пихать в этот блок, что захочется, и не мешать вам жить. Ура? Не совсем: попробуйте вызвать метод без блока. :004 > spartak.say_hello LocalJumpError: no woodcut given (yield) Ошибка! Ruby теперь в обязательном порядке требует от нас блок! В некоторых случаях без блока действительно никак, но чаще всего логика метода вполне позволяет обойтись без него, как в этом случае. Поэтому перед вызовом беспощадного yield следует удостовериться, а есть ли блок, с помощью метода block_given?: def say_hello puts "Поклон тебе, Цезарь!" yield if block_given? puts "Идущие на смерть приветствуют тебя!" end :003 > spartak.say_hello Поклон тебе, Цезарь! Идущие на смерть приветствуют тебя! => nil Обмен информацией с блоком Только вы расслабились, как ребята из страховой компании стучатся с новой проблемой. Им нужно вписать в завещание имя гладиатора, но решение «в лоб» не работает: :003 > spartak.say_hello do :004 > puts "ЗАВЕЩАНИЕ" :005?> puts "Я, #{name}, настоящим завещаю" :006?> end Поклон тебе, Цезарь! ЗАВЕЩАНИЕ NameError: undefined local variable or method 'name' for main:Object В реальном коде они используют вывод в файл, а не консоль. Почему так произошло, несмотря на то, что метод name у класса Gladiator определен? Дело в том, что хотя блок и вызывается непосредственно в методе, ему недоступны ни локальные переменные этого метода (say_hello), ни атрибуты объекта (spartak), метод которого вызывается. Эту проблему можно решить несколькими способами. Первый — явно передать блоку ту информацию, в которой он может нуждаться. Например, предоставить ему имя гладиатора: def say_hello puts "Поклон тебе, Цезарь!" yield(name) if block_given? puts "Идущие на смерть приветствуют тебя!" end Зная об этом, страховщики теперь могут писать: :003 > spartak.say_hello do |gladiator_name| :004 > puts "ЗАВЕЩАНИЕ" :005?> puts "Я, #{gladiator_name}, настоящим завещаю" :006?> end Поклон тебе, Цезарь! ЗАВЕЩАНИЕ Я, Спартак, настоящим завещаю Идущие на смерть приветствуют тебя! => nil gladiator_name — аргумент блока. Чтобы хорошо понять, что тут только что произошло, советую провести для себя некоторые параллели между блоком и методом. Забегая наперед, скажу, что блок не является методом, но это не мешает увидеть, что между ними есть много общего. У метода есть имя. Назвали имя — вызвали код, описанный в определении метода. Назвали yield — вызвали код, описанный в блоке. Метод может принимать аргументы, если они были указаны в его определении. Как видно из последнего примера, yield тоже можно вызывать с аргументами. И было бы логичным ожидать, что в самом блоке нужно как-то указать перечень принимаемых аргументов. Ожидания оправдываются во всём, кроме синтаксиса: вместо круглых скобок, как в методе, аргументы блока следует брать в прямые | |. После того, как аргумент объявлен, можно обращаться к нему в блоке, как к локальной переменной. Список аргументов записывается сразу после do (или {), перенос строки после него не обязателен. Продолжим аналогию. Метод всегда возвращает значение: это или значение последнего выражения, или значение выражения после ключевого слова return. Блок тоже возвращает значение, и его можно получить в методе как результат вызова yield. Пусть наш салютующий метод теперь выглядит так: def say_hello puts "Поклон тебе, Цезарь!" if block_given? # монетка бросается в блоке coin_flip = yield(name) if coin_flip == 1 # выпал аверс puts "Похоже, мне повезет в этой битве." else puts "Смерть улыбается всем нам." end end puts "Идущие на смерть приветствуют тебя!" end а его вызов с блоком — так: :003 > spartak.say_hello do |gladiator_name| :004 > puts "-> #{gladiator_name} бросает монетку" :005?> rand(2) :006?> end Поклон тебе, Цезарь! -> Спартак бросает монетку Похоже, мне повезет в этой битве. Идущие на смерть приветствуют тебя! => nil Метод rand случайно генерирует 0 или 1. На этот раз Спартаку повезло. Есть у блока с методом еще одно общее свойство: оба они создают новую локальную область видимости переменных. Другими словами, переменная, объявленная внутри них, недоступна в других участках выполняемого кода. :001 > def nonsense :002?> inside_method = "Я существую только внутри метода." :003?> yield if block_given? :004?> end => nil :005 > nonsense => nil :006 > puts inside_method NameError: undefined local variable or method 'inside_method' for main:Object :007 > nonsense { inside_block = "А я — только внутри блока." } => "А я — только внутри блока." :008 > puts inside_block NameError: undefined local variable or method 'inside_block' for main:Object Тут и далее код, не связанный с классом Gladiator. Я вызвал nonsense, чтобы гарантировать создание переменной inside_method и показать, что она существует только в момент выполнения метода. Обратите внимание на то, как значение присвоения переменной inside_block было последовательно возвращено блоком в метод, а затем — самим методом. Впрочем, это всё равно не сделало саму переменную видимой вне блока. Почему блок нельзя считать анонимным методом? Внешние переменные Основное и принципиальное отличие блока от метода в том, что первый имеет доступ к переменным, объявленным до него во внешнем окружении. Теперь вернитесь на пару абзацев назад и подумайте, не отрицает ли это сказанное выше про локальную область видимости, которую создает блок? Оказывается, нет. Даже если внутри блока происходит присвоение переменной, ранее объявленной не в нем, Ruby не создает локальную переменную, а позволяет присвоить внешней переменной новое значение. :001 > cat = "Голодный кот" => "Голодный кот" :002 > def locate_cat :003?> puts "Кажется, #{cat} на крыше." :004?> end => nil :005 > locate_cat NameError: undefined local variable or method 'cat' for main:Object :006 > def whatever :007?> yield :008?> end => nil :009 > whatever do :010 > puts "#{cat} добрался до колбасы!" :011?> cat = "Уже не голодный кот" :012?> end Голодный кот добрался до колбасы! => "Уже не голодный кот" :013 > puts cat Уже не голодный кот => nil Метод whatever имеет исключительно академическое значение, поскольку занимается только тем, что выполняет любой переданный ему блок. Смотрите, как переменная cat «просачивается» внутрь блока и даже меняет свое значение. При этом нужно понимать, что блок не может магическим образом видеть все переменные вне себя. Его способностей хватает только на те, которые находятся с ним на одном уровне, т. е. не скрыты от него внутри других методов или блоков, и объявлены на момент выполнения блока. :014 > whatever { dog = "Ленивый пёс" } => "Ленивый пёс" :015 > whatever { puts dog } NameError: undefined local variable or method 'dog' for main:Object Теперь, если вернуться к раннему примеру с завещанием для гладиатора, можно догадаться, как решить проблему с его именем, не используя аргументы блока: :003 > spartak.say_hello do :004 > puts "ЗАВЕЩАНИЕ" :005?> puts "Я, #{spartak.name}, настоящим завещаю" :006?> end Поскольку переменная spartak объявлена до выполнения блока, совершенно не зазорно использовать ее внутри. Столкновение переменных Что произойдет, если имя аргумента блока совпадает с внешней переменной? В этом случае Ruby создаст новую локальную переменную, и внешняя переменная внутри блока будет недоступна. :001 > def with_one :002?> yield(1) :003?> end :004 > number = 99 :005 > with_one { |number| puts "number равно #{number}" } number равно 1 => nil :006 > puts number 99 => nil Наконец, а что делать, если вы хотите, чтобы блок не захватывал внутри и менял внешнюю переменную? Для этого нужно в принудительном порядке создать одноименную локальную переменную блока, делается это ее указанием после точки с запятой в списке аргументов: :006 > puts number 99 => nil :007 > with_one do |i; number| :008 > puts "i равно #{i}" :009?> number = 123 :010?> end i равно 1 => 123 :011 > puts number 99 => nil with_one по-прежнему передает в блок единицу. Как это использовать? Поначалу может показаться, что всё это жадное «захватывание» переменных блоком (поэтому блоки еще называют замыканиями) не имеет никакого смысла, если не хуже — представляет опасность: можно очень легко изменить переменную, полагая, что работаешь с ней только внутри блока. На самом деле, практическое применение замыканий становится понятным, только когда их начинают перемещать между областями видимости, что выходит за рамки данной статьи. Поэтому пока что стоит принять на веру, что замыкание — полезная штука, если работать с ней аккуратно. Как метод и блок считают аргументы В этом еще одно отличие: метод слишком ворчливый и дотошный, он требует строгого соответствия заявленного и предоставленного количества аргументов. Блок же закрывает глаза на недостачу и даже на избыток аргументов! :001 > def feed_crocodile(name) :002?> puts "Сейчас будем кормить крокодила #{name}" :003?> yield(name) if block_given? :004?> end => nil :005 > feed_crocodile("Тотошку") Сейчас будем кормить крокодила Тотошку => nil :006 > feed_crocodile ArgumentError: wrong number of arguments (0 for 1) :007 > feed_crocodile("Тотошку", "бананами") ArgumentError: wrong number of arguments (2 for 1) :008 > feed_crocodile("Тотошку") { |name| puts "#{name} кормят мясом!" } Сейчас будем кормить крокодила Тотошку Тотошку кормят мясом! => nil :009 > feed_crocodile("Тотошку") { puts "Два дня не ел, бедняга" } Сейчас будем кормить крокодила Тотошку Два дня не ел, бедняга => nil :010 > feed_crocodile("Тотошку") { |name, food| puts "#{name} кормят #{food}!" } Сейчас будем кормить крокодила Тотошку Тотошку кормят ! => nil Всё внимание на второй и третий вызов с блоком: ошибок нет! Во втором мы вообще забыли указать список аргументов. Переданное в блок name считаем выброшенным в пропасть. В третьем мы объявили лишний аргумент. В этом случае Ruby создает локальную переменную supplies и присваивает ей значение nil, которое при интерполяции превращается в пустую строку. Поэтому последний вывод выглядит так: «кормят пробел восклицательный знак». Нечто, похожее на блоки Существует ряд операторов в Ruby, в которых используются ключевые слова do end — это циклы while, until, for. Хочется предостеречь от возможного заблуждения: в них не используются блоки, do end являются неотъемлемой составляющей конструкции этих операторов. Во-первых, хотя и пишем :001 > for i in [1, 2, 3] do :002 > print i :003?> end 123 => [1, 2, 3] но не получится написать for i in [1, 2, 3] { print i } Во-вторых, операторы циклов не создают локальную область видимости: :001 > x = 0 => 0 :002 > while x < 1 do :003 > inside = "Я внутри?" :004?> x += 1 :005?> end => nil :006 > puts inside Я внутри? => nil В то же время loop, реализующий бесконечный цикл, является системным методом, который как раз принимает блоки: loop { print "кольцокольцо" } Поэтичный стиль и блоки Поэтичным стилем (poetry mode) в Ruby называют стиль написания кода, при котором опускают скобки в тех местах, где анализатор может предположить их наличие, исходя из контекста. В основном это касается аргументов при вызове метода: puts("Hello") # классический стиль puts "Hello" # poetry mode Разница между { } и do end Фигурные скобки блоков имеют более высокий приоритет при разборе выражения, чем ключевые слова do end. На практике это имеет значение только и именно в тех случаях, когда используется поэтичный стиль. Допустим, есть метод kick_ass, который принимает в качестве единственного аргумента имя того, кому мы хотим надрать задницу. Тогда запись kick_ass "Джокер" do puts "Ты будешь сидеть в камере психушки вечно." end будет прочитана Ruby именно так, как вы задумали: kick_ass("Джокер") do puts "Ты будешь сидеть в камере психушки вечно." end А вот вызов метода с блоком в фигурных скобках kick_ass "Бэтмен" { puts "Если умеешь что-то, не делай этого бесплатно." } Ruby поймет — внимание! — как: kick_ass("Бэтмен" { puts "Если умеешь что-то, не делай этого бесплатно." }) Фактически вы пытаетесь передать блок строке, и это, конечно же, приведет к ошибке. Вывод: ставьте скобки в сложных выражениях. Блок и хеш Так получилось, что у блока и хеша одинаковая нотация: фигурные скобки. Поэтому, если попытаться передать последний в качестве аргумента, не взяв в круглые скобки, Ruby примет его за блок, и выдаст ошибку. Если сохранять стиль, нужно опустить и фигурные скобки: kick_ass { :joker => "2 раза", :penguin => "1 раз" } # синтаксическая ошибка kick_ass :joker => "2 раза", :penguin => "1 раз" # так всё нормально Наконец, если вам нужно передать методу и хеш, и блок, или используйте исключительно do end, или забудьте о поэтичном стиле и берите аргументы в скобки, как положено. Использование блоков в повседневном коде Итераторы Трудно себе представить рабочий код на Ruby без применения итераторов. Вся работа с массивами, хешами и другими объектами, способными себя перечислять, построена на итераторах, что намного удобнее использования циклов. Итератор — это метод, который некоторое количество раз вызывает блок и может передавать в него данные по определенному алгоритму. Итератор без блока — как туловище без головы: он знает, с чем ему нужно работать, но абсолютно без понятия, что ему нужно с этим делать. Самый известный итератор, each, последовательно передает в блок все элементы объекта, от имени которого вызван метод. :001 > [1, 2, 3].each { |i| puts i + 2 } 3 4 5 => nil Обёртывание кода Случается, что какой-то алгоритм состоит из множества типовых операций, между которыми необходимо выполнять уникальную. Если вы часто едите бутерброды, то знаете, что хлеб придется нарезать каждый раз, отличие только в начинке. Чтобы скрыть рутинные операции, связанные с покупкой хлеба, поиском ножа и нарезанием, вы оставляете их в методе, а подготовку колбасы — единственную уникальную операцию — предоставляете блоку, и только он будет у вас постоянно на виду. Это здорово повышает читаемость кода. Классическим примером является работа с файлами с помощью системного класса File. Метод unshut этого класса берет на себя заботу о создании дескриптора открытого файла и — главное — корректном закрытии файла в конце, пользователю же остается только самое важное — считывание и обработка данных, находящихся в файле. :001 > first_line = File.open("some.txt") do |file| :002 > file.readline :003?> end Бывает и обратная ситуация: когда вы хотите выполнить что-то перед или после постороннего метода. Для этого создается служебный метод, в котором будет выполняться это «что-то» и происходить вызов блока. Теперь достаточно передать посторонний метод в блоке вашему служебному методу. Классическим примером является измерение времени выполнения метода: def measure_time # запустить таймер # ... yield # остановить таймер и вывести потраченное время # ... end measure_time { make_coffee } DSL Если вы едите бутерброды каждый день, вполне возможно, вам захочется написать для этого целую библиотеку. В ней будет много-много методов, вроде «намазать», «нарезать» и «открыть», каждый из которых будет представлять собой низкоуровневый код на обычном Ruby. С этим кодом вполне можно работать и напрямую, но чтобы разобраться в алгоритме работы в контексте поставленной задачи, нужно напрячься. Другое дело — такая вот инструкция, всё строго по делу: slices = cut specie { by 3, :slices } unshut caviar_can, :with => :can_opener do |contents| slices.each { |slice| slice.spread :with => contents.about('20%') } end Ваш код остался синтаксически правильной последовательностью методов и блоков Ruby, но с точки зрения решения поставленной задачи — это уже надстройка над языком или DSL, а методы — операторы нового языка. Программы, написанные с помощью хорошо продуманного DSL, легко читать, они говорят сами за себя (отпадает необходимость в комментировании кода). Но надо понимать, что для каждой задачи потребуется свой DSL, на изучение/создание которого необходимо время. Классическим примером DSL является фреймворк для тестирования RSpec. Конструктор объекта Иногда конструктор объекта должен принять столько различных параметров, что передача их через список аргументов уже неудобна: легко запутаться, что чему мы присваиваем. Одним из вариантов решения этой проблемы является использование хеша {название параметра => значение параметра, …}. А можно использовать технику барона Мюнхгаузена: передать в блок конструктора самого себя, т. е. только что созданный объект, которому уже присваивать нужные параметры. matriculation Gladiator attr_accessor :name, :rank, :height, :weight def initialize yield(self) end #... end spartak = Gladiator.new do |his| his.name = "Спартак" his.weight = 91 end Порекомендуйте этот материал, если он вам понравился: все статьи все теги зачем и для кого rss Андрей Малышко, 26.07.2011