activerecord_nested_scope 1.0.4 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +1 -1
- data/.travis.yml +12 -4
- data/CHANGELOG.md +27 -0
- data/README.md +20 -18
- data/activerecord_nested_scope.gemspec +0 -3
- data/gemfiles/rails52.gemfile +2 -1
- data/gemfiles/rails60.gemfile +5 -0
- data/lib/activerecord_nested_scope/builder.rb +68 -63
- data/lib/activerecord_nested_scope/extension.rb +4 -3
- data/lib/activerecord_nested_scope/node.rb +81 -0
- data/lib/activerecord_nested_scope/version.rb +1 -1
- metadata +6 -46
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b1a5162acf18c8e70f87b21f4dc4df15d5a1c5a32de4c63aaf949b3a8b60c5c3
|
4
|
+
data.tar.gz: 34e4a88f240b89d517986ad960f11f27874afc8e186b79b7e4f0b7878e7e7191
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a2c93ab0ebd9e53f7b8aee4d1780974b989b5d232550f5feb30dad0a71c170c467e63563ab7774116953bc4aa809f5ffd7f01e0ed0f5b68a11f0a9394d073298
|
7
|
+
data.tar.gz: 9d6222989f2dc3d5c14d622f188319de79d7d61f8a60e1d6a686d689e62dd8d958aaddb2f15c3e26684e8cfc7fc23ed066801ab4debee02922394d7b25ecb6f2
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -3,6 +3,8 @@ rvm:
|
|
3
3
|
- 2.3
|
4
4
|
- 2.4
|
5
5
|
- 2.5
|
6
|
+
- 2.6
|
7
|
+
- 2.7
|
6
8
|
services:
|
7
9
|
- mysql
|
8
10
|
- postgresql
|
@@ -16,10 +18,16 @@ gemfile:
|
|
16
18
|
- gemfiles/rails50.gemfile
|
17
19
|
- gemfiles/rails51.gemfile
|
18
20
|
- gemfiles/rails52.gemfile
|
21
|
+
- gemfiles/rails60.gemfile
|
22
|
+
matrix:
|
23
|
+
exclude:
|
24
|
+
- rvm: 2.3
|
25
|
+
gemfile: gemfiles/rails60.gemfile
|
26
|
+
- rvm: 2.4
|
27
|
+
gemfile: gemfiles/rails60.gemfile
|
19
28
|
before_script:
|
20
29
|
- cd spec/dummy
|
21
|
-
- bundle exec rake db:create RAILS_ENV=test
|
22
|
-
- bundle exec rake db:migrate RAILS_ENV=test
|
23
|
-
- bundle exec rake db:seed RAILS_ENV=test
|
30
|
+
- bundle exec rake db:create db:migrate db:seed RAILS_ENV=test
|
24
31
|
- cd ../..
|
25
|
-
script:
|
32
|
+
script:
|
33
|
+
- bundle exec rspec
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# CHANGELOG
|
2
|
+
|
3
|
+
## 1.1.0
|
4
|
+
|
5
|
+
* Add short query feature for simple belongs_to association.
|
6
|
+
* Rename `_nested_scope_options` to `nested_scope_options`.
|
7
|
+
* Remove support for active_record_union.
|
8
|
+
|
9
|
+
## 1.0.4
|
10
|
+
|
11
|
+
* Fix query for string value.
|
12
|
+
|
13
|
+
## 1.0.3
|
14
|
+
|
15
|
+
* Remove nil from polymorphic types.
|
16
|
+
|
17
|
+
## 1.0.2
|
18
|
+
|
19
|
+
* Fix query error when there are no records in polymorphic tables.
|
20
|
+
|
21
|
+
## 1.0.1
|
22
|
+
|
23
|
+
* Fix query for array value.
|
24
|
+
|
25
|
+
## 1.0.0
|
26
|
+
|
27
|
+
* First release.
|
data/README.md
CHANGED
@@ -5,7 +5,8 @@ An ActiveRecord extension to build nested scopes through pre-defined association
|
|
5
5
|
## Dependencies
|
6
6
|
|
7
7
|
* ruby 2.3+
|
8
|
-
*
|
8
|
+
* activerecord 5.0+
|
9
|
+
* activesupport 5.0+
|
9
10
|
|
10
11
|
## Installation
|
11
12
|
|
@@ -57,35 +58,36 @@ UserConfig.in_group(id: 1)
|
|
57
58
|
|
58
59
|
### Polymorphic association
|
59
60
|
|
60
|
-
If you define a polymorphic association
|
61
|
+
If you define a polymorphic association,
|
62
|
+
`in_group` generates union of subqueries for each polymorpchic type as follows:
|
61
63
|
|
62
64
|
```ruby
|
63
|
-
class
|
64
|
-
belongs_to :
|
65
|
-
nested_scope :in_group, through: :
|
65
|
+
class Polymorphism < ActiveRecord::Base
|
66
|
+
belongs_to :record, polymorphic: true
|
67
|
+
nested_scope :in_group, through: :record
|
66
68
|
end
|
67
|
-
```
|
68
|
-
|
69
|
-
`in_group` scope generates union of subqueries for each polymorpchic type:
|
70
69
|
|
71
|
-
|
72
|
-
|
73
|
-
#=> SELECT "
|
74
|
-
#
|
75
|
-
# SELECT "
|
76
|
-
# SELECT "
|
77
|
-
#
|
78
|
-
# SELECT "names".* FROM "names" WHERE "names"."data_type" = 'User' AND "names"."data_id" IN (SELECT "users"."id" FROM "users" WHERE "users"."group_id" IN (SELECT "groups"."id" FROM "groups" WHERE "groups"."id" = 1))) AS names
|
70
|
+
Polymorphism.in_group(id: 1)
|
71
|
+
#=> SELECT "polymorphisms"."record_type" FROM "polymorphisms" GROUP BY "polymorphisms"."record_type"
|
72
|
+
#=> SELECT "polymorphisms".* FROM (
|
73
|
+
# SELECT "polymorphisms".* FROM "polymorphisms" WHERE "polymorphisms"."record_type" = 'Group' AND "polymorphisms"."record_id" IN (SELECT "groups"."id" FROM "groups" WHERE "groups"."id" = 1) UNION
|
74
|
+
# SELECT "polymorphisms".* FROM "polymorphisms" WHERE "polymorphisms"."record_type" = 'User' AND "polymorphisms"."record_id" IN (SELECT "users"."id" FROM "users" WHERE "users"."group_id" IN (SELECT "groups"."id" FROM "groups" WHERE "groups"."id" = 1)) UNION
|
75
|
+
# SELECT "polymorphisms".* FROM "polymorphisms" WHERE "polymorphisms"."record_type" = 'UserConfig' AND "polymorphisms"."record_id" IN (SELECT "user_configs"."id" FROM "user_configs" WHERE "user_configs"."user_id" IN (SELECT "users"."id" FROM "users" WHERE "users"."group_id" IN (SELECT "groups"."id" FROM "groups" WHERE "groups"."id" = 1)))
|
76
|
+
# ) AS polymorphisms
|
79
77
|
```
|
80
78
|
|
81
|
-
Note that the first SQL is executed to load `
|
79
|
+
Note that the first SQL is executed to load `record_type`,
|
80
|
+
and the second SQL is built using loaded `record_type` variations.
|
82
81
|
|
83
|
-
### Scope
|
82
|
+
### Scope arguments
|
84
83
|
|
85
84
|
```ruby
|
86
85
|
# pass a integer
|
87
86
|
User.in_group(1)
|
88
87
|
|
88
|
+
# pass an array
|
89
|
+
User.in_group([1, 2, 3])
|
90
|
+
|
89
91
|
# pass a hash
|
90
92
|
User.in_group(id: 1)
|
91
93
|
|
@@ -21,12 +21,9 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.add_dependency "activesupport", ">= 5.0"
|
22
22
|
|
23
23
|
spec.add_development_dependency "rails", ">= 5.0"
|
24
|
-
spec.add_development_dependency "active_record_union"
|
25
24
|
spec.add_development_dependency "mysql2"
|
26
25
|
spec.add_development_dependency "pg"
|
27
26
|
spec.add_development_dependency "sqlite3"
|
28
27
|
spec.add_development_dependency "rspec-rails"
|
29
28
|
spec.add_development_dependency "simplecov"
|
30
|
-
spec.add_development_dependency "pry-rails"
|
31
|
-
spec.add_development_dependency "pry-byebug"
|
32
29
|
end
|
data/gemfiles/rails52.gemfile
CHANGED
@@ -1,97 +1,102 @@
|
|
1
1
|
module ActiveRecordNestedScope
|
2
2
|
class Builder
|
3
3
|
def initialize(klass, name, args)
|
4
|
-
@
|
5
|
-
@name = name
|
4
|
+
@node = Node.new(klass, name)
|
6
5
|
@args = args
|
6
|
+
@args_type = args_type(args)
|
7
7
|
end
|
8
8
|
|
9
|
-
def build
|
10
|
-
|
9
|
+
def build(node = @node)
|
10
|
+
if node.leaf?
|
11
|
+
leaf_relation(node)
|
12
|
+
else
|
13
|
+
build_relation(node)
|
14
|
+
end
|
11
15
|
end
|
12
16
|
|
13
17
|
private
|
14
18
|
|
15
|
-
def
|
16
|
-
if
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
end
|
23
|
-
else
|
24
|
-
root_relation(klass)
|
25
|
-
end
|
19
|
+
def build_relation(node)
|
20
|
+
if node.has_many?
|
21
|
+
has_many_relation(node)
|
22
|
+
elsif node.polymorphic_belongs_to?
|
23
|
+
polymorphic_belongs_to_relation(node)
|
24
|
+
elsif node.belongs_to?
|
25
|
+
belongs_to_relation(node)
|
26
26
|
else
|
27
|
-
klass
|
27
|
+
raise ArgumentError.new("unsupported reflection: #{node.reflection} in #{node.klass}")
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
-
def
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
belongs_to_polymorphic_relation(klass, ref)
|
38
|
-
else
|
39
|
-
belongs_to_relation(klass, ref)
|
40
|
-
end
|
41
|
-
else
|
42
|
-
raise ArgumentError.new("unsupported reflection: #{ref} in #{klass}")
|
43
|
-
end
|
31
|
+
def has_many_relation(node)
|
32
|
+
child = node.children.first
|
33
|
+
return node.klass.none unless child
|
34
|
+
|
35
|
+
relation = child_relation(child, select: node.reflection.foreign_key)
|
36
|
+
node.klass.where(node.klass.primary_key => relation)
|
44
37
|
end
|
45
38
|
|
46
|
-
def
|
47
|
-
|
39
|
+
def belongs_to_relation(node)
|
40
|
+
child = node.children.first
|
41
|
+
return node.klass.none unless child
|
42
|
+
|
43
|
+
relation = child_relation(child, select: node.reflection.klass.primary_key)
|
44
|
+
node.klass.where(node.reflection.foreign_key => relation)
|
48
45
|
end
|
49
46
|
|
50
|
-
def
|
51
|
-
|
47
|
+
def polymorphic_belongs_to_relation(node)
|
48
|
+
rels = node.children.map do |child|
|
49
|
+
relation = child_relation(child, select: child.klass.primary_key)
|
50
|
+
node.klass.where(
|
51
|
+
node.reflection.foreign_type => child.klass.to_s,
|
52
|
+
node.reflection.foreign_key => relation
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
union(node.klass, rels)
|
52
57
|
end
|
53
58
|
|
54
|
-
def
|
55
|
-
|
56
|
-
|
57
|
-
rels = types.map { |type|
|
58
|
-
if (parent = type.safe_constantize)
|
59
|
-
klass.where(ref.foreign_type => type, ref.foreign_key => build_for(parent).merge(scoped(parent, ref.scope)))
|
60
|
-
else
|
61
|
-
klass.none
|
62
|
-
end
|
63
|
-
}
|
64
|
-
union(klass, rels)
|
59
|
+
def child_relation(child, select:)
|
60
|
+
if simple_leaf_relation?(child)
|
61
|
+
@args
|
65
62
|
else
|
66
|
-
|
63
|
+
relation = build(child).select(select)
|
64
|
+
relation = relation.merge(child.scope) if child.has_scope?
|
65
|
+
relation
|
67
66
|
end
|
68
67
|
end
|
69
68
|
|
70
|
-
def
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
69
|
+
def simple_leaf_relation?(child)
|
70
|
+
@args_type == :simple && child.leaf? && child.parent.belongs_to? && !child.has_scope?
|
71
|
+
end
|
72
|
+
|
73
|
+
def leaf_relation(node)
|
74
|
+
case @args_type
|
75
|
+
when :relation
|
76
|
+
node.klass.all.merge(@args)
|
77
|
+
when :hash
|
78
|
+
node.klass.where(@args)
|
79
|
+
when :simple
|
80
|
+
node.klass.where(node.klass.primary_key => @args)
|
77
81
|
else
|
78
|
-
|
82
|
+
raise ArgumentError.new("unexpected argument type: #{@args_type}")
|
79
83
|
end
|
80
84
|
end
|
81
85
|
|
82
|
-
def
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
+
def args_type(args)
|
87
|
+
if args.is_a?(ActiveRecord::Relation)
|
88
|
+
:relation
|
89
|
+
elsif args.is_a?(Hash)
|
90
|
+
:hash
|
91
|
+
else
|
92
|
+
:simple
|
93
|
+
end
|
86
94
|
end
|
87
95
|
|
88
96
|
def union(klass, rels)
|
89
|
-
if
|
90
|
-
|
91
|
-
|
92
|
-
union = rels.map { |rel| "#{rel.to_sql}" }.reject(&:empty?).join(' UNION ')
|
93
|
-
klass.from(Arel.sql("(#{union}) AS #{klass.table_name}"))
|
94
|
-
end
|
97
|
+
return klass.none if rels.blank?
|
98
|
+
union = rels.map { |rel| "#{rel.to_sql}" }.reject(&:empty?).join(' UNION ')
|
99
|
+
klass.from(Arel.sql("(#{union}) AS #{klass.table_name}"))
|
95
100
|
end
|
96
101
|
end
|
97
102
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'active_support'
|
2
|
+
require_relative 'node'
|
2
3
|
require_relative 'builder'
|
3
4
|
|
4
5
|
module ActiveRecordNestedScope
|
@@ -6,13 +7,13 @@ module ActiveRecordNestedScope
|
|
6
7
|
extend ActiveSupport::Concern
|
7
8
|
|
8
9
|
included do
|
9
|
-
class_attribute :
|
10
|
+
class_attribute :nested_scope_options
|
10
11
|
end
|
11
12
|
|
12
13
|
class_methods do
|
13
14
|
def nested_scope(name, options = {})
|
14
|
-
self.
|
15
|
-
self.
|
15
|
+
self.nested_scope_options ||= {}
|
16
|
+
self.nested_scope_options[name] = options
|
16
17
|
|
17
18
|
scope name, ->(args) {
|
18
19
|
ActiveRecordNestedScope::Builder.new(self, name, args).build
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module ActiveRecordNestedScope
|
2
|
+
class Node
|
3
|
+
attr_accessor :klass, :name, :parent
|
4
|
+
|
5
|
+
def initialize(klass, name, parent = nil)
|
6
|
+
@klass = klass
|
7
|
+
@name = name
|
8
|
+
@parent = parent
|
9
|
+
end
|
10
|
+
|
11
|
+
def has_options?
|
12
|
+
options = @klass.nested_scope_options
|
13
|
+
options && options[@name]
|
14
|
+
end
|
15
|
+
|
16
|
+
def options(key)
|
17
|
+
options = @klass.nested_scope_options.to_h
|
18
|
+
options.dig(@name, key)
|
19
|
+
end
|
20
|
+
|
21
|
+
def reflection
|
22
|
+
@klass.reflect_on_association(options(:through))
|
23
|
+
end
|
24
|
+
|
25
|
+
def leaf?
|
26
|
+
options(:through).blank?
|
27
|
+
end
|
28
|
+
|
29
|
+
def has_many?
|
30
|
+
reflection.class.name.in?(['ActiveRecord::Reflection::HasManyReflection', 'ActiveRecord::Reflection::HasOneReflection'])
|
31
|
+
end
|
32
|
+
|
33
|
+
def belongs_to?
|
34
|
+
reflection.class.name == 'ActiveRecord::Reflection::BelongsToReflection' && !reflection.polymorphic?
|
35
|
+
end
|
36
|
+
|
37
|
+
def polymorphic_belongs_to?
|
38
|
+
reflection.class.name == 'ActiveRecord::Reflection::BelongsToReflection' && reflection.polymorphic?
|
39
|
+
end
|
40
|
+
|
41
|
+
def children
|
42
|
+
@children ||= search_children.select(&:valid?)
|
43
|
+
end
|
44
|
+
|
45
|
+
def search_children
|
46
|
+
if leaf?
|
47
|
+
[]
|
48
|
+
elsif polymorphic_belongs_to?
|
49
|
+
polymorphic_klasses.map { |klass| Node.new(klass, @name, self) }
|
50
|
+
else
|
51
|
+
[Node.new(reflection.klass, @name, self)]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def valid?
|
56
|
+
return false unless has_options?
|
57
|
+
|
58
|
+
if options(:through) && !reflection
|
59
|
+
STDERR.puts "can't find reflection for #{options(:through)} in #{klass}"
|
60
|
+
return false
|
61
|
+
end
|
62
|
+
|
63
|
+
return true
|
64
|
+
end
|
65
|
+
|
66
|
+
def has_scope?
|
67
|
+
@parent && @parent.reflection.scope.present?
|
68
|
+
end
|
69
|
+
|
70
|
+
def scope
|
71
|
+
@klass.all.instance_eval(&@parent.reflection.scope) if has_scope?
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def polymorphic_klasses
|
77
|
+
types = @klass.unscoped.group(reflection.foreign_type).pluck(reflection.foreign_type).compact
|
78
|
+
types.map { |type| type.safe_constantize }.compact
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord_nested_scope
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yoshikazu Kaneta
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-09-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -52,20 +52,6 @@ dependencies:
|
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '5.0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: active_record_union
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - ">="
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - ">="
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '0'
|
69
55
|
- !ruby/object:Gem::Dependency
|
70
56
|
name: mysql2
|
71
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -136,34 +122,6 @@ dependencies:
|
|
136
122
|
- - ">="
|
137
123
|
- !ruby/object:Gem::Version
|
138
124
|
version: '0'
|
139
|
-
- !ruby/object:Gem::Dependency
|
140
|
-
name: pry-rails
|
141
|
-
requirement: !ruby/object:Gem::Requirement
|
142
|
-
requirements:
|
143
|
-
- - ">="
|
144
|
-
- !ruby/object:Gem::Version
|
145
|
-
version: '0'
|
146
|
-
type: :development
|
147
|
-
prerelease: false
|
148
|
-
version_requirements: !ruby/object:Gem::Requirement
|
149
|
-
requirements:
|
150
|
-
- - ">="
|
151
|
-
- !ruby/object:Gem::Version
|
152
|
-
version: '0'
|
153
|
-
- !ruby/object:Gem::Dependency
|
154
|
-
name: pry-byebug
|
155
|
-
requirement: !ruby/object:Gem::Requirement
|
156
|
-
requirements:
|
157
|
-
- - ">="
|
158
|
-
- !ruby/object:Gem::Version
|
159
|
-
version: '0'
|
160
|
-
type: :development
|
161
|
-
prerelease: false
|
162
|
-
version_requirements: !ruby/object:Gem::Requirement
|
163
|
-
requirements:
|
164
|
-
- - ">="
|
165
|
-
- !ruby/object:Gem::Version
|
166
|
-
version: '0'
|
167
125
|
description: An ActiveRecord extension to build nested scopes through pre-defined
|
168
126
|
associations
|
169
127
|
email:
|
@@ -175,6 +133,7 @@ files:
|
|
175
133
|
- ".gitignore"
|
176
134
|
- ".rspec"
|
177
135
|
- ".travis.yml"
|
136
|
+
- CHANGELOG.md
|
178
137
|
- Gemfile
|
179
138
|
- LICENSE
|
180
139
|
- README.md
|
@@ -183,9 +142,11 @@ files:
|
|
183
142
|
- gemfiles/rails50.gemfile
|
184
143
|
- gemfiles/rails51.gemfile
|
185
144
|
- gemfiles/rails52.gemfile
|
145
|
+
- gemfiles/rails60.gemfile
|
186
146
|
- lib/activerecord_nested_scope.rb
|
187
147
|
- lib/activerecord_nested_scope/builder.rb
|
188
148
|
- lib/activerecord_nested_scope/extension.rb
|
149
|
+
- lib/activerecord_nested_scope/node.rb
|
189
150
|
- lib/activerecord_nested_scope/railtie.rb
|
190
151
|
- lib/activerecord_nested_scope/version.rb
|
191
152
|
homepage: https://github.com/kanety/activerecord_nested_scope
|
@@ -206,8 +167,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
206
167
|
- !ruby/object:Gem::Version
|
207
168
|
version: '0'
|
208
169
|
requirements: []
|
209
|
-
|
210
|
-
rubygems_version: 2.5.2.2
|
170
|
+
rubygems_version: 3.1.2
|
211
171
|
signing_key:
|
212
172
|
specification_version: 4
|
213
173
|
summary: An ActiveRecord extension to build nested scopes
|