Created
July 19, 2017 00:59
-
-
Save sj26/6aaad62abc1fd7e1c6fa004cba412f82 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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