joiner 0.5.0 → 0.6.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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +23 -0
- data/.travis.yml +2 -2
- data/README.md +3 -3
- data/joiner.gemspec +4 -4
- data/lib/joiner.rb +2 -0
- data/lib/joiner/alias_tracker.rb +84 -0
- data/lib/joiner/join_aliaser.rb +41 -0
- data/lib/joiner/join_dependency.rb +2 -1
- data/lib/joiner/joins.rb +2 -4
- data/spec/spec_helper.rb +1 -1
- metadata +15 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 50d83b6e1af0b5ef8c29fbbaabbb56a8c33ed99acbf57f507d13bcff673d92b2
|
4
|
+
data.tar.gz: 62adfb53520243ff108e85f68c61ca121cf28f1e6344fac539aa36c725efe0c1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f96a6e7b1165be044ae4fba8dd71f9950f6635cbac352d7aa9d0aea9f7ead03d8bc0928f4fa69422539e4945976207394efc86704556c5fe44b8bcbda9ac2106
|
7
|
+
data.tar.gz: fa5f47d42b4d823581706dc5644b1b7b0ff4b47232f4216667c9526065e01fa13d1d1f46424e815f6df4bfc42b5659a56dc4831e5af1928ca3151d63bd0a7b5f
|
@@ -0,0 +1,23 @@
|
|
1
|
+
name: CI
|
2
|
+
|
3
|
+
on: [push, pull_request]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
build:
|
7
|
+
runs-on: ubuntu-latest
|
8
|
+
|
9
|
+
strategy:
|
10
|
+
fail-fast: false
|
11
|
+
matrix:
|
12
|
+
ruby: [ '2.5', '2.6', '2.7' ]
|
13
|
+
|
14
|
+
steps:
|
15
|
+
- name: Check out code
|
16
|
+
uses: actions/checkout@v2
|
17
|
+
- name: Set up ruby
|
18
|
+
uses: ruby/setup-ruby@v1
|
19
|
+
with:
|
20
|
+
ruby-version: ${{ matrix.ruby }}
|
21
|
+
bundler-cache: true
|
22
|
+
- name: Test
|
23
|
+
run: "bundle exec rspec"
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -9,7 +9,7 @@ If this gem is used by anyone other than myself/Thinking Sphinx, I'll be surpris
|
|
9
9
|
It's a gem - so you can either install it yourself, or add it to the appropriate Gemfile or gemspec.
|
10
10
|
|
11
11
|
```term
|
12
|
-
gem install joiner --version 0.
|
12
|
+
gem install joiner --version 0.6.0
|
13
13
|
```
|
14
14
|
|
15
15
|
## Usage
|
@@ -17,7 +17,7 @@ gem install joiner --version 0.5.0
|
|
17
17
|
First, create a join collection, based on an ActiveRecord model:
|
18
18
|
|
19
19
|
```ruby
|
20
|
-
joiner = Joiner::Joins.new
|
20
|
+
joiner = Joiner::Joins.new User
|
21
21
|
```
|
22
22
|
|
23
23
|
Then you can add joins for a given association path. For example, if User has many articles, and articles have many comments:
|
@@ -59,4 +59,4 @@ Please note that this project now has a [Contributor Code of Conduct](http://con
|
|
59
59
|
|
60
60
|
## Licence
|
61
61
|
|
62
|
-
Copyright (c) 2013-
|
62
|
+
Copyright (c) 2013-2020, Joiner is developed and maintained by [Pat Allan](http://freelancing-gods.com), and is released under the open MIT Licence.
|
data/joiner.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
Gem::Specification.new do |spec|
|
3
3
|
spec.name = 'joiner'
|
4
|
-
spec.version = '0.
|
4
|
+
spec.version = '0.6.0'
|
5
5
|
spec.authors = ['Pat Allan']
|
6
6
|
spec.email = ['pat@freelancing-gods.com']
|
7
7
|
spec.summary = %q{Builds ActiveRecord joins from association paths}
|
@@ -13,10 +13,10 @@ Gem::Specification.new do |spec|
|
|
13
13
|
spec.test_files = spec.files.grep(%r{^(spec)/})
|
14
14
|
spec.require_paths = ['lib']
|
15
15
|
|
16
|
-
spec.add_runtime_dependency 'activerecord', '>= 6.
|
16
|
+
spec.add_runtime_dependency 'activerecord', '>= 6.1.0'
|
17
17
|
|
18
18
|
spec.add_development_dependency 'combustion', '~> 1.1'
|
19
|
-
spec.add_development_dependency 'rails', '>= 6.
|
20
|
-
spec.add_development_dependency 'rspec-rails', '~>
|
19
|
+
spec.add_development_dependency 'rails', '>= 6.1.0'
|
20
|
+
spec.add_development_dependency 'rspec-rails', '~> 4'
|
21
21
|
spec.add_development_dependency 'sqlite3', '~> 1.4'
|
22
22
|
end
|
data/lib/joiner.rb
CHANGED
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/string/conversions"
|
4
|
+
|
5
|
+
# This code is taken straight from Rails, prior to v6.1.0.
|
6
|
+
# I'm maintaining a copy here to save myself having to work through aliasing
|
7
|
+
# logic myself - there's a good chance I don't need all of thiis, but it'll do
|
8
|
+
# to get this gem working with Rails 6.1.
|
9
|
+
|
10
|
+
class Joiner::AliasTracker # :nodoc:
|
11
|
+
def self.create(connection, initial_table, joins, aliases = nil)
|
12
|
+
if joins.empty?
|
13
|
+
aliases ||= Hash.new(0)
|
14
|
+
elsif aliases
|
15
|
+
default_proc = aliases.default_proc || proc { 0 }
|
16
|
+
aliases.default_proc = proc { |h, k|
|
17
|
+
h[k] = initial_count_for(connection, k, joins) + default_proc.call(h, k)
|
18
|
+
}
|
19
|
+
else
|
20
|
+
aliases = Hash.new { |h, k|
|
21
|
+
h[k] = initial_count_for(connection, k, joins)
|
22
|
+
}
|
23
|
+
end
|
24
|
+
aliases[initial_table] = 1
|
25
|
+
new(connection, aliases)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.initial_count_for(connection, name, table_joins)
|
29
|
+
quoted_name = nil
|
30
|
+
|
31
|
+
counts = table_joins.map do |join|
|
32
|
+
if join.is_a?(Arel::Nodes::StringJoin)
|
33
|
+
# quoted_name should be case ignored as some database adapters (Oracle) return quoted name in uppercase
|
34
|
+
quoted_name ||= connection.quote_table_name(name)
|
35
|
+
|
36
|
+
# Table names + table aliases
|
37
|
+
join.left.scan(
|
38
|
+
/JOIN(?:\s+\w+)?\s+(?:\S+\s+)?(?:#{quoted_name}|#{name})\sON/i
|
39
|
+
).size
|
40
|
+
elsif join.is_a?(Arel::Nodes::Join)
|
41
|
+
join.left.name == name ? 1 : 0
|
42
|
+
else
|
43
|
+
raise ArgumentError, "joins list should be initialized by list of Arel::Nodes::Join"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
counts.sum
|
48
|
+
end
|
49
|
+
|
50
|
+
# table_joins is an array of arel joins which might conflict with the aliases we assign here
|
51
|
+
def initialize(connection, aliases)
|
52
|
+
@aliases = aliases
|
53
|
+
@connection = connection
|
54
|
+
end
|
55
|
+
|
56
|
+
def aliased_table_for(table_name, aliased_name, type_caster)
|
57
|
+
if aliases[table_name].zero?
|
58
|
+
# If it's zero, we can have our table_name
|
59
|
+
aliases[table_name] = 1
|
60
|
+
Arel::Table.new(table_name, type_caster: type_caster)
|
61
|
+
else
|
62
|
+
# Otherwise, we need to use an alias
|
63
|
+
aliased_name = @connection.table_alias_for(aliased_name)
|
64
|
+
|
65
|
+
# Update the count
|
66
|
+
aliases[aliased_name] += 1
|
67
|
+
|
68
|
+
table_alias = if aliases[aliased_name] > 1
|
69
|
+
"#{truncate(aliased_name)}_#{aliases[aliased_name]}"
|
70
|
+
else
|
71
|
+
aliased_name
|
72
|
+
end
|
73
|
+
Arel::Table.new(table_name, type_caster: type_caster).alias(table_alias)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
attr_reader :aliases
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def truncate(name)
|
82
|
+
name.slice(0, @connection.table_alias_length - 2)
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# The core logic of this class is old Rails behaviour, replicated here because
|
2
|
+
# their own alias logic has evolved, but I haven't yet found a way to make use
|
3
|
+
# of it - and besides, this is only used to generate Thinking Sphinx's
|
4
|
+
# configuration rarely - not in any web requests, so performance issues are less
|
5
|
+
# critical here.
|
6
|
+
|
7
|
+
class Joiner::JoinAliaser
|
8
|
+
def self.call(join_root, alias_tracker)
|
9
|
+
new(join_root, alias_tracker).call
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(join_root, alias_tracker)
|
13
|
+
@join_root = join_root
|
14
|
+
@alias_tracker = alias_tracker
|
15
|
+
end
|
16
|
+
|
17
|
+
def call
|
18
|
+
join_root.each_children do |parent, child|
|
19
|
+
child.table = table_aliases_for(parent, child).first
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
attr_reader :join_root, :alias_tracker
|
26
|
+
|
27
|
+
def table_aliases_for(parent, node)
|
28
|
+
node.reflection.chain.map { |reflection|
|
29
|
+
alias_tracker.aliased_table_for(
|
30
|
+
reflection.table_name,
|
31
|
+
table_alias_for(reflection, parent, reflection != node.reflection),
|
32
|
+
reflection.klass.type_caster
|
33
|
+
)
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
def table_alias_for(reflection, parent, join)
|
38
|
+
name = reflection.alias_candidate(parent.table_name)
|
39
|
+
join ? "#{name}_join" : name
|
40
|
+
end
|
41
|
+
end
|
@@ -1,7 +1,8 @@
|
|
1
1
|
class Joiner::JoinDependency < ActiveRecord::Associations::JoinDependency
|
2
2
|
def join_association_for(path, alias_tracker = nil)
|
3
3
|
@alias_tracker = alias_tracker
|
4
|
-
|
4
|
+
|
5
|
+
Joiner::JoinAliaser.call join_root, alias_tracker
|
5
6
|
|
6
7
|
path.inject(join_root) do |node, piece|
|
7
8
|
node.children.detect { |child| child.reflection.name == piece }
|
data/lib/joiner/joins.rb
CHANGED
@@ -2,8 +2,6 @@ require 'active_record'
|
|
2
2
|
require 'active_support/ordered_hash'
|
3
3
|
|
4
4
|
class Joiner::Joins
|
5
|
-
ACTIVE_RECORD_VERSION = Gem::Version.new(ActiveRecord::VERSION::STRING)
|
6
|
-
|
7
5
|
attr_reader :model
|
8
6
|
|
9
7
|
def initialize(model)
|
@@ -21,7 +19,7 @@ class Joiner::Joins
|
|
21
19
|
return model.table_name if path.empty?
|
22
20
|
|
23
21
|
add_join_to path
|
24
|
-
association_for(path).
|
22
|
+
association_for(path).table.name
|
25
23
|
end
|
26
24
|
|
27
25
|
def join_values
|
@@ -35,7 +33,7 @@ class Joiner::Joins
|
|
35
33
|
attr_reader :joins_cache
|
36
34
|
|
37
35
|
def alias_tracker
|
38
|
-
|
36
|
+
Joiner::AliasTracker.create(
|
39
37
|
model.connection, table.name, []
|
40
38
|
)
|
41
39
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: joiner
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pat Allan
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-12-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 6.
|
19
|
+
version: 6.1.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 6.
|
26
|
+
version: 6.1.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: combustion
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -44,28 +44,28 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: 6.
|
47
|
+
version: 6.1.0
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: 6.
|
54
|
+
version: 6.1.0
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rspec-rails
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
61
|
+
version: '4'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
68
|
+
version: '4'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: sqlite3
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -88,6 +88,7 @@ executables: []
|
|
88
88
|
extensions: []
|
89
89
|
extra_rdoc_files: []
|
90
90
|
files:
|
91
|
+
- ".github/workflows/ci.yml"
|
91
92
|
- ".gitignore"
|
92
93
|
- ".travis.yml"
|
93
94
|
- Gemfile
|
@@ -96,6 +97,8 @@ files:
|
|
96
97
|
- Rakefile
|
97
98
|
- joiner.gemspec
|
98
99
|
- lib/joiner.rb
|
100
|
+
- lib/joiner/alias_tracker.rb
|
101
|
+
- lib/joiner/join_aliaser.rb
|
99
102
|
- lib/joiner/join_dependency.rb
|
100
103
|
- lib/joiner/joins.rb
|
101
104
|
- lib/joiner/path.rb
|
@@ -112,7 +115,7 @@ homepage: https://github.com/pat/joiner
|
|
112
115
|
licenses:
|
113
116
|
- MIT
|
114
117
|
metadata: {}
|
115
|
-
post_install_message:
|
118
|
+
post_install_message:
|
116
119
|
rdoc_options: []
|
117
120
|
require_paths:
|
118
121
|
- lib
|
@@ -127,8 +130,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
127
130
|
- !ruby/object:Gem::Version
|
128
131
|
version: '0'
|
129
132
|
requirements: []
|
130
|
-
rubygems_version: 3.
|
131
|
-
signing_key:
|
133
|
+
rubygems_version: 3.1.2
|
134
|
+
signing_key:
|
132
135
|
specification_version: 4
|
133
136
|
summary: Builds ActiveRecord joins from association paths
|
134
137
|
test_files:
|