Skip to content

Commit

Permalink
Merge pull request #1002 from gregmolnar/polyamorous-gem
Browse files Browse the repository at this point in the history
make polyamorous a separate gem
  • Loading branch information
gregmolnar authored Feb 12, 2019
2 parents f663d64 + b89ca69 commit ce254a9
Show file tree
Hide file tree
Showing 27 changed files with 370 additions and 0 deletions.
1 change: 1 addition & 0 deletions lib/ransack.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require 'active_support/core_ext'
require 'ransack/configuration'
require 'ransack/adapters'
require 'polyamorous'

Ransack::Adapters.object_mapper.require_constants

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
3 changes: 3 additions & 0 deletions polyamorous/lib/polyamorous/version.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module Polyamorous
VERSION = '2.1.1'
end
37 changes: 37 additions & 0 deletions polyamorous/polyamorous.gemspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# -*- encoding: utf-8 -*-
$:.push File.expand_path("../lib", __FILE__)
require "polyamorous/version"

Gem::Specification.new do |s|
s.name = "polyamorous"
s.version = Polyamorous::VERSION
s.authors = ["Ernie Miller", "Ryan Bigg", "Jon Atack", "Xiang Li"]
s.email = ["[email protected]", "[email protected]", "[email protected]", "[email protected]"]
s.homepage = "https://github.com/activerecord-hackery/ransack/tree/master/polyamorous"
s.license = "MIT"
s.summary = %q{
Loves/is loved by polymorphic belongs_to associations, Ransack, Squeel, MetaSearch...
}
s.description = %q{
This is just an extraction from Ransack/Squeel. You probably don't want to use this
directly. It extends ActiveRecord's associations to support polymorphic belongs_to
associations.
}

s.rubyforge_project = "polyamorous"

s.add_dependency 'activerecord', '>= 5.0'
s.add_development_dependency 'rspec', '~> 3'
s.add_development_dependency 'machinist', '~> 1.0.6'
s.add_development_dependency 'faker', '~> 1.6.5'
s.add_development_dependency 'sqlite3', '~> 1.3.3'

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

# specify any dependencies here; for example:
# s.add_development_dependency "rspec"
# s.add_runtime_dependency "rest-client"
end
5 changes: 5 additions & 0 deletions polyamorous/spec/blueprints/articles.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Article.blueprint do
person
title
body
end
5 changes: 5 additions & 0 deletions polyamorous/spec/blueprints/comments.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Comment.blueprint do
article
person
body
end
3 changes: 3 additions & 0 deletions polyamorous/spec/blueprints/notes.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Note.blueprint do
note
end
4 changes: 4 additions & 0 deletions polyamorous/spec/blueprints/people.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Person.blueprint do
name
salary
end
3 changes: 3 additions & 0 deletions polyamorous/spec/blueprints/tags.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Tag.blueprint do
name { Sham.tag_name }
end
26 changes: 26 additions & 0 deletions polyamorous/spec/helpers/polyamorous_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
module PolyamorousHelper
if ActiveRecord::VERSION::STRING >= "4.1"
def new_join_association(reflection, children, klass)
Polyamorous::JoinAssociation.new reflection, children, klass
end
else
def new_join_association(reflection, join_dependency, parent, klass)
Polyamorous::JoinAssociation.new reflection, join_dependency, parent, klass
end
end

if ActiveRecord::VERSION::STRING >= "5.2"
def new_join_dependency(klass, associations = {})
alias_tracker = ::ActiveRecord::Associations::AliasTracker.create(klass.connection, klass.table_name, [])
Polyamorous::JoinDependency.new klass, klass.arel_table, associations, alias_tracker
end
else
def new_join_dependency(klass, associations = {})
Polyamorous::JoinDependency.new klass, associations, []
end
end

def new_join(name, type = Polyamorous::InnerJoin, klass = nil)
Polyamorous::Join.new name, type, klass
end
end
29 changes: 29 additions & 0 deletions polyamorous/spec/polyamorous/join_association_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
require 'spec_helper'

module Polyamorous
describe JoinAssociation do

