active_record-union_relation 0.1.0 → 0.2.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.
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'active_record'
4
- require 'active_record/union_relation/version'
3
+ require "active_record"
4
+ require "active_record/union_relation/version"
5
5
 
6
6
  module ActiveRecord
7
7
  class UnionRelation
@@ -21,7 +21,7 @@ module ActiveRecord
21
21
  # raise this error so there's not some weird undefined method behavior.
22
22
  class NoConfiguredSubqueriesError < Error
23
23
  def initialize
24
- super('No subqueries have been configured for this union')
24
+ super("No subqueries have been configured for this union")
25
25
  end
26
26
  end
27
27
 
@@ -31,7 +31,7 @@ module ActiveRecord
31
31
  # Sometimes you need some columns in some subqeries that you don't need in
32
32
  # others. In order to accomplish that and still maintain the matching
33
33
  # number of columns, you can put a null in space of a column instead.
34
- NULL = Arel.sql('NULL')
34
+ NULL = Arel.sql("NULL")
35
35
 
36
36
  attr_reader :relation, :model_name, :sources
37
37
 
@@ -42,18 +42,20 @@ module ActiveRecord
42
42
  end
43
43
 
44
44
  def to_arel(columns, discriminator)
45
- relation
46
- .select(
47
- Arel.sql("'#{model_name}'").as(quote_column_name(discriminator)),
48
- *sources.zip(columns).map do |(source, column)|
45
+ relation.select(
46
+ Arel.sql("'#{model_name}'").as(quote_column_name(discriminator)),
47
+ *sources
48
+ .zip(columns)
49
+ .map do |(source, column)|
49
50
  Arel.sql(source.to_s).as(quote_column_name(column))
50
51
  end
51
- )
52
- .arel
52
+ ).arel
53
53
  end
54
54
 
55
55
  def to_mapping(columns)
56
- [model_name, columns.zip(sources).to_h]
56
+ # Remove the scope_name/table_name when using table_name.column
57
+ sources_without_scope = sources.map { _1.split(".").last }
58
+ [model_name, columns.zip(sources_without_scope).to_h]
57
59
  end
58
60
 
59
61
  private
@@ -96,16 +98,45 @@ module ActiveRecord
96
98
  mappings = subqueries.to_h { |subquery| subquery.to_mapping(columns) }
97
99
 
98
100
  Class.new(model) do
99
- define_singleton_method(:inheritance_column) { discriminator }
100
- define_singleton_method(:instantiate) do |attributes, column_types = {}, &block|
101
- type = attributes.delete(inheritance_column)
102
-
103
- instantiate_instance_of(
104
- Object.const_get(type),
105
- attributes.transform_keys(&mappings[type]),
106
- column_types,
107
- &block
108
- )
101
+ # Set the inheritance column and register the discriminator as a string
102
+ # column so that Active Record will instantiate the right subclass.
103
+ self.inheritance_column = discriminator
104
+ attribute inheritance_column, :string
105
+
106
+ define_singleton_method(:instantiate) do |attrs, columns = {}, &block|
107
+ mapped = {}
108
+ mapping = mappings[attrs[inheritance_column]]
109
+
110
+ # Map the result set columns back to their original source column
111
+ # names. This ensures that even though the UNION saw them as the same
112
+ # columns our resulting records see them as their original names.
113
+ attrs.each do |key, value|
114
+ case mapping[key]
115
+ when Subquery::NULL
116
+ # Ignore columns that didn't have a value.
117
+ when nil
118
+ # If we don't have a mapping for this column, then it's the
119
+ # discriminator. Map that column directly.
120
+ mapped[key] = value
121
+ else
122
+ # Otherwise, use the mapping to map the column back to its
123
+ # original name.
124
+ mapped[mapping[key]] = value
125
+ end
126
+ end
127
+
128
+ # Now that we've mapped all of the columns, we can call super with the
129
+ # mapped values.
130
+ super(mapped, columns, &block)
131
+ end
132
+
133
+ # Override the default find_sti_class method because it does sanity
134
+ # checks to ensure that the class you're trying to instantiate is a
135
+ # subclass of the current class. Since we want to explicitly _not_ do
136
+ # that, we will instead just check that it is a valid model class.
137
+ define_singleton_method(:find_sti_class) do |type_name|
138
+ type = type_name.constantize
139
+ type < ActiveRecord::Base ? type : super(type_name)
109
140
  end
110
141
  end
111
142
  end
@@ -113,9 +144,9 @@ module ActiveRecord
113
144
  def union_for(model)
