Блог 7even

про ruby, rails, sinatra, git и всё на свете

Краткое введение в Ruby, ч. 1

Эта статья посвящается моим друзьям/знакомым PHP- (и не только) программистам, которым лень/некогда (зачеркнуть второе лишнее) познакомиться с Ruby и освоить азы этого языка.

Установка

Ruby предоставляет интерактивную консоль irb, в которой можно вводить код и сразу видеть результат его выполнения (аналогично js-консоли в firebug). irb устанавливается вместе с самим Ruby, поэтому будем ставить его.

Сразу обращу внимание - есть способ пропустить этот шаг, и перейти на tryruby, где аналогичная консоль (с некоторыми разумными ограничениями) доступна прямо в браузере. Для тех, кто не боится процесса установки (а, возможно, Ruby уже есть в вашей системе), этот раздел.

Windows

Честно говоря, ни разу не пробовал заниматься подобными извращениями, но говорят, что это работает (там в комплекте и Ruby, и рельсы, и еще докучи всякого небесполезного барахла).

Mac OS X

Начиная с 10.5 (а, возможно, и с 10.4, точно не знаю) Ruby предустановлен в системе, и достаточно просто открыть Терминал.app и набрать irb; но там предустановлена стремительно устаревающая версия 1.8, и стоит установить последнюю самому. Самый удобный способ - это использовать RVM:

1
2
3
4
5
6
7
8
9
10
11
# ставим сам RVM
$ bash < <(curl -s https://rvm.beginrescueend.com/install/rvm)
# добавляем загрузку RVM в шелл
$ echo '[[ -s "$HOME/.rvm/scripts/rvm" ]] && . "$HOME/.rvm/scripts/rvm" # Load RVM function' >> ~/.bash_profile
# и перезагружаем .bash_profile (вместо этого можно открыть новую сессию в консоли)
$ source .bash_profile
# теперь ставим последнюю версию Ruby
$ rvm install 1.9.2
# и ставим ее активной для текущей сессии
# и дефолтной для всех последующих сессий
$ rvm use 1.9.2 --default

Пользователям zsh нужно заменить .bash_profile на .zshrc - хотя они и так об этом знают.

При ручной установке Ruby необходимо ставить дополнительные библиотеки вроде readline, и подключать их при компиляции интерпретатора Ruby - RVM делает это автоматически.

Linux

Ruby можно поставить из репозиториев большинства дистрибутивов, но иногда приходится подолгу ждать обновления пакета, чтобы получить свежую версию Ruby; также всегда можно собрать из исходников, но не все любят этим заниматься (особенно пользователи дистрибутивов, в которых установка пакета сводится к команде наподобие sudo apt-get install ruby); поэтому, опять же, я предлагаю использовать RVM, описанный в предыдущем подразделе.

irb

irb (Interactive Ruby Shell) - это консоль Ruby. Запускается, как несложно догадаться, командой

1
$ irb

Ввод любой строки кода, которая возвращает какое-то значение, повлечет за собой вывод этого значения:

1
2
ruby-1.9.2-p180 :001 > 2 * 2
 => 4

(узнав об этом, вскоре я начал использовать irb как калькулятор :) так как всегда держу открытым хотя бы одно окно консоли)

Методы

В Ruby не существует жесткого разделения на числа, строки, массивы и объекты - любая переменная является ссылкой на объект. Объект, в свою очередь, всегда относится к определенному классу. А у объектов, как мы знаем из ООП, обычно есть методы.

В Ruby обращение к методу объекта осуществляется через ‘.’ (точку).

1
2
3
4
ruby-1.9.2-p180 :002 > "i am a string".length
 => 13
ruby-1.9.2-p180 :003 > -7.abs
 => 7

Типы

В Ruby нет такого понятия, как тип переменной; его успешно заменяет класс объекта. Например, объект “hello” будет принадлежать классу String, а объект 3.14 - классу Float. Рассмотрим самые важные классы, зачастую представляемые в других языках примитивами.

String

