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.
Files changed (133) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +3 -0
  3. data/.github/workflows/develop.yml +90 -0
  4. data/.github/workflows/master.yml +67 -0
  5. data/.gitignore +8 -0
  6. data/.rubocop.yml +13 -5
  7. data/Appraisals +13 -0
  8. data/CHANGELOG.md +94 -5
  9. data/Gemfile +5 -0
  10. data/Gemfile.lock +62 -33
  11. data/Guardfile +4 -0
  12. data/README.md +67 -23
  13. data/Rakefile +11 -1
  14. data/arel_toolkit.gemspec +15 -6
  15. data/benchmark.rb +54 -0
  16. data/ext/pg_result_init/extconf.rb +52 -0
  17. data/ext/pg_result_init/pg_result_init.c +138 -0
  18. data/ext/pg_result_init/pg_result_init.h +6 -0
  19. data/gemfiles/active_record_6.gemfile +7 -0
  20. data/gemfiles/active_record_6.gemfile.lock +210 -0
  21. data/gemfiles/arel_gems.gemfile +10 -0
  22. data/gemfiles/arel_gems.gemfile.lock +284 -0
  23. data/gemfiles/default.gemfile +5 -0
  24. data/gemfiles/default.gemfile.lock +208 -0
  25. data/lib/arel/enhance.rb +17 -0
  26. data/lib/arel/enhance/context_enhancer/arel_table.rb +92 -0
  27. data/lib/arel/enhance/node.rb +232 -0
  28. data/lib/arel/enhance/path.rb +38 -0
  29. data/lib/arel/enhance/path_node.rb +26 -0
  30. data/lib/arel/enhance/query.rb +38 -0
  31. data/lib/arel/enhance/query_methods.rb +23 -0
  32. data/lib/arel/enhance/visitor.rb +97 -0
  33. data/lib/arel/extensions.rb +32 -6
  34. data/lib/arel/extensions/active_model_attribute_with_cast_value.rb +22 -0
  35. data/lib/arel/extensions/active_record_relation_query_attribute.rb +22 -0
  36. data/lib/arel/extensions/active_record_type_caster_connection.rb +7 -0
  37. data/lib/arel/extensions/active_record_type_caster_map.rb +7 -0
  38. data/lib/arel/extensions/array.rb +2 -9
  39. data/lib/arel/extensions/at_time_zone.rb +10 -3
  40. data/lib/arel/extensions/attributes_attribute.rb +47 -0
  41. data/lib/arel/extensions/binary.rb +7 -0
  42. data/lib/arel/extensions/bind_param.rb +15 -0
  43. data/lib/arel/extensions/bit_string.rb +2 -9
  44. data/lib/arel/extensions/case.rb +17 -0
  45. data/lib/arel/extensions/coalesce.rb +17 -3
  46. data/lib/arel/extensions/conflict.rb +9 -0
  47. data/lib/arel/extensions/contains.rb +27 -5
  48. data/lib/arel/extensions/current_catalog.rb +4 -0
  49. data/lib/arel/extensions/current_date.rb +4 -0
  50. data/lib/arel/extensions/current_of_expression.rb +2 -9
  51. data/lib/arel/extensions/current_role.rb +4 -0
  52. data/lib/arel/extensions/current_row.rb +7 -0
  53. data/lib/arel/extensions/current_schema.rb +4 -0
  54. data/lib/arel/extensions/current_user.rb +4 -0
  55. data/lib/arel/extensions/dealocate.rb +31 -0
  56. data/lib/arel/extensions/default_values.rb +4 -0
  57. data/lib/arel/extensions/delete_manager.rb +22 -6
  58. data/lib/arel/extensions/delete_statement.rb +46 -24
  59. data/lib/arel/extensions/dot.rb +11 -0
  60. data/lib/arel/extensions/exists.rb +59 -0
  61. data/lib/arel/extensions/extract_from.rb +3 -10
  62. data/lib/arel/extensions/factorial.rb +10 -2
  63. data/lib/arel/extensions/false.rb +7 -0
  64. data/lib/arel/extensions/function.rb +44 -14
  65. data/lib/arel/extensions/greatest.rb +17 -3
  66. data/lib/arel/extensions/indirection.rb +3 -12
  67. data/lib/arel/extensions/infer.rb +7 -7
  68. data/lib/arel/extensions/infix_operation.rb +17 -0
  69. data/lib/arel/extensions/insert_manager.rb +19 -3
  70. data/lib/arel/extensions/insert_statement.rb +31 -12
  71. data/lib/arel/extensions/into.rb +21 -0
  72. data/lib/arel/extensions/least.rb +17 -3
  73. data/lib/arel/extensions/named_argument.rb +3 -8
  74. data/lib/arel/extensions/named_function.rb +7 -0
  75. data/lib/arel/extensions/node.rb +10 -0
  76. data/lib/arel/extensions/ordering.rb +21 -6
  77. data/lib/arel/extensions/overlaps.rb +9 -0
  78. data/lib/arel/extensions/overlay.rb +9 -0
  79. data/lib/arel/extensions/position.rb +3 -8
  80. data/lib/arel/extensions/prepare.rb +39 -0
  81. data/lib/arel/extensions/range_function.rb +10 -2
  82. data/lib/arel/extensions/row.rb +3 -8
  83. data/lib/arel/extensions/select_core.rb +73 -0
  84. data/lib/arel/extensions/select_manager.rb +22 -6
  85. data/lib/arel/extensions/select_statement.rb +31 -9
  86. data/lib/arel/extensions/session_user.rb +4 -0
  87. data/lib/arel/extensions/set_to_default.rb +4 -0
  88. data/lib/arel/extensions/substring.rb +8 -0
  89. data/lib/arel/extensions/table.rb +43 -10
  90. data/lib/arel/extensions/time_with_precision.rb +6 -0
  91. data/lib/arel/extensions/to_sql.rb +27 -0
  92. data/lib/arel/extensions/top.rb +8 -0
  93. data/lib/arel/extensions/transaction.rb +3 -8
  94. data/lib/arel/extensions/tree_manager.rb +15 -0
  95. data/lib/arel/extensions/trim.rb +8 -0
  96. data/lib/arel/extensions/true.rb +7 -0
  97. data/lib/arel/extensions/type_cast.rb +7 -0
  98. data/lib/arel/extensions/unary.rb +7 -0
  99. data/lib/arel/extensions/unary_operation.rb +16 -0
  100. data/lib/arel/extensions/unknown.rb +4 -0
  101. data/lib/arel/extensions/update_manager.rb +22 -6
  102. data/lib/arel/extensions/update_statement.rb +36 -33
  103. data/lib/arel/extensions/user.rb +4 -0
  104. data/lib/arel/extensions/values_list.rb +15 -0
  105. data/lib/arel/extensions/variable_set.rb +9 -0
  106. data/lib/arel/extensions/variable_show.rb +3 -8
  107. data/lib/arel/middleware.rb +5 -1
  108. data/lib/arel/middleware/active_record_extension.rb +13 -0
  109. data/lib/arel/middleware/cache_accessor.rb +35 -0
  110. data/lib/arel/middleware/chain.rb +108 -33
  111. data/lib/arel/middleware/database_executor.rb +77 -0
  112. data/lib/arel/middleware/no_op_cache.rb +9 -0
  113. data/lib/arel/middleware/postgresql_adapter.rb +41 -5
  114. data/lib/arel/middleware/railtie.rb +15 -1
  115. data/lib/arel/middleware/result.rb +170 -0
  116. data/lib/arel/middleware/to_sql_executor.rb +15 -0
  117. data/lib/arel/middleware/to_sql_middleware.rb +33 -0
  118. data/lib/arel/sql_to_arel.rb +6 -3
  119. data/lib/arel/sql_to_arel/pg_query_visitor.rb +67 -38
  120. data/lib/arel/sql_to_arel/pg_query_visitor/frame_options.rb +1 -1
  121. data/lib/arel/sql_to_arel/result.rb +17 -4
  122. data/lib/arel/transformer.rb +8 -0
  123. data/lib/arel/transformer/prefix_schema_name.rb +183 -0
  124. data/lib/arel/transformer/remove_active_record_info.rb +40 -0
  125. data/lib/arel/transformer/replace_table_with_subquery.rb +31 -0
  126. data/lib/arel_toolkit.rb +15 -2
  127. data/lib/arel_toolkit/version.rb +1 -1
  128. metadata +179 -42
  129. data/.travis.yml +0 -29
  130. data/lib/arel/extensions/generate_series.rb +0 -9
  131. data/lib/arel/extensions/rank.rb +0 -9
  132. data/lib/arel/extensions/unbound_column_reference.rb +0 -5
  133. data/lib/arel/sql_formatter.rb +0 -59
@@ -0,0 +1,5 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec path: "../"
@@ -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
@@ -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