Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added support for STI ... #1

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ end

(The MIT License)

Copyright (c) 2012 Jonas Nicklas, Elabs AB
Copyright (c) 2012 Jonas Nicklas, Elabs AB, Michael Nowak

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
Expand Down
33 changes: 17 additions & 16 deletions ar_outer_join.gemspec
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../lib/ar_outer_joins/version', __FILE__)

Gem::Specification.new do |gem|
gem.authors = ["Jonas Nicklas", "Elabs AB"]
gem.email = ["[email protected]", "[email protected]"]
gem.description = %q{Adds the missing outer_joins method to ActiveRecord}
gem.summary = %q{outer_joins for ActiveRecord}
gem.homepage = "http://github.com/elabs/ar_outer_joins"
Gem::Specification.new do |spec|
spec.authors = ["Jonas Nicklas", "Elabs AB", "Michael Nowak"]
spec.email = ["[email protected]", "[email protected]", "[email protected]"]
spec.description = %q{Adds the missing outer_joins method to ActiveRecord}
spec.summary = %q{outer_joins for ActiveRecord}
spec.homepage = "http://github.com/mmichaa/ar_outer_joins"
spec.license = "MIT"

gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
gem.files = `git ls-files`.split("\n")
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
gem.name = "ar_outer_joins"
gem.require_paths = ["lib"]
gem.version = ArOuterJoins::VERSION
spec.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
spec.files = `git ls-files`.split("\n")
spec.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
spec.name = "ar_outer_joins"
spec.require_paths = ["lib"]
spec.version = ArOuterJoins::VERSION

gem.add_dependency "activerecord", ">=3.2"
gem.add_development_dependency "rspec"
gem.add_development_dependency "sqlite3"
gem.add_development_dependency "pry"
spec.add_dependency "activerecord", ">=3.2"
spec.add_development_dependency "rspec"
spec.add_development_dependency "sqlite3"
spec.add_development_dependency "pry"
end
10 changes: 10 additions & 0 deletions lib/ar_outer_joins/join_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,15 @@ def build
else
table = association.active_record.arel_table
primary_key = association.active_record.primary_key
joined_klass = association.klass
joined_table = association.klass.arel_table

case association.macro
when :belongs_to
on = Arel::Nodes::On.new(table[association.foreign_key].eq(joined_table[primary_key]))
#unless joined_klass.descends_from_active_record?
# on = on.and(joined_table[joined_klass.inheritance_column].eq(joined_klass.sti_name))
#end
[Arel::Nodes::OuterJoin.new(joined_table, on)]
when :has_and_belongs_to_many
join_model_table = if association.respond_to?(:join_table)
Expand All @@ -33,10 +37,16 @@ def build

on1 = Arel::Nodes::On.new(join_model_table[association.foreign_key].eq(table[primary_key]))
on2 = Arel::Nodes::On.new(join_model_table[association.association_foreign_key].eq(joined_table[joined_primary_key]))
unless joined_klass.descends_from_active_record?
on2 = on2.and(joined_table[joined_klass.inheritance_column].eq(joined_klass.sti_name))
end

[Arel::Nodes::OuterJoin.new(join_model_table, on1), Arel::Nodes::OuterJoin.new(joined_table, on2)]
when :has_many, :has_one
on = Arel::Nodes::On.new(joined_table[association.foreign_key].eq(table[primary_key]))
unless joined_klass.descends_from_active_record?
on = on.and(joined_table[joined_klass.inheritance_column].eq(joined_klass.sti_name))
end
[Arel::Nodes::OuterJoin.new(joined_table, on)]
else
raise OuterJoinError, "don't know what to do with #{association.macro} association"
Expand Down
2 changes: 1 addition & 1 deletion lib/ar_outer_joins/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module ArOuterJoins
VERSION = "0.0.1"
VERSION = "0.0.2"
end
110 changes: 110 additions & 0 deletions spec/outer_join_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@

class Product < ActiveRecord::Base
belongs_to :category
belongs_to :sub_category
belongs_to :site
has_many :line_items
has_many :sub_line_items
has_many :baskets, :through => :line_items
has_many :sub_baskets, :through => :sub_line_items, :source => :basket
has_many :discounts, :through => :line_items
has_and_belongs_to_many :tags
has_and_belongs_to_many :sub_tags, :join_table => 'products_tags', :association_foreign_key => 'tag_id'
has_one :image
has_one :sub_image
end

class LineItem < ActiveRecord::Base
Expand All @@ -16,18 +21,24 @@ class LineItem < ActiveRecord::Base
has_many :discounts
end

class SubLineItem < LineItem; end

class Image < ActiveRecord::Base
belongs_to :product
end

class SubImage < Image; end

class Discount < ActiveRecord::Base
belongs_to :line_item
end

class Basket < ActiveRecord::Base; end
class Category < ActiveRecord::Base; end
class Site < ActiveRecord::Base; end
class SubCategory < Category; end
class Tag < ActiveRecord::Base; end
class SubTag < Tag; end

describe ActiveRecord::Base do
describe ".outer_joins" do
Expand All @@ -42,6 +53,25 @@ class Tag < ActiveRecord::Base; end
query.to_a.should =~ [product2, product3]
end

it "performs an outer join with sti" do
category1 = Category.create! :name => "Shoes"
category2 = Category.create! :name => "Shirts"
subcategory1 = SubCategory.create! :name => "Cool Shoes"
subcategory2 = SubCategory.create! :name => "Cool Shirts"
product1 = Product.create! :category => category1
product2 = Product.create! :category => category2
product3 = Product.create! :published => true, :sub_category => subcategory1
product4 = Product.create! :category => category1, :sub_category => subcategory2

