Skip to content

Instantly share code, notes, and snippets.

@sj26
Created July 19, 2017 00:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sj26/6aaad62abc1fd7e1c6fa004cba412f82 to your computer and use it in GitHub Desktop.
Save sj26/6aaad62abc1fd7e1c6fa004cba412f82 to your computer and use it in GitHub Desktop.
gem "activerecord", "~> 4.0.0"
require "active_record"
## Implementation
module ActiveRecord::OmitMethods
# Inverse of #merge, negates and merges the conditions from <tt>other</tt>, if <tt>other</tt> is an <tt>ActiveRecord::Relation</tt>.
# Returns an array representing the subset of the resulting records not appearing in <tt>other</tt>, if <tt>other</tt> is an array.
# Post.where(published: true).joins(:comments).omit( Comment.where(spam: false) )
# # Performs a single join query with both where conditions.
def omit(other)
if other.is_a?(Array)
to_a - other
elsif other.is_a?(ActiveRecord::Relation)
spawn.omit!(other)
else
self
end
end
def omit!(other) # :nodoc:
merge!(other.invert)
end
protected
def invert
inverted_where_values = where_values.map do |value|
end
except(:where).where(inverted_where_values)
end
end
ActiveRecord::Relation.send(:include, ActiveRecord::OmitMethods)
## Example
require "sqlite3"
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
class User < ActiveRecord::Base
connection.create_table table_name do |t|
t.string :email
end
has_many :posts
end
class Post < ActiveRecord::Base
connection.create_table table_name do |t|
t.references :user, null: false
t.timestamp :published_at, null: true
end
belongs_to :user
scope :published, -> { where(arel_table[:published_at].lteq(Time.now)) }
scope :scheduled, -> { where(arel_table[:published_at].gt(Time.now)) }
scope :draft, -> { where(published_at: nil) }
end
## Specs
require "rspec/autorun"
describe Post, "#omit" do
let!(:user1) { User.create!(email: "user1@example.com") }
let!(:user2) { User.create!(email: "user1@example.com") }
let!(:user3) { User.create!(email: "user3@example.com") }
let!(:user1_posts) { user1.posts.create!([
{published_at: 3.days.ago},
{published_at: 2.days.ago},
{published_at: nil},
{published_at: 1.day.from_now},
]) }
let!(:user2_posts) { user2.posts.create!([
{published_at: 5.days.ago},
{published_at: 4.days.ago},
{published_at: nil},
{published_at: 3.days.from_now},
{published_at: 11.days.from_now},
]) }
let!(:user3_posts) { user3.posts.create!([
{published_at: 1.day.ago},
{published_at: nil},
{published_at: nil},
]) }
specify { Post.count.should == 12 }
specify { Post.published.count.should == 5 }
specify { Post.published.merge(user1.posts).count.should == 2 }
specify { Post.published.merge(user1.posts).count.should == 2 }
specify { Post.published.merge(user2.posts).count.should == 1 }
specify { Post.scheduled.count.should == 3 }
specify { Post.scheduled.merge(user1.posts).count.should == 1 }
specify { Post.scheduled.merge(user1.posts).count.should == 2 }
specify { Post.scheduled.merge(user2.posts).count.should == 0 }
specify { Post.published.omit(user1.posts).should =~ (user2.posts.published.to_a & user3.posts.published.to_a) }
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment