When you’re trying to make some controllers of your app tinier with agreement to have fatter models, you might think about scope
or writing functions for that Model class. In this post, I wanna write some examples of that Active Record
technique
Problem definition
The problem my rails app is about is:
-
- We have models:
User
,Post
andTag
- User and Post have
1-n
relationship whilePost
andTag
have then-n
relationship - We gonna find
Post
with many constraints relating toUser
andTag
- For
Post
, we havestate:string
- We have models:
Before DRY
In the Post model, we have state
as and is in ['draft', 'published']
. It’s is normal to query posts
by state
like
published_posts = Post.where(state: 'published') draft_posts = Post.where(state: 'draft')
How if you wanna check if a post
published or not
post = Post.where(id: 1).first is_published = post.state == 'published'
And now I wanna get posts of some authors
posts = Post.joins(:authors).where(author_id: params[:author_ids])
or posts with some tag names
posts = Post.joins(:tags).where(tags: {name: params[:tag][:names]})
With some improvements
All are good now, right? hm, it could be even better if we can give some logic to our model
Like this
class Post < ActiveRecord::Base STATES = ['published', 'draft'] belongs_to :author, :class_name => 'User', :foreign_key => 'author_id' has_many :tags, through: :post_tags scope :published, where(state: 'published') scope :tag, lambda{|tags| joins(:tags).where(tags: {name: tags})} STATES.each do |state_name| define_method "#{state_name}?" do self.state == state_name end end end
Then, we can have
posts = Post.tag(params[:tags]).published post = Post .find(params[:id]) post.published?
Happy Coding!