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

Простые приложения на 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)

Разбор: SecureRandom даёт криптостойкую случайность; Array#sample выбирает символ из пула; именованные аргументы (length:) — обычный стиль Ruby.


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

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

  • Файл: 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')

Разбор: readlines + map/reject — цепочка обработки; sort_by с локалью можно заменить на sort с блоком a.locale_compare(b, 'ru') при необходимости.


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

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

  • Файл: calc.rb
  • Команда: ruby calc.rb
  • Нужно: интерактивный терминал (ввод с клавиатуры).
  • Результат: запросы Операция, чисел; ответ = …; выход — q на запрос операции.
def calculate(a, b, op)
case op
when '+' then a + b
when '-' then a - b
when '*' then a * b
when '/'
raise ArgumentError, 'деление на ноль' if b.zero?
a / b
else
raise ArgumentError, "неизвестная операция: #{op}"
end
end

loop do
print 'Операция (+ - * / q): '
op = gets&.strip
break if op == 'q'

a = Float(gets.strip) rescue (puts 'число?'; next)
b = Float(gets.strip) rescue (puts 'число?'; next)

begin
puts "= #{calculate(a, b, op)}"
rescue ArgumentError => e
puts e.message
end
end

Разбор: case/when, исключения вместо «тихого» нуля при делении; loop + gets для REPL-подобного ввода.


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

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

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

DB = 'tasks.json'

def load_tasks
return [] unless File.exist?(DB)
JSON.parse(File.read(DB, encoding: 'UTF-8'))
rescue JSON::ParserError
[]
end

def save_tasks(tasks)
File.write(DB, JSON.pretty_generate(tasks), encoding: 'UTF-8')
end

def add_task(title)
tasks = load_tasks
tasks << { id: Time.now.to_i, title: title, done: false }
save_tasks(tasks)
end

def toggle(id)
tasks = load_tasks
task = tasks.find { |t| t['id'] == id }
return warn('нет задачи') unless task
task['done'] = !task['done']
save_tasks(tasks)
end

add_task('Изучить Ruby')
load_tasks.each { |t| puts "[#{t['done'] ? 'x' : ' '}] #{t['title']}" }

Разбор: хеши с строковыми ключами после JSON.parse; персистентность в одном файле — типичный паттерн для CLI-утилит.


Простой 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

Клиент на stdlib:

require 'net/http'
require 'json'

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

Разбор: WEBrick входит в stdlib MRI; для продакшена берут Puma или Unicorn, но для обучения достаточно.


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

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

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

def scan_dir(root, extensions = [])
stats = { files: 0, bytes: 0 }
Find.find(root) do |path|
next unless File.file?(path)
ext = File.extname(path)
next if extensions.any? && !extensions.include?(ext)
stats[:files] += 1
stats[:bytes] += File.size(path)
end
stats
end

p scan_dir('.', ['.rb', '.md'])

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

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

  • Файл: 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

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

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

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

def check_url(url, timeout: 5)
uri = URI(url)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = (uri.scheme == 'https')
http.open_timeout = http.read_timeout = timeout
res = http.request_head(uri.request_uri)
{ url: url, code: res.code, ok: res.is_a?(Net::HTTPSuccess) }
rescue StandardError => e
{ url: url, ok: false, error: e.message }
end

%w[https://example.com https://invalid.invalid].each { |u| p check_url(u) }

Характерный пример для 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}" }

Почему это «про Ruby»: group_by, transform_values, блоки { } — то, что редко встречается в C/Java в таком виде; вместо цикла for — выразительная цепочка.


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

См. также

Другие статьи этого же раздела в боковом меню (как на странице «О разделе»).