kbook


Project maintained by atilla777 Hosted on GitHub Pages — Theme by mattgraham
Главная страница
Ruby

Ruby

Основы Ruby

Структура кода

class Класс
  extend Модуль1
  include Модуль2

  CustomErrorKlass = Class.new(StandardError) # вложенные классы

  КОНСТАНТА = 20

  attr_reader :переменная

  def self.some_method
  end

  def initialize
  end

  def some_method
  end

  protected

  private
end

Присваивание значений

При присваивании переменной значения в переменную не помещается указанное значение, а с переменной связывается объект (другими словами хранит не объект, а ссылку на него).

a = [1, 2]
b = a
a << 3
b # содержит [1, 2, 3]

Присваивание значений нескольким переменным

a, b = 1, 2

Присвоение значения если значение еще не присвоено (memoization)

переменная ||= значение

или, в случае когда создание значения по умолчанию требует нескольких строк кода

переменная ||= begin
  код возвращающий значение
end

Если необходимо сделать memoization для значений по умолчанию nil или false (приведенный выше способ в таких случаях работать не будет) можно использовать способ memoization через singleton

class Класс
  переменная = метод
  def метод
    def self.метод
      @переменная
    end
    @переменная = значение
  end
end

В коде выше при первом вызове метода (присвоение значение переменной) создается синглтон метод объекта Класса, с тем же именем что и вызванный метод, после чего переменной экземпляра Класса присваивается значение по умолчанию.

Если, для получения значения переменной метод вызывается не в первый раз, то будет вызваться уже синглтон метод (он переопределил старый метод при его первом вызове), который просто возвращает, установленное в первый раз значение переменной класса.

Присвоение значения если значение присвоено

переменная &&= значение

Изменение значения если значение присвоено

переменная &&= значение.метод

Присваивание значений, возвращаемых методом, сразу нескольких переменных

переменная1, переменная2 = метод

Типы

Динамическая типизация - тип переменной не определяется до помещения в нее значения (другими словами у переменных нет типа, тип есть только у конкретных значений - объектов в случае Ruby).

В Ruby используется вид динамической типизации - Duck Typing.

Duck Typing это когда тип объекта определяется по тому что он умеет делать (имеющиеся у него методы).

Duck typing не заменяет Type Safety.

Type Safety реализуется проверкой данных на границы системы, например проверка параметров метода.

Для проверки данных в ruby можно использовать гем dry-validation.

Числа

Числовые типы данных (все числа наследуют от класса Numeric)

Для создания числа BigDecimal

require bigdecimal
Bigdecimal.new(0.1)

или

0.1E1

Еще есть

Что бы убрать пробелы в начале каждой строки можно использовать метод #strip_heredoc из ActiveSupport

переменная = <<-SQL.strip_heredoc
  SELECT *

или метод ruby #squish

переменная = <<-SQL.squish
  SELECT *

С версии 2.3 вместо #strip_heredoc можно указать перед закрывающей строкой ~

переменная = <<~SQL # вместо SQL может быть другая строка
  SELECT *
   FROM users
    ORDER BY name
  SQL

где ~ убирает все пробелы в отступах левее закрывающего символа SQL (в примере сверху выделены красным, ~ можно не указывать)

Для добавления строк лучше использовать « а не + :

переменная << 'строка'

Замена подстроки/символа – шаблон подстрока/символ

переменная.sub(‘подстрока’, ‘подстрока’)

Замена каждого символа соответствующему в первом аргументе, на соответствующий ему символ во втором аргументе шаблон подстрока/символ

переменная.tr(‘символы’, ‘символы’)

Массивы

Короткие массивы из строк лучше определять таким способом

массив = %w(строка1 строка2 строка3)

аналогично массив из символов

массив = %i(:символ1 :символ2)

Хэши

Для извлечения значения из хэша лучше использовать fetch с указанием значения по умолчанию

хэш.fetch(ключ, значение по умолчанию)

С версии 2.3.0 безопасное извлечение по ключу (вернет nil, если ключ не найден) выглядит так (в хэше ищется значение с ключем1, затем в найденном значении, которое также является хешем ищется значение с ключем2 и т.д.):

хэш.dig(:ключ1, :ключ2, :ключN)

Вычисления вычисляемых значений по умолчанию в fetch лучше указывать в блоке

хэш.fetch(ключ) {вычисление значения по умолчанию}

Извлечение нескольких значений по ключам

переменная1, переменная2 = хэш.values_at(ключ1, ключ2)

Время и дата

Экземпляр класса Time хранит и дату и время (внутри это время в секундах с 1 января 1970 г.).

Текущие дата и время

Time.new

или

Time.now

Указать конкретные дату и время (локальное время)

Time.new(массив)

или в секундах от 1 января 1970

Time.at(секунды)

где массив это [год, месяц, число, час, минута, секунда]

Указать конкретные дату и время (utc или gmt)

Time.utc(массив)

или

Time.gm(массив)

Для получения обратно массива

время.to_a

При вычитании из одного объекта Time, другого будет получена разница в секундах

Time.new(1977, 18, 4, 5, 5,5) - Time.new(1977, 18, 4, 5, 5,5)

К экземпляру можно добавлять секунды, результатом будет новый объект Time

Time.new(1977, 18, 4, 5, 5,5) + 100