query = Product.outer_joins(:category).where("categories.name = ? OR products.published = ?", "Shirts", true)
query.all.should =~ [product2, product3]

query = Product.outer_joins(:sub_category).where("categories.name = ? OR products.published = ?", "Shirts", true)
query.all.should =~ [product3]
query = Product.outer_joins(:sub_category).where("categories.name = ? OR products.published = ?", "Cool Shirts", true)
query.all.should =~ [product3, product4]
end

it "joins several associations" do
site1 = Site.create! :name => "Elabs"
category1 = Category.create! :name => "Shoes"
Expand All @@ -68,6 +98,24 @@ class Tag < ActiveRecord::Base; end
query = Product.outer_joins(:image).where("images.highres = ? OR products.published = ?", true, true)
query.to_a.should =~ [product1, product3]
end

it "performs an outer join with sti" do
product1 = Product.create!
product2 = Product.create!
product3 = Product.create! :published => true
product4 = Product.create!

Image.create! :highres => true, :product => product1
Image.create! :product => product2
SubImage.create! :highres => true, :product => product2
SubImage.create! :product => product1

query = Product.outer_joins(:image).where("images.highres = ? OR products.published = ?", true, true)
query.all.should =~ [product1, product2, product3]

query = Product.outer_joins(:sub_image).where("images.highres = ? OR products.published = ?", true, true)
query.all.should =~ [product2, product3]
end
end

context "with has_many" do
Expand All @@ -83,6 +131,24 @@ class Tag < ActiveRecord::Base; end
query = Product.outer_joins(:line_items).where("line_items.price = ? OR products.published = ?", 4, true)
query.to_a.should =~ [product1, product3]
end

it "performs an outer join with sti" do
product1 = Product.create!
product2 = Product.create!
product3 = Product.create! :published => true
product4 = Product.create!

LineItem.create! :price => 4, :product => product1
LineItem.create! :product => product2
SubLineItem.create! :price => 4, :product => product4
SubLineItem.create! :product => product2

query = Product.outer_joins(:line_items).where("line_items.price = ? OR products.published = ?", 4, true)
query.all.should =~ [product1, product3, product4]

query = Product.outer_joins(:sub_line_items).where("line_items.price = ? OR products.published = ?", 4, true)
query.all.should =~ [product3, product4]
end
end

context "with has_and_belongs_to_many" do
Expand All @@ -99,6 +165,25 @@ class Tag < ActiveRecord::Base; end
query = Product.outer_joins(:tags).where("tags.name = ? OR products.published = ?", "Red", true)
query.to_a.should =~ [product2, product3, product4]
end

it "performs an outer join with sti" do
red = Tag.create! :name => "Red"
blue = Tag.create! :name => "Blue"
green = SubTag.create! :name => "Green"
black = SubTag.create! :name => "black"

product1 = Product.create!
product2 = Product.create! :tags => [red, green]
product3 = Product.create! :tags => [red, blue, black]
product4 = Product.create! :published => true


query = Product.outer_joins(:tags).where("tags.name = ? OR products.published = ?", "Red", true)
query.all.should =~ [product2, product3, product4]

query = Product.outer_joins(:sub_tags).where("tags.name = ? OR products.published = ?", "Green", true)
query.all.should =~ [product2, product4]
end
end

context "with has_many :through" do
Expand All @@ -118,6 +203,31 @@ class Tag < ActiveRecord::Base; end
query = Product.outer_joins(:baskets).where("baskets.purchased = ? OR products.published = ?", true, true)
query.to_a.should =~ [product1, product3]
end

it "performs an outer join with sti" do
product1 = Product.create!
product2 = Product.create!
product3 = Product.create! :published => true
product4 = Product.create!
product5 = Product.create!
product6 = Product.create!

basket1 = Basket.create! :purchased => true
basket2 = Basket.create! :purchased => false

LineItem.create! :product => product1, :basket => basket1
LineItem.create! :product => product2, :basket => basket2
LineItem.create! :product => product3
SubLineItem.create! :product => product4, :basket => basket1
SubLineItem.create! :product => product5, :basket => basket2
SubLineItem.create! :product => product6

query = Product.outer_joins(:baskets).where("baskets.purchased = ? OR products.published = ?", true, true)
query.all.should =~ [product1, product3, product4]

query = Product.outer_joins(:sub_baskets).where("baskets.purchased = ? OR products.published = ?", true, true)
query.all.should =~ [product3, product4]
end
end

context "with nested associations" do
Expand Down
5 changes: 5 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@

ActiveRecord::Base.connection.create_table :products do |t|
t.integer :category_id
t.integer :sub_category_id
t.integer :site_id
t.boolean :published, :default => false, :null => false
end

ActiveRecord::Base.connection.create_table :line_items do |t|
t.string :type
t.integer :product_id
t.integer :basket_id
t.integer :discount_id
Expand All @@ -22,6 +24,7 @@
end

ActiveRecord::Base.connection.create_table :categories do |t|
t.string :type
t.string :name
end

Expand All @@ -35,6 +38,7 @@
end

ActiveRecord::Base.connection.create_table :tags do |t|
t.string :type
t.string :name
end

Expand All @@ -43,6 +47,7 @@
end

ActiveRecord::Base.connection.create_table :images do |t|
t.string :type
t.boolean :highres, :default => false, :null => false
t.integer :product_id
end
Expand Down