sunstone 5.0.0.beta3 → 5.0.0.1
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/.gitignore +1 -0
- data/.tm_properties +1 -0
- data/.travis.yml +36 -0
- data/README.md +1 -2
- data/Rakefile.rb +1 -1
- data/ext/active_record/associations/collection_association.rb +48 -6
- data/ext/active_record/attribute_methods.rb +25 -21
- data/ext/active_record/callbacks.rb +17 -0
- data/ext/active_record/finder_methods.rb +44 -2
- data/ext/active_record/persistence.rb +127 -1
- data/ext/active_record/relation.rb +13 -5
- data/ext/active_record/relation/calculations.rb +25 -0
- data/ext/active_record/statement_cache.rb +3 -2
- data/ext/active_record/transactions.rb +60 -0
- data/ext/arel/attributes/empty_relation.rb +31 -0
- data/ext/arel/attributes/relation.rb +3 -2
- data/lib/active_record/connection_adapters/sunstone/database_statements.rb +13 -2
- data/lib/active_record/connection_adapters/sunstone/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sunstone/schema_statements.rb +2 -2
- data/lib/active_record/connection_adapters/sunstone/type/uuid.rb +21 -0
- data/lib/active_record/connection_adapters/sunstone_adapter.rb +54 -30
- data/lib/arel/collectors/sunstone.rb +6 -4
- data/lib/arel/visitors/sunstone.rb +61 -39
- data/lib/sunstone.rb +18 -11
- data/lib/sunstone/connection.rb +62 -22
- data/lib/sunstone/exception.rb +3 -0
- data/lib/sunstone/gis.rb +1 -0
- data/lib/sunstone/version.rb +2 -2
- data/sunstone.gemspec +4 -5
- data/test/active_record/associations/has_and_belongs_to_many_test.rb +12 -0
- data/test/active_record/associations/has_many_test.rb +72 -0
- data/test/active_record/eager_loading_test.rb +15 -0
- data/test/active_record/persistance_test.rb +190 -0
- data/test/active_record/preload_test.rb +16 -0
- data/test/active_record/query_test.rb +91 -0
- data/test/models.rb +91 -0
- data/test/sunstone/connection/configuration_test.rb +44 -0
- data/test/sunstone/connection/cookie_store_test.rb +37 -0
- data/test/sunstone/connection/request_helper_test.rb +105 -0
- data/test/sunstone/connection/send_request_test.rb +164 -0
- data/test/sunstone/connection_test.rb +2 -298
- data/test/test_helper.rb +45 -2
- metadata +52 -47
- data/ext/active_record/associations/builder/has_and_belongs_to_many.rb +0 -48
- data/ext/active_record/calculations.rb +0 -32
- data/ext/active_record/query_methods.rb +0 -30
- data/ext/active_record/relation/predicate_builder.rb +0 -23
- data/test/models/ship.rb +0 -14
- data/test/query_test.rb +0 -134
- data/test/sunstone/parser_test.rb +0 -124
- data/test/sunstone_test.rb +0 -303
@@ -5,16 +5,24 @@ module ActiveRecord
|
|
5
5
|
@to_sql ||= begin
|
6
6
|
relation = self
|
7
7
|
connection = klass.connection
|
8
|
-
visitor = connection.visitor.is_a?(Arel::Visitors::Sunstone)
|
8
|
+
visitor = if connection.visitor.is_a?(Arel::Visitors::Sunstone)
|
9
|
+
Arel::Visitors::ToSql.new(connection)
|
10
|
+
else
|
11
|
+
connection.visitor
|
12
|
+
end
|
9
13
|
|
10
14
|
if eager_loading?
|
11
15
|
find_with_associations { |rel| relation = rel }
|
12
16
|
end
|
13
17
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
+
binds = if connection.visitor.is_a?(Arel::Visitors::Sunstone)
|
19
|
+
relation.arel.bind_values + relation.bound_attributes
|
20
|
+
else
|
21
|
+
relation.bound_attributes
|
22
|
+
end
|
23
|
+
binds = connection.prepare_binds_for_database(binds)
|
24
|
+
binds.map! { |value| connection.quote(value) }
|
25
|
+
collect = visitor.accept(relation.arel.ast, Arel::Collectors::Bind.new)
|
18
26
|
collect.substitute_binds(binds).join
|
19
27
|
end
|
20
28
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Calculations
|
3
|
+
|
4
|
+
def pluck(*column_names)
|
5
|
+
if loaded? && (column_names.map(&:to_s) - @klass.attribute_names - @klass.attribute_aliases.keys).empty?
|
6
|
+
return @records.pluck(*column_names)
|
7
|
+
end
|
8
|
+
|
9
|
+
if has_include?(column_names.first)
|
10
|
+
construct_relation_for_association_calculations.pluck(*column_names)
|
11
|
+
elsif klass.connection.is_a?(ActiveRecord::ConnectionAdapters::SunstoneAPIAdapter)
|
12
|
+
load
|
13
|
+
return @records.pluck(*column_names.map{|n| n.sub(/^#{klass.table_name}\./, "")})
|
14
|
+
else
|
15
|
+
relation = spawn
|
16
|
+
relation.select_values = column_names.map { |cn|
|
17
|
+
@klass.has_attribute?(cn) || @klass.attribute_alias?(cn) ? arel_attribute(cn) : cn
|
18
|
+
}
|
19
|
+
result = klass.connection.select_all(relation.arel, nil, bound_attributes)
|
20
|
+
result.cast_values(klass.attribute_types)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -22,7 +22,7 @@ module ActiveRecord
|
|
22
22
|
@values.compile(binds)
|
23
23
|
else
|
24
24
|
val = @values.dup
|
25
|
-
@indexes.each { |i| val[i] = connection.quote(
|
25
|
+
@indexes.each { |i| val[i] = connection.quote(binds.shift) }
|
26
26
|
val.join
|
27
27
|
end
|
28
28
|
end
|
@@ -30,7 +30,8 @@ module ActiveRecord
|
|
30
30
|
|
31
31
|
def self.partial_query(visitor, ast, collector)
|
32
32
|
collected = visitor.accept(ast, collector)
|
33
|
-
|
33
|
+
collected = collected.value if !visitor.is_a?(Arel::Visitors::Sunstone)
|
34
|
+
PartialQuery.new(collected, visitor.is_a?(Arel::Visitors::Sunstone))
|
34
35
|
end
|
35
36
|
|
36
37
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
# See ActiveRecord::Transactions::ClassMethods for documentation.
|
3
|
+
module Transactions
|
4
|
+
|
5
|
+
# # See ActiveRecord::Transactions::ClassMethods for detailed documentation.
|
6
|
+
# def transaction(options = {}, &block)
|
7
|
+
# self.class.transaction(options, &block)
|
8
|
+
# end
|
9
|
+
#
|
10
|
+
# def destroy #:nodoc:
|
11
|
+
# with_transaction_returning_status { super }
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# def save(*) #:nodoc:
|
15
|
+
# rollback_active_record_state! do
|
16
|
+
# with_transaction_returning_status { super }
|
17
|
+
# end
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# def save!(*) #:nodoc:
|
21
|
+
# with_transaction_returning_status { super }
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# def touch(*) #:nodoc:
|
25
|
+
# with_transaction_returning_status { super }
|
26
|
+
# end
|
27
|
+
|
28
|
+
def with_transaction_returning_status
|
29
|
+
if self.class.connection.is_a?(ActiveRecord::ConnectionAdapters::SunstoneAPIAdapter) && @updating
|
30
|
+
begin
|
31
|
+
status = yield
|
32
|
+
rescue ActiveRecord::Rollback
|
33
|
+
clear_transaction_record_state
|
34
|
+
status = nil
|
35
|
+
end
|
36
|
+
return status
|
37
|
+
end
|
38
|
+
|
39
|
+
status = nil
|
40
|
+
self.class.transaction do
|
41
|
+
add_to_transaction
|
42
|
+
begin
|
43
|
+
status = yield
|
44
|
+
rescue ActiveRecord::Rollback
|
45
|
+
clear_transaction_record_state
|
46
|
+
status = nil
|
47
|
+
end
|
48
|
+
|
49
|
+
raise ActiveRecord::Rollback unless status
|
50
|
+
end
|
51
|
+
status
|
52
|
+
ensure
|
53
|
+
if @transaction_state && @transaction_state.committed?
|
54
|
+
clear_transaction_record_state
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Arel
|
2
|
+
module Attributes
|
3
|
+
class EmptyRelation < Attribute
|
4
|
+
|
5
|
+
attr_accessor :collection, :for_write
|
6
|
+
|
7
|
+
def initialize(relation, name, collection = false, for_write=false)
|
8
|
+
self[:relation] = relation
|
9
|
+
self[:name] = name
|
10
|
+
@collection = collection
|
11
|
+
@for_write = for_write
|
12
|
+
end
|
13
|
+
|
14
|
+
def able_to_type_cast?
|
15
|
+
false
|
16
|
+
end
|
17
|
+
|
18
|
+
def table_name
|
19
|
+
nil
|
20
|
+
end
|
21
|
+
|
22
|
+
def eql? other
|
23
|
+
self.class == other.class &&
|
24
|
+
self.relation == other.relation &&
|
25
|
+
self.name == other.name &&
|
26
|
+
self.collection == other.collection
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -2,12 +2,13 @@ module Arel
|
|
2
2
|
module Attributes
|
3
3
|
class Relation < Attribute
|
4
4
|
|
5
|
-
attr_accessor :collection
|
5
|
+
attr_accessor :collection, :for_write
|
6
6
|
|
7
|
-
def initialize(relation, name, collection = false)
|
7
|
+
def initialize(relation, name, collection = false, for_write=false)
|
8
8
|
self[:relation] = relation
|
9
9
|
self[:name] = name
|
10
10
|
@collection = collection
|
11
|
+
@for_write = for_write
|
11
12
|
end
|
12
13
|
|
13
14
|
def able_to_type_cast?
|
@@ -15,13 +15,24 @@ module ActiveRecord
|
|
15
15
|
|
16
16
|
def exec_query(arel, name = 'SAR', binds = [], prepare: false)
|
17
17
|
sar = to_sar(arel, binds)
|
18
|
-
|
18
|
+
|
19
|
+
log_mess = sar.path.split('?', 2)
|
20
|
+
result = log("#{sar.method} #{log_mess[0]} #{(log_mess[1] && !log_mess[1].empty?) ? MessagePack.unpack(CGI.unescape(log_mess[1])) : '' }", name) {
|
21
|
+
response = @connection.send_request(sar)
|
22
|
+
if response.is_a?(Net::HTTPNoContent)
|
23
|
+
nil
|
24
|
+
else
|
25
|
+
JSON.parse(response.body)
|
26
|
+
end
|
27
|
+
}
|
19
28
|
|
20
29
|
if sar.instance_variable_get(:@sunstone_calculation)
|
21
30
|
# this is a count, min, max.... yea i know..
|
22
31
|
ActiveRecord::Result.new(['all'], [result], {:all => type_map.lookup('integer')})
|
23
|
-
|
32
|
+
elsif result.is_a?(Array)
|
24
33
|
ActiveRecord::Result.new(result[0] ? result[0].keys : [], result.map{|r| r.values})
|
34
|
+
else
|
35
|
+
ActiveRecord::Result.new(result.keys, [result])
|
25
36
|
end
|
26
37
|
end
|
27
38
|
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
module Sunstone
|
4
|
+
module ColumnDumper
|
5
|
+
|
6
|
+
# Adds +:array+ option to the default set
|
7
|
+
def prepare_column_options(column)
|
8
|
+
spec = super
|
9
|
+
spec[:array] = 'true' if column.array?
|
10
|
+
spec
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -25,13 +25,13 @@ module ActiveRecord
|
|
25
25
|
# - format_type includes the column size constraint, e.g. varchar(50)
|
26
26
|
# - ::regclass is a function that gives the id for a table name
|
27
27
|
def column_definitions(table_name) # :nodoc:
|
28
|
-
|
28
|
+
JSON.parse(@connection.get("/#{table_name}/schema").body)
|
29
29
|
rescue ::Sunstone::Exception::NotFound
|
30
30
|
raise ActiveRecord::StatementInvalid, "Table \"#{table_name}\" does not exist"
|
31
31
|
end
|
32
32
|
|
33
33
|
def tables
|
34
|
-
|
34
|
+
JSON.parse(@connection.get('/tables').body)
|
35
35
|
end
|
36
36
|
|
37
37
|
def views
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
module Sunstone
|
4
|
+
module Type # :nodoc:
|
5
|
+
class Uuid < ActiveRecord::Type::Value # :nodoc:
|
6
|
+
ACCEPTABLE_UUID = %r{\A\{?([a-fA-F0-9]{4}-?){8}\}?\z}x
|
7
|
+
|
8
|
+
alias_method :serialize, :deserialize
|
9
|
+
|
10
|
+
def type
|
11
|
+
:uuid
|
12
|
+
end
|
13
|
+
|
14
|
+
def cast(value)
|
15
|
+
value.to_s[ACCEPTABLE_UUID, 0]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -1,19 +1,20 @@
|
|
1
1
|
require 'active_record/connection_adapters/abstract_adapter'
|
2
|
-
require 'active_record/connection_adapters/statement_pool'
|
3
2
|
|
3
|
+
#require 'active_record/connection_adapters/statement_pool'
|
4
4
|
|
5
5
|
require 'active_record/connection_adapters/sunstone/database_statements'
|
6
6
|
require 'active_record/connection_adapters/sunstone/schema_statements'
|
7
|
+
require 'active_record/connection_adapters/sunstone/schema_dumper'
|
7
8
|
require 'active_record/connection_adapters/sunstone/column'
|
8
9
|
|
9
10
|
require 'active_record/connection_adapters/sunstone/type/date_time'
|
10
11
|
require 'active_record/connection_adapters/sunstone/type/array'
|
11
|
-
require 'active_record/connection_adapters/sunstone/type/
|
12
|
+
require 'active_record/connection_adapters/sunstone/type/uuid'
|
12
13
|
|
13
14
|
module ActiveRecord
|
14
15
|
module ConnectionHandling # :nodoc:
|
15
16
|
|
16
|
-
VALID_SUNSTONE_CONN_PARAMS = [:
|
17
|
+
VALID_SUNSTONE_CONN_PARAMS = [:url, :host, :port, :api_key, :use_ssl, :user_agent, :ca_cert]
|
17
18
|
|
18
19
|
# Establishes a connection to the database that's used by all Active Record
|
19
20
|
# objects
|
@@ -25,8 +26,8 @@ module ActiveRecord
|
|
25
26
|
# Map ActiveRecords param names to PGs.
|
26
27
|
conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
|
27
28
|
conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
|
28
|
-
if conn_params[:
|
29
|
-
uri = URI.parse(conn_params.delete(:
|
29
|
+
if conn_params[:url]
|
30
|
+
uri = URI.parse(conn_params.delete(:url))
|
30
31
|
conn_params[:api_key] ||= (uri.user ? CGI.unescape(uri.user) : nil)
|
31
32
|
conn_params[:host] ||= uri.host
|
32
33
|
conn_params[:port] ||= uri.port
|
@@ -67,7 +68,7 @@ module ActiveRecord
|
|
67
68
|
# include PostgreSQL::ReferentialIntegrity
|
68
69
|
include Sunstone::SchemaStatements
|
69
70
|
include Sunstone::DatabaseStatements
|
70
|
-
|
71
|
+
include Sunstone::ColumnDumper
|
71
72
|
# include Savepoints
|
72
73
|
|
73
74
|
# Returns 'SunstoneAPI' as adapter name for identification purposes.
|
@@ -75,17 +76,11 @@ module ActiveRecord
|
|
75
76
|
ADAPTER_NAME
|
76
77
|
end
|
77
78
|
|
78
|
-
# Adds `:array` option to the default set provided by the AbstractAdapter
|
79
|
-
def prepare_column_options(column, types) # :nodoc:
|
80
|
-
spec = super
|
81
|
-
spec[:array] = 'true' if column.respond_to?(:array) && column.array
|
82
|
-
spec
|
83
|
-
end
|
84
|
-
|
85
79
|
# Initializes and connects a SunstoneAPI adapter.
|
86
80
|
def initialize(connection, logger, connection_parameters, config)
|
87
81
|
super(connection, logger, config)
|
88
82
|
|
83
|
+
@prepared_statements = false
|
89
84
|
@visitor = Arel::Visitors::Sunstone.new
|
90
85
|
@connection_parameters, @config = connection_parameters, config
|
91
86
|
|
@@ -151,7 +146,7 @@ module ActiveRecord
|
|
151
146
|
end
|
152
147
|
|
153
148
|
def server_config
|
154
|
-
|
149
|
+
JSON.parse(@connection.get("/configuration").body)
|
155
150
|
end
|
156
151
|
|
157
152
|
def lookup_cast_type_from_column(column) # :nodoc:
|
@@ -162,7 +157,47 @@ module ActiveRecord
|
|
162
157
|
end
|
163
158
|
end
|
164
159
|
|
160
|
+
def transaction(requires_new: nil, isolation: nil, joinable: true)
|
161
|
+
Thread.current[:sunstone_transaction_count] ||= 0
|
162
|
+
Thread.current[:sunstone_request_sent] = nil if Thread.current[:sunstone_transaction_count] == 0
|
163
|
+
Thread.current[:sunstone_transaction_count] += 1
|
164
|
+
begin
|
165
|
+
yield
|
166
|
+
ensure
|
167
|
+
Thread.current[:sunstone_transaction_count] -= 1
|
168
|
+
if Thread.current[:sunstone_transaction_count] == 0
|
169
|
+
Thread.current[:sunstone_transaction_count] = nil
|
170
|
+
Thread.current[:sunstone_request_sent] = nil
|
171
|
+
end
|
172
|
+
end
|
173
|
+
rescue ActiveRecord::Rollback
|
174
|
+
# rollbacks are silently swallowed
|
175
|
+
end
|
176
|
+
|
177
|
+
def supports_json?
|
178
|
+
true
|
179
|
+
end
|
165
180
|
|
181
|
+
# Executes an INSERT query and returns the new record's ID
|
182
|
+
#
|
183
|
+
# +id_value+ will be returned unless the value is nil, in
|
184
|
+
# which case the database will attempt to calculate the last inserted
|
185
|
+
# id and return that value.
|
186
|
+
#
|
187
|
+
# If the next id was calculated in advance (as in Oracle), it should be
|
188
|
+
# passed in as +id_value+.
|
189
|
+
def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
|
190
|
+
sql, binds, pk, sequence_name = sql_for_insert(to_sql(arel, binds), pk, id_value, sequence_name, binds)
|
191
|
+
value = exec_insert(sql, name, binds, pk, sequence_name)
|
192
|
+
end
|
193
|
+
alias create insert
|
194
|
+
|
195
|
+
# Should be the defuat insert, but rails escapes if for SQL so we'll just
|
196
|
+
# catch the string "DEFATUL VALUES" in the visitor
|
197
|
+
# def empty_insert_statement_value
|
198
|
+
# {}
|
199
|
+
# end
|
200
|
+
|
166
201
|
private
|
167
202
|
|
168
203
|
def initialize_type_map(m) # :nodoc:
|
@@ -172,22 +207,11 @@ module ActiveRecord
|
|
172
207
|
m.register_type 'decimal', Type::Decimal.new
|
173
208
|
m.register_type 'datetime', Sunstone::Type::DateTime.new
|
174
209
|
m.register_type 'json', Type::Internal::AbstractJson.new
|
175
|
-
m.register_type '
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
# exec_cache(sql, name, binds)
|
181
|
-
sar = to_sar(arel, binds)
|
182
|
-
|
183
|
-
log(sar.is_a?(String) ? sar : "#{sar.class} #{CGI.unescape(sar.path)}", name) {
|
184
|
-
response = @connection.send_request(sar)
|
185
|
-
if response.is_a?(Net::HTTPNoContent)
|
186
|
-
nil
|
187
|
-
else
|
188
|
-
Wankel.parse(response.body)
|
189
|
-
end
|
190
|
-
}
|
210
|
+
m.register_type 'uuid', Sunstone::Type::Uuid.new
|
211
|
+
|
212
|
+
if defined?(Sunstone::Type::EWKB)
|
213
|
+
m.register_type 'ewkb', Sunstone::Type::EWKB.new
|
214
|
+
end
|
191
215
|
end
|
192
216
|
|
193
217
|
# Connects to a Sunstone API server and sets up the adapter depending on
|
@@ -89,13 +89,15 @@ module Arel
|
|
89
89
|
path += "/#{get_params[:where]['id']}"
|
90
90
|
get_params.delete(:where)
|
91
91
|
end
|
92
|
-
|
93
92
|
if get_params.size > 0
|
94
|
-
path +=
|
93
|
+
path += "?#{CGI.escape(MessagePack.pack(get_params))}"
|
95
94
|
end
|
96
|
-
|
95
|
+
|
97
96
|
request = request_type.new(path)
|
98
|
-
|
97
|
+
if get_params.size > 0
|
98
|
+
request['Query-Encoding'] = 'application/msgpack'
|
99
|
+
end
|
100
|
+
request.instance_variable_set(:@sunstone_calculation, true) if [:calculate, :delete].include?(operation)
|
99
101
|
|
100
102
|
if updates
|
101
103
|
request.body = body
|
@@ -78,30 +78,35 @@ module Arel
|
|
78
78
|
collector.operation = :insert
|
79
79
|
|
80
80
|
if o.values
|
81
|
-
|
82
|
-
values
|
83
|
-
|
81
|
+
|
82
|
+
if o.values.is_a?(Arel::Nodes::SqlLiteral) && o.values == 'DEFAULT VALUES'
|
83
|
+
collector.updates = {}
|
84
|
+
else
|
85
|
+
keys = o.values.right.map { |x| visit(x, collector) }
|
86
|
+
values = o.values.left
|
87
|
+
collector.updates = {}
|
84
88
|
|
85
89
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
90
|
+
keys.each_with_index do |k, i|
|
91
|
+
if k.is_a?(Hash)
|
92
|
+
add_to_bottom_of_hash_or_array(k, values[i])
|
93
|
+
collector.updates.deep_merge!(k) { |key, v1, v2|
|
94
|
+
if (v1.is_a?(Array) && v2.is_a?(Array))
|
95
|
+
v2.each_with_index do |v, j|
|
96
|
+
if v1[j].nil?
|
97
|
+
v1[j] = v2[j]
|
98
|
+
else
|
99
|
+
v1[j].deep_merge!(v2[j]) unless v2[j].nil?
|
100
|
+
end
|
96
101
|
end
|
102
|
+
v1
|
103
|
+
else
|
104
|
+
v2
|
97
105
|
end
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
}
|
103
|
-
else
|
104
|
-
collector.updates[k] = values[i]
|
106
|
+
}
|
107
|
+
else
|
108
|
+
collector.updates[k] = values[i]
|
109
|
+
end
|
105
110
|
end
|
106
111
|
end
|
107
112
|
end
|
@@ -198,9 +203,9 @@ module Arel
|
|
198
203
|
value.each do |key, v|
|
199
204
|
if key.is_a?(Hash)
|
200
205
|
add_to_bottom_of_hash_or_array(key, v)
|
201
|
-
collector.updates.deep_merge!(key) { |
|
206
|
+
collector.updates.deep_merge!(key) { |k, v1, v2|
|
202
207
|
if (v1.is_a?(Array) && v2.is_a?(Array))
|
203
|
-
v2.each_with_index do |
|
208
|
+
v2.each_with_index do |v2k, i|
|
204
209
|
if v1[i].nil?
|
205
210
|
v1[i] = v2[i]
|
206
211
|
else
|
@@ -689,18 +694,13 @@ module Arel
|
|
689
694
|
visit(o.left, collector) => {in: visit(o.right, collector)}
|
690
695
|
}
|
691
696
|
end
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
# collector = visit o.right, collector
|
700
|
-
# collector << ")"
|
701
|
-
# end
|
702
|
-
# end
|
703
|
-
#
|
697
|
+
|
698
|
+
def visit_Arel_Nodes_NotIn o, collector
|
699
|
+
{
|
700
|
+
visit(o.left, collector) => {not_in: visit(o.right, collector)}
|
701
|
+
}
|
702
|
+
end
|
703
|
+
|
704
704
|
def visit_Arel_Nodes_And o, collector
|
705
705
|
ors = []
|
706
706
|
ors << o.children.inject({}) do |c, x|
|
@@ -865,20 +865,42 @@ module Arel
|
|
865
865
|
end
|
866
866
|
end
|
867
867
|
|
868
|
-
def visit_Arel_Attributes_Relation o, collector
|
868
|
+
def visit_Arel_Attributes_Relation o, collector, top=true
|
869
|
+
value = if o.relation.is_a?(Arel::Attributes::Relation)
|
870
|
+
visit_Arel_Attributes_Relation(o.relation, collector, false)
|
871
|
+
else
|
872
|
+
visit(o.relation, collector)
|
873
|
+
end
|
874
|
+
value = value.to_s.split('.').last if !value.is_a?(Hash)
|
875
|
+
|
869
876
|
if o.collection
|
870
877
|
ary = []
|
871
|
-
ary[o.collection] =
|
872
|
-
|
878
|
+
ary[o.collection] = value
|
879
|
+
if top && o.name == collector.table
|
880
|
+
ary
|
881
|
+
elsif o.for_write
|
882
|
+
{"#{o.name}_attributes" => ary}
|
883
|
+
else
|
884
|
+
{o.name => ary}
|
885
|
+
end
|
873
886
|
else
|
874
|
-
|
887
|
+
if top && o.name == collector.table
|
888
|
+
value
|
889
|
+
elsif o.for_write
|
890
|
+
{"#{o.name}_attributes" => value}
|
891
|
+
else
|
892
|
+
{o.name => value}
|
893
|
+
end
|
875
894
|
end
|
876
895
|
end
|
896
|
+
|
897
|
+
def visit_Arel_Attributes_EmptyRelation o, collector, top=true
|
898
|
+
o.for_write ? "#{o.name}_attributes" : o.name
|
899
|
+
end
|
877
900
|
|
878
901
|
def visit_Arel_Attributes_Attribute o, collector
|
879
902
|
join_name = o.relation.table_alias || o.relation.name
|
880
903
|
collector.table == join_name ? o.name : "#{join_name}.#{o.name}"
|
881
|
-
# o.name
|
882
904
|
end
|
883
905
|
alias :visit_Arel_Attributes_Integer :visit_Arel_Attributes_Attribute
|
884
906
|
alias :visit_Arel_Attributes_Float :visit_Arel_Attributes_Attribute
|