Получить название timezone (для UTC - результат будет UTC)

время.zone

Получить смешение в секундах от UTC timezone (для UTC - результат 0)

время.utc_offset

С версии 1.9 Time по функционалу практически тоже, что и DateTime.

Отличия Time и DateTime

Возможные даты

Стандартный Time в Ruby работает с UTC и определенной в ОС часовым поясом, а Ruby on Rails расширяет его функционал до работы с различными часовыми поясами.

ActiveSupport::TimeWithZone расширяет Time, Date и DataTime (напрямую с классом TimeWithZone работать не надо, все операции выполняются через методы in_time_zone, Time, Date и DataTime).

Использование Time и DataTime в Ruby on Rails, то есть с расширением ActiveSupport::TimeWithZone -

Классу Time добавляются методы current и zone, а экземпляру in_time_zone.

Также добавляются методы типа 1.day.ago и т.п.

Текущая зона (согласно настройкам приложения Rails)

Time.zone

Получить время для заданной зоны

Time.zone = зона
Time.zone.now

Получить время в заданной зоне

Time.new(1977, 18, 4, 5, 5,5).in_time_zone(зона)

Локальные время и дата в приложении (с учетом настроек Rails)

Time.current
Date.current

Локальные время и дата в ОС

Time.now
Date.today

Операторы сравнения

Для сравнения объектов используется миксин Comparable, который подмешивается в класс сравниваемых объектов.

В этом миксине используется метод <=>, через который реализуются все остальные операторы сравнения: <, <=, ==, >=, > и метод between?, которые возвращают true или false.

Результат при сравнении левого операнда с правым методом<=>:

Для того что бы в пользовательском классе реализовать методы сравнения

Class
  include Comparable
  def <=>(объект)
    код выдающий -1, 0, 1 при сравнении
    значений self.величина и объект.величина
  end
end

где величина - это тот параметр, по которому объекта класса будут сравниваться между собою

Операторы определения равенства 1) операнды один и тот же объект (см. объект.object_id) equal?

2) значения операндов и их классы равны eql?

3) значения операндов равны (с учетом неявного приведения типов)

4) аналог оператора = =, который может сравнить строку с шаблоном в регулярном выражении.

Оператор === проверяет, что

Если объект2 это Класс, то результат может быть неожиданным, например в when может проверяться что объект1 это экземпляр объекта2 (Класса) – Класс.is_a?(объект1).

Например

String === 'строка' # вернет true

что аналогично

'строка'.is_a?(String) # вернет true

Если объект1 и объект2 это объекты, то поведение === аналогично ==.

Вместо сравнения с нулем, пустой строкой вместо оператора == лучше использовать соответствующие методы

объект.zero? # вместо объект == 0
объект.empty? # вместо объект == ''

Условия

Условный однострочный оператор

условие && действие

или

действие if условие

Условный однострочный оператор (действие в случае если условие не выполняется)

условие || действие

или

действие unless условие

Условный однострочный оператор с альтернативным действием

условие ? действие : альтернатива

Защитное выражение в методе (код метода не выполняется, если условие не выполняется)

def метод
  return unless условие
   # код
end

Циклы

Бесконечный цикл

loop do
  # код
end

Для прерывания цикла можно использовать break.

Классы

Посмотреть предков класса

Класс.ancestors

Посмотреть ID объекта

объект.object_id

Просмотреть класс объекта

объект.class

Просмотреть родительский класс класса объекта

объект.class.cuperclass

Просмотреть методы класса объекта, специфичные для него

объект.methods  объект.class.superclass.methods

Класс определяемый внутри другого класса лучше определять внутри отдельного файла в папке с именем класса (класс/вложенный_класс.rb), при этом определение вложенного класса определяется так

class Класс
  class ВложенныйКласс
  end
end

Вызов метода суперкласса

class Класс < СуперКласс
  def initialize
    super(параметры)
  end
end

Если класс служит только для хранения набора переменных экземпляра его целесообразно представить в виде новой структуры (Struct)

Person = Struct.new(:переменная1, :переменная2)

или с методом

Person = Struct.new(:переменная1, :переменная2) do
  def метод
  end
end
объект = Person.new (атрибут1, атрибут2)

Структуры часто применяют внутри классов для группировки атрибутов, относящихся к одной области.

OpenStruct аналогичен Struct, за исключением того, что позволяет “на лету” определять свойства и значения объекта

require 'ostruct'
s = OpenStruct.new
s.переменная = значение

Методы

Создание метода типа объект[параметр]

def [](параметр)
end

Модули

Модули используются для 1) Размещения функций и констант, которые будут использоваться в различных местах кода

module Модуль
  MY_CONSTANT=1
  def self.функция
    # …
  end
end

Модуль могут быть вложены один в другой

module Модуль1
  module Модуль2
  end
end

или так

module Модуль1
end
module Модуль1::Модуль2
end

Для использования файла с модулем

Для защиты пространства имен код в файлах-библиотеках лучше оформлять в виде модулей.

Для включения в код таких файлов используется директива require

require 'файл_с_модулем'

Файл для загрузки через require будет искаться в папках из переменной окружения $LOAD_PATH.

Для того что бы require искал файлы с кодом, например в папке lib проекта, перед require используется следующий код

$LOAD_PATH << './lib'

