Today I will share my set of not always obvious features and features of Active Record that I encountered during the development of Ruby on Rails applications or found in other people's blogs.
Validation bypass when using update_attributes')
The standard update_attributes method does not have a key to bypass validation, so you have to resort to assign_attributes followed by save
@user = User.find(params[:id]) @user.assign_attributes(:name, "") @user.save(validate: false)
Of course - it is better not to resort to this method very often :)
Division into 2 non-intersecting collectionsSometimes there is the task of dividing the selection of objects into 2 non-intersecting collections. You can do this by using this scope.
Article < ActiveRecord::Base scope :unchecked, where(:checked => false)
Well, accordingly, access to both collections can be obtained by
Article.unchecked Article.unchecked2(@unchecked_articles)
pluckIn the previous example, I used the pluck method. Surely each of you used something like
Article.all.select(:title).map(&:title)
or even
Article.all.map(&:title)
So - pluck makes it easier
Article.all.pluck(:title)
Access to the base classIn the process of working on one project, I was faced with a great nesting of classes of models and the need to get to the root class. The classes looked like this:
class Art < ActiveRecord::Base end class Picture < Art end class PlainPicture < Picture end
In order to get from PlainPicture to Art you can use the method becomes
@plain_pictures = PlainPicture.all @plain_pictures.map { |i| if i.class < Art then i.becomes(Art) else i end }.each do |pp|
first_or_create and first_or_initializeAnother great method is first_or_create. From the title it is clear what he does, and we let's see how it can be used
Art.where(name: "Black square").first_or_create
We can also use it in block construction.
Art.where(name: "Black square").first_or_create do |art| art.author = "Malevich" end
And if you do not want to save - you can use first_or_initialize for example in this way
@art = Art.where(name: "Black square").first_or_initialize
scoped and none
Pay attention to 2 more remarkable methods - scoped and none. How they work - I will show by example, but I want to note that it is necessary to separate their behavior in rails3 and rails4, since it differs.
def filter(filter_name) case filter_name when :all scoped when :published where(:published => true) when :unpublished where(:published => false) else none end end
How the method behaves if it is transferred to it: published and: unpublished. I hope you understand it, there are no differences in the rails versions.
Using scoped in our example in the case of rails3 allows you to create an anonymous skop that can be used for complex compound queries. If you try to apply it in rails4, you can see the message that the method has become deprecated and it is proposed to use Model.all instead. In the case of rails3, Model.all returns not ActiveRecord :: Relation expected by us, but Array.
The situation with none is similar to scoped exactly the opposite :) This method returns an empty ActiveRecord :: Relation, but it works only in rails4. It is needed if you need to return zero results. For use in rails3 there is such a workaround:
scope :none, where(:id => nil).where("id IS NOT ?", nil)
or even such (for example in initializer)
class ActiveRecord::Base def self.none where(arel_table[:id].eq(nil).and(arel_table[:id].not_eq(nil))) end end
find_eachThe find_each method is very convenient in order to process a large number of records from the database. One could of course make a sample of
Article.where(published: true).each do |article|
But in this case we will have to keep the entire sample in memory in its entirety, which is very unprofitable in the case of a large amount of data. In this case, it would be better to use this approach.
Article.where(published: true).find_each do |article|
which in small samples (1000 objects at a time by default) processes the data.
to_sql and explainTwo methods to help you figure out how your query works.
Art.joins(:user).to_sql
will return to you the sql query that the application will compile for asking for a database, and
Art.joins(:user).explain
will show technical information on request - approximate amount of time, sample size and other data.
scopingThis method allows you to make a sample within the sample, for example
Article.where(published: true).scoping do Article.first end
will request type
SELECT * FROM articles WHERE published = true LIMIT 1
mergeAnother interesting method that allows you to cross several samples. for example
class Account < ActiveRecord::Base
allows you to make a selection of all accounts in which there are unread messages.