polyamorous 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +27 -6
  3. data/Gemfile +15 -13
  4. data/README.md +7 -2
  5. data/lib/polyamorous.rb +30 -12
  6. data/lib/polyamorous/{activerecord_3_and_4.0 → activerecord_3_and_4.0_ruby_1.9}/join_association.rb +28 -27
  7. data/lib/polyamorous/{activerecord_3_and_4.0 → activerecord_3_and_4.0_ruby_1.9}/join_dependency.rb +7 -4
  8. data/lib/polyamorous/activerecord_4.1_ruby_1.9/join_association.rb +2 -0
  9. data/lib/polyamorous/activerecord_4.1_ruby_1.9/join_dependency.rb +4 -0
  10. data/lib/polyamorous/activerecord_4.1_ruby_2/join_association.rb +2 -0
  11. data/lib/polyamorous/activerecord_4.1_ruby_2/join_dependency.rb +3 -0
  12. data/lib/polyamorous/activerecord_4.1_ruby_2/make_joins.rb +11 -0
  13. data/lib/polyamorous/activerecord_4.2_ruby_1.9/join_association.rb +46 -0
  14. data/lib/polyamorous/activerecord_4.2_ruby_1.9/join_dependency.rb +94 -0
  15. data/lib/polyamorous/activerecord_4.2_ruby_2/join_association.rb +37 -0
  16. data/lib/polyamorous/{activerecord_4.1 → activerecord_4.2_ruby_2}/join_dependency.rb +39 -44
  17. data/lib/polyamorous/swapping_reflection_class.rb +11 -0
  18. data/lib/polyamorous/version.rb +1 -1
  19. data/polyamorous.gemspec +4 -3
  20. data/spec/polyamorous/join_association_spec.rb +3 -3
  21. data/spec/polyamorous/join_dependency_spec.rb +3 -3
  22. data/spec/polyamorous/join_spec.rb +2 -2
  23. data/spec/spec_helper.rb +10 -2
  24. data/spec/support/schema.rb +47 -51
  25. data/spec/support/shared_examples/join_association_3_and_4.0.rb +6 -3
  26. data/spec/support/shared_examples/join_association_4.1.rb +9 -4
  27. data/spec/support/shared_examples/join_dependency_3_and_4.0.rb +22 -11
  28. data/spec/support/shared_examples/join_dependency_4.1.rb +25 -6
  29. metadata +41 -12
  30. data/lib/polyamorous/activerecord_4.1/join_association.rb +0 -75
  31. data/lib/polyamorous/activerecord_4.2/join_dependency.rb +0 -12
@@ -0,0 +1,37 @@
1
+ # active_record_4.2_ruby_2/join_association.rb
2
+ module Polyamorous
3
+ module JoinAssociationExtensions
4
+ include SwappingReflectionClass
5
+ def self.prepended(base)
6
+ base.class_eval { attr_reader :join_type }
7
+ end
8
+
9
+ def initialize(reflection, children, polymorphic_class = nil,
10
+ join_type = Arel::Nodes::InnerJoin)
11
+ @join_type = join_type
12
+ if polymorphic_class && ::ActiveRecord::Base > polymorphic_class
13
+ swapping_reflection_klass(reflection, polymorphic_class) do |reflection|
14
+ super(reflection, children)
15
+ self.reflection.options[:polymorphic] = true
16
+ end
17
+ else
18
+ super(reflection, children)
19
+ end
20
+ end
21
+
22
+ # Reference https://github.com/rails/rails/commit/9b15db51b78028bfecdb85595624de4b838adbd1
23
+ # NOTE Not sure we still need it?
24
+ def ==(other)
25
+ base_klass == other.base_klass
26
+ end
27
+
28
+ def build_constraint(klass, table, key, foreign_table, foreign_key)
29
+ if reflection.polymorphic?
30
+ super(klass, table, key, foreign_table, foreign_key)
31
+ .and(foreign_table[reflection.foreign_type].eq(reflection.klass.name))
32
+ else
33
+ super(klass, table, key, foreign_table, foreign_key)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -1,41 +1,33 @@
1
+ # active_record_4.2_ruby_2/join_dependency.rb
1
2
  module Polyamorous