или с 1.9.2 (с этой версии в $LOAD_PATH не попадает текущая директория) require_relative (ищет файлы относительно файла из которого вызывается require_relative)

require_relative 'файл_с_модулем'

Require не вызовет загрузку кода из файла, если код из этого файла ранее в скрипте загружен оператором require.

В отличии от него оператор load ‘файл’ загружает файл каждый раз при вызове load.

2) Создания Миксинов (вместо множественного наследования) – добавление методов определенных в модуле в различные классы (добавление методов экземпляра в класс)

module Модуль
  def функция
  end
end

для использования файла с модулем

requiry 'модуль'
class Класс
  include Модуль
end

Добавить методы класса с помощью include в класс нельзя, для этого используется extend (метод может применяться к объектам в том числе и классам, в отличии от **include)

class Класс
  extend Модуль
end

или

Класс.extend Модуль

Для эмуляции include через extend (реализуется тем, что при создании объектов класса всегда вызывается initialize):

class Класс
  def initialize
    extend Модуль
  end
end

Для того что бы методы подмешиваемого к классу модуля искались прежде методов класса (переопределяли методы класса) используется директива

class Класс
  prepend Модуль
end

3) Добавления методов модуля в объект, т.е. создание синглтон методов (добавление методов в eigenclass объекта)

obj.extend MyModule

Класс в Ruby это «усовершенствованный модуль» (родителем Class является Module)

Для того что бы можно было использовать определенные в модуле методы (методы, определенные в модуле без явного указания приемника – то есть не def self.метод) без его подмешивания в класс используется module_function

module Модуль
  module_function
  def метод
  end
end

после чего метод можно использовать

Модуль.метод

Этого же результата можно достичь следующими способами (но так делать не рекомендуется) 1)

module Модуль
  class << self
    def метод
    end
  end
end

Запись class « объект означает выполнение последующего кода в контексте объекта — в данном случае Модуля.

2)

module Модуль
  extend self
  def метод
  end
end

Если определенные таким способом методы модуля «подмешать» в класс (директивой include), то такие методы станут private методами экземпляров этого класса.

Глобальные переменные ($переменная) лучше заменять переменными модуля

module Модуль
  class << self
    attr_accesor :переменная
  end
end
Модуль.переменная

Консольный ввод-вывод

Простейший ввод данных из строки

ввод = gets.chomp

Использование библиотеки Readline

Библиотека предоставляет пользователю историю введенных строк, автодополнение.

require 'readline'
Readline.readline('строка приглашения', true)

где true означает сохранение введенной пользователем строки в истории.

Потоки ввода-вывода

IO объекты могут быть

Константы STDOUT (puts), STDIN (gets), STDERROR (warn) ссылаются на соответствующие объекты ввода-вывода IO.

Для открытия файла

файл =File.new('путь', 'режим')

Для определения пути вместо строки лучше использовать pathname

require 'pathname'
path = Pathname.new('строка')

Где режимы

Для закрытия файла

файл.close

Альтернативным способом является использование метода open – если в качестве параметра этому методу передать блок, то файл автоматически закроется после выполнения кода блока

File.open('путь','режим'){|файл| код}

При этом если файл отсутствует он будет создан методом open.

Большие файлы лучше считывать построчно методом

File.foreach(‘путь’,’режим’){|строка| код}

Создать массив из строк файла

файл.readlines

Чтение файла

файл.read

или в блоке

File.open(‘путь’,’режим’){|файл| файл.read}

У экземпляра file (как и любого объекта класса IO) существуют следующие методы и итераторы

Чтение из файла

Добавления в файл

файл.puts ['строка1', 'строка1'] # добавление в файл строк
файл.print  'строка' # добавление в строку файла текста
файл.write 'строка'  # добавление в строку файла текста
файл << 'строка' # добавление в строку файла текста

Работа с путями файлов

Константа с именем файла исполняемого в настоящее время ruby скрипта:

__FILE__

Получение полного пути к файлу:

File.expand_path(файл)

Преобразование перечня имен папок в путь (в виде специфичном для используемой ОС):

File.join(‘папка’, ’папка’)

Получение полного пути и имени директории файла (папка/файл – файл):

File.dirname(«путь/файл»)

файл.gets
файл.read(количество считываемых символов)
файл.chomp

Перемещение по файлу

Текущая позиция в файле – файл.pos Установить позицию – файл.pos=(символ) Установить позицию на начало файла – файл.rewind Текущая строка – файл.lineno Проверка текущей позиции на конец файла – файл.eof?

Информация о файле

файл.stat файл.stat.size

Управление файлами

Переименование файла – File.rename Удаление файла – File.delete Проверка что файл — это обычный файл – File.file? Проверка доступности файла на чтение – File.readble?

Использование модуля FileUtils

require fileutils

FileUtils.copy
FileUtils.move
FileUtils - cd, chmod, chown, pwd, ln, touch, mkdir, rmdir

