active_record-union_relation 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: {}