2
3
  module JoinDependencyExtensions
3
- def self.included(base)
4
- base.extend ClassMethods
5
-
6
- base.class_eval do
7
- class << self
8
- alias_method_chain :walk_tree, :polymorphism
9
- end
10
-
11
- alias_method_chain :build, :polymorphism
12
- alias_method_chain :join_constraints, :polymorphism
13
-
14
- if base.method_defined?(:active_record)
15
- alias_method :base_klass, :active_record
16
- end
17
- end
18
- end
19
-
20
- def build_with_polymorphism(associations, base_klass)
4
+ # Replaces ActiveRecord::Associations::JoinDependency#build
5
+ def build(associations, base_klass)
21
6
  associations.map do |name, right|
22
7
  if name.is_a? Join
23
8
  reflection = find_reflection base_klass, name.name
24
9
  reflection.check_validity!
25
-
26
- if reflection.options[:polymorphic]
27
- JoinAssociation.new reflection, build(right, name.klass || base_klass), name.klass, name.type
10
+ if reflection.polymorphic?
11
+ JoinAssociation.new(
12
+ reflection,
13
+ build(right, name.klass || base_klass),
14
+ name.klass,
15
+ name.type
16
+ )
28
17
  else
29
- JoinAssociation.new reflection, build(right, reflection.klass), name.klass, name.type
18
+ JoinAssociation.new(
19
+ reflection,
20
+ build(right, reflection.klass),
21
+ name.klass,
22
+ name.type
23
+ )
30
24
  end
31
25
  else
32
26
  reflection = find_reflection base_klass, name
33
27
  reflection.check_validity!
34
-
35
- if reflection.options[:polymorphic]
28
+ if reflection.polymorphic?
36
29
  raise ActiveRecord::EagerLoadPolymorphicError.new(reflection)
37
30
  end
38
-
39
31
  JoinAssociation.new reflection, build(right, reflection.klass)
40
32
  end
41
33
  end
@@ -43,7 +35,7 @@ module Polyamorous
43
35
 
44
36
  def find_join_association_respecting_polymorphism(reflection, parent, klass)
45
37
  if association = parent.children.find { |j| j.reflection == reflection }
46
- unless reflection.options[:polymorphic]
38
+ unless reflection.polymorphic?
47
39
  association
48
40
  else
49
41
  association if association.base_klass == klass
@@ -52,40 +44,44 @@ module Polyamorous
52
44
  end
53
45
 
54
46
  def build_join_association_respecting_polymorphism(reflection, parent, klass)
55
- if reflection.options[:polymorphic] && klass
47
+ if reflection.polymorphic? && klass
56
48
  JoinAssociation.new(reflection, self, klass)
57
49
  else
58
50
  JoinAssociation.new(reflection, self)
59
51
  end
60
52
  end
61
53
 
62
- def join_constraints_with_polymorphism(outer_joins)
54
+ # Replaces ActiveRecord::Associations::JoinDependency#join_constraints
55
+ # to call #make_joins instead of #make_inner_joins.
56
+ def join_constraints(outer_joins)
63
57
  joins = join_root.children.flat_map { |child|
64
- make_joins join_root, child
58
+ make_joins(join_root, child)
65
59
  }
66
-
67
60
  joins.concat outer_joins.flat_map { |oj|
68
61
  if join_root.match? oj.join_root
69
- walk join_root, oj.join_root
62
+ walk(join_root, oj.join_root)
70
63
  else
71
64
  oj.join_root.children.flat_map { |child|
72
- make_outer_joins oj.join_root, child
65
+ make_outer_joins(oj.join_root, child)
73
66
  }
74
67
  end
75
68
  }
76
69
  end
77
70
 
71
+ # Replaces ActiveRecord::Associations::JoinDependency#make_inner_joins
78
72
  def make_joins(parent, child)
