Skip to content

Instantly share code, notes, and snippets.

@psylone
Last active September 27, 2016 12:14
Show Gist options
  • Save psylone/84eb4d50e0922d87f539 to your computer and use it in GitHub Desktop.
Save psylone/84eb4d50e0922d87f539 to your computer and use it in GitHub Desktop.
Задания к занятию 7

Задания к занятию 7

  • Стандартная библиотека Ruby Std-lib
  • Использование сторонних Gem-ов (библиотек)
  • Dependency Injection

Стандартная библиотека Ruby Std-lib

Как правило, языки программирования реализуют фундаментальные концепции и включают в себя основные функциональные возможности. В их числе: типы данных и общие функции для работы с ними, управляющие конструкции, возможности для определения собственных типов данных, функций и методов. Такой подход позволяет оставаться языку программирования достаточно компактным и вместе с тем предоставлять все возможности для решения множества задач из самых различных предметных областей. Это сродни строительным блокам или брускам пластелина, из которых вы можете создать то что вам требуется.

Однако, часто даже в различных предметных областях встречаются похожие задачи. Например, не зависимо от природы задачи, нам скорее всего потребуется возможность сетевого соединения и получения данных по сети, работа с данными в формате 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

Использование сторонних Gem-ов (библиотек)

Не смотря на широкие возможности 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

Dependency Injection

На самом деле, такая техника (шаблон проектирования) довольно естественно реализуется в 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.

1. Методы ActiveSupport API

Для класса Numeric:

  • Метод, возвращающий объект класса ActiveSupport::Duration, представляющий n часов, n минут, n секунд

  • Метод, возвращающий экземпляр класса Time, со значением времени через n секунд от текущего момента

  • Метод, возвращающий экземпляр класса Time, со значением времени n секунд тому назад от текущего момента

Для класса String:

  • Метод, возвращающий константу по имени строки

  • Метод, возвращающий множественное число для значения строки

  • Метод, возвращающий строку без подстрок совпавших по шаблону который передан в метод в качестве аргумента

  • Метод, проверяющий, стостоит ли строка лишь из пробельных символов

  • Метод, заменяющий символ _ на -

Для модуля ActiveSupport::Configurable:

  • Метод для доступа к упорядоченному хэшу, хранящему конфигурацию (пользовательские настройки) для класса

2. FizzBuzz

  • Капитан, нам только что сообщили! Специалисты из отряда шифрования смогли вычислить условия запуска гиперпространственных ворот.

  • Хм... Тогда почему в вашем голосе я слышу ноты досады, лейтенант?

  • Всё дело в том что мы смогли узнать лишь условия. Необходим алгоритм, который позволит сформировать правильную последовательность символов.

  • Что ж, тогда я думаю нам лучше не терять времени. Что за условия?

  • Там целые числа от 1 до 100. Нужно вывести их таким образом, что если число делится нацело на 3, вместо него выводить 'Fizz'. Если делится нацело на 5 - выводить 'Buzz'. Если соблюдаются оба этих условия, выводить 'FizzBuzz', а если ни одного условия не соблюдается - выводить само число.

  • Каким же странным сознанием была наделена эта цивилизация?

  • Не знаю, Капитан, но в том что мы смогли расшифровать сказано, что для представления условий необходимо использовать каррирование.

  • Что-нибудь известно о том какой язык возможно использовать? Может быть, Java или Python?

  • Никак нет, Капитан. На одной из створок ворот мы обнаружили гравировку с несколькими строками кода Ruby.

  • Всё ясно. Похоже нам крупно повезло: лучшие профессионалы как раз сейчас изучают Ruby. Они большие молодцы, и хотя не все темы ещё пройдены, я уверен что они с этим справятся!

3. Максимальная сумма

Существует массив числовых элементов. Напишите метод maximum_sequence, который возвращает непрерывный подмассив, содержащий хотя бы один элемент, сумма элементов которого максимальна. Например:

origin = [-1, -13, -2, 1, -3, 4, -1, 2, 1, -5, 4]

maximum_sequence(origin) # => [4, -1, 2, 1]

4. Соловьиная трель

Используя Инъекцию Зависимостей напишите класс 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. Не забудьте послушать песни в файлах :)

5. Benchmark rescue nil

Проведите небольшое расследование и выясните что работает быстрее, классическая схема обработки исключения:

begin
  # Buggy code
rescue
end

или обработка исключения в одну строку:

# Buggy code rescue nil

Контакты для связи

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment