association_scope 0.3.0 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b6737d583c54a2ce44c51f9313544bc2b73d5d083b86fa502b6e82bbca3c48d1
4
- data.tar.gz: b85b28120cf9b7ac32a92f47db2ce4526391734188e9c13fcdfcb0762c177ba3
3
+ metadata.gz: 67d2f037f4c3007105757fe5b90f5307eb56614979e7cd4fa83d9036b5584321
4
+ data.tar.gz: d775072058420799584cf18dc27d80c246070dfd2ae1246e084f0a25acf3aa44
5
5
  SHA512:
6
- metadata.gz: 162342ebf690b58e11a7ebb201073ac17bbeec4bd57e4b51c5f88a1ee2b1077182e98430e604d343fc31a237f615cafd7a736fcf99beb700dd4ee004d401d74f
7
- data.tar.gz: 17e05e72a62217351f2d7bca06d6e0d2477ee9445df53cfb16681e728630cca3e6b2309b490fb50d6e5d6d175e1e0d7ecbb65fd81889eb54cdc714ff219006a9
6
+ metadata.gz: 5aa81882d47e6207b82deb659f43f3010e83d985fe1bed9a411e29c5796082509c4d19f4435bfdbb9d8a636878c06ac3dde49e7543bf5f7b7db38ec4583d0504
7
+ data.tar.gz: 22d3f940c21d6e1722eef994d0294a80d6e3f80a87a3bbadba1f457f1eeb6ef54fecc6ef3c9d761100213dcdc669f04c5f242cc8e95f4546151639f15727eb25
data/README.md CHANGED
@@ -19,7 +19,7 @@ class Topic < ApplicationRecord
19
19
  scope :of_users, -> (users) { joins(:user).where(users: users) }
20
20
  end
21
21
  ```
22
- over and over again across all of my models to write `Topic.of_users(current_user.friends)` when I wanted to write `current_user.friends.topics` instead.
22
+ over and over again across all of my models to write something like `Topic.of_users(current_user.friends)` when I wanted to write `current_user.friends.topics` instead.
23
23
  And `belongs_to` is the easiest part.
24
24
 
25
25
  When you have this problem, the AssociationScope gem is for you!
@@ -57,6 +57,23 @@ Topic.all.users
57
57
  ```
58
58
  to retrieve the users of all of the topics of your application.
59
59
 
60
+ ### Migration from `.of_model`
61
+ When you already use any form of `.of_model` scope, you can replace it with association scopes:
62
+
63
+ ```ruby
64
+ # replace
65
+ Topic.of_users(current_user.friends)
66
+ # with
67
+ current_user.friends.topics
68
+ ```
69
+ When you chain scopes, you have to merge with the previous scope:
70
+ ```ruby
71
+ # replace
72
+ scope.of_users(users)
73
+ # with
74
+ users.topics.merge(scope)
75
+ ```
76
+
60
77
  ## Known Issues
61
78
  * This gem works with `reflections`.
62
79
  To make this work, the `has_association_scope_on` call has to be below your association definitions.
@@ -11,7 +11,7 @@ module AssociationScope
11
11
  end
12
12
 
13
13
  def message
14
- "Association #{association} missing in #{missing_in}!"
14
+ "Association :#{association} missing in #{missing_in}!"
15
15
  end
16
16
  end
17
17
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AssociationScope
4
+ class PolymorphicAssociationError < StandardError
5
+ attr_accessor :association,
6
+ :model
7
+
8
+ def initialize(association: nil, model: nil)
9
+ @association = association
10
+ @model = model
11
+ end
12
+
13
+ def message
14
+ "Association :#{association} is polymorph in #{model}!"
15
+ end
16
+ end
17
+ end
@@ -4,22 +4,16 @@ module AssociationScope
4
4
  class Scope
5
5
  class BelongsToReflection < Scope
6
6
  def apply
7
+ if reflection_details.options[:polymorphic]
8
+ raise PolymorphicAssociationError.new association: association, model: model
9
+ end
10
+
7
11
  association = @association
8
- details = model.reflections[association]
9
- class_name = details.options[:class_name]&.constantize || association.camelize.constantize
12
+ class_name = reflection_details.options[:class_name]&.constantize || association.camelize.constantize
13
+ foreign_key = reflection_details.options[:foreign_key]
10
14
  association_name = association.to_s.underscore.to_sym
11
- foreign_key = details.options[:foreign_key]
12
15
  own_table_name = class_name.to_s.pluralize.underscore
