active_record_extended 1.1.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +87 -15
- data/lib/active_record_extended.rb +2 -1
- data/lib/active_record_extended/active_record.rb +2 -9
- data/lib/active_record_extended/active_record/relation_patch.rb +21 -4
- data/lib/active_record_extended/arel.rb +2 -0
- data/lib/active_record_extended/arel/aggregate_function_name.rb +40 -0
- data/lib/active_record_extended/arel/nodes.rb +32 -41
- data/lib/active_record_extended/arel/predications.rb +4 -1
- data/lib/active_record_extended/arel/sql_literal.rb +16 -0
- data/lib/active_record_extended/arel/visitors/postgresql_decorator.rb +40 -1
- data/lib/active_record_extended/query_methods/any_of.rb +10 -8
- data/lib/active_record_extended/query_methods/either.rb +1 -1
- data/lib/active_record_extended/query_methods/inet.rb +7 -3
- data/lib/active_record_extended/query_methods/json.rb +156 -50
- data/lib/active_record_extended/query_methods/select.rb +118 -0
- data/lib/active_record_extended/query_methods/unionize.rb +14 -43
- data/lib/active_record_extended/query_methods/where_chain.rb +14 -6
- data/lib/active_record_extended/query_methods/window.rb +93 -0
- data/lib/active_record_extended/query_methods/with_cte.rb +102 -35
- data/lib/active_record_extended/utilities/order_by.rb +77 -0
- data/lib/active_record_extended/utilities/support.rb +178 -0
- data/lib/active_record_extended/version.rb +1 -1
- data/spec/query_methods/any_of_spec.rb +40 -40
- data/spec/query_methods/array_query_spec.rb +14 -14
- data/spec/query_methods/either_spec.rb +14 -14
- data/spec/query_methods/hash_query_spec.rb +11 -11
- data/spec/query_methods/inet_query_spec.rb +33 -31
- data/spec/query_methods/json_spec.rb +42 -27
- data/spec/query_methods/select_spec.rb +115 -0
- data/spec/query_methods/unionize_spec.rb +56 -56
- data/spec/query_methods/window_spec.rb +51 -0
- data/spec/query_methods/with_cte_spec.rb +22 -12
- data/spec/spec_helper.rb +1 -1
- data/spec/sql_inspections/any_of_sql_spec.rb +12 -12
- data/spec/sql_inspections/arel/aggregate_function_name_spec.rb +41 -0
- data/spec/sql_inspections/arel/array_spec.rb +7 -7
- data/spec/sql_inspections/arel/inet_spec.rb +7 -7
- data/spec/sql_inspections/contains_sql_queries_spec.rb +14 -14
- data/spec/sql_inspections/either_sql_spec.rb +11 -11
- data/spec/sql_inspections/json_sql_spec.rb +44 -8
- data/spec/sql_inspections/unionize_sql_spec.rb +27 -27
- data/spec/sql_inspections/window_sql_spec.rb +98 -0
- data/spec/sql_inspections/with_cte_sql_spec.rb +52 -23
- data/spec/support/models.rb +24 -4
- metadata +31 -20
- data/lib/active_record_extended/patch/5_0/predicate_builder_decorator.rb +0 -87
- data/lib/active_record_extended/utilities.rb +0 -141
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9934d5a90324b46dc3faa7b496724ac219a2582d264db89e64e2b9be574adab6
|
4
|
+
data.tar.gz: c1359988f9a6b83148f36548e842a25cab68a191e7a839caef7e783b1415b346
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e7385b414ef4c8d8b0c5b29eaeb70e77ba79f07828255deef5db79e99cf33f18de7bab696501711dee1f8e056ca3bdf628cdd498b341a91b5ad5cd2c766bd61c
|
7
|
+
data.tar.gz: 10e8d4f65c570dba757b2cf47aaaff7f8a5538974e9c8aadb3c267e3e3fccb9508b05a8722eb5d6680937cb15827f6fa4f88d6dd219d8240c58323385fe44eb2
|
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
|
|
@@ -46,15 +49,27 @@ The only problem is that this has created a wild west of environments of sorts.
|
|
46
49
|
|
47
50
|
Active Record Extended is essentially providing users with the other half of Postgreses querying abilities. Due to Rails/ActiveRecord/Arel being designed to be DB agnostic, there are a lot of left out features; Either by choice or the simple lack of supporting API's for other databases. However some features are not exactly PG explicit. Some are just helper methods to express an idea much more easily.
|
48
51
|
|
49
|
-
|
50
52
|
## Compatibility
|
51
53
|
|
52
54
|
This package is designed align and work with any officially supported Ruby and Rails versions.
|
53
|
-
- Minimum Ruby Version: 2.
|
54
|
-
- Minimum Rails Version: 5.
|
55
|
-
-
|
56
|
-
- Latest
|
57
|
-
-
|
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)
|
61
|
+
|
62
|
+
## Installation
|
63
|
+
|
64
|
+
Add this line to your application's Gemfile:
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
gem 'active_record_extended'
|
68
|
+
```
|
69
|
+
|
70
|
+
And then execute:
|
71
|
+
|
72
|
+
$ bundle
|
58
73
|
|
59
74
|
## Usage
|
60
75
|
|
@@ -416,7 +431,7 @@ While quite the mouthful of an explanation. The implementation of combining unre
|
|
416
431
|
product_query =
|
417
432
|
Product.select(:id)
|
418
433
|
.joins(:items)
|
419
|
-
.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|
|
420
435
|
item_scope.where("outer_items.product_id = products.id")
|
421
436
|
# Results to:
|
422
437
|
# SELECT ..., ARRAY(SELECT ROW_TO_JSON("outer_items")
|
@@ -426,7 +441,7 @@ While quite the mouthful of an explanation. The implementation of combining unre
|
|
426
441
|
end
|
427
442
|
|
428
443
|
# Not defining a key will automatically generate a random key between a-z
|
429
|
-
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)
|
430
445
|
Category.json_build_object(:physical_category, category_query.where(id: physical_cat.id)).results
|
431
446
|
#=> {
|
432
447
|
# "physical_category" => {
|
@@ -776,22 +791,79 @@ SELECT "people".*
|
|
776
791
|
) ) ORDER BY personal_id DESC, id DESC) people
|
777
792
|
```
|
778
793
|
|
794
|
+
#### Window Functions
|
795
|
+
[Postgres Window Functions](https://www.postgresql.org/docs/current/tutorial-window.html)
|
779
796
|
|
780
|
-
|
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.
|
781
800
|
|
782
|
-
|
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
|
783
812
|
|
784
813
|
```ruby
|
785
|
-
|
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)
|
786
818
|
```
|
787
819
|
|
788
|
-
|
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
|
+
```
|
789
828
|
|
790
|
-
|
829
|
+
#### Select Window
|
791
830
|
|
792
|
-
|
831
|
+
Once you've define a window, the next step to to utilize it on one of the many provided postgres window functions.
|
793
832
|
|
794
|
-
|
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
|
+
```
|
795
867
|
|
796
868
|
## License
|
797
869
|
|
@@ -1,7 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_record_extended/version"
|
4
|
-
require "active_record_extended/utilities"
|
4
|
+
require "active_record_extended/utilities/support"
|
5
|
+
require "active_record_extended/utilities/order_by"
|
5
6
|
require "active_record_extended/active_record"
|
6
7
|
require "active_record_extended/arel"
|
7
8
|
|
@@ -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"
|
@@ -21,11 +16,9 @@ require "active_record_extended/query_methods/any_of"
|
|
21
16
|
require "active_record_extended/query_methods/either"
|
22
17
|
require "active_record_extended/query_methods/inet"
|
23
18
|
require "active_record_extended/query_methods/json"
|
19
|
+
require "active_record_extended/query_methods/select"
|
24
20
|
|
25
|
-
if ActiveRecord::VERSION::MAJOR == 5 && ActiveRecord::VERSION::MINOR
|
26
|
-
if ActiveRecord::VERSION::MINOR.zero?
|
27
|
-
require "active_record_extended/patch/5_0/predicate_builder_decorator"
|
28
|
-
end
|
21
|
+
if ActiveRecord::VERSION::MAJOR == 5 && ActiveRecord::VERSION::MINOR == 1
|
29
22
|
require "active_record_extended/patch/5_1/where_clause"
|
30
23
|
elsif ActiveRecord::VERSION::MAJOR >= 5
|
31
24
|
require "active_record_extended/patch/5_2/where_clause"
|
@@ -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, 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,5 +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"
|
5
|
+
require "active_record_extended/arel/aggregate_function_name"
|
4
6
|
require "active_record_extended/arel/predications"
|
5
7
|
require "active_record_extended/arel/visitors/postgresql_decorator"
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Arel
|
4
|
+
module Nodes
|
5
|
+
class AggregateFunctionName < ::Arel::Nodes::Node
|
6
|
+
include Arel::Predications
|
7
|
+
include Arel::WindowPredications
|
8
|
+
attr_accessor :name, :expressions, :distinct, :alias, :orderings
|
9
|
+
|
10
|
+
def initialize(name, expr, distinct = false)
|
11
|
+
super()
|
12
|
+
@name = name.to_s.upcase
|
13
|
+
@expressions = expr
|
14
|
+
@distinct = distinct
|
15
|
+
end
|
16
|
+
|
17
|
+
def order_by(expr)
|
18
|
+
@orderings = expr
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def as(aliaz)
|
23
|
+
self.alias = SqlLiteral.new(aliaz)
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
def hash
|
28
|
+
[@name, @expressions, @distinct, @alias, @orderings].hash
|
29
|
+
end
|
30
|
+
|
31
|
+
def eql?(other)
|
32
|
+
self.class == other.class &&
|
33
|
+
expressions == other.expressions &&
|
34
|
+
orderings == other.orderings &&
|
35
|
+
distinct == other.distinct
|
36
|
+
end
|
37
|
+
alias == eql?
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -1,56 +1,47 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "arel/nodes/binary"
|
4
|
+
require "arel/nodes/function"
|
4
5
|
|
5
6
|
module Arel
|
6
7
|
module Nodes
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
8
|
+
[
|
9
|
+
"Overlap",
|
10
|
+
"Contains",
|
11
|
+
"ContainsHStore",
|
12
|
+
"ContainsArray",
|
13
|
+
"ContainedInArray"
|
14
|
+
].each { |binary_node_name| const_set(binary_node_name, Class.new(::Arel::Nodes::Binary)) }
|
15
|
+
|
16
|
+
[
|
17
|
+
"RowToJson",
|
18
|
+
"JsonBuildObject",
|
19
|
+
"JsonbBuildObject",
|
20
|
+
"ToJson",
|
21
|
+
"ToJsonb",
|
22
|
+
"Array",
|
23
|
+
"ArrayAgg"
|
24
|
+
].each do |function_node_name|
|
25
|
+
func_klass = Class.new(::Arel::Nodes::Function) do
|
26
|
+
def initialize(*args)
|
27
|
+
super
|
28
|
+
return if @expressions.is_a?(::Array)
|
29
|
+
|
30
|
+
@expressions = @expressions.is_a?(::Arel::Nodes::Node) ? [@expressions] : [::Arel.sql(@expressions)]
|
28
31
|
end
|
29
32
|
end
|
30
|
-
end
|
31
33
|
|
32
|
-
|
33
|
-
def initialize(*args)
|
34
|
-
super
|
35
|
-
@expressions = Array(@expressions)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
class JsonbBuildObject < JsonBuildObject
|
34
|
+
const_set(function_node_name, func_klass)
|
40
35
|
end
|
41
36
|
|
42
37
|
module Inet
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
end
|
51
|
-
|
52
|
-
class ContainsOrContainedWithin < Arel::Nodes::Binary
|
53
|
-
end
|
38
|
+
[
|
39
|
+
"Contains",
|
40
|
+
"ContainsEquals",
|
41
|
+
"ContainedWithin",
|
42
|
+
"ContainedWithinEquals",
|
43
|
+
"ContainsOrContainedWithin"
|
44
|
+
].each { |binary_node_name| const_set(binary_node_name, Class.new(::Arel::Nodes::Binary)) }
|
54
45
|
end
|
55
46
|
end
|
56
47
|
end
|
@@ -21,12 +21,15 @@ module Arel
|
|
21
21
|
def contains(other)
|
22
22
|
Nodes::Contains.new self, Nodes.build_quoted(other, self)
|
23
23
|
end
|
24
|
-
alias inet_contains contains
|
25
24
|
|
26
25
|
def contained_in_array(other)
|
27
26
|
Nodes::ContainedInArray.new self, Nodes.build_quoted(other, self)
|
28
27
|
end
|
29
28
|
|
29
|
+
def inet_contains(other)
|
30
|
+
Nodes::Inet::Contains.new self, Nodes.build_quoted(other, self)
|
31
|
+
end
|
32
|
+
|
30
33
|
def inet_contains_or_is_contained_within(other)
|
31
34
|
Nodes::Inet::ContainsOrContainedWithin.new self, Nodes.build_quoted(other, self)
|
32
35
|
end
|
@@ -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)
|
@@ -23,7 +23,7 @@ module ActiveRecordExtended
|
|
23
23
|
elsif left_column.try(:array)
|
24
24
|
visit_Arel_Nodes_ContainsArray(object, collector)
|
25
25
|
else
|
26
|
-
|
26
|
+
visit_Arel_Nodes_Inet_Contains(object, collector)
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
@@ -59,6 +59,45 @@ module ActiveRecordExtended
|
|
59
59
|
aggregate "JSONB_BUILD_OBJECT", object, collector
|
60
60
|
end
|
61
61
|
|
62
|
+
def visit_Arel_Nodes_ToJson(object, collector)
|
63
|
+
aggregate "TO_JSON", object, collector
|
64
|
+
end
|
65
|
+
|
66
|
+
def visit_Arel_Nodes_ToJsonb(object, collector)
|
67
|
+
aggregate "TO_JSONB", object, collector
|
68
|
+
end
|
69
|
+
|
70
|
+
def visit_Arel_Nodes_Array(object, collector)
|
71
|
+
aggregate "ARRAY", object, collector
|
72
|
+
end
|
73
|
+
|
74
|
+
def visit_Arel_Nodes_ArrayAgg(object, collector)
|
75
|
+
aggregate "ARRAY_AGG", object, collector
|
76
|
+
end
|
77
|
+
|
78
|
+
def visit_Arel_Nodes_AggregateFunctionName(object, collector)
|
79
|
+
collector << "#{object.name}("
|
80
|
+
collector << "DISTINCT " if object.distinct
|
81
|
+
collector = inject_join(object.expressions, collector, ", ")
|
82
|
+
|
83
|
+
if object.orderings
|
84
|
+
collector << " ORDER BY "
|
85
|
+
collector = inject_join(object.orderings, collector, ", ")
|
86
|
+
end
|
87
|
+
collector << ")"
|
88
|
+
|
89
|
+
if object.alias
|
90
|
+
collector << " AS "
|
91
|
+
visit object.alias, collector
|
92
|
+
else
|
93
|
+
collector
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def visit_Arel_Nodes_Inet_Contains(object, collector)
|
98
|
+
infix_value object, collector, " >> "
|
99
|
+
end
|
100
|
+
|
62
101
|
def visit_Arel_Nodes_Inet_ContainedWithinEquals(object, collector)
|
63
102
|
infix_value object, collector, " <<= "
|
64
103
|
end
|