join_base, join_association_args, polymorphic = [:join_root, 'parent.children', 'reflection.options[:polymorphic]']
let(:join_dependency) { new_join_dependency Note, {} }
let(:reflection) { Note.reflect_on_association(:notable) }
let(:parent) { join_dependency.send(join_base) }
let(:join_association) {
eval("new_join_association(reflection, #{join_association_args}, Article)")
}

it 'leaves the orginal reflection intact for thread safety' do
reflection.instance_variable_set(:@klass, Article)
join_association
.swapping_reflection_klass(reflection, Person) do |new_reflection|
expect(new_reflection.options).not_to equal reflection.options
expect(new_reflection.options).not_to have_key(:polymorphic)
expect(new_reflection.klass).to eq(Person)
expect(reflection.klass).to eq(Article)
end
end

it 'sets the polymorphic option to true after initializing' do
expect(join_association.instance_eval(polymorphic)).to be true
end
end
end
93 changes: 93 additions & 0 deletions polyamorous/spec/polyamorous/join_dependency_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
require 'spec_helper'

module Polyamorous
describe JoinDependency do

method, join_associations, join_base =
if ActiveRecord::VERSION::STRING >= '4.1'
[:instance_eval, 'join_root.drop(1)', :join_root]
else
[:send, 'join_associations', :join_base]
end

context 'with symbol joins' do
subject { new_join_dependency Person, articles: :comments }

specify { expect(subject.send(method, join_associations).size)
.to eq(2) }
specify { expect(subject.send(method, join_associations).map(&:join_type))
.to be_all { Polyamorous::InnerJoin } }
end

context 'with has_many :through association' do
subject { new_join_dependency Person, :authored_article_comments }

specify { expect(subject.send(method, join_associations).size)
.to eq 1 }
specify { expect(subject.send(method, join_associations).first.table_name)
.to eq 'comments' }
end

context 'with outer join' do
subject { new_join_dependency Person, new_join(:articles, :outer) }

specify { expect(subject.send(method, join_associations).size)
.to eq 1 }
specify { expect(subject.send(method, join_associations).first.join_type)
.to eq Polyamorous::OuterJoin }
end

context 'with nested outer joins' do
subject { new_join_dependency Person,
new_join(:articles, :outer) => new_join(:comments, :outer) }

specify { expect(subject.send(method, join_associations).size)
.to eq 2 }
specify { expect(subject.send(method, join_associations).map(&:join_type))
.to eq [Polyamorous::OuterJoin, Polyamorous::OuterJoin] }
specify { expect(subject.send(method, join_associations).map(&:join_type))
.to be_all { Polyamorous::OuterJoin } }
end

context 'with polymorphic belongs_to join' do
subject { new_join_dependency Note, new_join(:notable, :inner, Person) }

specify { expect(subject.send(method, join_associations).size)
.to eq 1 }
specify { expect(subject.send(method, join_associations).first.join_type)
.to eq Polyamorous::InnerJoin }
specify { expect(subject.send(method, join_associations).first.table_name)
.to eq 'people' }
end

context 'with polymorphic belongs_to join and nested symbol join' do
subject { new_join_dependency Note,
new_join(:notable, :inner, Person) => :comments }

specify { expect(subject.send(method, join_associations).size)
.to eq 2 }
specify { expect(subject.send(method, join_associations).map(&:join_type))
.to be_all { Polyamorous::InnerJoin } }
specify { expect(subject.send(method, join_associations).first.table_name)
.to eq 'people' }
specify { expect(subject.send(method, join_associations)[1].table_name)
.to eq 'comments' }
end

context '#left_outer_join in Rails 5 overrides join type specified',
if: ActiveRecord::VERSION::MAJOR >= 5 && ActiveRecord::VERSION::MINOR < 2 do

let(:join_type_class) do
new_join_dependency(
Person,
new_join(:articles)
).join_constraints(
[],
Arel::Nodes::OuterJoin
).first.joins.map(&:class)
end

