1# Это комментарий
2
3=begin
4Это многострочный комментарий
5Никто их не использует
6И они не рекомендуются к использованию
7=end
8
9# Первое и самое главное: Всё является объектом.
10
11# Числа это объекты
12
133.class #=> Fixnum
14
153.to_s #=> "3"
16
17
18# Немного простой арифметики
191 + 1 #=> 2
208 - 1 #=> 7
2110 * 2 #=> 20
2235 / 5 #=> 7
232**5 #=> 32
245 % 3 #=> 2
25
26# Побитовые операторы
273 & 5 #=> 1
283 | 5 #=> 7
293 ^ 5 #=> 6
30
31# Арифметика -- это синтаксический сахар
32# над вызовом метода для объекта
331.+(3) #=> 4
3410.* 5 #=> 50
35
36# Логические величины -- это объекты
37nil # Здесь ничего нет
38true # истина
39false # ложь
40
41nil.class #=> NilClass
42true.class #=> TrueClass
43false.class #=> FalseClass
44
45# Операция равенства
461 == 1 #=> true
472 == 1 #=> false
48
49# Операция неравенства
501 != 1 #=> false
512 != 1 #=> true
52
53# nil -- имеет такое же логическое значение, как и false
54
55!nil #=> true
56!false #=> true
57!0 #=> false
58
59# Больше операций сравнения
601 < 10 #=> true
611 > 10 #=> false
622 <= 2 #=> true
632 >= 2 #=> true
64
65# Оператор сравнения <=>
661 <=> 10 #=> -1
6710 <=> 1 #=> 1
681 <=> 1 #=> 0
69
70# Булевы операторы
71true && false #=> false
72true || false #=> true
73!true #=> false
74
75# Существуют альтернативные версии логических операторов с гораздо меньшим
76# приоритетом. Они используются для связывания операций, пока одна из них
77# не вернёт false или true
78
79# `do_something_else` будет вызван если `do_something` вернёт истинное значение
80do_something() and do_something_else()
81# `log_error` будет вызван если `do_something` вернёт (nil/false)
82do_something() or log_error()
83
84
85# Строки -- это объекты
86
87'Я строка'.class #=> String
88"Я тоже строка".class #=> String
89
90placeholder = "использовать интерполяцию строк"
91"Я могу #{placeholder}, когда создаю строку с двойными кавычками"
92#=> "Я могу использовать интерполяцию строк,
93# когда создаю строку с двойными кавычками"
94
95# Конкатенация строк
96'hello ' + 'world' #=> "hello world"
97'hello ' + 3 #=> TypeError: can't convert Fixnum into String
98'hello ' + 3.to_s #=> "hello 3"
99
100# Умножение строк
101'hello ' * 3 #=> "hello hello hello "
102
103# Добавление к строке
104'hello' << ' world' #=> "hello world"
105
106# печатать в стандартный вывод
107puts "Я печатаюсь!"
108
109# Переменные
110x = 25 #=> 25
111x #=> 25
112
113# Присваивание значения возвращает то самое присвоенное значение.
114# Это позволяет делать множественные присваивания:
115
116x = y = 10 #=> 10
117x #=> 10
118y #=> 10
119
120# По соглашению, используйте snake_case для имён переменных
121snake_case = true
122
123# Используйте подробные имена для переменных
124# Но не переборщите!
125path_to_project_root = '/good/name/'
126path = '/bad/name/'
127
128# Идентификаторы (тоже объекты)
129
130# Идентификаторы -- это неизменяемые, многоразовые константы.
131# Для каждого идентификатора (кроме текста) сохраняется цифровой хэш.
132# При последующем использовании идентификатора, заместо создания нового объекта,
133# будет найден уже существующий по цифровому хэшу.
134# Они часто используются вместо строк для ускорения работы приложений
135
136:pending.class #=> Symbol
137
138status = :pending
139
140status == :pending #=> true
141
142status == 'pending' #=> false
143
144status == :approved #=> false
145
146# Массивы
147
148# Это массив
149array = [1, 2, 3, 4, 5] #=> [1, 2, 3, 4, 5]
150
151# Массив может содержать различные типы значений
152
153[1, "hello", false] #=> [1, "hello", false]
154
155# Значение в массиве можно получить по индексу с левой границы
156array[0] #=> 1
157array.first #=> 1
158array[12] #=> nil
159
160# Как и арифметика, доступ к значению в массиве
161# это синтаксический сахар над вызовом метода для объекта
162array.[] 0 #=> 1
163array.[] 12 #=> nil
164
165# Также, можно получить по индексу с правой границы
166array[-1] #=> 5
167array.last #=> 5
168
169# Задавая индекс и количество элементов
170array[0,2] #=> [1, 2]
171array[0,999] #=> [1, 2, 3, 4, 5]
172
173# Или с использованием диапазона значений
174array[1..3] #=> [2, 3, 4]
175
176# Перестановка элементов в обратном порядке
177a = [1, 2, 3]
178a.reverse #=> [3, 2, 1]
179
180# Вот так можно добавить значение в массив
181array << 6 #=> [1, 2, 3, 4, 5, 6]
182# Или так
183array.push(6) #=> [1, 2, 3, 4, 5, 6]
184
185# Проверка включения элемента в массив
186array.include?(1) #=> true
187
188# Хэши -- это массив пар "ключ => значение".
189# Хэши объявляются с использованием фигурных скобок:
190hash = {'color' => 'green', 'number' => 5}
191
192hash.keys #=> ['color', 'number']
193hash.values #=> ['green', 5]
194
195# Значение в хэше легко может быть найдено по ключу:
196hash['color'] #=> 'green'
197hash['number'] #=> 5
198
199# Поиск по ключу, которого в хэше нет вернёт nil:
200hash['nothing here'] #=> nil
201
202# начиная с Ruby 1.9, существует специальный синтаксис
203# при использовании идентификаторов как ключей хэша:
204
205new_hash = { defcon: 3, action: true}
206
207new_hash.keys #=> [:defcon, :action]
208
209# Проверка существования ключа и значения в хеше
210new_hash.key?(:defcon) #=> true
211new_hash.value?(3) #=> true
212
213# Массивы и Хэши -- перечисляемые типы данных
214# У них есть много полезных методов, например: each, map, count и другие
215
216# Управление ходом выполнения (Управляющие структуры)
217
218# Условия
219if true
220 'Если истина'
221elsif false
222 'Иначе, если ложь (опционально)'
223else
224 'Во всех других случаях (тоже опционально)'
225end
226
227# Если условие контролирует выполнение не блока кода, а единственного выражения,
228# можно использовать постфиксную запись условного оператора
229warnings = ['Отсутствует отчество', 'Слишком короткий адрес']
230puts("Обратите внимание:\n" + warnings.join("\n")) if !warnings.empty?
231
232# Иногда условие лучше звучит с `unless`, чем с `if`
233puts("Обратите внимание:\n" + warnings.join("\n")) unless warnings.empty?
234
235# Циклы
236for counter in 1..5
237 puts "итерация #{counter}"
238end
239#=> итерация 1
240#=> итерация 2
241#=> итерация 3
242#=> итерация 4
243#=> итерация 5
244
245# Однако, никто не использует "for" для циклов.
246# Вместо него Вы должны использовать метод "each" вместе с блоком кода.
247#
248# Блок кода -- это один из вариантов создания замыканий (лямбды,
249# анонимные функции).
250# Блок может только передаваться методу, сам по себе он существовать не может.
251# "for" не имеет своей области видимости и все переменные, объявленные в нём
252# будут доступны отовсюду. "each" вместе с блоком создаёт свою область видимости
253
254# Метод "each" для диапазона значений запускает блок кода один раз
255# для каждого из значений диапазона
256# Блок передаёт счётчик (counter) в качестве параметра.
257# Вызов метода "each" с блоком выглядит следующим образом:
258
259(1..5).each do |counter|
260 puts "итерация #{counter}"
261end
262#=> итерация 1
263#=> итерация 2
264#=> итерация 3
265#=> итерация 4
266#=> итерация 5
267
268# Вы также можете ограничивать блоки фигурными скобками:
269(1..5).each { |counter| puts "итерация #{counter}" }
270
271# Содержимое структурных данных также можно перебирать используя "each":
272array.each do |element|
273 puts "#{element} -- часть массива"
274end
275hash.each do |key, value|
276 puts "#{key} -- это #{value}"
277end
278
279# Если вам нужен индекс вы можете использовать "each_with_index"
280# В этом случае индекс будет начинаться с 0
281array.each_with_index do |element, index|
282 puts "#{element} is number #{index} in the array"
283end
284
285# Если индекс должен начинаться с произвольного значения,
286# используйте "each.with_index"
287[:q, :w, :e].each.with_index(100) do |element, index|
288 puts "#{element} -> #{index}"
289end
290#=> :q -> 100
291#=> :w -> 101
292#=> :e -> 102
293
294counter = 1
295while counter <= 5 do
296 puts "итерация #{counter}"
297 counter += 1
298end
299#=> итерация 1
300#=> итерация 2
301#=> итерация 3
302#=> итерация 4
303#=> итерация 5
304
305# Существует большое количество других полезных функций,
306# например "map", "reduce", "inject", и так далее. Например, "map"
307# выполняет связанный с ним блок для каждого элемента перечисляемого объекта,
308# возвращая массив результатов.
309array = [1, 2, 3, 4, 5]
310doubled = array.map do |element|
311 element * 2
312end
313puts doubled
314#=> [2, 4, 6, 8, 10]
315puts array
316#=> [1, 2, 3, 4, 5]
317
318grade = 'B'
319
320case grade
321when 'A'
322 puts 'Так держать, детка!'
323when 'B'
324 puts 'Тебе повезёт в следующий раз'
325when 'C'
326 puts 'Ты можешь сделать лучше'
327when 'D'
328 puts 'Выскоблил последнее'
329when 'F'
330 puts 'Ты провалился!'
331else
332 puts 'Альтернативная система оценок, да?'
333end
334#=> 'Тебе повезёт в следующий раз'
335
336# в when также можно использовать диапазоны
337grade = 82
338case grade
339when 90..100
340 puts 'Ура!'
341when 80...90
342 puts 'Хорошая работа!'
343else
344 puts 'Вы не справились!'
345end
346#=> 'Хорошая работа!'
347
348# Обработка исключений
349begin
350 # здесь код, который может вызвать исключение
351 raise NoMemoryError, 'У вас закончилась память.'
352rescue NoMemoryError => exception_variable
353 puts 'Был вызван NoMemoryError', exception_variable
354rescue RuntimeError => other_exception_variable
355 puts 'Был вызван RuntimeError'
356else
357 puts 'Этот код будет выполнятся, если исключения не были вызваны'
358ensure
359 puts 'Этот код выполняется всегда'
360end
361#=> Был вызван NoMemoryError
362#=> У вас закончилась память.
363#=> Этот код выполняется всегда
364
365# Функции
366
367def double(x)
368 x * 2
369end
370
371# Функции (и все блоки) неявно возвращают значение последней операции
372double(2) #=> 4
373
374# Скобки необязательны, если возвращаемый результат однозначен
375double 3 #=> 6
376
377double double 3 #=> 12
378
379def sum(x,y)
380 x + y
381end
382
383# Аргументы метода разделены запятой
384sum 3, 4 #=> 7
385
386sum sum(3,4), 5 #=> 12
387
388# yield
389# Все методы имеют неявный, опциональный параметр,
390# который может быть вызван с помощью инструкции "yield"
391
392def surround
393 puts "{"
394 yield
395 puts "}"
396end
397
398surround { puts 'hello world' }
399
400# {
401# hello world
402# }
403
404
405# Вы можете передать блок методу
406# "&" отмечает ссылку на переданный блок
407def guests(&block)
408 block.call 'some_argument'
409end
410
411# Чтобы метод принимал произвольное количество аргументов, спереди
412# одного из параметров ставится префикс "*"
413def method(first, *rest)
414 p rest
415end
416method(1, 2, 3, 4) #=> [2, 3, 4]
417
418# Если метод возвращает массив. можно использовать множественное присваивание
419def foods
420 ['pancake', 'sandwich', 'quesadilla']
421end
422breakfast, lunch, dinner = foods
423breakfast #=> 'pancake'
424dinner #=> 'quesadilla'
425
426# По соглашению, все методы, возвращающие булево значение
427# оканчиваются символом "?"
4285.even? #=> false
4295.odd? #=> true
430
431# Если метод заканчивается восклицательным знаком, значит он делает что-то
432# опасное или необратимое, например изменяет внутреннее состояние объекта.
433# Многие из таких методов-мутаторов часто имеют "безопасную" версию без "!"
434# которая возвращает новое значение
435company_name = "Dunder Mifflin"
436company_name.upcase #=> "DUNDER MIFFLIN"
437company_name #=> "Dunder Mifflin"
438company_name.upcase! # Изменяем зачение company_name!
439company_name #=> "DUNDER MIFFLIN"
440
441
442# Определение класса с помощью ключевого слова "class"
443class Human
444
445 # Переменная класса, она является общей для всех экземпляров класса
446 @@species = "H. sapiens"
447
448 # Базовый метод-конструктор
449 def initialize(name, age=0)
450 # Присвоить аргумент "name" переменной "name" экземпляра класса
451 @name = name
452 # Если аргумент "age" не задан,
453 # мы используем значение по умолчанию из списка аргументов
454 @age = age
455 end
456
457 # Базовый метод установки значения для переменной (setter)
458 def name=(name)
459 @name = name
460 end
461
462 # Базовый метод получения значения переменной (getter)
463 def name
464 @name
465 end
466
467 # Тоже самое можно определить с помощью attr_accessor
468 attr_accessor :name
469
470 # Также можно создать методы только для записи или чтения
471 attr_reader :name
472 attr_writer :name
473
474 # Метод класса определяется с ключевым словом "self",
475 # чтобы можно было отличить его от метода экземпляра класса.
476 # Он может быть вызван только на уровне класса, но не экземпляра.
477 def self.say(msg)
478 puts "#{msg}"
479 end
480
481 def species
482 @@species
483 end
484
485end
486
487
488# Создание экземпляра класса
489jim = Human.new("Jim Halpert")
490
491dwight = Human.new("Dwight K. Schrute")
492
493# Давайте вызовем несколько методов
494jim.species #=> "H. sapiens"
495jim.name #=> "Jim Halpert"
496jim.name = "Jim Halpert II" #=> "Jim Halpert II"
497jim.name #=> "Jim Halpert II"
498dwight.species #=> "H. sapiens"
499dwight.name #=> "Dwight K. Schrute"
500
501# Вызов метода класса
502Human.say("Hi") #=> "Hi"
503
504# Область видимости переменной определяется тем, как мы даём имя переменной.
505# Переменные, имя которых начинается с "$" имеют глобальную область видимости
506$var = "I'm a global var"
507defined? $var #=> "global-variable"
508
509# Переменная экземпляра класса, она видна только в экземпляре
510@var = "I'm an instance var"
511defined? @var #=> "instance-variable"
512
513# Переменная класса, видна для всех экземпляров этого класса и в самом классе
514@@var = "I'm a class var"
515defined? @@var #=> "class variable"
516
517# Имена переменных с большой буквы используются для создания констант
518Var = "I'm a constant"
519defined? Var #=> "constant"
520
521# Класс тоже объект в Ruby. Класс может иметь переменные экземпляра.
522# Переменная класса доступна в классе, его экземплярах и его потомках.
523
524# Пример класса
525class Human
526 @@foo = 0
527
528 def self.foo
529 @@foo
530 end
531
532 def self.foo=(value)
533 @@foo = value
534 end
535end
536
537# Производный класс (класс-потомок)
538class Worker < Human
539end
540
541Human.foo # 0
542Worker.foo # 0
543
544Human.foo = 2 # 2
545Worker.foo # 2
546
547# Переменная экземпляра класса недоступна в потомках этого класса.
548
549class Human
550 @bar = 0
551
552 def self.bar
553 @bar
554 end
555
556 def self.bar=(value)
557 @bar = value
558 end
559end
560
561class Doctor < Human
562end
563
564Human.bar # 0
565Doctor.bar # nil
566
567module ModuleExample
568 def foo
569 'foo'
570 end
571end
572
573# Включение модулей в класс добавляет их методы в экземпляр класса
574# Или в сам класс, зависит только от метода подключения
575class Person
576 include ModuleExample
577end
578
579class Book
580 extend ModuleExample
581end
582
583Person.foo # => NoMethodError: undefined method `foo' for Person:Class
584Person.new.foo # => 'foo'
585Book.foo # => 'foo'
586Book.new.foo # => NoMethodError: undefined method `foo'
587
588# Коллбэки при подключении модуля
589
590module ConcernExample
591 def self.included(base)
592 base.extend(ClassMethods)
593 base.send(:include, InstanceMethods)
594 end
595
596 module ClassMethods
597 def bar
598 'bar'
599 end
600 end
601
602 module InstanceMethods
603 def qux
604 'qux'
605 end
606 end
607end
608
609class Something
610 include ConcernExample
611end
612
613Something.bar # => 'bar'
614Something.qux # => NoMethodError: undefined method `qux'
615Something.new.bar # => NoMethodError: undefined method `bar'
616Something.new.qux # => 'qux'