Работа с папками
Dir[«/папка/*»]  массив из имен всех файлов папки
Dir.pwd  текущая папка
Dir.chdir(путь)
Dir.entries
Dir.mkdir(имя)
Dir.delete(имя)

Обработка исключений

begin
	код в котором контролируется появление исключений
rescue Исключение
	код выполняемый при возникновении исключения
else
   	код выполняемый при отсутствии исключения
ensure
	код выполняемый всегда
end

Если параметр Исключение в rescue не задан, по умолчанию будут отлавливаться ошибка StandardError Если в качестве параметра указать Exception, то будут перехватываться все исключения (Exception является родительским классом для всех остальных исключений). В приложении лучше перехватывать только StandardError, так как нет смысла обрабатывать ошибки синтаксиса, прерывания и т.п. (т.е. все Exception) в приложении.

Для того что бы передать сведения об ошибке в код выполняемые в rescue используется следующая конструкция:

rescue Исключение => переменная
	Код использующий переменную
End

Если одни и те же исключения обрабатываются несколько раз их обработку лучше обернуть в метод:

def метод
   yield
   rescue IOError
обработка ошибки класса IOError
end

метод { контролируемый код }

В Ruby catch и throw используются для перехода от места вызова throw к выполнению кода, следующего за концом блока catch (аналог goto):

catch :метка  do
	код1
	throw :метка if условие
	код2
end

код3 # если сработает throw, то вместо кода 2 будет выполнен код3

Протоколирование

Для протоколирования событий приложения используется библиотека Logger. Создание объекта – логера: require ‘logger’ журнал = Loger.new(вывод, level: уровень, progname: ‘программа’)

где вывод – куда протоколировать событий – STDOUT уровень – :info, :debug и т.д. progname – имя программы ruby (действия которой протоколируются)

Запись сообщения в журнал: журнал.уровень(‘сообщение’)

Создание CLI приложений require ‘thor’

Структуры данных

Массив следует использовать для хранения данных, если: требуется их упорядоченное хранение (сортировка)

Массив не следует использовать если: требуется осуществлять проверку наличия элемента в массиве и/или обращение к произвольному элементу массива по его значению. В таком случае в качестве структуры данных лучше использовать: 1) Set – как структуру (неупорядоченный набор неповторяющихся записей) с фиксированным временем поиска наличия элемента (можно использовать с #each, #include?, #map). 2) хэш – как структуру с фиксированным временем поиска элемента по ключу.

require ‘set’ a = Set.new [1, 2, 3] a.include? 2

Метапрограммирование в Ruby

  1. Объект Объект представляет собой совокупность переменных экземпляра и ссылок на методы экземпляра в классе. Объекты взаимодействуют между собой через методы.

  2. Переменные экземпляра Переменные экземпляра уникальны для каждого экземпляра. Если добавить в экземпляр переменную она будет только у конкретного экземпляра. А вот методы у всех экземпляров общие и определены в классе.

  3. Класс как объект Класс – это тоже объект класса Class. Суперклассом класса Class (его родительским классом) является класс Module. В ruby имена, начинающиеся с большой буквы, являются константами. Имена классов – это константы. Важно понимать, что в ruby весь код в тексте программы, кроме кода в определении методов def, исполняется сразу. То есть при определении класса код в нем будет выполняться сразу, а не использоваться как «шаблон» при создании экземпляра.

Методы класса Все методы определенные в классе как self.define или Класс.define являются методами класса (т.е. при их вызове приёмником будет класс): сlass Класс def self.метод … end end

или сlass Класс class « self def метод … end end

end

Вызывается такой метод следующим образом: Класс.метод

И еще раз – так как ruby интерпретируемый язык, то определенный в классе код выполняется как только встречается в тексте программы, кроме заключенного в def кода методов.

Приватные методы (private) не могут быть вызваны с явным указанием приемника, приемник должен быть неявным (а это в таком случае всегда self экземпляра в контексте которого выполняется код) то есть: class Класс private def priv_met end self.priv_met # не сработает priv_met # сработает end

obj= Класс.new obj.priv_met # не сработает

protectd методы - методы который могут быть использованы потомками одного класса.

Методы синглтоны – это методы, имеющиеся только у одного объекта. Методы класса – это методы синглтоны, так как они имеются только у одного объекта – Класса. Синглтон методы могут также быть у модуля. Метод синглтон у объекта можно создать так:

объект = ’строка’ def объект.метод … end

или в контексте класса eigenclass: class « объект def метод … end end

При создании метода синглтона между объектом и его классом создается класс eigenclass (eigen, айген – собственный), который содержит добавленный метод синглтон. Суперклассом класса eigenclass является класс объекта. Для того что бы попасть в контекст класса eigenclass используется следующая конструкция: class « объект код в контексте eigenclass end

где объектом может быть как экземпляр класса так и Классом

Обобщая:

Hook Method – методы объектов класса Class или методы класса Module (то есть эти методы имеются у всех модулей, классов и экземпляров класса), вызываемые при определенных событиях. По умолчанию Hook Method методы ничего не делают, но их можно переопределить. Например, вызов определенных действий при событии – наследование класса:

class A def self.inherited(subclass) … end end

определенный выше метод будет вызван при наследовании:

class B < A … end

Другие Hook Method: включение модуля в класс – included(модуль). Пример автоматического вызова и создание в классе методов класса, определенного в модуле ClassMethods, при включении модуля М в класс С:

module M def self.included(klass) klass.extend(ClassMethods) end module ClassMethods def метод_класса ‘a class method’ end end end

class C include M end

Переопределение методов. При переопределении методов, новый метод полностью заменяет старый. При переопределении метода в суперклассе переопределятся методы всех субклассов. При переопределении методов субкласса методы суперкласса не изменяются. Переопределяющий метод (метод субкласса) может вызывать функционал переопределяемого метода (метода суперкласса) через super: class A def met end end

class B < A def met super end end

Модули Модули служат для разделения пространств имен. Для избегания конфликтов имен константы (в том числе классы и другие модули) следует помещать в модули. Обращение на константу в модуле осуществляется следующим образом: Модуль::Константа

Где константой может быть КОНСТАНТА, Модуль, Класс.

Для того что бы сделать метод доступным в любом месте программы его надо определить в модуле Kernel: module Kernel def метод … end end

::Константа – сошлется на самый верхний уровень

Классы и модули, определенные в модуле будут доступны только по пути (если не использовать include Модуль): Модуль::Класс Модуль:: Модуль

Класс Object (класс от которого наследуется все объекты) включает в себя модуль Kernel, который включает в себя все основные методы (функции языка) ruby (puts и т.д.) которые доступны всем потомкам Object, то есть доступны во всех контекстах программы (в том числе и в объекте main).

Прокси классы При включении модуля в класс с помощью инструкции include создается анонимный класс, который будет суперклассом класса, в который включается модуль. При этом методы модуля, теперь становятся методами анонимного класса (его еще называют прокси-классом) и доступны всем потомкам класса, включившего в себя модуль. Для добавления в класс методов модуля: module Модуль def метод … end end

class Класс include Модуль end

Расширение Класса - если в класс необходимо добавить методы класса из модуля, т.е. создать для класса eigenclass и включить в него синглтон метод: module Модуль def метод … end end

class Класс class « self include Модуль end end

или с помощью специального метода extend: class Класс extend Модуль end

или Класс.extend Модуль

Расширение Объекта - если в объект (экземпляр класса) необходимо добавить синглтон методы из модуля: class « объект include Модуль end

или с помощью специального метода extend: объект.extend Модуль

  1. Методы Для вызова метода нужен приемник – объект, метод которого вызывается и цепочка наследования. При вызове метода приемник получает роль self. Self это контекст текущего объекта в котором выполняется код ruby. Переменные экземпляра принадлежат экземпляру и всегда относятся к self. Если у метода не указан приемник, то приемник self (вызов метода с неявно указанным приемником). Приемник можно указать явно (для не приватных методов): self.метод объект.метод Приемник нужно указать явно в случае когда метод используется для присвоения значения, то есть если есть метод: def метод=(параметр) end

Надо вызвать его так: объект.метод = значение или self.метод = значение

Иначе метод нельзя будет отличить от присвоения значения локальной переменной.

Приемник нельзя указать явно для приватных (private) методов (в этом случае приёмник всегда текущий экземпляр класса, то есть – self). При поиске вызванного у объекта метода вызванный метод последовательно ищется в цепочке классов объекта: 1) сначала в eigenclass (если такой имеется), 2) затем в классе, 3) затем у суперкласса класса и т.д., включая методы из анонимных классов (прокси-классов), из модулей подключенных к классам объекта. 4) Если метод найден не будет, то вызовется метод модуля Kernel method_missing.

Динамическая отправка метода (Dynamic Dispatch) это способ вызова метода, при котором имя вызываемого метода определяется динамически. Реализуется это с помощью методов send (вызывает и приватные и публичные методы) или public_send (вызывает только публичные методы, с ruby 1.9): объект.send(:метод) объект.public_send(:метод)

Динамическое создание метода (Dynamic Method) это когда метод создается в процессе выполнения кода класса: define_method метод { | параметры создаваемого метода | код }

Динамическое создание метода класса: define_singleton_method метод { | параметры создаваемого метода | код }

Динамически созданный метод можно сделать приватным: private :метод

Пример использования (создание методов типа attr_accessor): class Суперкласс def self.метод1(параметр метода1) define_method(:метод2) do |параметр метода2| код end end end

Динамическое создание методов по типу attr_accessor (Pattern Dispatch или Class Macros): class Суперкласс def self.метод1 def метод2 … end end end

и в классе потомке: сlass Класс < Суперкласс метод1 # будет вставлен метод2 end

или class Суперкласс def self.метод1(параметр метода1) define_method(:метод2) do |параметр метода2| … end end end

и в классе потомке : сlass Класс < Суперкласс метод1 :параметр метода1 # будет вставлен метод2 end

Несуществующий метод (Ghost Method): def method_missing(name,*args) end

Псевдонимы методов – создается копия оригинального метода: alias new_method old_method

Вместо ключевого слова alias для создания псевдонима можно использовать метод Module#alias_method: alias_method :псевдоним, :метод

Можно создать псевдоним метода, затем можно переопределить старый метод организовав вызов внутри него старого метода по его новому псевдониму. Достигается это за счет того, что если методу назначен псевдоним, а затем метод переопределен, то псевдоним продолжает ссылаться на старую версию метода (до переопределения). Эта техника называется Around Alias.

def method … end

alias :simple_method, :method def method simple_method … end

alias :extended_method, :method

ActiveSupport в Rails добавляет метод Mudule#alias_method_chain который автоматически задает для оригинального метода два псевдонима – один без улучшения, а другой с улучшением

alias_method_chain :метод, :улучшение

Создаст следующие псевдонимы: метод_without_улучшение метод_with_улучшение

останется только переопределить метод с улучшением def метод_with_улучшение метод_without_улучшение … end

6. Области видимости локальных переменных (Scope Gate) Scope Gate это конструкции языка ruby устанавливающие окружение (ландшафт, привязки) выполнения текущих операторов ruby: module ... end class ... end def ... end Scope Gate ограничивают видимость локальных переменных, но не переменных экземпляра (начинаются с @). Границей видимости переменных экземпляра является объект (экземпляр), на текущий экземпляр указывает self (фактически self выдает информацию о том какой объект является текущим и в контексте какого объекта выполняется программа). Стоит отметить, что весь код ruby исполняется в контексте объекта main.

Flat Scope это область, в которой нет Scope Gate конструкций (class, def, module) и соответственно доступны все локальны переменные области. Реализуется такая область путем замены ключевых слов, используемых для определения классов и методов вызовом функции их создающих: Class.new Module.new define_method

Share Scope это общая область видимости переменных Flat Scope обернутая в Scope Gate. Пример реализации такой области внутри модуля (MyClass и my_method будут иметь общую область видимости переменных): module MyModule MyClass=Class.new do var1=1 end define_method (:my_method) do var1=var1+1 # теперь var=2 end end

Одним из способов смешивания областей видимости является использование метода instance_eval, который позволяет выполнить блок кода в контексте экземпляра, но при этом блоку доступны локальные переменные из области определения блока: a = 1 obj = Objet.new obj. instance_eval{puts a} С ruby 1.9 появился метод instance_exec, который умеет передавать параметр в блок: a = 1 obj = Objet.new obj.instance_exec(‘параметр+’){ | x | puts x + a } # выведет параметр + a

class_eval и module_eval выполняют код соответственно в контексте класса или модуля.
  1. Блоки Блок не является объектом. Блок выполняется в том окружении, в котором он определен (то есть с теми локальными переменными и методами):

def метод a = 1 yeild end a = 2 метод { puts a } # будет выведено 2

Метод который выполняет код блока в контексте экземпляра: instance_eval {блок}

Методу в качестве параметра можно передать блок: метод(параметры) {блок}

Когда в методе исполнение кода дойдет до yield, произойдет вызов кода в блоке, при этом блоку будут переданы параметры, заданные при вызове yield:

def method(param) yield(param) end метод(‘УРА!’) {|word_to_print| puts word_to_print } # выведет УРА!

  1. Lambda и Proc это анонимные функции. Если необходимо представить блок как объект (например, необходимо передать блок как параметр метода и не исполнить его внутри метода через yield, а передать другому методу как параметр) используются объекты класса Proc. В свою очередь Proc объекты бывают двух типов – lambda и proc и создать их можно следующими способами: lamda_obj = lambda{|параметры| блок} proc_obj = Proc.new{|параметры| блок}

или c ruby 1.9: lamda_obj=- > ( параметры){блок} # создаем lambda proc_obj=proc{|параметры| блок} # создаем proc

Вызов на исполнение блока кода в объекте Proc: proc_obj.call(параметры) или lamda_obj.call(параметры)

Отличие lambda от proc: 1) Lambda проверяет количество параметров, переданных при вызове кода блока на исполнение, а proc нет (все параметры lambda являются одинаковыми); 2) Lambda выполняется в контексте lambda и команда return завершит выполнение блока кода в объекте lambda, а команда return в блоке объекта proc завершит выполнение не блока, а того контекста в котором был сделан вызов на исполнение кода в proc (например функции в которой запущен на выполнение код proc). Для автоматического преобразования блока в объект Proc, при передаче блока в качестве параметра методу, последним параметром в определении метода указывается имя параметра начинающееся с &:

def my_method(value, &my_block) other_mehod(&my_block) end

Также приставка & вызывает обратное преобразование объекта Proc в блок:

proc_obj = proc{ puts 1 } obj = Object.new obj.instance_eval(&proc_obj)

Команда & преобразует ее параметр (&параметр) в объект proc, то есть вызывает для своего аргумента метод to_proc (& = параметр.to_proc)

Clean Room – объект который создается только для выполнения блока в его контексте: obj = Objet.new obj.instance_eval{ puts 1 }

Прочее

  1. Смена кодировки в Windows консоли на utf-8: сhcp 65001 при это шрифт надо установить Lucida Console

Уверенный (правильный) код Ruby

Цель данной методики – получить код метода, который будет читаться как некая последовательность действий («история») по выполнению решаемой данным методом задачи.

  1. Этапы проектирования структуры метода и объектной модели приложения 1) Определение сообщений – директив (будущих методов), посылаемых объектам приложения (переменным) для выполнения задачи этого приложения. Название сигналов целесообразно выбирать в терминологии области в которой решается задача. Сигналы используемые в методе должны быть с одного уровня абстракции (капитан не отдает указаний о количестве угля в топку или какой рукой рулевой вращает штурвал, он оперирует направлением и скоростью). 2) Формирование перечня ролей (переменных) которым будут предназначаться сообщения (у которых будут иметься соответствующие методы), то есть которые отвечают за выполнение соответствующих сообщений. 3) Описание в терминах ролей и принимаемых ими сообщений (переменных соответствующих объектов и их методов) последовательности работы метода. 4) Оформление полученного на этапе 3 описания как метода. В идеале роли должны совпасть с имеющимися в приложении классами.

  2. Структура метода 1) Сбор данных 2) Выполнение работы 3) Выдача результата 4) Обработка ошибок

  3. Сбор данных Исходными данными для метода могут быть cследующие внешние по отношению к методу источники: Параметр метода; Константа; Вызов метода класса (непрямой способ сбора данных, большое количество непрямых способов сбора увеличивает вероятность «поломки» приложения при внесении изменений в его структуру).

3.1 Использование встроенных в Ruby преобразований типов

Неявное (implicit) преобразование типов – метод преобразующий тип вызывается автоматически для параметра (объекта) передаваемого какой либо функции. Поменяется когда нам надо быть уверенным, что преобразование будет проведено если такой метод имеется у объекта к которому оно применяется. Пример: class EmacsConfigFile def initialize @filename=”#{ENV[‘HOME’]}/.emacs” end def to_path @filename end end emacs_config = EmacsConfigFile.new File.open(emacs_config).lines.count # к параметру передаваемому File.open автоматически (неявно) будет вызван метод #to_path

Явное (explicit) преобразование типов – метод преобразования явно вызывается у объекта, который преобразуется. Явное преобразование реализуется ядром Ruby, а не объектом к которому оно применяется. Поменяется когда нам надо гарантировано преобразовать в нужный тип. Примеры: объект.to_s объект.to_i

3.2 Условный вызов преобразования типов

def my_open(filename) filename = filename.to_path if filename.respond_to?(:to_path) filename = filename.to_str

end

3.4 Создание собственных преобразований типов def draw_line(start, endpoint) start = start.to_coords if start.respond_to?(:to_coords) start = start.to_ary end

Преобразование созданных программистом типов def draw_line(start, endpoint) start = start.to_coords if start.respond_to?(:to_coords) start = start.to_ary end

Прочее DevKit

DevKit/bin и rubyXXX/bin должны быть прописаны в PATH. Все пути (к DebKit, rubyXXX) должны быть без пробелов, иначе будет вываливаться ошибка неправильной опции RUBYOЗT (первая буква после пробела в пути из PATH будет расцениваться как опция RUYOPT). Перед началом использования в devkit надо выполнить ruby dk.rb init ruby dk.rb install При этом если до этого использовалась другая версия rubyXXX (произошли изменения в confog.yaml) необходимо выполнить: ruby dk.rb install –f

DevKit/bin и rubyXXX/bin должны быть прописаны в PATH.

Паттерны Ruby

Не использовать super! При кастомизации методов родительского класса при инициализации дочернего, вместо вызова super лучше переопределять пустые родительские методы, вызываемые при инициализации родительского класса. Такой подход избавляет от необходимости вызывать метод super при инициализации дочернего класса.

class AClass def initialize код _custom # вызов пустого метода end def _custom # определение пустого метода end end

class BClass < AClass
	def _custom # кастомизация (переопределение) пустого род. метода
	end
end

Так как при инициализации внучатого класса все равно придется вызывать super, в более сложных случаях наследования лучше воспользоваться обратными вызовами из ActiveSupport. require ‘active_support/callbacks’ class AClass include ActiveSupport::Callbacks define_callbacks :метод def initialize код run_callbacks :метод end end def метод end end

class BClass < AClass set_callbacks :метод, after :метод2 #вызов родительского метода после дочернего
	def метод2
	end
end

Замер производительности кода require ‘benchmark’

puts Benchmark.measure { код }

Refinement

Для того что бы monkey patching (переопределение методов базовых классов) ограничивался определенной областью используется Refinement: module Модуль refine Класс do def метод код end end end после чего переопределенный метод будет доступен только в области в которой используется вызов using: using Модуль Класс. Метод

C 2.3 using может указываться внутри определения класса.

Модуль Enumerable Модуль используется (подмешиванием) для создания методов у пользовательских объектов использующихся для хранения коллекций - элементов которые могут сравниваться, сортироваться, перечисляться. При включении модуля Enumerable в пользовательский класс и определении в этом классе метода #each, для экземпляров класса станут доступны методы модуля Enumerable. class Класс include Enumerable

def initialize
	@коллекция = [] # или {}W
end

def each &block
	@коллекция.each {|элемент| block.call(элемент) }
end end При вызове у объекта в класс которого подмешан enumerable некоторых методов, в том числе each, map без параметров будет возвращен экземпляр класса Enumerator. Это дает возможность использовать методы Enumerable по цепочке. К примеру - аналог each with index: ['a', 'b', 'c'].map.each_with_index {|element, index| код}

Методы модуля Enumerable – преобразование в блоке каждого элемента; colleсt или map {|элемент| выражение преобразующее элемент}

поиск элементов, для которых выражение в блоке возвращает true: select {|элемент| выражение с элементом}

поиск первого элемента, для которого выражение в блоке возвращает true: detect {|элемент| выражение с элементом}

проверка что выражение в блоке хотя бы для одного элемента возвращает true: any?{|элемент| выражение с элементом}

проверка что выражение в блоке для всех элементов возвращает true: all? {|элемент| выражение с элементом}

отбросить элементы, для которых выражение в блоке возвращает true: reject {|элемент| выражение с элементом}

формирует аккумулятор последовательно размещая (замещая) в нем результат последнего вычисления (выражения) в блоке: inject или reduce(начальное значение) {|аккумулятор, элемент| выражение с элементом и аккумулятором}

для суммирования элементов можно использовать inject или reduce(:+)

или c 2.3 просто sum

Формирование результата - объекта (аккамулятора) из последовательности: each_with_object(объект) { |элемент, объект| код с объектом}

reduce (inject) и each_with_object отличаются следующим образом: 1) в reduce (inject) аккумулятор (объект) указывается первым параметром в блоке в each_with_object аккумулятор (объект) указывается вторым параметром в блоке 2) в reduce (inject) в аккумулятор в каждой итерации помещается результат последнего выражения в блоке в each_with_object аккумулятор (объект) аккумулятор надо изменять в блоке В итоге reduce (inject) лучше подходит для формирования агегированых результатов действий над элементами последовательности как одного значения (предельный случай такого использование метод sum) each_with_objec лучше подходит для формирования новых последовательностей (например для преобразования массива или хэша или получени нового массива или хэша)

zip

проверка того, что коллекция содержит объект: include? Объект

перебор элементов и получение их индекса: each_with_index {|элемент, индекс| выражение с элементом и индексом}

Объект в который подмешан Enumerable можно сделать ленивым: (1..Float::INFINITY).lazy .select { блок } .to_a .first(10) # вместо бесконечного перечисления, будут перечислены первые 10 элементов

Наваждение примитивов

Использование объектов простых классов (строк, массивов, хэшей), для хранения структур данных имеет следующие недостатки – не очевидно, какие данные хранит объект; необходимость изменить код их использующий при измени внутренней структуры. Что бы избежать этого необходимо использовать имеющиеся (Pathname, URI) или собственные классы вместо примитивов.

Работа с IP Создание объекта - IP адреса (из строки): require ‘ipaddr’ ip = IPAddr.new(’10.1.1.1’)

или из целого числа ip = IPAddr.new(167837953, Socket::AF_INET)

Новое в 2.3.0

  1. «Безопасное» обращение к значению хэша по ключу (аналог try из Active Support) хэш.dig(:ключ1, :ключ2, :ключN)

  2. «Безопасный» вызов метода по цепочке (аналог объект.метод1 && объект .метод1.методN)

объект&.метод1&.метод2&.методN

  1. «Замороженные строки»

Конкурентное программирование

Три основных подхода к конкурентному программированию: 1) использование нескольких отдельных приложений, копий основного приложения (forks). Преимущество – изолированность данных каждого процесса от другого обеспечивает целостность этих данных. Недостаток – каждый процесс содержит одну и туже копию программного кода в памяти. Пример – unicorn. 2) использование нескольких потоков в рамках одного приложения. Преимущество – код программы общий и не дублируется (не происходит перерасходывания памяти). Недостаток – целостность данных не гарантируется (например, параллельно выполняемые потоки могут, не зная о действиях друг друга, одновременно изменять одну и туже область памяти). Этой проблемы нет в MRI (GIL не позволяет потокам работать одновременно (одновременно может осуществляться только ввод-вывод), но тогда теряется возможность утилизировать все ядра процессора). В тоже время rubinius и jruby позволяют утилизировать все ядра (в них нет GIL), при этом не будет гарантироваться целостность данных. Пример – puma. 3) Event-loop Преимущество – способность обслуживать большое количество конкурентных запросов. Пример – thin. В руби поток создается следующим образом: threads « Tyhread.new() do код потока end threads.each(&:join) # основная программа будет ждать завершения всех потоков (.join) — не прекратит работу пока все они не завершатся

Новое в 2.4

  1. Быстрая проверка соответствия (без возврата совпавшего значения): /регулярное выражение/.match?(строка)

  2. Создание хэша именованных совпадений в регулярном выражении: результат = /(?<имя>выражение)/.match.named_captures аналогично, но только результат - массив значений из перечисленных именованных групп: результат = /(?<имя>выражение)/.match.namvalues_at(:ключ1, ключ2)

  3. Проверка пустых папок и файлов: Dir.empty?(папка) File.empty?(файл)

  4. Метод перевода целого числа в массив цифр его составляющих: число.digist

  5. Метод #sum для модуля Enumerable (суммирование элементов массива, значения хэша, конкатенация массива строк): массив.sum

так как по умолчанию начальное значение аккамулятора - 0, для объединения строк его надо указывать явно: массив.sum(‘’)

  1. Запуск отладчика (irb сеcсии) в коде: binding.irb

Новое в 2.5

Новый метод yield_self Метод yield_self улучшает читабельность кода, при вызове методов по цепочке - вместо объект.метод2(метод1(объект))

можно вызывать: объект.yield_self { | объект | результат } .yield_self { | объект | результат }

Отличия между yield_self, try и tap Все три метода передают в блок объект, который является приёмником этого метода. Отличаются они возвращаемым результатом: yield_self – возвращает результат последнего оператора в блоке объект.yield_self { | объект | результат } # вернет результат

try – возвращает результат последнего оператора в блоке, является частью Rails и не вернёт результат выражения в блоке если исходный объект nil nil. try { | объект | результат } # вернет nil

tap – возвращает исходный объект (объект не меняется в блоке) объект.tap { | объект | результат } # вернет объект