active_record_extended 1.2.0 → 2.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +85 -7
- data/lib/active_record_extended/active_record.rb +2 -11
- data/lib/active_record_extended/active_record/relation_patch.rb +21 -4
- data/lib/active_record_extended/arel.rb +1 -0
- data/lib/active_record_extended/arel/nodes.rb +24 -21
- data/lib/active_record_extended/arel/predications.rb +3 -2
- data/lib/active_record_extended/arel/sql_literal.rb +16 -0
- data/lib/active_record_extended/arel/visitors/postgresql_decorator.rb +1 -1
- data/lib/active_record_extended/query_methods/any_of.rb +5 -4
- data/lib/active_record_extended/query_methods/either.rb +2 -1
- data/lib/active_record_extended/query_methods/inet.rb +6 -2
- data/lib/active_record_extended/query_methods/json.rb +14 -17
- data/lib/active_record_extended/query_methods/select.rb +13 -12
- data/lib/active_record_extended/query_methods/unionize.rb +13 -7
- data/lib/active_record_extended/query_methods/where_chain.rb +17 -8
- data/lib/active_record_extended/query_methods/window.rb +93 -0
- data/lib/active_record_extended/query_methods/with_cte.rb +104 -37
- data/lib/active_record_extended/utilities/order_by.rb +11 -30
- data/lib/active_record_extended/utilities/support.rb +21 -18
- data/lib/active_record_extended/version.rb +1 -1
- data/spec/query_methods/any_of_spec.rb +2 -2
- data/spec/query_methods/either_spec.rb +11 -0
- data/spec/query_methods/json_spec.rb +5 -5
- data/spec/query_methods/select_spec.rb +13 -13
- data/spec/query_methods/unionize_spec.rb +5 -5
- data/spec/query_methods/window_spec.rb +51 -0
- data/spec/query_methods/with_cte_spec.rb +12 -2
- data/spec/spec_helper.rb +1 -1
- data/spec/sql_inspections/any_of_sql_spec.rb +2 -2
- data/spec/sql_inspections/contains_sql_queries_spec.rb +8 -8
- data/spec/sql_inspections/either_sql_spec.rb +19 -3
- data/spec/sql_inspections/json_sql_spec.rb +7 -1
- data/spec/sql_inspections/unionize_sql_spec.rb +2 -2
- data/spec/sql_inspections/window_sql_spec.rb +98 -0
- data/spec/sql_inspections/with_cte_sql_spec.rb +30 -1
- data/spec/support/models.rb +18 -0
- metadata +23 -20
- data/lib/active_record_extended/patch/5_0/predicate_builder_decorator.rb +0 -87
- data/lib/active_record_extended/patch/5_0/regex_match.rb +0 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bbcdc3e208ac89fd2d55b8944ad7c0483e57f21a89941a4848beb548b56ce293
|
4
|
+
data.tar.gz: 10465bfe73686aae4fad33a7da0e533ed22401c68da5655654e1538a8bbb34c0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1ed8f4d6fe19c384bbb8add11443cc78b6f23cf4adc1808bdd0a1b6328a2dbeac22d4e0fa8fc6257dde505646d1c3613ed90711bbd36321cbb74231cf621958d
|
7
|
+
data.tar.gz: e139454fc996ad112d70332e39b002ba164d2172100f6b61f2494f090a0a3afe16c0e5e51998ab8f8cb8e839c6b0cb1104d152f821ae65d55bec4c83ec9c990a
|
data/README.md
CHANGED
@@ -36,6 +36,9 @@
|
|
36
36
|
- [Union As](#union-as)
|
37
37
|
- [Union Order](#union-order)
|
38
38
|
- [Union Reorder](#union-reorder)
|
39
|
+
- [Window Functions](#window-functions)
|
40
|
+
- [Define Window](#define-window)
|
41
|
+
- [Select Window](#select-window)
|
39
42
|
|
40
43
|
## Description and History
|
41
44
|
|
@@ -49,11 +52,12 @@ Active Record Extended is essentially providing users with the other half of Pos
|
|
49
52
|
## Compatibility
|
50
53
|
|
51
54
|
This package is designed align and work with any officially supported Ruby and Rails versions.
|
52
|
-
- Minimum Ruby Version: 2.
|
53
|
-
- Minimum Rails Version: 5.
|
54
|
-
-
|
55
|
-
- Latest
|
56
|
-
-
|
55
|
+
- Minimum Ruby Version: 2.4.x **(EOL warning!)**
|
56
|
+
- Minimum Rails Version: 5.1.x **(EOL warning!)**
|
57
|
+
- Minimum Postgres Version: 9.6.x **(EOL warning!)**
|
58
|
+
- Latest Ruby supported: 2.7.x
|
59
|
+
- Latest Rails supported: 6.1.x
|
60
|
+
- Postgres: 9.6-current(13) (probably works with most older versions to a certain point)
|
57
61
|
|
58
62
|
## Installation
|
59
63
|
|
@@ -427,7 +431,7 @@ While quite the mouthful of an explanation. The implementation of combining unre
|
|
427
431
|
product_query =
|
428
432
|
Product.select(:id)
|
429
433
|
.joins(:items)
|
430
|
-
.select_row_to_json(item_query, key: :outer_items, as: :items,
|
434
|
+
.select_row_to_json(item_query, key: :outer_items, as: :items, cast_with: :array) do |item_scope|
|
431
435
|
item_scope.where("outer_items.product_id = products.id")
|
432
436
|
# Results to:
|
433
437
|
# SELECT ..., ARRAY(SELECT ROW_TO_JSON("outer_items")
|
@@ -437,7 +441,7 @@ While quite the mouthful of an explanation. The implementation of combining unre
|
|
437
441
|
end
|
438
442
|
|
439
443
|
# Not defining a key will automatically generate a random key between a-z
|
440
|
-
category_query = Category.select(:name, :id).select_row_to_json(product_query, as: :products,
|
444
|
+
category_query = Category.select(:name, :id).select_row_to_json(product_query, as: :products, cast_with: :array)
|
441
445
|
Category.json_build_object(:physical_category, category_query.where(id: physical_cat.id)).results
|
442
446
|
#=> {
|
443
447
|
# "physical_category" => {
|
@@ -787,6 +791,80 @@ SELECT "people".*
|
|
787
791
|
) ) ORDER BY personal_id DESC, id DESC) people
|
788
792
|
```
|
789
793
|
|
794
|
+
#### Window Functions
|
795
|
+
[Postgres Window Functions](https://www.postgresql.org/docs/current/tutorial-window.html)
|
796
|
+
|
797
|
+
Let's address the elephant in the room. Arel has had, for a long time now, window function capabilities;
|
798
|
+
However they've never seen the lime light in ActiveRecord's query logic.
|
799
|
+
The following brings the dormant Arel methods up to the ActiveRecord Querying level.
|
800
|
+
|
801
|
+
#### Define Window
|
802
|
+
|
803
|
+
To set up a window function, we first must establish the window and we do this by using the `.define_window/1` method.
|
804
|
+
This method also requires you to call chain `.partition_by/2`
|
805
|
+
|
806
|
+
`.define_window/1` - Establishes the name of the window you'll reference later on in [.select_window](#select-window)
|
807
|
+
- Aliased name of window
|
808
|
+
|
809
|
+
`.partition_by/2` - Establishes the windows operations a [pre-defined window function](https://www.postgresql.org/docs/current/functions-window.html) will leverage.
|
810
|
+
- column name being partitioned against
|
811
|
+
- (**optional**) `order_by`: Processes how the window should be ordered
|
812
|
+
|
813
|
+
```ruby
|
814
|
+
User
|
815
|
+
.define_window(:number_window).partition_by(:number, order_by: { id: :desc })
|
816
|
+
.define_window(:name_window).partition_by(:name, order_by: :id)
|
817
|
+
.define_window(:no_order_name).partition_by(:name)
|
818
|
+
```
|
819
|
+
|
820
|
+
Query Output
|
821
|
+
```sql
|
822
|
+
SELECT *
|
823
|
+
FROM users
|
824
|
+
WINDOW number_window AS (PARTITION BY number ORDER BY id DESC),
|
825
|
+
name_window AS (PARTITION BY name ORDER BY id),
|
826
|
+
no_order_name AS (PARTITION BY name)
|
827
|
+
```
|
828
|
+
|
829
|
+
#### Select Window
|
830
|
+
|
831
|
+
Once you've define a window, the next step to to utilize it on one of the many provided postgres window functions.
|
832
|
+
|
833
|
+
`.select_window/3`
|
834
|
+
- [window function name](https://www.postgresql.org/docs/current/functions-window.html)
|
835
|
+
- (**optional**) Window function arguments (treated as a splatted array)
|
836
|
+
- (**optional**) `as:` : Alias name of the final result
|
837
|
+
- `over:` : name of [defined window](#define-window)
|
838
|
+
|
839
|
+
```ruby
|
840
|
+
User.create!(name: "Alice", number: 100) #=> id: 1
|
841
|
+
User.create!(name: "Randy", number: 100) #=> id: 2
|
842
|
+
User.create!(name: "Bob", number: 300) #=> id: 3
|
843
|
+
|
844
|
+
User
|
845
|
+
.define_window(:number_window).partition_by(:number, order_by: { id: :desc })
|
846
|
+
.select(:id, :name)
|
847
|
+
.select_window(:row_number, over: :number_window, as: :row_id)
|
848
|
+
.select_window(:first_value, :name, over: :number_window, as: :first_value_name)
|
849
|
+
#=> [
|
850
|
+
# { id: 1, name: "Alice", row_id: 2, first_value_name: "Randy" }
|
851
|
+
# { id: 2, name: "Randy", row_id: 1, first_value_name: "Randy" }
|
852
|
+
# { id: 3, name: "Bob", row_id: 1, first_value_name: "Bob" }
|
853
|
+
# ]
|
854
|
+
#
|
855
|
+
|
856
|
+
```
|
857
|
+
|
858
|
+
Query Output
|
859
|
+
```sql
|
860
|
+
SELECT "users"."id",
|
861
|
+
"users"."name",
|
862
|
+
(ROW_NUMBER() OVER number_window) AS "row_id",
|
863
|
+
(FIRST_VALUE(name) OVER number_window) AS "first_value_name"
|
864
|
+
FROM "users"
|
865
|
+
WINDOW number_window AS (PARTITION BY number ORDER BY id DESC)
|
866
|
+
```
|
867
|
+
|
790
868
|
## License
|
791
869
|
|
792
870
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
@@ -1,10 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# TODO: Remove this when ruby 2.3 support is dropped
|
4
|
-
unless Hash.instance_methods(false).include?(:compact!)
|
5
|
-
require "active_support/all"
|
6
|
-
end
|
7
|
-
|
8
3
|
require "active_record"
|
9
4
|
require "active_record/relation"
|
10
5
|
require "active_record/relation/merger"
|
@@ -23,12 +18,8 @@ require "active_record_extended/query_methods/inet"
|
|
23
18
|
require "active_record_extended/query_methods/json"
|
24
19
|
require "active_record_extended/query_methods/select"
|
25
20
|
|
26
|
-
if
|
27
|
-
if ActiveRecord::VERSION::MINOR.zero?
|
28
|
-
require "active_record_extended/patch/5_0/regex_match"
|
29
|
-
require "active_record_extended/patch/5_0/predicate_builder_decorator"
|
30
|
-
end
|
21
|
+
if Gem::Requirement.new("~> 5.1.0").satisfied_by?(ActiveRecord.gem_version)
|
31
22
|
require "active_record_extended/patch/5_1/where_clause"
|
32
|
-
|
23
|
+
else
|
33
24
|
require "active_record_extended/patch/5_2/where_clause"
|
34
25
|
end
|
@@ -1,27 +1,44 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_record_extended/query_methods/window"
|
3
4
|
require "active_record_extended/query_methods/unionize"
|
4
5
|
require "active_record_extended/query_methods/json"
|
5
6
|
|
6
7
|
module ActiveRecordExtended
|
7
8
|
module RelationPatch
|
8
9
|
module QueryDelegation
|
9
|
-
delegate :with, :foster_select, to: :all
|
10
|
+
delegate :with, :define_window, :select_window, :foster_select, to: :all
|
10
11
|
delegate(*::ActiveRecordExtended::QueryMethods::Unionize::UNIONIZE_METHODS, to: :all)
|
11
12
|
delegate(*::ActiveRecordExtended::QueryMethods::Json::JSON_QUERY_METHODS, to: :all)
|
12
13
|
end
|
13
14
|
|
14
15
|
module Merger
|
15
16
|
def normal_values
|
16
|
-
super + [:
|
17
|
+
super + [:union, :define_window]
|
18
|
+
end
|
19
|
+
|
20
|
+
def merge
|
21
|
+
merge_ctes!
|
22
|
+
super
|
23
|
+
end
|
24
|
+
|
25
|
+
def merge_ctes!
|
26
|
+
return unless other.with_values?
|
27
|
+
|
28
|
+
if other.recursive_value? && !relation.recursive_value?
|
29
|
+
relation.with!(:chain).recursive(other.cte)
|
30
|
+
else
|
31
|
+
relation.with!(other.cte)
|
32
|
+
end
|
17
33
|
end
|
18
34
|
end
|
19
35
|
|
20
36
|
module ArelBuildPatch
|
21
37
|
def build_arel(*aliases)
|
22
38
|
super.tap do |arel|
|
23
|
-
|
24
|
-
|
39
|
+
build_windows(arel) if window_values?
|
40
|
+
build_unions(arel) if union_values?
|
41
|
+
build_with(arel) if with_values?
|
25
42
|
end
|
26
43
|
end
|
27
44
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_record_extended/arel/nodes"
|
4
|
+
require "active_record_extended/arel/sql_literal"
|
4
5
|
require "active_record_extended/arel/aggregate_function_name"
|
5
6
|
require "active_record_extended/arel/predications"
|
6
7
|
require "active_record_extended/arel/visitors/postgresql_decorator"
|
@@ -5,28 +5,31 @@ require "arel/nodes/function"
|
|
5
5
|
|
6
6
|
module Arel
|
7
7
|
module Nodes
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
8
|
+
if Gem::Requirement.new("< 6.1").satisfied_by?(ActiveRecord.gem_version)
|
9
|
+
["Contains", "Overlaps"].each { |binary_node_name| const_set(binary_node_name, Class.new(::Arel::Nodes::Binary)) }
|
10
|
+
end
|
11
|
+
|
12
|
+
[
|
13
|
+
"ContainsHStore",
|
14
|
+
"ContainsArray",
|
15
|
+
"ContainedInArray"
|
14
16
|
].each { |binary_node_name| const_set(binary_node_name, Class.new(::Arel::Nodes::Binary)) }
|
15
17
|
|
16
|
-
|
17
|
-
RowToJson
|
18
|
-
JsonBuildObject
|
19
|
-
JsonbBuildObject
|
20
|
-
ToJson
|
21
|
-
ToJsonb
|
22
|
-
Array
|
23
|
-
ArrayAgg
|
18
|
+
[
|
19
|
+
"RowToJson",
|
20
|
+
"JsonBuildObject",
|
21
|
+
"JsonbBuildObject",
|
22
|
+
"ToJson",
|
23
|
+
"ToJsonb",
|
24
|
+
"Array",
|
25
|
+
"ArrayAgg"
|
24
26
|
].each do |function_node_name|
|
25
27
|
func_klass = Class.new(::Arel::Nodes::Function) do
|
26
28
|
def initialize(*args)
|
27
29
|
super
|
28
30
|
return if @expressions.is_a?(::Array)
|
29
|
-
|
31
|
+
|
32
|
+
@expressions = @expressions.is_a?(::Arel::Nodes::Node) ? [@expressions] : [::Arel.sql(@expressions)]
|
30
33
|
end
|
31
34
|
end
|
32
35
|
|
@@ -34,12 +37,12 @@ module Arel
|
|
34
37
|
end
|
35
38
|
|
36
39
|
module Inet
|
37
|
-
|
38
|
-
Contains
|
39
|
-
ContainsEquals
|
40
|
-
ContainedWithin
|
41
|
-
ContainedWithinEquals
|
42
|
-
ContainsOrContainedWithin
|
40
|
+
[
|
41
|
+
"Contains",
|
42
|
+
"ContainsEquals",
|
43
|
+
"ContainedWithin",
|
44
|
+
"ContainedWithinEquals",
|
45
|
+
"ContainsOrContainedWithin"
|
43
46
|
].each { |binary_node_name| const_set(binary_node_name, Class.new(::Arel::Nodes::Binary)) }
|
44
47
|
end
|
45
48
|
end
|
@@ -14,9 +14,10 @@ module Arel
|
|
14
14
|
Arel::Nodes::Equality.new(Nodes.build_quoted(other, self), all_tags_function)
|
15
15
|
end
|
16
16
|
|
17
|
-
def
|
18
|
-
Nodes::
|
17
|
+
def overlaps(other)
|
18
|
+
Nodes::Overlaps.new(self, Nodes.build_quoted(other, self))
|
19
19
|
end
|
20
|
+
alias overlap overlaps
|
20
21
|
|
21
22
|
def contains(other)
|
22
23
|
Nodes::Contains.new self, Nodes.build_quoted(other, self)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "arel/nodes/sql_literal"
|
4
|
+
|
5
|
+
# CTE alias fix for Rails 6.1
|
6
|
+
module Arel
|
7
|
+
module Nodes
|
8
|
+
module SqlLiteralDecorator
|
9
|
+
def name
|
10
|
+
self
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
Arel::Nodes::SqlLiteral.prepend(Arel::Nodes::SqlLiteralDecorator)
|
@@ -61,13 +61,14 @@ module ActiveRecordExtended
|
|
61
61
|
# In Rails 5.2 the arel table maintains attribute binds
|
62
62
|
def bind_attributes(query)
|
63
63
|
return [] unless query.respond_to?(:bound_attributes)
|
64
|
+
|
64
65
|
query.bound_attributes.map(&:value)
|
65
66
|
end
|
66
67
|
|
67
68
|
# Rails 5.1 fix
|
68
69
|
def unprepared_query(query)
|
69
|
-
query.gsub(/((?<!\\)'.*?(?<!\\)'|(?<!\\)".*?(?<!\\)")|(
|
70
|
-
Regexp.last_match(2)&.gsub(
|
70
|
+
query.gsub(/((?<!\\)'.*?(?<!\\)'|(?<!\\)".*?(?<!\\)")|(=\ \$\d+)/) do |match|
|
71
|
+
Regexp.last_match(2)&.gsub(/=\ \$\d+/, "= ?") || match
|
71
72
|
end
|
72
73
|
end
|
73
74
|
|
@@ -78,9 +79,9 @@ module ActiveRecordExtended
|
|
78
79
|
def generate_where_clause(query)
|
79
80
|
case query
|
80
81
|
when String, Hash
|
81
|
-
@scope.where(query)
|
82
|
+
@scope.unscoped.where(query)
|
82
83
|
when Array
|
83
|
-
@scope.where(*query)
|
84
|
+
@scope.unscoped.where(*query)
|
84
85
|
else
|
85
86
|
query
|
86
87
|
end
|
@@ -30,7 +30,7 @@ module ActiveRecordExtended
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def sort_order_sql(dir)
|
33
|
-
|
33
|
+
["asc", "desc"].include?(dir.to_s) ? dir.to_s : "asc"
|
34
34
|
end
|
35
35
|
|
36
36
|
def xor_field_options(options)
|
@@ -52,6 +52,7 @@ module ActiveRecordExtended
|
|
52
52
|
def xor_field_options_for_associations(associations)
|
53
53
|
associations.each_with_object({}) do |association_name, options|
|
54
54
|
reflection = reflect_on_association(association_name)
|
55
|
+
reflection = reflection.through_reflection if reflection.through_reflection?
|
55
56
|
options[reflection.table_name] = reflection.foreign_key
|
56
57
|
end
|
57
58
|
end
|
@@ -74,8 +74,12 @@ module ActiveRecordExtended
|
|
74
74
|
# #=> "SELECT \"users\".* FROM \"users\" WHERE \"users\".\"ip\" && '127.0.0.255/32'"
|
75
75
|
#
|
76
76
|
def inet_contains_or_is_contained_within(opts, *rest)
|
77
|
-
substitute_comparisons(
|
78
|
-
|
77
|
+
substitute_comparisons(
|
78
|
+
opts,
|
79
|
+
rest,
|
80
|
+
Arel::Nodes::Inet::ContainsOrContainedWithin,
|
81
|
+
"inet_contains_or_is_contained_within"
|
82
|
+
)
|
79
83
|
end
|
80
84
|
end
|
81
85
|
end
|
@@ -8,7 +8,7 @@ module ActiveRecordExtended
|
|
8
8
|
:json_build_object,
|
9
9
|
:jsonb_build_object,
|
10
10
|
:json_build_literal,
|
11
|
-
:jsonb_build_literal
|
11
|
+
:jsonb_build_literal
|
12
12
|
].freeze
|
13
13
|
|
14
14
|
class JsonChain
|
@@ -51,7 +51,7 @@ module ActiveRecordExtended
|
|
51
51
|
private
|
52
52
|
|
53
53
|
def build_json_literal(arel_klass, values:, col_alias: DEFAULT_ALIAS)
|
54
|
-
json_values = flatten_to_sql(values.to_a
|
54
|
+
json_values = flatten_to_sql(values.to_a) { |value| literal_key(value) }
|
55
55
|
col_alias = double_quote(col_alias)
|
56
56
|
json_build_obj = arel_klass.new(json_values)
|
57
57
|
@scope.select(nested_alias_escape(json_build_obj, col_alias))
|
@@ -77,7 +77,7 @@ module ActiveRecordExtended
|
|
77
77
|
row_to_json = ::Arel::Nodes::ToJsonb.new(row_to_json) if options.dig(:cast_with, :to_jsonb)
|
78
78
|
|
79
79
|
dummy_table = from_clause_constructor(from, key).select(row_to_json)
|
80
|
-
dummy_table = dummy_table.instance_eval(&block) if
|
80
|
+
dummy_table = dummy_table.instance_eval(&block) if block
|
81
81
|
return dummy_table if options[:col_alias].blank?
|
82
82
|
|
83
83
|
query = wrap_row_to_json(dummy_table, options)
|
@@ -98,7 +98,6 @@ module ActiveRecordExtended
|
|
98
98
|
end
|
99
99
|
end
|
100
100
|
|
101
|
-
# TODO: [V2 release] Drop support for option :cast_as_array in favor of a more versatile :cast_with option
|
102
101
|
def json_object_options(args, except: [], only: []) # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
|
103
102
|
options = {}
|
104
103
|
lean_opts = lambda do |key, &block|
|
@@ -113,12 +112,12 @@ module ActiveRecordExtended
|
|
113
112
|
next if arg.nil?
|
114
113
|
|
115
114
|
if arg.is_a?(Hash)
|
116
|
-
lean_opts.call(:key) { arg.
|
117
|
-
lean_opts.call(:value) { arg
|
118
|
-
lean_opts.call(:col_alias) { arg
|
119
|
-
lean_opts.call(:order_by) { order_by_expression(arg
|
120
|
-
lean_opts.call(:from) { arg
|
121
|
-
lean_opts.call(:cast_with) { casting_options(arg
|
115
|
+
lean_opts.call(:key) { arg.fetch(:key, key_generator) }
|
116
|
+
lean_opts.call(:value) { arg[:value].presence }
|
117
|
+
lean_opts.call(:col_alias) { arg[:as] }
|
118
|
+
lean_opts.call(:order_by) { order_by_expression(arg[:order_by]) }
|
119
|
+
lean_opts.call(:from) { arg[:from].tap { |from_clause| pipe_cte_with!(from_clause) } }
|
120
|
+
lean_opts.call(:cast_with) { casting_options(arg[:cast_with]) }
|
122
121
|
end
|
123
122
|
|
124
123
|
unless except.include?(:values)
|
@@ -155,9 +154,6 @@ module ActiveRecordExtended
|
|
155
154
|
# - key: [Symbol or String] (default=[random letter]) What the row clause will be set as.
|
156
155
|
# - This is useful if you would like to add additional mid-level clauses (see mid-level scope example)
|
157
156
|
#
|
158
|
-
# - cast_as_array [boolean] (default=false): Determines if the query should be nested inside an Array() function
|
159
|
-
# * Will be deprecated in V2.0 in favor of `cast_with` argument
|
160
|
-
#
|
161
157
|
# - cast_with [Symbol or Array of symbols]: Actions to transform your query
|
162
158
|
# * :to_jsonb
|
163
159
|
# * :array
|
@@ -172,13 +168,13 @@ module ActiveRecordExtended
|
|
172
168
|
#
|
173
169
|
# Examples:
|
174
170
|
# subquery = Group.select(:name, :category_id).where("user_id = users.id")
|
175
|
-
# User.select(:name, email).select_row_to_json(subquery, as: :users_groups,
|
171
|
+
# User.select(:name, email).select_row_to_json(subquery, as: :users_groups, cast_with: :array)
|
176
172
|
# #=> [<#User name:.., email:.., users_groups: [{ name: .., category_id: .. }, ..]]
|
177
173
|
#
|
178
174
|
# - Adding mid-level scopes:
|
179
175
|
#
|
180
176
|
# subquery = Group.select(:name, :category_id)
|
181
|
-
# User.select_row_to_json(subquery, key: :group,
|
177
|
+
# User.select_row_to_json(subquery, key: :group, cast_with: :array) do |scope|
|
182
178
|
# scope.where(group: { name: "Nerd Core" })
|
183
179
|
# end
|
184
180
|
# #=> ```sql
|
@@ -252,7 +248,8 @@ module ActiveRecordExtended
|
|
252
248
|
def select_row_to_json(from = nil, **options, &block)
|
253
249
|
from.is_a?(Hash) ? options.merge!(from) : options.reverse_merge!(from: from)
|
254
250
|
options.compact!
|
255
|
-
raise ArgumentError
|
251
|
+
raise ArgumentError.new("Required to provide a non-nilled from clause") unless options.key?(:from)
|
252
|
+
|
256
253
|
JsonChain.new(spawn).row_to_json!(**options, &block)
|
257
254
|
end
|
258
255
|
|
@@ -273,7 +270,7 @@ module ActiveRecordExtended
|
|
273
270
|
# - Generic example:
|
274
271
|
#
|
275
272
|
# subquery = Group.select(:name, :category_id).where("user_id = users.id")
|
276
|
-
# User.select(:name, email).select_row_to_json(subquery, as: :users_groups,
|
273
|
+
# User.select(:name, email).select_row_to_json(subquery, as: :users_groups, cast_with: :array)
|
277
274
|
# #=> [<#User name:.., email:.., users_groups: [{ name: .., category_id: .. }, ..]]
|
278
275
|
#
|
279
276
|
# - Setting a custom value:
|