79
- tables = child.tables
80
- joins = make_constraints parent, child, tables, child.join_type || Arel::Nodes::InnerJoin
81
-
82
- joins.concat child.children.flat_map { |c| make_joins(child, c) }
73
+ [
74
+ make_constraints(
75
+ parent, child, child.tables, child.join_type || Arel::Nodes::InnerJoin
76
+ )
77
+ ] + child.children.flat_map { |c| make_inner_joins(child, c) }
83
78
  end
84
79
 
85
80
  private :make_joins
86
81
 
87
82
  module ClassMethods
88
- def walk_tree_with_polymorphism(associations, hash)
83
+ # Prepended before ActiveRecord::Associations::JoinDependency#self.walk_tree
84
+ def walk_tree(associations, hash)
89
85
  case associations
90
86
  when TreeNode
91
87
  associations.add_to_tree(hash)
@@ -93,16 +89,15 @@ module Polyamorous
93
89
  associations.each do |k, v|
94
90
  cache =
95
91
  case k
96
- when TreeNode
97
- k.add_to_tree(hash)
98
- else
99
- hash[k] ||= {}
100
- end
101
-
92
+ when TreeNode
93
+ k.add_to_tree(hash)
94
+ else
95
+ hash[k] ||= {}
96
+ end
102
97
  walk_tree(v, cache)
103
98
  end
104
99
  else
105
- walk_tree_without_polymorphism(associations, hash)
100
+ super(associations, hash)
106
101
  end
107
102
  end
108
103
  end
@@ -0,0 +1,11 @@
1
+ module Polyamorous
2
+ module SwappingReflectionClass
3
+ def swapping_reflection_klass(reflection, klass)
4
+ new_reflection = reflection.clone
5
+ new_reflection.instance_variable_set(:@options, reflection.options.clone)
6
+ new_reflection.options.delete(:polymorphic)
7
+ new_reflection.instance_variable_set(:@klass, klass)
8
+ yield new_reflection
9
+ end
10
+ end
11
+ end
@@ -1,3 +1,3 @@
1
1
  module Polyamorous
2
- VERSION = "1.1.0"
2
+ VERSION = '1.2.0'
3
3
  end
@@ -5,9 +5,10 @@ require "polyamorous/version"
5
5
  Gem::Specification.new do |s|
6
6
  s.name = "polyamorous"
7
7
  s.version = Polyamorous::VERSION
8
- s.authors = ["Ernie Miller"]
9
- s.email = ["ernie@metautonomo.us"]
10
- s.homepage = "http://github.com/ernie/polyamorous"
8
+ s.authors = ["Ernie Miller", "Ryan Bigg", "Jon Atack", "Xiang Li"]
9
+ s.email = ["ernie@erniemiller.org", "radarlistener@gmail.com", "jonnyatack@gmail.com", "bigxiang@gmail.com"]
10
+ s.homepage = "https://github.com/activerecord-hackery/polyamorous"
11
+ s.license = "MIT"
11
12
  s.summary = %q{
12
13
  Loves/is loved by polymorphic belongs_to associations, Ransack, Squeel, MetaSearch...
13
14
  }
@@ -2,10 +2,10 @@ require 'spec_helper'
2
2
 
3
3
  module Polyamorous
4
4
  describe JoinAssociation do
5
- if ActiveRecord::VERSION::STRING >= "4.1"
6
- include_examples "Join Association on ActiveRecord 4.1"
5
+ if ActiveRecord::VERSION::STRING >= '4.1'
6
+ include_examples 'Join Association on ActiveRecord 4.1'
7
7
  else
8
- include_examples "Join Association on ActiveRecord 3 and 4.0"
8
+ include_examples 'Join Association on ActiveRecord 3 and 4.0'
9
9
  end
10
10
  end
11
11
  end
@@ -2,10 +2,10 @@ require 'spec_helper'
2
2
 
3
3
  module Polyamorous
4
4
  describe JoinDependency do
5
- if ActiveRecord::VERSION::STRING >= "4.1"
6
- include_examples "Join Dependency on ActiveRecord 4.1"
5
+ if ActiveRecord::VERSION::STRING >= '4.1'
6
+ include_examples 'Join Dependency on ActiveRecord 4.1'
7
7
  else
