arel_toolkit 0.3.0 → 0.4.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.codeclimate.yml +3 -0
- data/.github/workflows/develop.yml +90 -0
- data/.github/workflows/master.yml +67 -0
- data/.gitignore +8 -0
- data/.rubocop.yml +13 -5
- data/Appraisals +13 -0
- data/CHANGELOG.md +94 -5
- data/Gemfile +5 -0
- data/Gemfile.lock +62 -33
- data/Guardfile +4 -0
- data/README.md +67 -23
- data/Rakefile +11 -1
- data/arel_toolkit.gemspec +15 -6
- data/benchmark.rb +54 -0
- data/ext/pg_result_init/extconf.rb +52 -0
- data/ext/pg_result_init/pg_result_init.c +138 -0
- data/ext/pg_result_init/pg_result_init.h +6 -0
- data/gemfiles/active_record_6.gemfile +7 -0
- data/gemfiles/active_record_6.gemfile.lock +210 -0
- data/gemfiles/arel_gems.gemfile +10 -0
- data/gemfiles/arel_gems.gemfile.lock +284 -0
- data/gemfiles/default.gemfile +5 -0
- data/gemfiles/default.gemfile.lock +208 -0
- data/lib/arel/enhance.rb +17 -0
- data/lib/arel/enhance/context_enhancer/arel_table.rb +92 -0
- data/lib/arel/enhance/node.rb +232 -0
- data/lib/arel/enhance/path.rb +38 -0
- data/lib/arel/enhance/path_node.rb +26 -0
- data/lib/arel/enhance/query.rb +38 -0
- data/lib/arel/enhance/query_methods.rb +23 -0
- data/lib/arel/enhance/visitor.rb +97 -0
- data/lib/arel/extensions.rb +32 -6
- data/lib/arel/extensions/active_model_attribute_with_cast_value.rb +22 -0
- data/lib/arel/extensions/active_record_relation_query_attribute.rb +22 -0
- data/lib/arel/extensions/active_record_type_caster_connection.rb +7 -0
- data/lib/arel/extensions/active_record_type_caster_map.rb +7 -0
- data/lib/arel/extensions/array.rb +2 -9
- data/lib/arel/extensions/at_time_zone.rb +10 -3
- data/lib/arel/extensions/attributes_attribute.rb +47 -0
- data/lib/arel/extensions/binary.rb +7 -0
- data/lib/arel/extensions/bind_param.rb +15 -0
- data/lib/arel/extensions/bit_string.rb +2 -9
- data/lib/arel/extensions/case.rb +17 -0
- data/lib/arel/extensions/coalesce.rb +17 -3
- data/lib/arel/extensions/conflict.rb +9 -0
- data/lib/arel/extensions/contains.rb +27 -5
- data/lib/arel/extensions/current_catalog.rb +4 -0
- data/lib/arel/extensions/current_date.rb +4 -0
- data/lib/arel/extensions/current_of_expression.rb +2 -9
- data/lib/arel/extensions/current_role.rb +4 -0
- data/lib/arel/extensions/current_row.rb +7 -0
- data/lib/arel/extensions/current_schema.rb +4 -0
- data/lib/arel/extensions/current_user.rb +4 -0
- data/lib/arel/extensions/dealocate.rb +31 -0
- data/lib/arel/extensions/default_values.rb +4 -0
- data/lib/arel/extensions/delete_manager.rb +22 -6
- data/lib/arel/extensions/delete_statement.rb +46 -24
- data/lib/arel/extensions/dot.rb +11 -0
- data/lib/arel/extensions/exists.rb +59 -0
- data/lib/arel/extensions/extract_from.rb +3 -10
- data/lib/arel/extensions/factorial.rb +10 -2
- data/lib/arel/extensions/false.rb +7 -0
- data/lib/arel/extensions/function.rb +44 -14
- data/lib/arel/extensions/greatest.rb +17 -3
- data/lib/arel/extensions/indirection.rb +3 -12
- data/lib/arel/extensions/infer.rb +7 -7
- data/lib/arel/extensions/infix_operation.rb +17 -0
- data/lib/arel/extensions/insert_manager.rb +19 -3
- data/lib/arel/extensions/insert_statement.rb +31 -12
- data/lib/arel/extensions/into.rb +21 -0
- data/lib/arel/extensions/least.rb +17 -3
- data/lib/arel/extensions/named_argument.rb +3 -8
- data/lib/arel/extensions/named_function.rb +7 -0
- data/lib/arel/extensions/node.rb +10 -0
- data/lib/arel/extensions/ordering.rb +21 -6
- data/lib/arel/extensions/overlaps.rb +9 -0
- data/lib/arel/extensions/overlay.rb +9 -0
- data/lib/arel/extensions/position.rb +3 -8
- data/lib/arel/extensions/prepare.rb +39 -0
- data/lib/arel/extensions/range_function.rb +10 -2
- data/lib/arel/extensions/row.rb +3 -8
- data/lib/arel/extensions/select_core.rb +73 -0
- data/lib/arel/extensions/select_manager.rb +22 -6
- data/lib/arel/extensions/select_statement.rb +31 -9
- data/lib/arel/extensions/session_user.rb +4 -0
- data/lib/arel/extensions/set_to_default.rb +4 -0
- data/lib/arel/extensions/substring.rb +8 -0
- data/lib/arel/extensions/table.rb +43 -10
- data/lib/arel/extensions/time_with_precision.rb +6 -0
- data/lib/arel/extensions/to_sql.rb +27 -0
- data/lib/arel/extensions/top.rb +8 -0
- data/lib/arel/extensions/transaction.rb +3 -8
- data/lib/arel/extensions/tree_manager.rb +15 -0
- data/lib/arel/extensions/trim.rb +8 -0
- data/lib/arel/extensions/true.rb +7 -0
- data/lib/arel/extensions/type_cast.rb +7 -0
- data/lib/arel/extensions/unary.rb +7 -0
- data/lib/arel/extensions/unary_operation.rb +16 -0
- data/lib/arel/extensions/unknown.rb +4 -0
- data/lib/arel/extensions/update_manager.rb +22 -6
- data/lib/arel/extensions/update_statement.rb +36 -33
- data/lib/arel/extensions/user.rb +4 -0
- data/lib/arel/extensions/values_list.rb +15 -0
- data/lib/arel/extensions/variable_set.rb +9 -0
- data/lib/arel/extensions/variable_show.rb +3 -8
- data/lib/arel/middleware.rb +5 -1
- data/lib/arel/middleware/active_record_extension.rb +13 -0
- data/lib/arel/middleware/cache_accessor.rb +35 -0
- data/lib/arel/middleware/chain.rb +108 -33
- data/lib/arel/middleware/database_executor.rb +77 -0
- data/lib/arel/middleware/no_op_cache.rb +9 -0
- data/lib/arel/middleware/postgresql_adapter.rb +41 -5
- data/lib/arel/middleware/railtie.rb +15 -1
- data/lib/arel/middleware/result.rb +170 -0
- data/lib/arel/middleware/to_sql_executor.rb +15 -0
- data/lib/arel/middleware/to_sql_middleware.rb +33 -0
- data/lib/arel/sql_to_arel.rb +6 -3
- data/lib/arel/sql_to_arel/pg_query_visitor.rb +67 -38
- data/lib/arel/sql_to_arel/pg_query_visitor/frame_options.rb +1 -1
- data/lib/arel/sql_to_arel/result.rb +17 -4
- data/lib/arel/transformer.rb +8 -0
- data/lib/arel/transformer/prefix_schema_name.rb +183 -0
- data/lib/arel/transformer/remove_active_record_info.rb +40 -0
- data/lib/arel/transformer/replace_table_with_subquery.rb +31 -0
- data/lib/arel_toolkit.rb +15 -2
- data/lib/arel_toolkit/version.rb +1 -1
- metadata +179 -42
- data/.travis.yml +0 -29
- data/lib/arel/extensions/generate_series.rb +0 -9
- data/lib/arel/extensions/rank.rb +0 -9
- data/lib/arel/extensions/unbound_column_reference.rb +0 -5
- data/lib/arel/sql_formatter.rb +0 -59
@@ -0,0 +1,208 @@
|
|
1
|
+
PATH
|
2
|
+
remote: ..
|
3
|
+
specs:
|
4
|
+
arel_toolkit (0.4.3)
|
5
|
+
activerecord
|
6
|
+
pg (~> 1.1.4)
|
7
|
+
pg_query (~> 1.3)
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: https://rubygems.org/
|
11
|
+
specs:
|
12
|
+
activemodel (5.2.3)
|
13
|
+
activesupport (= 5.2.3)
|
14
|
+
activerecord (5.2.3)
|
15
|
+
activemodel (= 5.2.3)
|
16
|
+
activesupport (= 5.2.3)
|
17
|
+
arel (>= 9.0)
|
18
|
+
activesupport (5.2.3)
|
19
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
20
|
+
i18n (>= 0.7, < 2)
|
21
|
+
minitest (~> 5.1)
|
22
|
+
tzinfo (~> 1.1)
|
23
|
+
addressable (2.7.0)
|
24
|
+
public_suffix (>= 2.0.2, < 5.0)
|
25
|
+
ansi (1.5.0)
|
26
|
+
appraisal (2.2.0)
|
27
|
+
bundler
|
28
|
+
rake
|
29
|
+
thor (>= 0.14.0)
|
30
|
+
approvals (0.0.24)
|
31
|
+
nokogiri (~> 1.6)
|
32
|
+
thor (~> 0.18)
|
33
|
+
arel (9.0.0)
|
34
|
+
ast (2.4.0)
|
35
|
+
binding_of_caller (0.8.0)
|
36
|
+
debug_inspector (>= 0.0.1)
|
37
|
+
coderay (1.1.2)
|
38
|
+
concurrent-ruby (1.1.5)
|
39
|
+
database_cleaner (1.7.0)
|
40
|
+
debug_inspector (0.0.3)
|
41
|
+
diff-lcs (1.3)
|
42
|
+
docile (1.3.2)
|
43
|
+
dpl (1.10.12)
|
44
|
+
faraday (0.17.0)
|
45
|
+
multipart-post (>= 1.2, < 3)
|
46
|
+
faraday-http-cache (2.0.0)
|
47
|
+
faraday (~> 0.8)
|
48
|
+
ffi (1.11.1)
|
49
|
+
formatador (0.2.5)
|
50
|
+
github_changelog_generator (1.15.0)
|
51
|
+
activesupport
|
52
|
+
faraday-http-cache
|
53
|
+
multi_json
|
54
|
+
octokit (~> 4.6)
|
55
|
+
rainbow (>= 2.2.1)
|
56
|
+
rake (>= 10.0)
|
57
|
+
retriable (~> 3.0)
|
58
|
+
guard (2.15.0)
|
59
|
+
formatador (>= 0.2.4)
|
60
|
+
listen (>= 2.7, < 4.0)
|
61
|
+
lumberjack (>= 1.0.12, < 2.0)
|
62
|
+
nenv (~> 0.1)
|
63
|
+
notiffany (~> 0.0)
|
64
|
+
pry (>= 0.9.12)
|
65
|
+
shellany (~> 0.0)
|
66
|
+
thor (>= 0.18.1)
|
67
|
+
guard-compat (1.2.1)
|
68
|
+
guard-rake (1.0.0)
|
69
|
+
guard
|
70
|
+
rake
|
71
|
+
guard-rspec (4.7.3)
|
72
|
+
guard (~> 2.1)
|
73
|
+
guard-compat (~> 1.1)
|
74
|
+
rspec (>= 2.99.0, < 4.0)
|
75
|
+
guard-rubocop (1.3.0)
|
76
|
+
guard (~> 2.0)
|
77
|
+
rubocop (~> 0.20)
|
78
|
+
hirb (0.7.3)
|
79
|
+
i18n (1.7.0)
|
80
|
+
concurrent-ruby (~> 1.0)
|
81
|
+
interception (0.5)
|
82
|
+
jaro_winkler (1.5.3)
|
83
|
+
json (2.2.0)
|
84
|
+
listen (3.1.5)
|
85
|
+
rb-fsevent (~> 0.9, >= 0.9.4)
|
86
|
+
rb-inotify (~> 0.9, >= 0.9.7)
|
87
|
+
ruby_dep (~> 1.2)
|
88
|
+
lumberjack (1.0.13)
|
89
|
+
memory_profiler (0.9.14)
|
90
|
+
method_source (0.9.2)
|
91
|
+
mini_portile2 (2.4.0)
|
92
|
+
minitest (5.13.0)
|
93
|
+
multi_json (1.14.1)
|
94
|
+
multipart-post (2.1.1)
|
95
|
+
nenv (0.3.0)
|
96
|
+
nokogiri (1.10.3)
|
97
|
+
mini_portile2 (~> 2.4.0)
|
98
|
+
notiffany (0.1.1)
|
99
|
+
nenv (~> 0.1)
|
100
|
+
shellany (~> 0.0)
|
101
|
+
octokit (4.14.0)
|
102
|
+
sawyer (~> 0.8.0, >= 0.5.3)
|
103
|
+
parallel (1.17.0)
|
104
|
+
parser (2.6.3.0)
|
105
|
+
ast (~> 2.4.0)
|
106
|
+
pg (1.1.4)
|
107
|
+
pg_query (1.3.0)
|
108
|
+
pry (0.12.2)
|
109
|
+
coderay (~> 1.1.0)
|
110
|
+
method_source (~> 0.9.0)
|
111
|
+
pry-alias (0.0.1)
|
112
|
+
binding_of_caller
|
113
|
+
pry
|
114
|
+
pry-doc (1.0.0)
|
115
|
+
pry (~> 0.11)
|
116
|
+
yard (~> 0.9.11)
|
117
|
+
pry-nav (0.3.0)
|
118
|
+
pry (>= 0.9.10, < 0.13.0)
|
119
|
+
pry-rescue (1.5.0)
|
120
|
+
interception (>= 0.5)
|
121
|
+
pry (>= 0.12.0)
|
122
|
+
pry-stack_explorer (0.4.9.3)
|
123
|
+
binding_of_caller (>= 0.7)
|
124
|
+
pry (>= 0.9.11)
|
125
|
+
public_suffix (4.0.1)
|
126
|
+
rainbow (3.0.0)
|
127
|
+
rake (13.0.1)
|
128
|
+
rake-compiler (1.0.7)
|
129
|
+
rake
|
130
|
+
rb-fsevent (0.10.3)
|
131
|
+
rb-inotify (0.10.0)
|
132
|
+
ffi (~> 1.0)
|
133
|
+
retriable (3.1.2)
|
134
|
+
rspec (3.8.0)
|
135
|
+
rspec-core (~> 3.8.0)
|
136
|
+
rspec-expectations (~> 3.8.0)
|
137
|
+
rspec-mocks (~> 3.8.0)
|
138
|
+
rspec-core (3.8.2)
|
139
|
+
rspec-support (~> 3.8.0)
|
140
|
+
rspec-expectations (3.8.4)
|
141
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
142
|
+
rspec-support (~> 3.8.0)
|
143
|
+
rspec-mocks (3.8.1)
|
144
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
145
|
+
rspec-support (~> 3.8.0)
|
146
|
+
rspec-support (3.8.2)
|
147
|
+
rubocop (0.71.0)
|
148
|
+
jaro_winkler (~> 1.5.1)
|
149
|
+
parallel (~> 1.10)
|
150
|
+
parser (>= 2.6)
|
151
|
+
rainbow (>= 2.2.2, < 4.0)
|
152
|
+
ruby-progressbar (~> 1.7)
|
153
|
+
unicode-display_width (>= 1.4.0, < 1.7)
|
154
|
+
ruby-progressbar (1.10.1)
|
155
|
+
ruby_dep (1.5.0)
|
156
|
+
sawyer (0.8.2)
|
157
|
+
addressable (>= 2.3.5)
|
158
|
+
faraday (> 0.8, < 2.0)
|
159
|
+
shellany (0.0.1)
|
160
|
+
simplecov (0.16.1)
|
161
|
+
docile (~> 1.1)
|
162
|
+
json (>= 1.8, < 3)
|
163
|
+
simplecov-html (~> 0.10.0)
|
164
|
+
simplecov-console (0.4.2)
|
165
|
+
ansi
|
166
|
+
hirb
|
167
|
+
simplecov
|
168
|
+
simplecov-html (0.10.2)
|
169
|
+
stackprof (0.2.13)
|
170
|
+
thor (0.20.3)
|
171
|
+
thread_safe (0.3.6)
|
172
|
+
tzinfo (1.2.5)
|
173
|
+
thread_safe (~> 0.1)
|
174
|
+
unicode-display_width (1.6.0)
|
175
|
+
yard (0.9.20)
|
176
|
+
|
177
|
+
PLATFORMS
|
178
|
+
ruby
|
179
|
+
|
180
|
+
DEPENDENCIES
|
181
|
+
appraisal (~> 2.2.0)
|
182
|
+
approvals (~> 0.0.24)
|
183
|
+
arel_toolkit!
|
184
|
+
bundler (~> 2.0)
|
185
|
+
database_cleaner (~> 1.7.0)
|
186
|
+
dpl (~> 1.10.11)
|
187
|
+
github_changelog_generator (~> 1.15)
|
188
|
+
guard (~> 2.15)
|
189
|
+
guard-rake (~> 1.0.0)
|
190
|
+
guard-rspec (~> 4.7)
|
191
|
+
guard-rubocop (~> 1.3.0)
|
192
|
+
memory_profiler (~> 0.9)
|
193
|
+
pry
|
194
|
+
pry-alias
|
195
|
+
pry-doc
|
196
|
+
pry-nav
|
197
|
+
pry-rescue
|
198
|
+
pry-stack_explorer
|
199
|
+
rake (~> 13.0)
|
200
|
+
rake-compiler (~> 1.0)
|
201
|
+
rspec (~> 3.8)
|
202
|
+
rubocop (= 0.71.0)
|
203
|
+
simplecov (~> 0.16.1)
|
204
|
+
simplecov-console (~> 0.4.2)
|
205
|
+
stackprof (~> 0.2)
|
206
|
+
|
207
|
+
BUNDLED WITH
|
208
|
+
2.1.4
|
data/lib/arel/enhance.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require_relative './enhance/node'
|
2
|
+
require_relative './enhance/path'
|
3
|
+
require_relative './enhance/path_node'
|
4
|
+
require_relative './enhance/query'
|
5
|
+
require_relative './enhance/query_methods'
|
6
|
+
require_relative './enhance/visitor'
|
7
|
+
|
8
|
+
module Arel
|
9
|
+
module Enhance
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.enhance(object)
|
13
|
+
return object if object.is_a?(Arel::Enhance::Node)
|
14
|
+
|
15
|
+
Arel::Enhance::Visitor.new.accept(object)
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module Arel
|
2
|
+
module Enhance
|
3
|
+
module ContextEnhancer
|
4
|
+
class ArelTable
|
5
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
6
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
7
|
+
# rubocop:disable Metrics/AbcSize
|
8
|
+
def self.call(node)
|
9
|
+
context = node.context.merge!(
|
10
|
+
range_variable: false, column_reference: false, alias: false,
|
11
|
+
)
|
12
|
+
parent_object = node.parent.object
|
13
|
+
|
14
|
+
# Using Arel::Table as SELECT ... FROM <table>
|
15
|
+
if parent_object.is_a?(Arel::Nodes::JoinSource)
|
16
|
+
context[:range_variable] = true
|
17
|
+
|
18
|
+
# NOTE: only applies to ActiveRecord generated Arel
|
19
|
+
# which does not use Arel::Table#alias but Arel::TableAlias instead
|
20
|
+
# Using Arel::Table as SELECT ... FROM <table> AS alias
|
21
|
+
elsif parent_object.is_a?(Arel::Nodes::TableAlias) &&
|
22
|
+
node.parent.parent.object.is_a?(Arel::Nodes::JoinSource)
|
23
|
+
context[:range_variable] = true
|
24
|
+
|
25
|
+
# Using Arel::Table as SELECT ... FROM [<table>]
|
26
|
+
elsif parent_object.is_a?(Array) &&
|
27
|
+
node.parent.parent.object.is_a?(Arel::Nodes::JoinSource)
|
28
|
+
context[:range_variable] = true
|
29
|
+
|
30
|
+
# NOTE: only applies to ActiveRecord generated Arel
|
31
|
+
# which does not use Arel::Table#alias but Arel::TableAlias instead
|
32
|
+
# Using Arel::Table as SELECT ... FROM [<table> AS alias]
|
33
|
+
elsif parent_object.is_a?(Arel::Nodes::TableAlias) &&
|
34
|
+
node.parent.parent.object.is_a?(Array) &&
|
35
|
+
node.parent.parent.parent.object.is_a?(Arel::Nodes::JoinSource)
|
36
|
+
context[:range_variable] = true
|
37
|
+
|
38
|
+
# Using Arel::Table as SELECT ... INNER JOIN <table> ON TRUE
|
39
|
+
elsif parent_object.is_a?(Arel::Nodes::Join)
|
40
|
+
context[:range_variable] = true
|
41
|
+
|
42
|
+
# Using Arel::Table as an attribute SELECT <table>.id ...
|
43
|
+
elsif parent_object.is_a?(Arel::Attributes::Attribute)
|
44
|
+
context[:column_reference] = true
|
45
|
+
|
46
|
+
# Using Arel::Table in an INSERT INTO <table>
|
47
|
+
elsif parent_object.is_a?(Arel::Nodes::InsertStatement)
|
48
|
+
context[:range_variable] = true
|
49
|
+
|
50
|
+
# Using Arel::Table in an UPDATE <table> ...
|
51
|
+
elsif parent_object.is_a?(Arel::Nodes::UpdateStatement)
|
52
|
+
context[:range_variable] = true
|
53
|
+
|
54
|
+
# Arel::Table in UPDATE ... FROM [<table>]
|
55
|
+
elsif parent_object.is_a?(Array) &&
|
56
|
+
node.parent.parent.object.is_a?(Arel::Nodes::UpdateStatement)
|
57
|
+
context[:range_variable] = true
|
58
|
+
|
59
|
+
# Using Arel::Table in an DELETE FROM <table>
|
60
|
+
elsif parent_object.is_a?(Arel::Nodes::DeleteStatement)
|
61
|
+
context[:range_variable] = true
|
62
|
+
|
63
|
+
# Arel::Table in DELETE ... USING [<table>]
|
64
|
+
elsif parent_object.is_a?(Array) &&
|
65
|
+
node.parent.parent.object.is_a?(Arel::Nodes::DeleteStatement)
|
66
|
+
context[:range_variable] = true
|
67
|
+
|
68
|
+
# Using Arel::Table as an "alias" for WITH <table> AS (SELECT 1) SELECT 1
|
69
|
+
elsif parent_object.is_a?(Arel::Nodes::As) &&
|
70
|
+
node.parent.parent.parent.object.is_a?(Arel::Nodes::With)
|
71
|
+
context[:alias] = true
|
72
|
+
|
73
|
+
# Using Arel::Table as an "alias" for WITH RECURSIVE <table> AS (SELECT 1) SELECT 1
|
74
|
+
elsif parent_object.is_a?(Arel::Nodes::As) &&
|
75
|
+
node.parent.parent.parent.object.is_a?(Arel::Nodes::WithRecursive)
|
76
|
+
context[:alias] = true
|
77
|
+
|
78
|
+
# Using Arel::Table as an "alias" for SELECT INTO <table> ...
|
79
|
+
elsif parent_object.is_a?(Arel::Nodes::Into)
|
80
|
+
context[:alias] = true
|
81
|
+
|
82
|
+
else
|
83
|
+
raise "Unknown AST location for table #{node.inspect}, #{node.root_node.to_sql}"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
87
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
88
|
+
# rubocop:enable Metrics/AbcSize
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,232 @@
|
|
1
|
+
module Arel
|
2
|
+
module Enhance
|
3
|
+
class Node
|
4
|
+
attr_reader :object
|
5
|
+
attr_reader :parent
|
6
|
+
attr_reader :local_path
|
7
|
+
attr_reader :fields
|
8
|
+
attr_reader :children
|
9
|
+
attr_reader :root_node
|
10
|
+
attr_reader :context
|
11
|
+
|
12
|
+
def initialize(object)
|
13
|
+
@object = object
|
14
|
+
@root_node = self
|
15
|
+
@fields = []
|
16
|
+
@children = {}
|
17
|
+
@dirty = false
|
18
|
+
@context = {}
|
19
|
+
end
|
20
|
+
|
21
|
+
def inspect
|
22
|
+
recursive_inspect('')
|
23
|
+
end
|
24
|
+
|
25
|
+
def value
|
26
|
+
return unless value?
|
27
|
+
|
28
|
+
@fields.first
|
29
|
+
end
|
30
|
+
|
31
|
+
def each(&block)
|
32
|
+
return enum_for(:each) unless block_given?
|
33
|
+
|
34
|
+
yield self
|
35
|
+
|
36
|
+
children.each_value do |child|
|
37
|
+
child.each(&block)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def value?
|
42
|
+
children.empty?
|
43
|
+
end
|
44
|
+
|
45
|
+
def dirty?
|
46
|
+
root_node.instance_values.fetch('dirty')
|
47
|
+
end
|
48
|
+
|
49
|
+
def remove
|
50
|
+
mutate(nil, remove: true)
|
51
|
+
end
|
52
|
+
|
53
|
+
def replace(new_arel_node)
|
54
|
+
mutate(new_arel_node)
|
55
|
+
end
|
56
|
+
|
57
|
+
def add(path_node, node)
|
58
|
+
node.local_path = path_node
|
59
|
+
node.parent = self
|
60
|
+
node.root_node = root_node
|
61
|
+
@children[path_node.value.to_s] = node
|
62
|
+
end
|
63
|
+
|
64
|
+
def to_sql(engine = Table.engine)
|
65
|
+
return nil if children.empty?
|
66
|
+
|
67
|
+
if object.respond_to?(:to_sql)
|
68
|
+
object.to_sql(engine)
|
69
|
+
else
|
70
|
+
collector = Arel::Collectors::SQLString.new
|
71
|
+
collector = engine.connection.visitor.accept object, collector
|
72
|
+
collector.value
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def to_sql_and_binds(engine = Table.engine)
|
77
|
+
object.to_sql_and_binds(engine)
|
78
|
+
end
|
79
|
+
|
80
|
+
def method_missing(name, *args, &block)
|
81
|
+
child = @children[name.to_s]
|
82
|
+
return super if child.nil?
|
83
|
+
|
84
|
+
child
|
85
|
+
end
|
86
|
+
|
87
|
+
def respond_to_missing?(method, include_private = false)
|
88
|
+
child = @children[method.to_s]
|
89
|
+
child.present? || super
|
90
|
+
end
|
91
|
+
|
92
|
+
def [](key)
|
93
|
+
@children.fetch(key.to_s)
|
94
|
+
end
|
95
|
+
|
96
|
+
def child_at_path(path_items)
|
97
|
+
selected_node = self
|
98
|
+
path_items.each do |path_item|
|
99
|
+
selected_node = selected_node[path_item]
|
100
|
+
return nil if selected_node.nil?
|
101
|
+
end
|
102
|
+
selected_node
|
103
|
+
end
|
104
|
+
|
105
|
+
def query(**kwargs)
|
106
|
+
Arel::Enhance::Query.call(self, kwargs)
|
107
|
+
end
|
108
|
+
|
109
|
+
def full_path
|
110
|
+
the_path = [local_path]
|
111
|
+
current_parent = parent
|
112
|
+
|
113
|
+
while current_parent
|
114
|
+
the_path.unshift current_parent.local_path
|
115
|
+
current_parent = current_parent.parent
|
116
|
+
end
|
117
|
+
|
118
|
+
the_path.compact
|
119
|
+
end
|
120
|
+
|
121
|
+
protected
|
122
|
+
|
123
|
+
attr_writer :local_path
|
124
|
+
attr_writer :parent
|
125
|
+
attr_writer :root_node
|
126
|
+
|
127
|
+
# rubocop:disable Metrics/AbcSize
|
128
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
129
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
130
|
+
def recursive_inspect(string, indent = 1)
|
131
|
+
string << "<#{inspect_name} #{full_path.inspect}\n"
|
132
|
+
string << "#{spacing(indent)}sql = #{to_sql}\n" unless to_sql.nil?
|
133
|
+
string << "#{spacing(indent)}parent = #{parent.nil? ? nil.inspect : parent.inspect_name}"
|
134
|
+
string << "\n" unless children.length.zero?
|
135
|
+
children.each do |key, child|
|
136
|
+
string << "#{spacing(indent)}#{key} =\n"
|
137
|
+
string << spacing(indent + 1)
|
138
|
+
child.recursive_inspect(string, indent + 2)
|
139
|
+
end
|
140
|
+
string << "\n" if children.length.zero? && value?
|
141
|
+
string << "#{spacing(indent)}value = #{value.inspect}" if value?
|
142
|
+
|
143
|
+
string << if children.length.zero?
|
144
|
+
">\n"
|
145
|
+
else
|
146
|
+
"#{spacing(indent - 1)}>\n"
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# rubocop:enable Metrics/AbcSize
|
151
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
152
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
153
|
+
attr_writer :object
|
154
|
+
|
155
|
+
def inspect_name
|
156
|
+
"Node(#{object.class.name})"
|
157
|
+
end
|
158
|
+
|
159
|
+
def spacing(indent)
|
160
|
+
indent.times.reduce('') do |memo|
|
161
|
+
memo << ' '
|
162
|
+
memo
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def deep_copy_object
|
167
|
+
# https://github.com/mvgijssel/arel_toolkit/issues/97
|
168
|
+
new_object = Marshal.load(Marshal.dump(object))
|
169
|
+
self.object = new_object
|
170
|
+
|
171
|
+
recursive_update_object(new_object)
|
172
|
+
end
|
173
|
+
|
174
|
+
def recursive_update_object(arel_tree)
|
175
|
+
children.each_value do |child|
|
176
|
+
tree_child = arel_tree.send(*child.local_path.method)
|
177
|
+
child.object = tree_child
|
178
|
+
child.recursive_update_object(tree_child)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def mark_as_dirty
|
183
|
+
return if dirty?
|
184
|
+
|
185
|
+
@dirty = true
|
186
|
+
deep_copy_object
|
187
|
+
end
|
188
|
+
|
189
|
+
private
|
190
|
+
|
191
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
192
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
193
|
+
# rubocop:disable Metrics/AbcSize
|
194
|
+
def mutate(new_node, remove: false)
|
195
|
+
root_node.mark_as_dirty
|
196
|
+
|
197
|
+
parent_object = parent.object
|
198
|
+
new_arel_node = new_node.is_a?(Arel::Enhance::Node) ? new_node.object : new_node
|
199
|
+
new_arel_node = [] if remove && object.is_a?(Array)
|
200
|
+
|
201
|
+
if parent_object.respond_to?("#{local_path.value}=")
|
202
|
+
parent_object.send("#{local_path.value}=", new_arel_node)
|
203
|
+
|
204
|
+
elsif parent_object.instance_values.key?(local_path.value)
|
205
|
+
parent_object.instance_variable_set("@#{local_path.value}", new_arel_node)
|
206
|
+
|
207
|
+
elsif local_path.arguments? && parent_object.respond_to?(local_path.method[0])
|
208
|
+
if remove
|
209
|
+
parent_object.delete_at(local_path.value)
|
210
|
+
|
211
|
+
else
|
212
|
+
parent_object[local_path.value] = new_arel_node
|
213
|
+
end
|
214
|
+
else
|
215
|
+
raise "Don't know how to replace `#{local_path.value}` in #{parent_object.inspect}"
|
216
|
+
end
|
217
|
+
|
218
|
+
if new_node.is_a?(Arel::Enhance::Node)
|
219
|
+
parent.add(local_path, new_node)
|
220
|
+
parent[local_path.value]
|
221
|
+
else
|
222
|
+
new_parent_tree = Visitor.new.accept_with_root(parent_object, parent)
|
223
|
+
parent.parent.add(parent.local_path, new_parent_tree)
|
224
|
+
new_parent_tree[local_path.value]
|
225
|
+
end
|
226
|
+
end
|
227
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
228
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
229
|
+
# rubocop:enable Metrics/AbcSize
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|