From e2ceda9e06c2c6a2d9586afe3046db134347f82c Mon Sep 17 00:00:00 2001 From: THExSYSTEM Date: Sun, 9 Dec 2012 00:20:55 +0100 Subject: [PATCH 1/3] added support for STI on has_one, has_many and has_and_belongs_to_many association; added specs for STI-cases --- ar_outer_join.gemspec | 4 +- lib/ar_outer_joins/join_builder.rb | 10 +++ lib/ar_outer_joins/version.rb | 2 +- spec/outer_join_spec.rb | 110 +++++++++++++++++++++++++++++ spec/spec_helper.rb | 5 ++ 5 files changed, 128 insertions(+), 3 deletions(-) diff --git a/ar_outer_join.gemspec b/ar_outer_join.gemspec index 10e424f..07a530d 100644 --- a/ar_outer_join.gemspec +++ b/ar_outer_join.gemspec @@ -2,8 +2,8 @@ require File.expand_path('../lib/ar_outer_joins/version', __FILE__) Gem::Specification.new do |gem| - gem.authors = ["Jonas Nicklas", "Elabs AB"] - gem.email = ["jonas.nicklas@gmail.com", "dev@elabs.se"] + gem.authors = ["Jonas Nicklas", "Elabs AB", "Michael Nowak"] + gem.email = ["jonas.nicklas@gmail.com", "dev@elabs.se", "thexsystem@gmail.com"] 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" diff --git a/lib/ar_outer_joins/join_builder.rb b/lib/ar_outer_joins/join_builder.rb index f8259ec..56355db 100644 --- a/lib/ar_outer_joins/join_builder.rb +++ b/lib/ar_outer_joins/join_builder.rb @@ -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 = Arel::Table.new(association.options[:join_table]) @@ -29,10 +33,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" diff --git a/lib/ar_outer_joins/version.rb b/lib/ar_outer_joins/version.rb index cacdf99..f43ee22 100644 --- a/lib/ar_outer_joins/version.rb +++ b/lib/ar_outer_joins/version.rb @@ -1,3 +1,3 @@ module ArOuterJoins - VERSION = "0.0.1" + VERSION = "0.0.2" end diff --git a/spec/outer_join_spec.rb b/spec/outer_join_spec.rb index 4bd6465..be4f3dd 100644 --- a/spec/outer_join_spec.rb +++ b/spec/outer_join_spec.rb @@ -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 @@ -16,10 +21,14 @@ 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 @@ -27,7 +36,9 @@ class Discount < ActiveRecord::Base 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 @@ -42,6 +53,25 @@ class Tag < ActiveRecord::Base; end query.all.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" @@ -68,6 +98,24 @@ class Tag < ActiveRecord::Base; end query = Product.outer_joins(:image).where("images.highres = ? OR products.published = ?", true, true) query.all.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 @@ -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.all.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 @@ -99,6 +165,25 @@ class Tag < ActiveRecord::Base; end query = Product.outer_joins(:tags).where("tags.name = ? OR products.published = ?", "Red", true) query.all.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 @@ -118,6 +203,31 @@ class Tag < ActiveRecord::Base; end query = Product.outer_joins(:baskets).where("baskets.purchased = ? OR products.published = ?", true, true) query.all.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 diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index da0019d..f6526b7 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -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 @@ -22,6 +24,7 @@ end ActiveRecord::Base.connection.create_table :categories do |t| + t.string :type t.string :name end @@ -35,6 +38,7 @@ end ActiveRecord::Base.connection.create_table :tags do |t| + t.string :type t.string :name end @@ -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 From 42ce528de19fbd14c82605de4ea966075b8d1643 Mon Sep 17 00:00:00 2001 From: THExSYSTEM Date: Sun, 9 Dec 2012 00:25:32 +0100 Subject: [PATCH 2/3] updated README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1c7ff12..67d18f5 100644 --- a/README.md +++ b/README.md @@ -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 From bc401c5e435e11be3130024b1640132f24cca269 Mon Sep 17 00:00:00 2001 From: Michael Nowak Date: Tue, 2 Jul 2013 00:19:14 +0200 Subject: [PATCH 3/3] added license spec; changed rails dependecy to >= 3.2 --- ar_outer_join.gemspec | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/ar_outer_join.gemspec b/ar_outer_join.gemspec index 07a530d..e6627a1 100644 --- a/ar_outer_join.gemspec +++ b/ar_outer_join.gemspec @@ -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", "Michael Nowak"] - gem.email = ["jonas.nicklas@gmail.com", "dev@elabs.se", "thexsystem@gmail.com"] - 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 = ["jonas.nicklas@gmail.com", "dev@elabs.se", "thexsystem@gmail.com"] + 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