diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..669c91c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,18 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[Makefile] +indent_style = tab + +[*.md] +indent_size = 4 diff --git a/.gitignore b/.gitignore index 397b4a7..b0b03d4 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.log +tmp diff --git a/Makefile b/Makefile index c8bd2c7..6e155fa 100644 --- a/Makefile +++ b/Makefile @@ -1,38 +1,39 @@ -include /opt/basics/common/common.mk -# export RUBYLIB=$(realpath lib) -# export PATH:=$(realpath bin):$(PATH) - compose-setup: compose-build compose-install compose: - docker-compose up + docker-compose up --abort-on-container-exit compose-install: - docker-compose run exercises bundle install + docker-compose run --rm exercises bundle install compose-bash: - docker-compose run exercises bash + docker-compose run --rm exercises bash + +compose-down: + docker-compose down -v --remove-orphans compose-build: docker-compose build compose-test: - docker-compose run exercises make test + docker-compose run --rm exercises make test compose-code-lint: - docker-compose run exercises make code-lint + docker-compose run --rm exercises make code-lint compose-lint: - docker-compose run exercises make code-lint + docker-compose run --rm exercises make code-lint compose-description-lint: - docker-compose run exercises make description-lint + docker-compose run --rm exercises make description-lint compose-schema-validate: - docker-compose run exercises make schema-validate + docker-compose run --rm exercises make schema-validate ci-check: + docker-compose --file docker-compose.yml down -v --remove-orphans docker-compose --file docker-compose.yml build docker-compose --file docker-compose.yml up --abort-on-container-exit diff --git a/modules/10-basics/10-hello-world/ru/EXERCISE.md b/modules/10-basics/10-hello-world/ru/EXERCISE.md index 686a78e..4afbef6 100644 --- a/modules/10-basics/10-hello-world/ru/EXERCISE.md +++ b/modules/10-basics/10-hello-world/ru/EXERCISE.md @@ -1,6 +1,25 @@ +Наберите в редакторе код, чтобы программа вывела **точно такую строку**: -Наберите в редакторе код из задания символ в символ и нажмите «Проверить». - -```ruby -puts 'Hello, World!' +```text +Hello, World! ``` + +### Важно! + +* Строка должна совпадать **символ в символ**: заглавные и строчные буквы, запятые, пробелы и восклицательный знак. +* Кавычки обязательны. +* Скобки у `puts` можно ставить или нет — это не повлияет на результат. + +### Типичные ошибки + +* `hello, World!` — первая буква маленькая +* `Hello, world!` — буква "w" маленькая +* `Hello World!` — пропущена запятая +* `puts Hello, World!` — забыты кавычки +* `puts(‘Hello, World!’)` — неправильные кавычки + +### Если тест не пройден + +1. Сравните вывод вашей программы с образцом `Hello, World!`. +2. Исправьте отличия **символ в символ**. +3. Запустите проверку снова. diff --git a/modules/10-basics/10-hello-world/test.rb b/modules/10-basics/10-hello-world/test.rb index 77b52de..166e196 100644 --- a/modules/10-basics/10-hello-world/test.rb +++ b/modules/10-basics/10-hello-world/test.rb @@ -4,7 +4,12 @@ describe 'output' do it 'should works' do - _(-> { require_relative 'index' }).must_output(/^Hello, World!$/) - puts 'Hello, World!' + out, _err = capture_io do + require_relative 'index' + end + + result = out.strip + + assert { result == "Hello, World!" } end end diff --git a/modules/10-basics/15-ruby-as-a-second-language/ru/EXERCISE.md b/modules/10-basics/15-ruby-as-a-second-language/ru/EXERCISE.md index 2aaaba2..bd15830 100644 --- a/modules/10-basics/15-ruby-as-a-second-language/ru/EXERCISE.md +++ b/modules/10-basics/15-ruby-as-a-second-language/ru/EXERCISE.md @@ -1,8 +1,18 @@ -На данном этапе мы знаем ещё слишком мало, чтобы писать какой-то интересный код, поэтому просто скопируйте и выполните код ниже. В этом коде используется библиотека ActiveSupport, которая расширяет язык некоторыми полезными возможностями. Изначально она была создана для Rails, но используется и за его пределами. +На данном этапе мы знаем ещё слишком мало, чтобы писать какой-то интересный код, поэтому просто скопируйте и выполните код ниже. В этом коде используется библиотека ActiveSupport, которая расширяет язык некоторыми полезными возможностями.Изначально она была создана для Rails, но используется и за его пределами. + +Библиотека ActiveSupport уже подключена в файл, поэтому просто наберите код: ```ruby # На сервере время в UTC # Считаться будет от него puts 1.day.ago - 1.week + 3.hours ``` + +Код должен нужно набрать между специальными комментариями: + +```text +# BEGIN (write your solution here) +... тут пишем код +# END +``` diff --git a/modules/10-basics/15-ruby-as-a-second-language/test.rb b/modules/10-basics/15-ruby-as-a-second-language/test.rb index 814d14f..ca4bf7c 100644 --- a/modules/10-basics/15-ruby-as-a-second-language/test.rb +++ b/modules/10-basics/15-ruby-as-a-second-language/test.rb @@ -3,12 +3,28 @@ require 'active_support/all' require 'test_helper' -describe 'function' do +describe 'output' do it 'should works' do - puts "current time: #{Time.now}" + # Захватываем stdout из index.rb + out, _err = capture_io do + require_relative 'index' + end + + puts "Current time:" + puts Time.now + puts "Captured time output:" + puts out + + # Вычисляем ожидаемую дату date = 1.day.ago - 1.week + 3.hours - r = Regexp.new(date.year.to_s, Regexp::MULTILINE) - _(-> { require_relative 'index' }).must_output(r) - puts date + + # Приводим вывод к строке без лишних переводов строки + out_str = out.strip + + # Убираем миллисекунды, сравниваем только по секундам + expected = date.change(usec: 0).to_s + + # Power Assert проверка + assert { out_str == expected } end end diff --git a/modules/10-basics/20-everything-is-object/index.rb b/modules/10-basics/20-everything-is-object/index.rb index d3e66bf..b0423fa 100644 --- a/modules/10-basics/20-everything-is-object/index.rb +++ b/modules/10-basics/20-everything-is-object/index.rb @@ -1,3 +1 @@ -# BEGIN -puts 'hexlet'.methods.grep(/\?/) -# END +puts 'hexlet'.methods.shuffle.first(5) diff --git a/modules/10-basics/20-everything-is-object/ru/EXERCISE.md b/modules/10-basics/20-everything-is-object/ru/EXERCISE.md index c679523..7b51c2f 100644 --- a/modules/10-basics/20-everything-is-object/ru/EXERCISE.md +++ b/modules/10-basics/20-everything-is-object/ru/EXERCISE.md @@ -2,10 +2,26 @@ Напечатайте на экран следующий вызов: ```ruby -# Здесь выводятся на экран все методы строк, -# которые содержат знак вопроса в имени -# Фактически — это вывод списка предикатов -# То же самое можно сделать для любого типа -puts 'hexlet'.methods.grep(/\?/) -# Будет выведено около 20 имен +puts 'hexlet'.methods.shuffle.first(10) ``` + +Вот что он делает: + +1. `'hexlet'.methods` — массив всех методов объекта строки. +3. `.shuffle` — перемешивает массив методов случайным образом. +4. `.first(10)` — берёт первые 10 методов из перемешанного массива. +5. `puts` — выводит их по одному на строку. + +💡 Важное: **результат будет случайным**, потому что `.shuffle` перемешивает методы. + +Пример возможного вывода (точно будет другим при каждом запуске): + +```text +reverse +inspect +lines +display +casecmp +``` + +То есть **это 5 случайных методов строки**. diff --git a/modules/10-basics/20-everything-is-object/test.rb b/modules/10-basics/20-everything-is-object/test.rb index 16039fd..adeb17d 100644 --- a/modules/10-basics/20-everything-is-object/test.rb +++ b/modules/10-basics/20-everything-is-object/test.rb @@ -1,10 +1,23 @@ # frozen_string_literal: true +require 'active_support/all' require 'test_helper' -describe 'function' do +describe 'output' do it 'should works' do - _(-> { require_relative 'index' }).must_output(/respond_to/) - puts 'hexlet'.methods.grep(/\?/) + # Захватываем stdout из index.rb + out, _err = capture_io do + require_relative 'index' + end + + puts out + + methods = out.strip.split("\n") + + assert { methods.size == 5 } + + methods.each do |method| + assert { "hexlet".respond_to? method } + end end end diff --git a/modules/10-basics/25-first-function/index.rb b/modules/10-basics/25-first-function/index.rb index 24e4cfb..b480f4e 100644 --- a/modules/10-basics/25-first-function/index.rb +++ b/modules/10-basics/25-first-function/index.rb @@ -1,7 +1,3 @@ -# frozen_string_literal: true - -# BEGIN def double(num) num * 2 end -# END diff --git a/modules/10-basics/25-first-function/ru/EXERCISE.md b/modules/10-basics/25-first-function/ru/EXERCISE.md index 6e22165..da1e7bb 100644 --- a/modules/10-basics/25-first-function/ru/EXERCISE.md +++ b/modules/10-basics/25-first-function/ru/EXERCISE.md @@ -1,7 +1,11 @@ -Реализуйте функцию `double()`, которая удваивает любое переданное ей число и возвращает его: +Создайте функцию `double()`, которая удваивает любое переданное ей число и возвращает его. + +Пример вызова: ```ruby double(3) # 6 double(8) # 16 ``` + +Функцию не нужно вызывать вручную, она будет вызвана в тестах. diff --git a/modules/10-basics/25-first-function/ru/README.md b/modules/10-basics/25-first-function/ru/README.md index 559ec90..ab6b7d5 100644 --- a/modules/10-basics/25-first-function/ru/README.md +++ b/modules/10-basics/25-first-function/ru/README.md @@ -1,5 +1,22 @@ -Начнем сразу с комплексного примера, включающего в себя определение функции, переменной и простые арифметические операции. Посмотрим на синтаксис и разберём некоторые интересные особенности Ruby. Ниже дано определение функции, находящей среднее арифметическое: +В Ruby функция выглядит так: + +```ruby +# Определение функции +def имя_функции(параметр) + # тело функции +end + +# Вызов функции +имя_функции(значение) +``` + +* `def` — ключевое слово для создания функции +* `имя_функции` — название функции (используем `snake_case`) +* `(параметр)` — имя переменной, которая будет использоваться внутри функции +* `end` — завершает функцию + +Ниже дано определение функции, находящей среднее арифметическое: ```ruby # def – определение функции @@ -21,6 +38,8 @@ find_average 1, 2 find_average(1) ``` +## Стиль кода в Ruby + Сначала пара слов о стиле. В Ruby существует ровно один общепринятый стиль оформления кода, которого [придерживается](https://ukupat.github.io/tabs-or-spaces/) все комьюнити: * Отступы — два пробела @@ -43,3 +62,30 @@ sum = a + b ``` По своему смыслу функция `find_average()` должна возвращать число с плавающей точкой, поэтому такое преобразование необходимо. + +## Где проверить код + +Если у вас установлен Ruby, то можно запустить код внутри REPL, набрав в терминале команду `irb`: + +```bash +irb +irb(main):001* def find_average(a, b) +irb(main):002* sum = a + b +irb(main):003* sum.to_f / 2 +irb(main):004> end +=> :find_average +irb(main):005> +irb(main):005> find_average(1, 5) +=> 3.0 +irb(main):006> +``` + +Или онлайн в [Repl.it Ruby](https://replit.com/languages/ruby). + +## Итог + +* Функции в Ruby определяются через `def ... end` +* Имя функции пишется в стиле `snake_case` +* Аргументы перечисляются в круглых скобках +* `return` писать не обязательно — функция возвращает результат последнего выражения +* Функции можно запускать как с круглыми скобками, так и без diff --git a/modules/10-basics/27-conditions/ru/EXERCISE.md b/modules/10-basics/27-conditions/ru/EXERCISE.md index 53fe9d5..7f55645 100644 --- a/modules/10-basics/27-conditions/ru/EXERCISE.md +++ b/modules/10-basics/27-conditions/ru/EXERCISE.md @@ -1,7 +1,11 @@ +Реализуйте функцию предикат `even?`, которая проверяет, является ли переданное число чётным. Функция должна возвращать `true`, если число чётное и `false`, если не чётное. -Реализуйте функцию предикат `even?`, которая проверяет, является ли переданное число чётным. Функция должна возвращать `true`, если число чётное и `false`, если не чётное. Не используйте встроенные функции для определения четности: +В этом задании мы учимся работать с условиями, поэтому не используйте встроенные функции для определения четности. + +Примеры вызова: ```ruby even?(5) # false -even?(6) # true +# Скобки при вызове можно опустить +even? 6 # true ``` diff --git a/modules/10-basics/27-conditions/ru/README.md b/modules/10-basics/27-conditions/ru/README.md index 9972e79..b4d73d9 100644 --- a/modules/10-basics/27-conditions/ru/README.md +++ b/modules/10-basics/27-conditions/ru/README.md @@ -24,6 +24,16 @@ nil && false # nil В Ruby только `nil` и `false` рассматриваются как *falsey*, все остальные значения в логических выражениях приводятся к `true`. +## Spaceship-оператор + +Этот оператор удобно использовать в функциях сортировки для определения того, нужно менять местами два соседних элемента или нет. Всего у функции возможны три разных варианта возврата: + +```ruby +1 <=> 1 # 0 числа равны +2 <=> 1 # 1 левое больше правого +1 <=> 2 # -1 левое меньше правого +``` + ## Значение по умолчанию В Ruby широко используется такой код: @@ -58,3 +68,45 @@ items.any? # пустой массив items.empty? ``` + +Пример: функция, которая проверяет, что число положительное: + +```ruby +def positive?(num) + num > 0 +end + +positive?(3) # true +positive?(-1) # false +``` + +**Правила:** + +* Название метода должно заканчиваться на `?` +* Метод возвращает `true` или `false` +* Параметры могут быть любыми объектами + + +## Типичные ошибки + +```ruby +# ❌ Не используем ? в имени +def even_length(str) + str.length.even? +end + +# ❌ Возвращаем не логическое значение +def even_length?(str) + str.length +end +``` + +## Итог + +* Логические значения в Ruby: `true` и `false` +* Логические операторы: `&&` (и), `||` (или), `!` (не) +* В Ruby только `nil` и `false` — falsey, остальные значения считаются true +* Значение по умолчанию можно задавать через `||=` +* Предикаты — методы, которые возвращают `true` или `false` +* Методы-предикаты по соглашению заканчиваются на `?` +* Можно создавать свои предикаты для любых проверок diff --git a/modules/10-basics/30-if/index.rb b/modules/10-basics/30-if/index.rb index cf0542b..b5066a3 100644 --- a/modules/10-basics/30-if/index.rb +++ b/modules/10-basics/30-if/index.rb @@ -1,13 +1,9 @@ -# frozen_string_literal: true - -# BEGIN -def compare(a, b) - if a > b - 1 - elsif b > a - -1 +def check_number(num) + if num > 0 + "positive" + elsif num < 0 + "negative" else - 0 + "zero" end end -# END diff --git a/modules/10-basics/30-if/ru/EXERCISE.md b/modules/10-basics/30-if/ru/EXERCISE.md index 61adc39..8f29bb6 100644 --- a/modules/10-basics/30-if/ru/EXERCISE.md +++ b/modules/10-basics/30-if/ru/EXERCISE.md @@ -1,16 +1,16 @@ +Вашей задачей в этом задании будет написать функцию `check_number()`, которая число и возвращает число с описанием числа. Функция принимает два числа и возвращает числа: -В Ruby встроен оператор `<=>` (spaceship). Этот оператор удобно использовать в функциях сортировки для определения того, нужно менять местами два соседних элемента или нет. Всего у функции возможны три разных варианта возврата: +* Если число больше нуля, то возвращает `positive`, +* Если меньше нуля то `negative`, +* Если равно нулю то `zero` -```ruby -1 <=> 1 # 0 числа равны -2 <=> 1 # 1 левое больше правого -1 <=> 2 # -1 левое меньше правого -``` - -Вашей задачей в этом задании будет написать функцию `compare()`, которая ведёт себя так же, как и spaceship-оператор: +Пример: ```ruby -compare(1, 1) # 0 числа равны -compare(2, 1) # 1 левое больше правого -compare(1, 2) # -1 левое меньше правого +# Примеры вызова: +puts check_number(10) # => "positive" +puts check_number(-5) # => "negative" +puts check_number(0) # => "zero" ``` + +Функция должна **возвращать** строку. diff --git a/modules/10-basics/30-if/ru/README.md b/modules/10-basics/30-if/ru/README.md index 92d740e..d8637a5 100644 --- a/modules/10-basics/30-if/ru/README.md +++ b/modules/10-basics/30-if/ru/README.md @@ -18,6 +18,6 @@ sentence_type 'Hexlet for humans' # statement sentence_type 'boo!' # exclamation ``` -В Ruby условиям не нужны скобки. Выражения пишутся сразу после ключевого слова `if`. Для дополнительных условий используется ключевое словое `elsif`. И для всего остального — привычный `else`. В конце добавляется `end`. +В Ruby условиям не нужны скобки. Выражения пишутся сразу после ключевого слова `if`. Для дополнительных условий используется ключевое слово `elsif`. И для всего остального — привычный `else`. В конце добавляется `end`. В примере используется предикат (функция, возвращающая `true` или `false`) `end_with?()`. Ключевое отличие Ruby от других популярных языков — в использовании знака вопроса на конце, вместо `is` и его аналогов в начале. Этот подход перекочевал из Lisp-языков. diff --git a/modules/10-basics/30-if/test.rb b/modules/10-basics/30-if/test.rb index 4617b78..965e697 100644 --- a/modules/10-basics/30-if/test.rb +++ b/modules/10-basics/30-if/test.rb @@ -3,10 +3,12 @@ require 'test_helper' require_relative 'index' -describe 'function' do - it 'should works' do - assert { compare(1, 1).zero? } - assert { compare(0, 100) == -1 } - assert { compare(234, 2) == 1 } +describe 'check_number' do + it 'should work' do + assert { check_number(3) == 'positive' } + assert { check_number(10) == 'positive' } + assert { check_number(-1) == 'negative' } + assert { check_number(-5) == 'negative' } + assert { check_number(0) == 'zero' } end end diff --git a/modules/10-basics/40-blocks/ru/README.md b/modules/10-basics/40-blocks/ru/README.md index a6c1a35..6aa76bf 100644 --- a/modules/10-basics/40-blocks/ru/README.md +++ b/modules/10-basics/40-blocks/ru/README.md @@ -33,7 +33,7 @@ end) Это довольно необычная концепция. Сама по себе она не привносит никаких новых возможностей в язык, но даёт новые визуальные возможности по оформлению кода. Именно из-за этой особенности Ruby так хорошо подходит и часто используется, как язык для построения DSL (языков предметной области). Подробнее об этом в следующих уроках. -И, наконец, сам блок. Можно представить, что внутри функции он попадает в переменную, которая вызывается, как обычная функция. Сам блок — как функция (а он является в том числе функцией), и умеет принимать параметры. Внутрь блока они попадают через конструкцию `|i|`, идущую сразу после *do*. Этот синтаксис пришел в Ruby из Smalltalk. Если параметров несколько, то они просто перечисляются через запятую `|one, two|`. +И, наконец, сам блок. Можно представить, что внутри функции он попадает в переменную, которая вызывается, как обычная функция. Сам блок — как функция (а он является, в том числе функцией), и умеет принимать параметры. Внутрь блока они попадают через конструкцию `|i|`, идущую сразу после *do*. Этот синтаксис пришел в Ruby из Smalltalk. Если параметров несколько, то они просто перечисляются через запятую `|one, two|`. Блок работает как замыкание, а значит внутри него можно использовать любые переменные, определенные снаружи и выше блока: @@ -78,7 +78,7 @@ end # => ? ``` -Однострочный вариант блока будет относиться к самой правой функции. При полной форме `do ... end` блок относится к самой первой функции +Однострочный вариант блока будет относиться к самой правой функции. При полной форме `do ... end` блок относится к самой первой функции: ```ruby print_hello_f1 print_hello_f2 print_hello_f3 { 'Petya' } @@ -110,3 +110,14 @@ end) ``` Не переживайте, если прямо сейчас блоки вам непонятны. Для их осознания нужно время и практика. В Ruby они встречаются повсеместно, поэтому понимание работы с блоками приходит быстро. Буквально в следующем модуле они будут уже везде. +Вот блок "Итоги", который можно добавить в конец урока после последнего абзаца: + +## Итоги + +* Блоки в Ruby — это кусок кода, который можно передавать в метод и выполнять многократно. +* Синтаксис блоков: `do ... end` для многострочных блоков и `{ ... }` для однострочных. +* Аргументы блока (`|i|`) позволяют получать значения от метода (`times`, `upto`, `downto` и др.) для каждой итерации. +* Переменные блока живут только внутри блока; внешние переменные остаются неизменными, если их явно не изменять. +* Приоритет: `{}` имеет более высокий приоритет, чем `do/end`, что важно при вложенных вызовах. +* Однострочный вариант блока будет относиться к самой правой функции. При полной форме `do ... end` блок относится к самой первой функции. +* С помощью скобок можно определить, к какой функции блок будет относиться. diff --git a/modules/10-basics/40-blocks/test.rb b/modules/10-basics/40-blocks/test.rb index 3162961..fe28adc 100644 --- a/modules/10-basics/40-blocks/test.rb +++ b/modules/10-basics/40-blocks/test.rb @@ -3,18 +3,22 @@ require 'test_helper' require_relative 'index' -describe 'function' do - it 'should works 1' do - output = /3\n2\n1/ - assert_output(output) do +describe 'output' do + it 'should works' do + out, _err = capture_io do show_me_numbers(3) end - end - it 'should works 1' do - output = /4\n3\n2\n1/ - assert_output(output) do + puts out + + assert { out == "3\n2\n1\n" } + + out2, _err = capture_io do show_me_numbers(4) end + + puts out2 + + assert { out2 == "4\n3\n2\n1\n" } end end