Блог Ruby-разработчика

ActiveRecord Attributes в Rails 5

| Comments

В Rails 5 много нововведений, одним из них является ActiveRecord Attributes. Эта фича позволяет добавлять аттрибут в модель Rails и обработать его согласно указанному типу. Посмотрим насколько полезен этот новый функционал.

Рассмотрим пример. У нас есть модель пользователей - User. Миграция и сама модель описаны ниже.

1
2
3
4
5
6
7
8
9
10
11
12
13
class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string :name
      t.integer :age
      t.string :male
      t.timestamps(null: false)
    end
  end
end

class User < ActiveRecord::Base
end

Обратите внимание на колонку male, мы могли бы вполне сделать ее с типом boolean, но в данном примере это сделано специально, чтобы показать как мы сможем использовать Attributes.

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

Давайте воспользуемся нововведением. Укажем колонку male как аттрибут, с типом boolean

1
2
3
class User < ActiveRecord::Base
  attribute :male, :boolean
end

Теперь мы можем использовать колонку так, как будто ее тип boolean, например:

1
2
3
4
5
6
7
8
9
10
11
user = User.new(male: 'yes')
user.male
# => true

user = User.new(male: 'f')
user.male
# => false

user = User.new(male: 0)
user.male
# => false

Вот так легко теперь можно решить проблему несовместимости типов данных указанных в таблице БД и требований согласно бизнес логике. ActiveRecord Attributes поддерживает основные типы данных, ниже список:

  • :big_integer
  • :binary
  • :boolean
  • :date
  • :date_time
  • :decimal
  • :float
  • :integer
  • :string
  • :text
  • :time

Аттрибут не обязательно может быть колонкой в таблице, можно применять его и для обычных аттрибутов модели. Добавим для модели User аттрибут confirmed_at, и укажем тип данных :date_time, и все будет корректно обрабатываться. При этом мы не указываем аттрибут через attr_accessor.

1
2
3
4
5
6
7
8
9
10
11
12
13
class User
  attribute :confirmed_at, :date_time
end

user = User.new
user.confirmed_at = '2016-02-12 03:00'
user.confirmed_at
# => Fri, 12 Feb 2016 03:00:00 UTC +03:00

user = User.new
user.confirmed_at = '2016/02/12'
user.confirmed_at
# => Fri, 12 Feb 2016 00:00:00 UTC +03:00

Свой обработчик ActiveRecord Attributes

Допустим у нас есть модель Transaction, и в поле цена, к нам приходит строка в виде $100, а мы хотим сохранять в базу данных значение в центах, и цифрой, а также использовать строку в запросах.

Для решения этой задачи, нам необходимо добавить свой тип данных, и в этом Attributes нам поможет. Создадим класс MoneyType, и напишем свой метод type_cast, который используется для преобразования данных.

1
2
3
4
5
6
7
8
9
10
class MoneyType < ActiveRecord::Type::Integer
  def type_cast(value)
    if value.include?('$')
      price_in_dollars = value.gsub(/\$/, '').to_f
      price_in_dollars * 100
    else
      value.to_i
    end
  end
end

Укажем обработчик для колонки price

1
2
3
class Transaction < ActiveRecord::Base
  attribute :price, MoneyType.new
end

Если все сделали верно, то теперь мы можем делать вот такие запросы, и быть уверенными что все отработает как надо

1
2
Transaction.where(price: '$10.00')
# => SELECT * FROM transactions WHERE price = 1000

Как видите очень удобно и просто. Возьмите на заметку :)

Comments