active_record_extended 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0e7c1bd1aaee6cd16497cd2f64bed3f06907a31ac48059da909dc33c0f4caa26
4
- data.tar.gz: 5d1e21f8547272af210b11c7590e7094e69eeaad6714a50f76566b04eef3003e
3
+ metadata.gz: d3332acf1ce7aa530fcbaddc871d5bdfc4560f71794fc4efbdd33653c6ea1eb5
4
+ data.tar.gz: 73df494665607c73c5ddb2999aa0e021e166fc436a1cd75784cd1ffa30e1fe9e
5
5
  SHA512:
6
- metadata.gz: 320aa807eded4abeb5f519b7e58b66a4cdaba77f62eaa0549bffeddc000bf6426726733337dd3dfc18e1c651594c7f2fb4f09c32f4d9bbe640a71abe16ce5fda
7
- data.tar.gz: 3e8ffdc9e76ea8eb5ec47dba4d8d795bbcda5f780879213fc385074dea024ab01851b4b552631cb1011b2b83a949416348efbd749c442255e76b00e4ebcd9f38
6
+ metadata.gz: 94c04d0fb830fd9e4b7f0b21bea2fb7a3b753903f4fb41ffb71fc72ed147ac3247507438411a37bf74235aaea943c2f98b5b5141467cf09b663dd9930269d2f0
7
+ data.tar.gz: 3c5915ec558d623704dc4b824f052639dc25df2a0c0385f143eb597f4984f4beab1ef1d4083c7fbbf696ac0314b3ddd860640f8ecdc601681a4b042fe0224256
@@ -2,10 +2,16 @@
2
2
 
3
3
  require "active_record"
4
4
  require "active_record/relation"
5
+ require "active_record/relation/merger"
5
6
  require "active_record/relation/query_methods"
6
7
 
7
8
  require "active_record_extended/predicate_builder/array_handler_decorator"
8
- Dir["#{File.dirname(__FILE__)}/query_methods/**/*.rb"].each { |f| require File.expand_path(f) }
9
+
10
+ require "active_record_extended/query_methods/where_chain"
11
+ require "active_record_extended/query_methods/with_cte"
12
+ require "active_record_extended/query_methods/any_of"
13
+ require "active_record_extended/query_methods/either"
14
+ require "active_record_extended/query_methods/inet"
9
15
 
10
16
  if ActiveRecord::VERSION::MAJOR == 5 && ActiveRecord::VERSION::MINOR <= 1
11
17
  if ActiveRecord::VERSION::MINOR.zero?
@@ -1,4 +1,3 @@
1
-
2
1
  # frozen_string_literal: true
3
2
 
4
3
  require "active_record_extended/arel/nodes"
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecordExtended
4
+ module QueryMethods
5
+ module MergerCTE
6
+ def normal_values
7
+ super + [:with]
8
+ end
9
+ end
10
+
11
+ module QueryDelegationCTE
12
+ delegate :with, to: :all
13
+ end
14
+
15
+ module WithCTE
16
+ class WithChain
17
+ def initialize(scope)
18
+ @scope = scope
19
+ end
20
+
21
+ def recursive(*args)
22
+ @scope.tap do |scope|
23
+ scope.with_values += args
24
+ scope.recursive_value = true
25
+ end
26
+ end
27
+ end
28
+
29
+ def with_values
30
+ @values[:with] || []
31
+ end
32
+
33
+ def with_values?
34
+ !(@values[:with].nil? || @values[:with].empty?)
35
+ end
36
+
37
+ def with_values=(values)
38
+ @values[:with] = values
39
+ end
40
+
41
+ def recursive_value=(value)
42
+ raise ImmutableRelation if @loaded
43
+ @values[:recursive] = value
44
+ end
45
+
46
+ def recursive_value
47
+ @values[:recursive]
48
+ end
49
+ alias recursive_value? recursive_value
50
+
51
+ def with(opts = :chain, *rest)
52
+ return WithChain.new(spawn) if opts == :chain
53
+ opts.blank? ? self : spawn.with!(opts, *rest)
54
+ end
55
+
56
+ def with!(opts = :chain, *rest)
57
+ return WithChain.new(self) if opts == :chain
58
+ self.with_values += [opts] + rest
59
+ self
60
+ end
61
+
62
+ def build_arel(*aliases)
63
+ super.tap do |arel|
64
+ build_with(arel) if with_values?
65
+ end
66
+ end
67
+
68
+ def build_with_hashed_value(with_value)
69
+ with_value.map do |name, expression|
70
+ select =
71
+ case expression
72
+ when String
73
+ Arel::Nodes::SqlLiteral.new("(#{expression})")
74
+ when ActiveRecord::Relation, Arel::SelectManager
75
+ Arel::Nodes::SqlLiteral.new("(#{expression.to_sql})")
76
+ end
77
+ next if select.nil?
78
+ Arel::Nodes::As.new(Arel::Nodes::SqlLiteral.new(PG::Connection.quote_ident(name.to_s)), select)
79
+ end
80
+ end
81
+
82
+ def build_with(arel)
83
+ with_statements = with_values.flat_map do |with_value|
84
+ case with_value
85
+ when String, Arel::Nodes::As
86
+ with_value
87
+ when Hash
88
+ build_with_hashed_value(with_value)
89
+ end
90
+ end.compact
91
+
92
+ return if with_statements.empty?
93
+ recursive_value? ? arel.with(:recursive, with_statements) : arel.with(with_statements)
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ ActiveRecord::Relation.prepend(ActiveRecordExtended::QueryMethods::WithCTE)
100
+ ActiveRecord::Relation::Merger.prepend(ActiveRecordExtended::QueryMethods::MergerCTE)
101
+ ActiveRecord::Querying.prepend(ActiveRecordExtended::QueryMethods::QueryDelegationCTE)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecordExtended
4
- VERSION = "0.6.0"
4
+ VERSION = "0.7.0"
5
5
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "spec_helper"
4
+
3
5
  RSpec.describe "Active Record Any / None of Methods" do