13
-
14
- inverse_reflection = class_name.reflections[model.to_s.underscore.singularize] || class_name.reflections[model.to_s.underscore.pluralize]
15
- case inverse_reflection&.source_reflection&.class&.to_s&.split("::")&.last
16
- when "HasOneReflection"
17
- table_name = model.to_s.underscore.to_sym
18
- when "HasManyReflection"
19
- table_name = model.to_s.underscore.pluralize.to_sym
20
- else
21
- raise AssociationMissingError.new missing_in: class_name, association: model.to_s.underscore.pluralize
22
- end
16
+ table_name = table_name class_name
23
17
 
24
18
  model.class_eval <<-RUBY, __FILE__, __LINE__ + 1
25
19
  scope association.pluralize, -> do
@@ -36,6 +30,27 @@ module AssociationScope
36
30
  end
37
31
  RUBY
38
32
  end
33
+
34
+ private
35
+
36
+ def table_name(class_name)
37
+ case inverse_reflection(class_name)&.source_reflection&.class&.to_s&.split("::")&.last
38
+ when "HasOneReflection"
39
+ model.to_s.underscore.to_sym
40
+ when "HasManyReflection"
41
+ model.to_s.underscore.pluralize.to_sym
42
+ else
43
+ raise AssociationMissingError.new missing_in: class_name, association: model.to_s.underscore.pluralize
44
+ end
45
+ end
46
+
47
+ def inverse_reflection(class_name)
48
+ class_name.reflections[model.to_s.underscore.singularize] || class_name.reflections[model.to_s.underscore.pluralize]
49
+ end
50
+
51
+ def reflection_details
52
+ model.reflections[association]
53
+ end
39
54
  end
40
55
  end
41
56
  end
@@ -5,13 +5,12 @@ module AssociationScope
5
5
  class HasAndBelongsToManyReflection < Scope
6
6
  def apply
7
7
  association = @association.pluralize
8
- details = model.reflections[association]
9
- class_name = details.options[:class_name]&.constantize || association.singularize.camelize.constantize
8
+ class_name = reflection_details.options[:class_name]&.constantize || association.singularize.camelize.constantize
10
9
  table_name = model.to_s.underscore.pluralize.to_sym
11
10
 
12
- model.class_eval <<-RUBY, __FILE__, __LINE__ + 1
13
- raise AssociationMissingError.new(missing_in: class_name, association: table_name) unless class_name.reflections.has_key?(table_name.to_s)
11
+ raise AssociationMissingError.new(missing_in: class_name, association: table_name) unless class_name.reflections.has_key?(table_name.to_s)
14
12
 
13
+ model.class_eval <<-RUBY, __FILE__, __LINE__ + 1
15
14
  scope association, -> do
16
15
  class_name
17
16
  .joins(table_name)
@@ -20,6 +19,12 @@ module AssociationScope
20
19
  end
21
20
  RUBY
22
21
  end
22
+
23
+ private
24
+
25
+ def reflection_details
26
+ model.reflections[association]
27
+ end
23
28
  end
24
29
  end
25
30
  end
@@ -4,14 +4,14 @@ module AssociationScope
4
4
  class Scope
5
5
  class HasManyReflection < Scope
6
6
  def apply
7
- details = model.reflections[@association]
8
- class_name = details.options[:class_name]&.constantize || association.singularize.camelize.constantize
7
+ class_name = reflection_details.options[:class_name]&.constantize || association.singularize.camelize.constantize
8
+
9
9
  association = @association.pluralize
10
- column_name = model.to_s.underscore
10
+ column_name = reflection_details.options[:as] || model.to_s.underscore
11
11
 
12
- model.class_eval <<-RUBY, __FILE__, __LINE__ + 1
13
- raise AssociationMissingError.new(missing_in: class_name, association: column_name) unless class_name.reflections.has_key?(column_name)
12
+ raise AssociationMissingError.new(missing_in: class_name, association: column_name) unless class_name.reflections.has_key?(column_name.to_s)
14
13
 
14
+ model.class_eval <<-RUBY, __FILE__, __LINE__ + 1
15
15
  scope association, -> do
16
16
  class_name
17
17
  .where(column_name => self)
@@ -19,6 +19,12 @@ module AssociationScope
19
19
  end
20
20
  RUBY
21
21
  end
22
+
23
+ private
24
+
25
+ def reflection_details
26
+ model.reflections[@association]
27
+ end
22
28
  end
23
29
  end
24
30
  end
@@ -3,6 +3,7 @@
3
3
  module AssociationScope
4
4
  class Scope
5
5
  class HasOneReflection < HasManyReflection
6
+ # works the same way like has_many reflections
6
7
  end
7
8
  end
8
9
  end
@@ -4,24 +4,14 @@ module AssociationScope
4
4
  class Scope
5
5
  class ThroughReflection < Scope