Строки в Ruby можно создавать несколькими способами. Самый популярный - это использование одинарных либо двойных кавычек, во втором случае происходит интерполяция переменных:

1
2
3
4
5
6
ruby-1.9.2-p180 :004 > single_quoted_string = 'hello Vasya!'
 => "hello Vasya!"
ruby-1.9.2-p180 :005 > vasya = 'Vasya'
 => "Vasya"
ruby-1.9.2-p180 :006 > double_quoted_string = "hello #{vasya}"
 => "hello Vasya"

Класс String предоставляет множество полезных методов для работы со строками. Например, для разбиения строки на части по определенному разделителю можно использовать метод split:

1
2
3
4
ruby-1.9.2-p180 :007 > str = 'hello;ruby;world'
 => "hello;ruby;world"
ruby-1.9.2-p180 :008 > str.split(';')
 => ["hello", "ruby", "world"]

Fixnum

Целые числа в определенном диапазоне (как правило это −230 … 230 - 1 или −262 … 262 - 1) относятся к классу Fixnum. Более крупные числа относятся к классу Bignum.

Для работы с числами доступна вся стандартная арифметика (+, -, *, /, %). Также целочисленные типы предоставляют ряд итераторов:

1
2
3
4
5
6
7
8
9
ruby-1.9.2-p180 :010 > 5.times do |n|
ruby-1.9.2-p180 :011 >   puts n
ruby-1.9.2-p180 :012?> end
0
1
2
3
4
 => 5

Массивы и хэши

В Ruby для коллекций объектов существуют объекты классов Array (массив) и Hash (хэш). Они отличаются друг от друга индексацией элементов: в массивах объекты индексируются последовательными целыми числами, а в хэшах ключом элемента может быть любой объект.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ruby-1.9.2-p180 :013 > array = ['string', 123, 3.14]
 => ["string", 123, 3.14]
ruby-1.9.2-p180 :014 > array[0]
 => "string"
ruby-1.9.2-p180 :015 > array << 'another string'
 => ["string", 123, 3.14, "another string"]
ruby-1.9.2-p180 :016 > hash = {'key1' => 'value1', 'key2' => 'value2'}
 => {"key1"=>"value1", "key2"=>"value2"}
ruby-1.9.2-p180 :017 > hash['key1']
 => "value1"
ruby-1.9.2-p180 :018 > hash['key3'] = 'value3'
 => "value3"
ruby-1.9.2-p180 :019 > hash
 => {"key1"=>"value1", "key2"=>"value2", "key3"=>"value3"}

Для обхода элементов массива или хэша используются итераторы, например each:

1
2
3
4
5
6
7
8
ruby-1.9.2-p180 :020 > ['i', 'am', 'a', 'string'].each do |str|
ruby-1.9.2-p180 :021 >   puts str.length
ruby-1.9.2-p180 :022?> end
1
2
1
6
 => ["i", "am", "a", "string"]

Классы

Как я уже писал чуть выше, каждый объект относится к определенному классу. Узнать этот класс можно с помощью метода class:

1
2
ruby-1.9.2-p180 :004 > "hello world".class
 => String

Попробуем написать свой элементарный класс:

1
2
3
ruby-1.9.2-p180 :005 > class Person
ruby-1.9.2-p180 :006?> end
 => nil

и создать объект этого класса:

1
2
ruby-1.9.2-p180 :007 > vasya = Person.new
 => #<Person:0x00000100e53c28>

Создание объекта происходит посредством вызова метода new того класса, к которому будет относиться создаваемый объект.

Основное предназначение класса - это определение поведения его объектов, то есть методов. Напишем какой-нибудь метод:

1
2
3
4
5
6
ruby-1.9.2-p180 :008 > class Person
ruby-1.9.2-p180 :009?>   def say_hello
ruby-1.9.2-p180 :010?>     "hello!"
ruby-1.9.2-p180 :011?>   end
ruby-1.9.2-p180 :012?> end
 => nil

и вызовем его, предварительно создав объект:

1
2
3
4
ruby-1.9.2-p180 :013 > petya = Person.new
 => #<Person:0x00000100de9580> 
ruby-1.9.2-p180 :014 > petya.say_hello
 => "hello!"

Наш метод просто возвращает строку “hello!”, а irb выводит результат каждой возвращающей что-либо строки кода; поэтому при вызове метода мы видим возвращаемую строку.

Тут необходимо сделать небольшое отступление - Ruby позволяет открывать уже определенные классы, и определять новые методы, так же как и переопределять уже существующие. Это дает возможность применять технику, известную как monkeypatching - изменение методов встроенных (либо добавляемых какой-нибудь сторонней библиотекой) классов, чтобы получить метод, ведущий себя немного не так, как задумывалось автором, но не переписывать для этого слишком многое.

Переменные

Переменные в Ruby различаются на несколько типов по своему названию:

  • $global - переменные, название которых начинается с $, являются глобальными и доступны в любой области видимости
  • @@class - переменные, название которых начинается с @@, являются классовыми и доступны в контексте класса, к которому они принадлежат, а также в контексте классов-наследников
  • @object - переменные, название которых начинается с @, являются переменными объекта (инстанса) и доступны в контексте этого объекта
  • local - прочие переменные являются локальными, и выпадают из области видимости при первой же смене контекста (например, если локальная переменная была объявлена в теле определения метода, то вне этого определения она будет уже недоступна)

Блоки

Блок - это кусок кода, который может иметь какие-то параметры, и возвращает определенное значение. Во многом блоки похожи на методы, но они не используются независимо - блок можно только передать методу.

При вызове метода ему можно передавать аргументы, а также блок. Для этого предусмотрено два варианта синтаксиса:

1
2
3
4
5
6
7
8
9
10
11
12
ruby-1.9.2-p180 :001 > 3.times { |i| puts i }
0
1
2
 => 3
ruby-1.9.2-p180 :002 > 3.times do |i|
ruby-1.9.2-p180 :003 >   puts i
ruby-1.9.2-p180 :004?> end
0
1
2
 => 3

Как видно из примера, результат применения обоих вариантов одинаковый; но синтаксис с использованием фигурных скобок принято применять в случаях, когда блок содержит всего одну строку кода, а синтаксис do ... end - для многострочных блоков.

Код, переданный в блоке, будет выполняться в зависимости от метода определенное количество раз (итераторы), при определенных условиях (условные конструкции), и не только - блоки в Ruby находят достаточно широкое применение.

Чтобы метод выполнил код блока, служит ключевое слово yield - при выполнении метода в этом месте будет выполнен код блока:

1
2
3
4
5
6
7
ruby-1.9.2-p180 :005 > def method
ruby-1.9.2-p180 :006?>   yield
ruby-1.9.2-p180 :007?> end
 => nil
ruby-1.9.2-p180 :008 > method { puts "hello from block" }
hello from block
 => nil

Если у блока предполагаются параметры, их нужно передавать параметрами в yield:

1
2
3
4
5
6
7
ruby-1.9.2-p180 :009 > def other_method
ruby-1.9.2-p180 :010?>   yield("world")
ruby-1.9.2-p180 :011?>   end
 => nil
ruby-1.9.2-p180 :012 > other_method { |word| puts "hello #{word}" }
hello world
 => nil

Запуск ruby-скриптов

Разумеется, программирование на Ruby не ограничивается irb - можно создавать скрипты (общепринятое расширение для исходников скриптов на Ruby - .rb) и запускать из командной строки:

1
2
3
$ echo 'puts "Hello from script"' > script.rb
$ ruby script.rb
Hello from script

Заключение

Разумеется, в одной статье невозможно охватить все грани языка, но надеюсь, что смог описать основы в относительно понятном виде. В следующей части будут описаны модули, наследование классов, примеси (mixin-ы), контроль доступа к методам (public/protected/private), гибкие возможности списков аргументов у методов и, возможно, что-нибудь еще.

Комментарии