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

make polyamorous a separate gem #1002

Merged
merged 1 commit into from
Feb 12, 2019
Merged
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
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