6
6
  def apply
7
- details = model.reflections[@association]
8
7
  association = @association
9
- class_name = details.options[:class_name]&.constantize || association.singularize.camelize.constantize
8
+ class_name = reflection_details.options[:class_name]&.constantize || association.singularize.camelize.constantize
9
+ first_join = inverse_reflection(class_name)&.options&.fetch(:through, nil) || inverse_reflection(class_name)&.options&.fetch(:source, nil)
10
+ second_join = compute_second_join class_name
10
11
 
11
- inverse = details.options[:inverse_of]&.to_s || model.to_s.underscore
12
- inverse_reflection = class_name.reflections[inverse.singularize] || class_name.reflections[inverse.pluralize]
13
-
14
- first_join = inverse_reflection&.options&.fetch(:through, nil) || inverse_reflection&.options&.fetch(:source, nil)
15
-
16
- reflection_type = inverse_reflection&.source_reflection&.class&.to_s&.split("::")&.last
17
- second_join = if %w[HasOneReflection BelongsToReflection].include?(reflection_type)
18
- model.to_s.underscore.to_sym
19
- else
20
- model.to_s.underscore.pluralize.to_sym
21
- end
12
+ raise AssociationMissingError.new missing_in: class_name, association: inverse unless inverse_reflection(class_name)
22
13
 
23
14
  model.class_eval <<-RUBY, __FILE__, __LINE__ + 1
24
- raise AssociationMissingError.new missing_in: class_name, association: inverse unless inverse_reflection
25
15
  scope association.pluralize, -> do
26
16
  class_name
27
17
  .joins(first_join => second_join)
@@ -29,6 +19,32 @@ module AssociationScope
29
19
  end
30
20
  RUBY
31
21
  end
22
+
23
+ private
24
+
25
+ def reflection_details
26
+ model.reflections[association]
27
+ end
28
+
29
+ def inverse
30
+ reflection_details.options[:inverse_of]&.to_s || model.to_s.underscore
31
+ end
32
+
33
+ def inverse_reflection class_name
34
+ class_name.reflections[inverse.singularize] || class_name.reflections[inverse.pluralize]
35
+ end
36
+
37
+ def reflection_type class_name
38
+ inverse_reflection(class_name)&.source_reflection&.class&.to_s&.split("::")&.last
39
+ end
40
+
41
+ def compute_second_join class_name
42
+ if %w[HasOneReflection BelongsToReflection].include?(reflection_type(class_name))
43
+ model.to_s.underscore.to_sym
44
+ else
45
+ model.to_s.underscore.pluralize.to_sym
46
+ end
47
+ end
32
48
  end
33
49
  end
34
50
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AssociationScope
4
- VERSION = "0.3.0"
4
+ VERSION = "0.4.0"
5
5
  end
@@ -9,6 +9,7 @@ require "association_scope/scope/has_and_belongs_to_many_reflection"
9
9
  require "association_scope/scope/through_reflection"
10
10
 
11
11
  require "association_scope/errors/association_missing_error"
12
+ require "association_scope/errors/polymorphic_association_error"
12
13
 
13
14
  module AssociationScope
14
15
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: association_scope
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - datae
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-11-07 00:00:00.000000000 Z
11
+ date: 2021-11-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '3.10'
69
+ - !ruby/object:Gem::Dependency
70
+ name: awesome_print
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 1.9.2
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 1.9.2
69
83
  description: AssociationScope adds useful scopes targeting Associations in ActiveRecord.
70
84
  email:
71
85
  - accounts@datae.de
@@ -78,6 +92,7 @@ files:
78
92
  - Rakefile
79
93
  - lib/association_scope.rb
80
94
  - lib/association_scope/errors/association_missing_error.rb
95
+ - lib/association_scope/errors/polymorphic_association_error.rb
81
96
  - lib/association_scope/scope.rb
82
97
  - lib/association_scope/scope/belongs_to_reflection.rb
83
98
  - lib/association_scope/scope/has_and_belongs_to_many_reflection.rb
@@ -100,14 +115,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
100
115
  requirements:
101
116
  - - ">="
102
117
  - !ruby/object:Gem::Version
103
- version: '0'
118
+ version: 2.6.8
104
119
  required_rubygems_version: !ruby/object:Gem::Requirement
105
120
  requirements:
106
121
  - - ">="
107
122
  - !ruby/object:Gem::Version
108
123
  version: '0'
109
124
  requirements: []
110
- rubygems_version: 3.1.4
125
+ rubygems_version: 3.2.25
111
126
  signing_key:
112
127
  specification_version: 4
113
128
  summary: AssociationScope adds useful scopes targeting Associations in ActiveRecord.