Перейти к основному содержимому

Простые приложения на Ruby

Разработчику Архитектору

Простые приложения на Ruby

Ruby удобен для коротких скриптов — читаемый синтаксис, богатая стандартная библиотека (File, JSON, Net::HTTP, SecureRandom). Ниже — типовые задачи "первого уровня" без гемов и без Rails: каждый пример можно сохранить в .rb и запустить через ruby script.rb.

Рекомендуемый порядок: после первой программы и типов, до Rails.


Как запускать примеры из главы

  1. Скопируйте код раздела в отдельный файл (имя — в Как запустить у примера).
  2. В терминале перейдите в каталог с файлом: cd путь/к/каталогу.
  3. Выполните: ruby имя_файла.rb.

Проверка интерпретатора: ruby -v (нужен Ruby 3.x).


Генератор паролей

Как запустить

  • Файл: password.rb
  • Команда: ruby password.rb
  • Нужно: Ruby 3.x, stdlib (securerandom).
  • Результат: в терминале одна строка — пароль (~20 символов).
require 'securerandom'

def generate_password(length: 16, digits: true, symbols: true)
pool = [*'a'..'z', *'A'..'Z']
pool.concat(('0'..'9').to_a) if digits
pool.concat(%w[! @ # $ % ^ & *]) if symbols
Array.new(length) { pool.sample(random: SecureRandom) }.join
end

puts generate_password(length: 20)

Разбор:

  • require 'securerandom' подключает модуль стандартной библиотеки для криптостойкой случайности, что важно для паролей.
  • def generate_password(...) объявляет метод с именованными аргументами, поэтому при вызове сразу видно смысл каждого параметра.
  • pool = [*'a'..'z', *'A'..'Z'] собирает базовый набор символов через диапазоны и оператор распаковки *.
  • pool.concat(...) if digits и if symbols расширяют набор символов по флагам, чтобы поведение функции было гибким.
  • Array.new(length) { pool.sample(random: SecureRandom) } создаёт массив из length символов и каждый раз берёт случайный элемент из пула.
  • .join склеивает массив символов в готовую строку-пароль.
  • puts generate_password(length: 20) печатает результат в stdout и демонстрирует, как переопределяется длина по умолчанию.

Сортировщик текстового файла

Как запустить

  • Файл: sort_lines.rb
  • Подготовка: рядом положите input.txt (несколько строк текста).
  • Команда: ruby sort_lines.rb — в конце файла раскомментируйте или добавьте sort_lines('input.txt', 'sorted.txt').
  • Результат: файл sorted.txt и сообщение Строк: N → sorted.txt.
def sort_lines(input_path, output_path)
lines = File.readlines(input_path, encoding: 'UTF-8')
.map(&:strip)
.reject(&:empty?)
sorted = lines.sort_by(&:downcase)
File.write(output_path, sorted.join("\n") + "\n", encoding: 'UTF-8')
puts "Строк: #{sorted.size} → #{output_path}"
rescue Errno::ENOENT
warn "Файл не найден: #{input_path}"
end

sort_lines('input.txt', 'sorted.txt')

Разбор:

  • File.readlines(..., encoding: 'UTF-8') читает файл построчно и сразу фиксирует кодировку, чтобы избежать ошибок с кириллицей.
  • .map(&:strip) удаляет пробелы и перевод строки по краям каждой строки.
  • .reject(&:empty?) убирает пустые строки из дальнейшей обработки.
  • sort_by(&:downcase) сортирует без учёта регистра, приводя строки к нижнему регистру для сравнения.
  • File.write(..., sorted.join("\n") + "\n") записывает результат обратно в файл и добавляет финальный перевод строки.
  • rescue Errno::ENOENT перехватывает типичную ошибку "файл не найден" и выводит понятное сообщение через warn.

Консольный калькулятор

Как запустить

  • Файл: calc.rb
  • Команда: ruby calc.rb
  • Нужно: интерактивный терминал (ввод с клавиатуры).
  • Результат: запросы Операция, чисел; ответ = …; выход — q на запрос операции.

Код ITЗагрузка примера кода…

Разбор:

  • case op выбирает ветку вычисления по знаку операции и делает код компактным.
  • В ветке деления проверка b.zero? защищает от недопустимой операции и выбрасывает ArgumentError.
  • В else тоже выбрасывается ArgumentError, если пользователь ввёл неподдерживаемую операцию.
  • loop do ... end организует циклический ввод, а break if op == 'q' завершает программу по команде выхода.
  • gets&.strip использует safe-navigation &., чтобы не упасть, если ввод вернул nil.
  • Float(gets.strip) rescue (...) пытается преобразовать ввод в число и мягко возвращает цикл к следующей итерации при ошибке.
  • Блок begin/rescue вокруг calculate перехватывает ошибки бизнес-логики и показывает человеку точную причину.

Трекер задач в JSON

Как запустить

  • Файл: tasks.rb
  • Команда: ruby tasks.rb
  • Нужно: stdlib json; прав на запись в текущий каталог.
  • Результат: создаётся tasks.json, в консоли — список задач с [ ] / [x].

Код ITЗагрузка примера кода…

Разбор:

  • DB = 'tasks.json' задаёт единое место хранения, чтобы все функции работали с одним и тем же файлом.
  • load_tasks проверяет наличие файла и безопасно возвращает пустой массив, если данных пока нет.
  • JSON.parse(...) загружает задачи как массив хэшей; ключи после парсинга строковые ('id', 'title', 'done').
  • save_tasks пишет JSON в человекочитаемом формате через JSON.pretty_generate, что удобно для отладки.
  • add_task создаёт новую задачу и сразу сохраняет весь список обратно в файл.
  • toggle(id) ищет задачу по id, инвертирует флаг done и сохраняет результат.
  • Тернарный оператор в puts превращает булево поле в понятный чекбокс [x] или [ ].

Простой HTTP-сервер (WEBrick)

Как запустить

  • Файлы: server.rb (сервер), client.rb (клиент — второй блок кода).
  • Терминал 1: ruby server.rb — процесс слушает 127.0.0.1:4567.
  • Терминал 2: ruby client.rb — в консоли JSON, например {"ok"=>true, "time"=>"..."}.
  • Остановка сервера: Ctrl+C.
  • Нужно: stdlib webrick, net/http, json.
require 'webrick'

server = WEBrick::HTTPServer.new(Port: 4567, BindAddress: '127.0.0.1')
server.mount_proc '/' do |_req, res|
res['Content-Type'] = 'application/json'
res.body = { ok: true, time: Time.now.iso8601 }.to_json
end

trap('INT') { server.shutdown }
server.start

Разбор:

  • WEBrick::HTTPServer.new(...) поднимает встроенный HTTP-сервер на локальном адресе и порту.
  • server.mount_proc '/' регистрирует обработчик для корневого пути.
  • res['Content-Type'] = 'application/json' явно указывает формат ответа клиенту.
  • res.body = {...}.to_json формирует JSON-ответ как строку.
  • trap('INT') { server.shutdown } добавляет корректное завершение по Ctrl+C.
  • server.start запускает цикл обработки запросов и держит процесс в активном состоянии.

Клиент на stdlib:

require 'net/http'
require 'json'

uri = URI('http://127.0.0.1:4567/')
json = JSON.parse(Net::HTTP.get(uri))
puts json

Разбор:

  • URI(...) разбирает строку URL в объект с хостом, портом и путём.
  • Net::HTTP.get(uri) выполняет простой GET-запрос и возвращает тело ответа строкой.
  • JSON.parse(...) преобразует JSON-строку в хэш Ruby.
  • puts json печатает структуру данных и помогает проверить, что API действительно отвечает.

Сканер каталога

Как запустить

  • Файл: scan_dir.rb
  • Команда: ruby scan_dir.rb (из каталога, который хотите обойти).
  • Результат: в консоли хеш { files: N, bytes: M } для .rb и .md в текущей папке рекурсивно.

Код ITЗагрузка примера кода…

Разбор:

  • Find.find(root) рекурсивно обходит дерево каталогов от заданной точки входа.
  • next unless File.file?(path) отфильтровывает директории и оставляет только файлы.
  • File.extname(path) достаёт расширение файла для фильтрации по интересующим типам.
  • Проверка extensions.any? && !extensions.include?(ext) позволяет использовать как фильтр, так и режим "все файлы".
  • stats[:files] и stats[:bytes] аккумулируют количество файлов и суммарный размер.
  • Возврат stats делает функцию переиспользуемой: её можно вызывать в других скриптах.

Резервное копирование файла

Как запустить

  • Файл: backup.rb
  • Подготовка: любой файл для копии, например notes.txt.
  • Команда: в конце скрипта вызовите puts backup('notes.txt'), затем ruby backup.rb.
  • Результат: копия в папке backups/ с префиксом даты в имени; путь печатается в stdout.
require 'fileutils'

def backup(source, backup_dir = 'backups')
FileUtils.mkdir_p(backup_dir)
stamp = Time.now.strftime('%Y%m%d-%H%M%S')
dest = File.join(backup_dir, "#{stamp}_#{File.basename(source)}")
FileUtils.cp(source, dest)
dest
end

Разбор:

  • FileUtils.mkdir_p(backup_dir) создаёт каталог для бэкапов и не падает, если он уже существует.
  • Time.now.strftime(...) формирует временную метку, чтобы копии не перезаписывали друг друга.
  • File.basename(source) оставляет только имя файла без пути и безопасно встраивает его в имя копии.
  • File.join(...) собирает путь платформонезависимо.
  • FileUtils.cp(source, dest) копирует исходный файл в каталог бэкапов.
  • Возврат dest позволяет вызвать функцию из другого кода и получить путь к созданной копии.

Проверка доступности URL

Как запустить

  • Файл: check_url.rb
  • Команда: ruby check_url.rb
  • Нужно: доступ в интернет для https://example.com.
  • Результат: две строки с хешами :url, :code, :ok (для несуществующего домена — :ok=>false).

Код ITЗагрузка примера кода…

Разбор:

  • URI(url) разбирает адрес и отдаёт готовые поля host, port, scheme.
  • Net::HTTP.new(uri.host, uri.port) подготавливает HTTP-клиент под конкретный хост.
  • http.use_ssl = (uri.scheme == 'https') автоматически включает TLS для HTTPS.
  • open_timeout и read_timeout ограничивают время ожидания и защищают от подвисаний.
  • request_head(...) отправляет HEAD-запрос, чтобы проверить доступность без загрузки тела.
  • Возвращается структурированный хэш с :code и :ok, что удобно для последующей автоматической обработки.
  • rescue StandardError превращает сетевые исключения в понятный результат с полем :error.

Характерный пример для Ruby — блоки и Enumerable

Как запустить

  • Файл: word_stats.rb
  • Команда: ruby word_stats.rb
  • Результат: три строки — всего слов, уникальных, топ-3 по частоте.
text = 'Ruby учит думать объектами и блоками'
words = text.split(/\s+/)
stats = words
.map(&:downcase)
.group_by(&:itself)
.transform_values(&:size)

puts "Всего слов: #{words.size}"
puts "Уникальных: #{stats.size}"
puts stats.sort_by { |_, c| -c }.first(3).map { |w, c| "#{w}: #{c}" }

Разбор:

  • split(/\s+/) разбивает строку на слова по одному или нескольким пробельным символам.
  • map(&:downcase) нормализует регистр, чтобы Ruby и ruby считались одним словом.
  • group_by(&:itself) группирует одинаковые слова в хэш вида слово => [вхождения].
  • transform_values(&:size) превращает массивы в счётчики частоты.
  • sort_by { |_, c| -c } сортирует по убыванию частоты.
  • first(3) выбирает только топ-3 элемента.
  • map { |w, c| "#{w}: #{c}" } переводит пары ключ-значение в удобные строки для вывода.

Дополнительные мини-приложения

Парсинг CSV-строки в консольной утилите

Как запустить

  • Файл: parse_csv_line.rb
  • Команда: ruby parse_csv_line.rb
  • Результат: хэш с полями пользователя и числом age.
line = "42, Ada Lovelace, ada@example.com, 28"
id, name, email, age = line.split(",").map(&:strip)

user = {
id: id.to_i,
name: name,
email: email,
age: age.to_i
}

p user

Разбор:

  • split(",") разбивает CSV-строку на колонки по запятой.
  • map(&:strip) убирает пробелы вокруг значений.
  • Параллельное присваивание раскладывает список колонок в отдельные переменные.
  • to_i явно приводит числовые поля к целому типу.
  • Сниппет показывает базовую ETL-логику для CLI-скриптов без внешних библиотек.

Лог-фильтр ошибок из текстового файла

Как запустить

  • Файл: filter_errors.rb
  • Подготовка: создать app.log с несколькими строками.
  • Команда: ruby filter_errors.rb
  • Результат: печатает только строки, где есть ERROR.
File.foreach("app.log", encoding: "UTF-8") do |line|
next unless line.include?("ERROR")
puts line.strip
end

Разбор:

  • File.foreach читает файл построчно и не грузит всё содержимое в память сразу.
  • next unless ... быстро пропускает неинтересные строки.
  • include?("ERROR") — простая и быстрая фильтрация по подстроке.
  • line.strip убирает перенос строки и лишние пробелы перед выводом.
  • Это минималистичный шаблон для первых утилит мониторинга логов.

См. также: первая программа · ООП · Rails · Sinatra и гемы

Основа по протоколу

Базовый разбор HTTP и HTTPS находится в отдельной статье — HTTP как основа веб-интеграций.