specify { expect(join_type_class).to eq [Arel::Nodes::OuterJoin] }
end
end
end
19 changes: 19 additions & 0 deletions polyamorous/spec/polyamorous/join_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
require 'spec_helper'

module Polyamorous
describe Join do
it 'is a tree node' do
join = new_join(:articles, :outer)
expect(join).to be_kind_of(TreeNode)
end

it 'can be added to a tree' do
join = new_join(:articles, :outer)

tree_hash = {}
join.add_to_tree(tree_hash)

expect(tree_hash[join]).to be {}
end
end
end
43 changes: 43 additions & 0 deletions polyamorous/spec/spec_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
require 'machinist/active_record'
require 'sham'
require 'faker'
require 'polyamorous'

Time.zone = 'Eastern Time (US & Canada)'

Dir[File.expand_path('../{helpers,support,blueprints}/**/*.rb', __FILE__)]
.each do |f|
require f
end

Sham.define do
name { Faker::Name.name }
title { Faker::Lorem.sentence }
body { Faker::Lorem.paragraph }
salary { |index| 30000 + (index * 1000) }
tag_name { Faker::Lorem.words(3).join(' ') }
note { Faker::Lorem.words(7).join(' ') }
end

RSpec.configure do |config|
config.before(:suite) do
message = "Running Polyamorous specs with #{
ActiveRecord::Base.connection.adapter_name
}, Active Record #{::ActiveRecord::VERSION::STRING}, Arel #{Arel::VERSION
} and Ruby #{RUBY_VERSION}"
line = '=' * message.length
puts line, message, line
Schema.create
end
config.before(:all) { Sham.reset(:before_all) }
config.before(:each) { Sham.reset(:before_each) }

config.include PolyamorousHelper
end

RSpec::Matchers.define :be_like do |expected|
match do |actual|
actual.gsub(/^\s+|\s+$/, '').gsub(/\s+/, ' ').strip ==
expected.gsub(/^\s+|\s+$/, '').gsub(/\s+/, ' ').strip
end
end
98 changes: 98 additions & 0 deletions polyamorous/spec/support/schema.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
require 'active_record'

ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')

class Person < ActiveRecord::Base
belongs_to :parent, class_name: 'Person', foreign_key: :parent_id
has_many :children, class_name: 'Person', foreign_key: :parent_id
has_many :articles
has_many :comments
has_many :authored_article_comments, through: :articles,
foreign_key: :person_id, source: :comments
has_many :notes, as: :notable
end

class Article < ActiveRecord::Base
belongs_to :person
has_many :comments
has_and_belongs_to_many :tags
has_many :notes, as: :notable
end

class Comment < ActiveRecord::Base
belongs_to :article
belongs_to :person
end

class Tag < ActiveRecord::Base
has_and_belongs_to_many :articles
end

class Note < ActiveRecord::Base
belongs_to :notable, polymorphic: true
end

module Schema
def self.create
ActiveRecord::Migration.verbose = false

ActiveRecord::Schema.define do
create_table :people, force: true do |t|
t.integer :parent_id
t.string :name
t.integer :salary
t.boolean :awesome, default: false
t.timestamps null: false
end

create_table :articles, force: true do |t|
t.integer :person_id
t.string :title
t.text :body
end

create_table :comments, force: true do |t|
t.integer :article_id
t.integer :person_id
t.text :body
end

create_table :tags, force: true do |t|
t.string :name
end

create_table :articles_tags, force: true, id: false do |t|
t.integer :article_id
t.integer :tag_id
end

create_table :notes, force: true do |t|
t.integer :notable_id
t.string :notable_type
t.string :note
end
end

10.times do
person = Person.make
Note.make(notable: person)
3.times do
article = Article.make(person: person)
3.times do
article.tags = [Tag.make, Tag.make, Tag.make]
end
Note.make(notable: article)
10.times do
Comment.make(article: article)
end
end
2.times do
Comment.make(person: person)
end
end

Comment.make(
body: 'First post!', article: Article.make(title: 'Hello, world!')
)
end
end
Loading

0 comments on commit ce254a9

Please sign in to comment.