8
- include_examples "Join Dependency on ActiveRecord 3 and 4.0"
8
+ include_examples 'Join Dependency on ActiveRecord 3 and 4.0'
9
9
  end
10
10
  end
11
11
  end
@@ -2,12 +2,12 @@ require 'spec_helper'
2
2
 
3
3
  module Polyamorous
4
4
  describe Join do
5
- it "is a tree node" do
5
+ it 'is a tree node' do
6
6
  join = new_join(:articles, :outer)
7
7
  expect(join).to be_kind_of(TreeNode)
8
8
  end
9
9
 
10
- it "can be add to a tree" do
10
+ it 'can be add to a tree' do
11
11
  join = new_join(:articles, :outer)
12
12
 
13
13
  tree_hash = {}
@@ -20,7 +20,15 @@ Sham.define do
20
20
  end
21
21
 
22
22
  RSpec.configure do |config|
23
- config.before(:suite) { Schema.create }
23
+ config.before(:suite) do
24
+ message = "Running Polyamorous specs with #{
25
+ ActiveRecord::Base.connection.adapter_name
26
+ }, Active Record #{::ActiveRecord::VERSION::STRING}, Arel #{Arel::VERSION
27
+ } and Ruby #{RUBY_VERSION}"
28
+ line = '=' * message.length
29
+ puts line, message, line
30
+ Schema.create
31
+ end
24
32
  config.before(:all) { Sham.reset(:before_all) }
25
33
  config.before(:each) { Sham.reset(:before_each) }
26
34
 
@@ -32,4 +40,4 @@ RSpec::Matchers.define :be_like do |expected|
32
40
  actual.gsub(/^\s+|\s+$/, '').gsub(/\s+/, ' ').strip ==
33
41
  expected.gsub(/^\s+|\s+$/, '').gsub(/\s+/, ' ').strip
34
42
  end
35
- end
43
+ end
@@ -1,25 +1,22 @@
1
1
  require 'active_record'
2
2
 
3
- ActiveRecord::Base.establish_connection(
4
- :adapter => 'sqlite3',
5
- :database => ':memory:'
6
- )
3
+ ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
7
4
 
8
5
  class Person < ActiveRecord::Base
9
- belongs_to :parent, :class_name => 'Person', :foreign_key => :parent_id
10
- has_many :children, :class_name => 'Person', :foreign_key => :parent_id
6
+ belongs_to :parent, class_name: 'Person', foreign_key: :parent_id
7
+ has_many :children, class_name: 'Person', foreign_key: :parent_id
11
8
  has_many :articles
12
9
  has_many :comments
13
- has_many :authored_article_comments, :through => :articles,
14
- :foreign_key => :person_id, :source => :comments
15
- has_many :notes, :as => :notable
10
+ has_many :authored_article_comments, through: :articles,
11
+ foreign_key: :person_id, source: :comments
12
+ has_many :notes, as: :notable
16
13
  end
17
14
 
18
15
  class Article < ActiveRecord::Base
19
16
  belongs_to :person
20
17
  has_many :comments
21
18
  has_and_belongs_to_many :tags
22
- has_many :notes, :as => :notable
19
+ has_many :notes, as: :notable
23
20
  end
24
21
 
25
22
  class Comment < ActiveRecord::Base
@@ -32,71 +29,70 @@ class Tag < ActiveRecord::Base
32
29
  end
33
30
 
34
31
  class Note < ActiveRecord::Base
35
- belongs_to :notable, :polymorphic => true
32
+ belongs_to :notable, polymorphic: true
36
33
  end
37
34
 
38
35
  module Schema
39
36
  def self.create
40
- silence_stream(STDOUT) do
41
-
42
- ActiveRecord::Schema.define do
43
- create_table :people, :force => true do |t|
44
- t.integer :parent_id
45
- t.string :name
46
- t.integer :salary
47
- t.boolean :awesome, :default => false
48
- t.timestamps
49
- end
50
-
51
- create_table :articles, :force => true do |t|
52
- t.integer :person_id
53
- t.string :title
54
- t.text :body
55
- end
37
+ ActiveRecord::Migration.verbose = false
38
+
39
+ ActiveRecord::Schema.define do
40
+ create_table :people, force: true do |t|
41
+ t.integer :parent_id
42
+ t.string :name
43
+ t.integer :salary
44
+ t.boolean :awesome, default: false
45
+ t.timestamps null: false
46
+ end
56
47
 
