- Стандартная библиотека Ruby Std-lib
- Использование сторонних Gem-ов (библиотек)
- Dependency Injection
Как правило, языки программирования реализуют фундаментальные концепции и включают в себя основные функциональные возможности. В их числе: типы данных и общие функции для работы с ними, управляющие конструкции, возможности для определения собственных типов данных, функций и методов. Такой подход позволяет оставаться языку программирования достаточно компактным и вместе с тем предоставлять все возможности для решения множества задач из самых различных предметных областей. Это сродни строительным блокам или брускам пластелина, из которых вы можете создать то что вам требуется.
Однако, часто даже в различных предметных областях встречаются похожие задачи. Например, не зависимо от природы задачи, нам скорее всего потребуется возможность сетевого соединения и получения данных по сети, работа с данными в формате JSON или XML, вычисление хэш функций или логирование. Кроме того, нам могут пригодиться реализации инженерных концепций: веб сервера для локальной разработки, шаблонов проектирования программного кода или обработка параметров командной строки. Все эти задачи, с одной стороны, не вполне вписываются в базовую функциональность ядра языка, а с другой - достаточно часто встречаются при решении задач. Именно здесь стандартная библиотека играет ключевую роль.
Стандартная библиотека Ruby Std-lib поставляется вместе с интерпретатором (это значит что после установки Ruby ничего дополнительно устанавливать не нужно), однако при запуске программы Std-lib по умолчанию не доступна. Это понятно, поскольку заранее не известно потребуется ли нам что-то из Std-lib или нет. В том случае когда мы нуждаемся, например в возможности обработки данных в формате JSON, мы можем задействовать соответствующую библиотеку из Std-lib с помощью вызова метода require
:
require 'json'
После этого необходимые модули, классы и методы становятся доступны для работы с JSON данными:
require 'json'
user = { first_name: 'John', last_name: 'Doe', age: 26 }
json_string = user.to_json
parsed_user = JSON.parse json_string
parsed_user['first_name'] # => 'John'
Обратите внимание на одну особенность: после парсинга объекта user
ключи являются объектами класса String
, а не класса Symbol
как было изначально.
Подробности и документация Ruby Std-lib находится здесь: http://ruby-doc.org/stdlib
Не смотря на широкие возможности Ruby Std-lib, нам часто могут понадобиться дополнительные инструменты. Например, фреймворк или библиотека для реализации модели «Конечный автомат». За более чем 20 лет жизни Ruby было создано огромное количество сторонних библиотек для облегчения решения всевозможных задач: от загрузки изображений до консольного менеджера задач. Де факто основным источником сторонних библиотек является http://rubygems.org
Остаются 2 вопроса: как заполучить необходимую библиотеку в свой проект и как найти эту библиотеку.
Для установки библиотек с http://rubygems.org де-факто используется встроенный в Ruby менеждер библиотек RubyGems. Он позволяет вам установить нужную библиотеку с помощью команды gem install
. Например, для установки фреймворка Rails: gem install rails
, для установки Gem-а для работы с конечным автоматом: gem install workflow
.
После этого вы сможете использовать Gem в вашем приложении подключив его также как и стандартную библиотеку (с помощью метода require
):
require 'workflow'
class Comp
include Workflow
workflow do
state :off do
event :turn_on, transitions_to: :on
end
state :on do
event :turn_off, transitions_to: :off
event :reboot, transitions_to: :rebooting
end
state :rebooting do
event :load, transitions_to: :on
end
end
end
wizard = Comp.new
wizard.current_state.name # => :off
wizard.on? # => false
wizard.turn_on!
wizard.on? # => true
Как вы уже заметили, библиотеки в Ruby принято называть Gem-ами.
Второй вопрос касается того, как найти Gem для решения требуемой задачи? В этом могут помочь ресурсы https://www.ruby-toolbox.com/categories/by_name и http://awesome-ruby.com
На самом деле, такая техника (шаблон проектирования) довольно естественно реализуется в Ruby. Настолько естественно что мы можем даже не замечать этого. Piotr Solnica очень хорошо описывает Dependency Injection (Инъекцию зависимостей). Если вы не так хорошо знаете английский язык, не пугайтесь!. Последовательно просмотрите и проанализируйте код в статье.
Немного копнув глубже, представьте ситуацию когда у нас есть класс для скачивания и сохранения песен птиц:
class HTTPClient
def get
end
end
class DataStore
end
class BirdSong
def initialize
@client = HTTPClient.new
@store = DataStore.new
end
end
Обратите внимание на то что внутри конструктора класса BirdSong
мы создаём объекты HTTP клиента и хранилища данных. Здесь возникает несколько проблем.
Во-первых, для того чтобы протестировать класс BirdSong
нам необходимо определить классы HTTPClient
и DataStore
. То есть класс BirdSong
не является изолированным.
Во-вторых, наш класс BirdSong
сильно связан с другими классами и если мы захотим, например, изменить класс хранилища данных или класс HTTP клиента, нам придётся залезать внутрь класса BirdSong
.
Подход с использованием Dependency Injection может выглядеть так:
# Вариант с конструктором
class BirdSong
def initialize client:, store:
end
end
# Вариант с использованием setters
class BirdSong
attr_writer :client, :store
end
Теперь класс BirdSong
ничего не знает о классах HTTPClient
и DataStore
. Всё что ему известно, так это то что используются зависимости HTTP клиента (который отвечает на метод get
) и хранилища данных (отвечает на метод save
). Теперь мы можем использовать всё это так:
# Вариант с конструктором
bird_song = BirdSong.new(client: HTTPClient.new, store: DataStore.new)
# Вариант с использованием setters
bird_song = BirdSong.new
bird_song.client = HTTPClient.new
bird_song.store = DataStore.new
Теперь всё очень удобно, хотя на первый взгляд и может показаться немного более многословным чем самый первый вариант без Dependency Injection.
Для класса Numeric
:
-
Метод, возвращающий объект класса
ActiveSupport::Duration
, представляющий n часов, n минут, n секунд -
Метод, возвращающий экземпляр класса
Time
, со значением времени через n секунд от текущего момента -
Метод, возвращающий экземпляр класса
Time
, со значением времени n секунд тому назад от текущего момента
Для класса String
:
-
Метод, возвращающий константу по имени строки
-
Метод, возвращающий множественное число для значения строки
-
Метод, возвращающий строку без подстрок совпавших по шаблону который передан в метод в качестве аргумента
-
Метод, проверяющий, стостоит ли строка лишь из пробельных символов
-
Метод, заменяющий символ
_
на-
Для модуля ActiveSupport::Configurable
:
- Метод для доступа к упорядоченному хэшу, хранящему конфигурацию (пользовательские настройки) для класса
-
Капитан, нам только что сообщили! Специалисты из отряда шифрования смогли вычислить условия запуска гиперпространственных ворот.
-
Хм... Тогда почему в вашем голосе я слышу ноты досады, лейтенант?
-
Всё дело в том что мы смогли узнать лишь условия. Необходим алгоритм, который позволит сформировать правильную последовательность символов.
-
Что ж, тогда я думаю нам лучше не терять времени. Что за условия?
-
Там целые числа от 1 до 100. Нужно вывести их таким образом, что если число делится нацело на 3, вместо него выводить 'Fizz'. Если делится нацело на 5 - выводить 'Buzz'. Если соблюдаются оба этих условия, выводить 'FizzBuzz', а если ни одного условия не соблюдается - выводить само число.
-
Каким же странным сознанием была наделена эта цивилизация?
-
Не знаю, Капитан, но в том что мы смогли расшифровать сказано, что для представления условий необходимо использовать каррирование.
-
Что-нибудь известно о том какой язык возможно использовать? Может быть, Java или Python?
-
Никак нет, Капитан. На одной из створок ворот мы обнаружили гравировку с несколькими строками кода Ruby.
-
Всё ясно. Похоже нам крупно повезло: лучшие профессионалы как раз сейчас изучают Ruby. Они большие молодцы, и хотя не все темы ещё пройдены, я уверен что они с этим справятся!
Существует массив числовых элементов. Напишите метод maximum_sequence
, который возвращает непрерывный подмассив, содержащий хотя бы один элемент, сумма элементов которого максимальна. Например:
origin = [-1, -13, -2, 1, -3, 4, -1, 2, 1, -5, 4]
maximum_sequence(origin) # => [4, -1, 2, 1]
Используя Инъекцию Зависимостей напишите класс BirdSong
и определите в нём метод fetch
. Данный метод должен делать вызов к API стороннего сервиса, получать результат и сохранять его в YAML файле в виде:
- gen: ''
sp: ''
ssp: ''
file: ''
- gen: ''
sp: ''
ssp: ''
file: ''
Используйте этот код в качестве отправной точки:
class HTTPClient
def get(url, params)
# TODO: Implement me
end
end
class DataStore
def save(data)
# TODO: Implement me
end
end
class BirdSong
# NOTE: пример окончательной строки запроса: http://www.xeno-canto.org/api/2/recordings?query=nightingale+cnt:russia+q:A
URL = 'http://www.xeno-canto.org/api/2/recordings'
def fetch(bird:, country:, quality: :A)
# TODO: Implement me
end
end
BirdSong.new.fetch(bird: :nightingale, country: :russia)
Дополнительная информация по API сервиса находится здесь: http://www.xeno-canto.org/article/153
Для обращения по сети вы можете использовать стандартную библиотеку Ruby open-uri
или подходящий Gem, например httparty или rest-client. Не забудьте послушать песни в файлах :)
Проведите небольшое расследование и выясните что работает быстрее, классическая схема обработки исключения:
begin
# Buggy code
rescue
end
или обработка исключения в одну строку:
# Buggy code rescue nil
- GeekBrains
- Электронная почта:
[email protected]
- Slack канал