active_record_extended 1.2.0 → 2.0.3
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 +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:
|