114
145
  Arel::Nodes::As.new(
115
146
  subqueries
116
- .map { |subquery| subquery.to_arel(columns, discriminator) }
147
+ .map { |subquery| subquery.to_arel(columns, discriminator).ast }
117
148
  .inject { |left, right| Arel::Nodes::Union.new(left, right) },
118
- Arel.sql(model.connection.quote_table_name('union'))
149
+ Arel.sql(model.connection.quote_table_name("union"))
119
150
  )
120
151
  end
121
152
  end
@@ -129,7 +160,7 @@ module ActiveRecord
129
160
  # One additional column will be added to the query in order to discriminate
130
161
  # between all of the unioned types. Then when the objects are going to be
131
162
  # instantiated, we map the columns back to their original names.
132
- def self.union(*columns, discriminator: 'discriminator')
163
+ def self.union(*columns, discriminator: "discriminator")
133
164
  UnionRelation.new(columns, discriminator).tap { |union| yield union }.all
134
165
  end
135
166
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_record-union_relation
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
- - Kevin Deisz
8
- autorequire:
7
+ - Kevin Newton
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-10-08 00:00:00.000000000 Z
11
+ date: 2024-02-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -28,69 +28,69 @@ dependencies:
28
28
  name: minitest
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '5.14'
33
+ version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '5.14'
40
+ version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: pg
42
+ name: rails
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '1.2'
47
+ version: '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: '1.2'
54
+ version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: rails
56
+ name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - "~>"
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: '6.0'
61
+ version: '0'
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: '6.0'
68
+ version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: rake
70
+ name: syntax_tree
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - "~>"
73
+ - - ">="
74
74
  - !ruby/object:Gem::Version
75
- version: '13.0'
75
+ version: '0'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - "~>"
80
+ - - ">="
81
81
  - !ruby/object:Gem::Version
82
- version: '13.0'
83
- description:
82
+ version: '0'
83
+ description:
84
84
  email:
85
- - kevin.deisz@gmail.com
85
+ - kddnewton@gmail.com
86
86
  executables: []
87
87
  extensions: []
88
88
  extra_rdoc_files: []
89
89
  files:
90
90
  - ".github/dependabot.yml"
91
+ - ".github/workflows/auto-merge.yml"
91
92
  - ".github/workflows/main.yml"
92
93
  - ".gitignore"
93
- - ".mergify.yml"
94
94
  - CHANGELOG.md
95
95
  - CODE_OF_CONDUCT.md
96
96
  - Gemfile
@@ -101,13 +101,23 @@ files:
101
101
  - active_record-union_relation.gemspec
102
102
  - bin/console
103
103
  - bin/setup
104
+ - gemfiles/mysql/Gemfile
105
+ - gemfiles/mysql/Gemfile.lock
106
+ - gemfiles/postgresql/Gemfile
107
+ - gemfiles/postgresql/Gemfile.lock
108
+ - gemfiles/sqlite/Gemfile
109
+ - gemfiles/sqlite/Gemfile.lock
104
110
  - lib/active_record/union_relation.rb
105
111
  - lib/active_record/union_relation/version.rb
106
- homepage: https://github.com/kddeisz/active_record-union_relation
112
+ homepage: https://github.com/kddnewton/active_record-union_relation
107
113
  licenses:
108
114
  - MIT
109
- metadata: {}
110
- post_install_message:
115
+ metadata:
116
+ bug_tracker_uri: https://github.com/kddnewton/active_record-union_relation/issues
117
+ changelog_uri: https://github.com/kddnewton/active_record-union_relation/blob/v0.2.0/CHANGELOG.md
118
+ source_code_uri: https://github.com/kddnewton/active_record-union_relation
119
+ rubygems_mfa_required: 'true'
120
+ post_install_message:
111
121
  rdoc_options: []
112
122
  require_paths:
113
123
  - lib
@@ -122,8 +132,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
122
132
  - !ruby/object:Gem::Version
123
133
  version: '0'
124
134
  requirements: []
125
- rubygems_version: 3.1.2
126
- signing_key:
135
+ rubygems_version: 3.4.1
136
+ signing_key:
127
137
  specification_version: 4
128
138
  summary: Create ActiveRecord relations from UNIONs
129
139
  test_files: []
data/.mergify.yml DELETED
@@ -1,10 +0,0 @@
1
- pull_request_rules:
2
- - name: Automatically merge dependencies
3
- conditions:
4
- - base=master
5
- - label=dependencies
6
- - status-success=CI
7
- actions:
8
- merge:
9
- strict: true
10
- delete_head_branch: {}