Cайт веб-разработчика, программиста Ruby on Rails ESV Corp. Екатеринбург, Москва, Санкт-Петербург, Первоуральск

Админка на Ruby on Rails

Новое в Rails 4: Active Record

Паттерн Null Object

В Rails 4 введен ActiveRecord::QueryMethods.none, который реализует паттерн null oject. Он используется в экземплярах, где у вас есть метод, который возвращает relation, но есть условие, в котором вы не хотите, чтобы был произведен запрос к базе данных. Все последующие условия в цепочке будут работать без проблемы, избавляя от необходимости постоянно проверять, работает ли объект ваш с relation.

Team.none.where(name: 'Justice League')
# => #

 

Для скоупов (scopes) требуются вызываемые объекты

Скоупы (scopes) всегда были поводом расстройства для новых разработчиков Rails. Наибольшая проблема в том, что выражение скоупа, определенного не через lambda, вычисляется при инициализации. Пример этого - базируемые на временных интервалах:

scope :recent, where(published_at: Time.now - 2.weeks)

Любой будущий вызов всегда будет определять записи, опубликованные за 2 недели до начальной инициализации объекта, но не текущего времени.

В Rails 4 все скоупы должны быть вызываемым объектом, таким как Proc или lambda:

class Team < ActiveRecord::Base
  scope :active, -> { where(status: 'active') }
end

 

Убран IdentityMap

Так как у IdentityMap были некоторые несоответствия с ассоциациями, которые никогда не разрешались, он был удален из Rails 4. Это может возвратиться однажды, если проблемы будут устранены. Если Вы обновляете Rails 3 приложения, пожалуйста удалите active_record.identity_map = true из файла конфигурации config/application.rb.

destroy!

В ActiveRecord теперь есть метод #destroy!. Если запись не может быть уничтожена, исключение ActiveRecord::RecordNotDestroyed будет иницировано.

update_columns

В ActiveRecord есть два новых метода #update_columns и #update_column. При вызове этих методов будут обновлены атрибут(ы) модели, пропуская валидацию и вызов callbacks-методов.

@team.update_column(:name, 'Name')
@team.update_columns(name: 'Name', universe: 'Marvel')

 

Model.all

Больше не поддерживается выполнение Model.all, которое исполняется немедленно и возвращает массив записей. В Rails 4 вызов Model.all эквивалентно вызову (ныне устаревшего) Model.scoped. Это подразумевает, что множество зависимостей и условий могут быть объеденены в цепочку Model.all и результат будет вычислен "лениво" (по запросу) (lazily). Model.all возвращает Relation. Если вы все же хотите получить массив записей, используйте Model.all.to_a.

Сортировка по умолчанию добавлена в first

При вызове first в предыдущих версиях Rails в запросе просто добавлялось LIMIT 1. Это не гарантировало того, что будет возвращена запись с наименьшим первичным ключем.

Вызов first в Rails 4 будет добавляь явный порядок сортировки в запросе, что гарантирует, что будет возвращена запись с наименьшим первичным ключем.

Team.first
Rails 3: SELECT "teams".* FROM "teams" LIMIT 1
Rails 4: SELECT "teams".* FROM "teams" ORDER BY "teams"."id" ASC LIMIT 1

 

Устаревшие динамические методы поиска

В предыдущих версиях Rails ActiveRecord обеспечивал метод поиска для каждого поля в вашей таблице базы данных. Например, если у вас есть поле email в модели User, вы можете использовать: User.find_by_email('some@emailaddress.com') или User.find_all_by_email('some@emailaddress.com'). Более того, вы могли объеденить в запросе несколько полей, указав в названии метода and между названиями полей.

Динамические методы поиска объявлены полностью устаревшими в Rails 4.

Примеры замены устаревших динамических методов поиска в Rails 4:

# find_all_by_
Rails 3: Team.find_all_by_name('Justice League')
Rails 4: Team.where(name: 'Justice League')

# find_last_by_
Rails 3: Team.find_last_by_name('Justice League')
Rails 4: Team.where(name: 'Justice League').last

# find_or_create_by_
Rails 3: Team.find_or_create_by_name('Justice League')
Rails 4: Team.where(name: 'Justice League').first_or_create

# find_or_create_by_...!
Rails 3: Team.find_or_create_by_name!('Justice League')
Rails 4: Team.where(name: 'Justice League').first_or_create!

# find_or_initialize_by_
Rails 3: Team.find_or_initialize_by_name('Justice League')
Rails 4: Team.where(name: 'Justice League').first_or_initialize

# scoped_by_
Rails 3: Team.scoped_by_name('Justice League')
Rails 4: Team.where(name: 'Justice League')

Метод поиска основанный на использовании хэша также объявлен устаревшим. Если вы уже использовали новый стиль, представленный в Rails 3, это не должно повлиять на ваш код.

Детали: activerecord-deprecated_finders.

Пример использования вместо устаревшего метода поиска с использованием ActiveRecord::QueryMethods:

Rails 3: Team.find(:all, conditions: { name: 'Name' })
Rails 4: Team.where(name: 'Name')

 

find_by и find_by!

Новые методы поиска (взамен устаревших) были добавлены в Rails 4:

Member.find_by name: 'Batman', city: 'Gotham'
Member.find_by! name: 'Superman'

 

Множество полей с помощью pluck

Метод #pluck позволяет вам выбрать значения одного поля и вернуть результат в виде массива значений полея записей таблицы. В Rails 4 вы можете указать множество полей для выборки, используя #pluck:

Member.pluck(:name, :bio)
# >> [["Superman", "From Krypton"], ["Batman", "From Gotham City"]]

 

References

Метод запроса .includes предназначается для нетерпеливой загрузки ассоциаций. Некоторые разработчики используют .includes метод, чтобы выполнить некоторые запросы LEFT OUTER JOIN на ассоциациях, используя строковые отрывки SQL. В Rails 4, при добавлении строкового условия включенной ассоциации приводит к выдаче предупреждения.

Вот пример, который выбирает все Teams, у которых есть элемент по имени Batman:

Team.includes(:members).where('members.name = ?', 'Batman')

# Rails 3
SQL (0.7ms)  SELECT "teams"."id" AS t0_r0, "teams"."name" AS t0_r1, "teams"."created_at" AS t0_r2, "teams"."updated_at" AS t0_r3, "members"."id" AS t1_r0, "members"."name" AS t1_r1, "members"."bio" AS t1_r2, "members"."team_id" AS t1_r3, "members"."created_at" AS t1_r4, "members"."updated_at" AS t1_r5 FROM "teams" LEFT OUTER JOIN "members" ON "members"."team_id" = "teams"."id" WHERE (members.name = 'Batman')

# Rails 4
DEPRECATION WARNING: It looks like you are eager loading table(s) (one of: teams, members) that are referenced in a string SQL snippet.
...

В то время как запрос все еще выполняется в Rails 4, рекомендуется включать новый метод запроса ActiveRecord::QueryMethods.references вместо этого:

Team.includes(:members).where("members.name = ?", 'Batman').references(:members)

Однако, если вы использовали синтаксис хеша с условиями ассоциации, он все еще выполнит LEFT OUTER JOIN без любых предупреждений:

Team.includes(:members).where(members: { name:  'Batman' })

Кроме того, упорядочивание с помощью строковых отрывков SQL на включенных ассоциациях будет все еще работать без необходимости указания references:

Team.includes(:members).order('members.name')