57
- create_table :comments, :force => true do |t|
58
- t.integer :article_id
59
- t.integer :person_id
60
- t.text :body
61
- end
48
+ create_table :articles, force: true do |t|
49
+ t.integer :person_id
50
+ t.string :title
51
+ t.text :body
52
+ end
62
53
 
63
- create_table :tags, :force => true do |t|
64
- t.string :name
65
- end
54
+ create_table :comments, force: true do |t|
55
+ t.integer :article_id
56
+ t.integer :person_id
57
+ t.text :body
58
+ end
66
59
 
67
- create_table :articles_tags, :force => true, :id => false do |t|
68
- t.integer :article_id
69
- t.integer :tag_id
70
- end
60
+ create_table :tags, force: true do |t|
61
+ t.string :name
62
+ end
71
63
 
72
- create_table :notes, :force => true do |t|
73
- t.integer :notable_id
74
- t.string :notable_type
75
- t.string :note
76
- end
64
+ create_table :articles_tags, force: true, id: false do |t|
65
+ t.integer :article_id
66
+ t.integer :tag_id
67
+ end
77
68
 
69
+ create_table :notes, force: true do |t|
70
+ t.integer :notable_id
71
+ t.string :notable_type
72
+ t.string :note
78
73
  end
79
74
  end
80
75
 
81
76
  10.times do
82
77
  person = Person.make
83
- Note.make(:notable => person)
78
+ Note.make(notable: person)
84
79
  3.times do
85
- article = Article.make(:person => person)
80
+ article = Article.make(person: person)
86
81
  3.times do
87
82
  article.tags = [Tag.make, Tag.make, Tag.make]
88
83
  end
89
- Note.make(:notable => article)
84
+ Note.make(notable: article)
90
85
  10.times do
91
- Comment.make(:article => article)
86
+ Comment.make(article: article)
92
87
  end
93
88
  end
94
89
  2.times do
95
- Comment.make(:person => person)
90
+ Comment.make(person: person)
96
91
  end
97
92
  end
98
93
 
99
- Comment.make(:body => 'First post!', :article => Article.make(:title => 'Hello, world!'))
100
-
94
+ Comment.make(
95
+ body: 'First post!', article: Article.make(title: 'Hello, world!')
96
+ )
101
97
  end
102
98
  end
@@ -1,8 +1,10 @@
1
- shared_examples "Join Association on ActiveRecord 3 and 4.0" do
1
+ shared_examples 'Join Association on ActiveRecord 3 and 4.0' do
2
2
  let(:join_dependency) { new_join_dependency Note, {} }
3
3
  let(:reflection) { Note.reflect_on_association(:notable) }
4
4
  let(:parent) { join_dependency.join_base }
5
- let(:join_association) { new_join_association(reflection, join_dependency, parent, Article) }
5
+ let(:join_association) {
6
+ new_join_association(reflection, join_dependency, parent, Article)
7
+ }
6
8
 
7
9
  subject {
8
10
  join_dependency.build_join_association_respecting_polymorphism(
@@ -25,7 +27,8 @@ shared_examples "Join Association on ActiveRecord 3 and 4.0" do
25
27
 
26
28
  it 'leaves the orginal reflection intact for thread safety' do
27
29
  reflection.instance_variable_set(:@klass, Article)
28
- join_association.swapping_reflection_klass(reflection, Person) do |new_reflection|
30
+ join_association
31
+ .swapping_reflection_klass(reflection, Person) do |new_reflection|
29
32
  expect(new_reflection.options).not_to equal reflection.options
30
33
  expect(new_reflection.options).not_to have_key(:polymorphic)
31
34
  expect(new_reflection.klass).to eq(Person)