4
6
  let!(:one) { Person.create!(personal_id: 1) }
5
7
  let!(:two) { Person.create!(personal_id: 2) }
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe "Active Record With CTE Query Methods" do
6
+ let!(:person_one) { Person.create! }
7
+ let!(:person_two) { Person.create! }
8
+ let!(:profile_one) { ProfileL.create!(person_id: person_one.id, likes: 200) }
9
+ let!(:profile_two) { ProfileL.create!(person_id: person_two.id, likes: 500) }
10
+
11
+ describe ".with/1" do
12
+ context "when using as a standalone query" do
13
+ it "should only return a person with less than 300 likes" do
14
+ query = Person.with(profile: ProfileL.where("likes < 300"))
15
+ .joins("JOIN profile ON profile.id = people.id")
16
+
17
+ expect(query).to match_array([person_one])
18
+ end
19
+
20
+ it "should return anyone with likes greater than or equal to 200" do
21
+ query = Person.with(profile: ProfileL.where("likes >= 200"))
22
+ .joins("JOIN profile ON profile.id = people.id")
23
+
24
+ expect(query).to match_array([person_one, person_two])
25
+ end
26
+ end
27
+
28
+ context "when merging in query" do
29
+ let!(:version_one) { VersionControl.create!(versionable: profile_one, source: { help: "me" }) }
30
+ let!(:version_two) { VersionControl.create!(versionable: profile_two, source: { help: "no one" }) }
31
+
32
+ it "will maintain the CTE table when merging into existing AR queries" do
33
+ sub_query = ProfileL.with(version_controls: VersionControl.where.contains(source: { help: "me" }))
34
+ query = Person.joins(profile_l: :version).merge(sub_query)
35
+
36
+ expect(query).to match_array([person_one])
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe "Active Record WITH CTE tables" do
6
+ let(:with_personal_query) { /WITH.+personal_id_one.+AS \(SELECT.+people.+FROM.+WHERE.+people.+personal_id.+ = 1\)/ }
7
+
8
+ it "should contain WITH statement that creates the CTE table" do
9
+ query = Person.with(personal_id_one: Person.where(personal_id: 1))
10
+ .joins("JOIN personal_id_one ON personal_id_one.id = people.id")
11
+ .to_sql
12
+ expect(query).to match_regex(with_personal_query)
13
+ end
14
+
15
+ it "will maintain the CTE table when merging" do
16
+ query = Person.all
17
+ .merge(Person.with(personal_id_one: Person.where(personal_id: 1)))
18
+ .joins("JOIN personal_id_one ON personal_id_one.id = people.id")
19
+ .to_sql
20
+
21
+ expect(query).to match_regex(with_personal_query)
22
+ end
23
+
24
+ context "when multiple CTE's" do
25
+ let(:chained_with) do
26
+ Person.with(personal_id_one: Person.where(personal_id: 1))
27
+ .with(personal_id_two: Person.where(personal_id: 2))
28
+ .joins("JOIN personal_id_one ON personal_id_one.id = people.id")
29
+ .joins("JOIN personal_id_two ON personal_id_two.id = people.id")
30
+ .to_sql
31
+ end
32
+
33
+ let(:with_arguments) do
34
+ Person.with(personal_id_one: Person.where(personal_id: 1), personal_id_two: Person.where(personal_id: 2))
35
+ .joins("JOIN personal_id_one ON personal_id_one.id = people.id")
36
+ .joins("JOIN personal_id_two ON personal_id_two.id = people.id")
37
+ .to_sql
38
+ end
39
+ it "Should only contain a single WITH statement" do
40
+ expect(with_arguments.scan(/WITH/).count).to eq(1)
41
+ expect(with_arguments.scan(/AS/).count).to eq(2)
42
+ end
43
+
44
+ it "Should only contain a single WITH statement when chaining" do
45
+ expect(chained_with.scan(/WITH/).count).to eq(1)
46
+ expect(chained_with.scan(/AS/).count).to eq(2)
47
+ end
48
+ end
49
+
50
+ context "when chaining the recursive method" do
51
+ let(:with_recursive_personal_query) do
52
+ /WITH.+RECURSIVE.+personal_id_one.+AS \(SELECT.+people.+FROM.+WHERE.+people.+personal_id.+ = 1\)/
53
+ end
54
+
55
+ let(:with_recursive) do
56
+ Person.with
57
+ .recursive(personal_id_one: Person.where(personal_id: 1))
58
+ .joins("JOIN personal_id_one ON personal_id_one.id = people.id")
59
+ .to_sql
60
+ end
61
+
62
+ it "generates an expression with recursive" do
63
+ expect(with_recursive).to match_regex(with_recursive_personal_query)
64
+ end
65
+ end
66
+ end
@@ -1,4 +1,3 @@
1
-
2
1
  # frozen_string_literal: true
3
2
 
4
3
  class Person < ActiveRecord::Base
@@ -13,8 +12,14 @@ end
13
12
 
14
13
  class ProfileL < ActiveRecord::Base
15
14
  belongs_to :person
15
+ has_one :version, as: :versionable, class_name: "VersionControl"
16
16
  end
17
17
 
18
18
  class ProfileR < ActiveRecord::Base
19
19
  belongs_to :person
20
+ has_one :version, as: :versionable, class_name: "VersionControl"
21
+ end
22
+
23
+ class VersionControl < ActiveRecord::Base
24
+ belongs_to :versionable, polymorphic: true, optional: false
20
25
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_record_extended
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - George Protacio-Karaszi
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2018-07-25 00:00:00.000000000 Z
13
+ date: 2018-09-22 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activerecord
@@ -160,6 +160,7 @@ files:
160
160
  - lib/active_record_extended/query_methods/either.rb
161
161
  - lib/active_record_extended/query_methods/inet.rb
162
162
  - lib/active_record_extended/query_methods/where_chain.rb
163
+ - lib/active_record_extended/query_methods/with_cte.rb
163
164
  - lib/active_record_extended/version.rb
164
165
  - spec/active_record_extended_spec.rb
165
166
  - spec/query_methods/any_of_spec.rb
@@ -167,12 +168,14 @@ files:
167
168
  - spec/query_methods/either_spec.rb
168
169
  - spec/query_methods/hash_query_spec.rb
169
170
  - spec/query_methods/inet_query_spec.rb
171
+ - spec/query_methods/with_cte_spec.rb
170
172
  - spec/spec_helper.rb
171
173
  - spec/sql_inspections/any_of_sql_spec.rb
172
174
  - spec/sql_inspections/arel/array_spec.rb
173
175
  - spec/sql_inspections/arel/inet_spec.rb
174
176
  - spec/sql_inspections/contains_sql_queries_spec.rb
175
177
  - spec/sql_inspections/either_sql_spec.rb
178
+ - spec/sql_inspections/with_cte_sql_spec.rb
176
179
  - spec/support/database_cleaner.rb
177
180
  - spec/support/models.rb
178
181
  homepage: https://github.com/georgekaraszi/ActiveRecordExtended
@@ -206,11 +209,13 @@ test_files:
206
209
  - spec/query_methods/either_spec.rb
207
210
  - spec/query_methods/hash_query_spec.rb
208
211
  - spec/query_methods/inet_query_spec.rb
212
+ - spec/query_methods/with_cte_spec.rb
209
213
  - spec/spec_helper.rb
210
214
  - spec/sql_inspections/any_of_sql_spec.rb
211
215
  - spec/sql_inspections/arel/array_spec.rb
212
216
  - spec/sql_inspections/arel/inet_spec.rb
213
217
  - spec/sql_inspections/contains_sql_queries_spec.rb
214
218
  - spec/sql_inspections/either_sql_spec.rb
219
+ - spec/sql_inspections/with_cte_sql_spec.rb
215
220
  - spec/support/database_cleaner.rb
216
221
  - spec/support/models.rb