sunstone 7.0.0 → 7.1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ext/active_record/associations/collection_association.rb +4 -26
- data/ext/active_record/associations.rb +5 -1
- data/ext/active_record/attribute_methods.rb +14 -13
- data/ext/active_record/callbacks.rb +4 -1
- data/ext/active_record/finder_methods.rb +14 -4
- data/ext/active_record/persistence.rb +55 -21
- data/ext/active_record/relation/calculations.rb +4 -1
- data/ext/active_record/transactions.rb +2 -2
- data/lib/active_record/connection_adapters/sunstone/column.rb +10 -0
- data/lib/active_record/connection_adapters/sunstone/database_statements.rb +47 -13
- data/lib/active_record/connection_adapters/sunstone/schema_statements.rb +6 -5
- data/lib/active_record/connection_adapters/sunstone/type/binary.rb +3 -3
- data/lib/active_record/connection_adapters/sunstone_adapter.rb +104 -65
- data/lib/arel/visitors/sunstone.rb +69 -58
- data/lib/sunstone/connection.rb +3 -3
- data/lib/sunstone/version.rb +1 -1
- metadata +11 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3956ea045dce2994b10bcac8535462bdf08597b85d7e15fc325110149a576aa6
|
4
|
+
data.tar.gz: 02be730df372f32557112e9fb618befb5aafd7a8ef15ce1e12a866f8c18c2ed7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c978ff5919d301007ccd326a632ed915cce129584e0ff9c25c199bddb9a65e517361f95a7d3a8af4ea70546acd1bb16fbc1118ff050a0c209f4168f9ec71feb9
|
7
|
+
data.tar.gz: 3a24f347b5ca8ccf2816e8d50a28ca3effedea79a897ab3692801b5cd8095d9fc324f37f792ffba621a9669eb7ea92f58be762b62d2a1e9096a9306fc1d46be9
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# The last ref that this code was synced with Rails
|
2
|
+
# ref: 9269f634d471ad6ca46752421eabd3e1c26220b5
|
1
3
|
module ActiveRecord
|
2
4
|
module Associations
|
3
5
|
class CollectionAssociation
|
@@ -8,7 +10,7 @@ module ActiveRecord
|
|
8
10
|
|
9
11
|
if owner.new_record?
|
10
12
|
replace_records(other_array, original_target)
|
11
|
-
elsif owner.class.connection.is_a?(ActiveRecord::ConnectionAdapters::SunstoneAPIAdapter) && owner.instance_variable_defined?(:@
|
13
|
+
elsif owner.class.connection.is_a?(ActiveRecord::ConnectionAdapters::SunstoneAPIAdapter) && owner.instance_variable_defined?(:@sunstone_updating) && owner.instance_variable_get(:@sunstone_updating)
|
12
14
|
replace_common_records_in_memory(other_array, original_target)
|
13
15
|
|
14
16
|
# Remove from target
|
@@ -41,7 +43,7 @@ module ActiveRecord
|
|
41
43
|
end
|
42
44
|
|
43
45
|
def insert_record(record, validate = true, raise = false, &block)
|
44
|
-
if record.class.connection.is_a?(ActiveRecord::ConnectionAdapters::SunstoneAPIAdapter) && owner.instance_variable_defined?(:@
|
46
|
+
if record.class.connection.is_a?(ActiveRecord::ConnectionAdapters::SunstoneAPIAdapter) && owner.instance_variable_defined?(:@sunstone_updating) && owner.instance_variable_get(:@sunstone_updating)
|
45
47
|
true
|
46
48
|
elsif raise
|
47
49
|
record.save!(validate: validate, &block)
|
@@ -70,27 +72,3 @@ module ActiveRecord
|
|
70
72
|
|
71
73
|
end
|
72
74
|
end
|
73
|
-
|
74
|
-
module ActiveRecord
|
75
|
-
module Persistence
|
76
|
-
|
77
|
-
# Updates the attributes of the model from the passed-in hash and saves the
|
78
|
-
# record, all wrapped in a transaction. If the object is invalid, the saving
|
79
|
-
# will fail and false will be returned.
|
80
|
-
def update(attributes)
|
81
|
-
@updating = :updating
|
82
|
-
Thread.current[:sunstone_updating_model] = self
|
83
|
-
|
84
|
-
# The following transaction covers any possible database side-effects of the
|
85
|
-
# attributes assignment. For example, setting the IDs of a child collection.
|
86
|
-
with_transaction_returning_status do
|
87
|
-
assign_attributes(attributes)
|
88
|
-
save
|
89
|
-
end
|
90
|
-
ensure
|
91
|
-
@updating = false
|
92
|
-
Thread.current[:sunstone_updating_model] = nil
|
93
|
-
end
|
94
|
-
|
95
|
-
end
|
96
|
-
end
|
@@ -1,5 +1,9 @@
|
|
1
|
+
# The last ref that this code was synced with Rails
|
2
|
+
# ref: 9269f634d471ad6ca46752421eabd3e1c26220b5
|
3
|
+
|
1
4
|
require 'active_record'
|
2
5
|
require 'active_record/associations'
|
6
|
+
|
3
7
|
module ActiveRecord
|
4
8
|
module Associations
|
5
9
|
module ClassMethods
|
@@ -44,4 +48,4 @@ module ActiveRecord
|
|
44
48
|
end
|
45
49
|
end
|
46
50
|
end
|
47
|
-
end
|
51
|
+
end
|
@@ -1,14 +1,15 @@
|
|
1
|
+
# The last ref that this code was synced with Rails
|
2
|
+
# ref: 9269f634d471ad6ca46752421eabd3e1c26220b5
|
3
|
+
|
1
4
|
module ActiveRecord
|
2
5
|
module AttributeMethods
|
3
|
-
|
6
|
+
|
4
7
|
protected
|
5
|
-
|
8
|
+
|
6
9
|
# Returns a Hash of the Arel::Attributes and attribute values that have been
|
7
10
|
# typecasted for use in an Arel insert/update method.
|
8
11
|
def attributes_with_values(attribute_names)
|
9
|
-
attrs = attribute_names.index_with
|
10
|
-
_read_attribute(name)
|
11
|
-
end
|
12
|
+
attrs = attribute_names.index_with { |name| @attributes[name] }
|
12
13
|
|
13
14
|
if self.class.connection.is_a?(ActiveRecord::ConnectionAdapters::SunstoneAPIAdapter)
|
14
15
|
self.class.reflect_on_all_associations.each do |reflection|
|
@@ -25,17 +26,17 @@ module ActiveRecord
|
|
25
26
|
end
|
26
27
|
end
|
27
28
|
end
|
28
|
-
|
29
|
+
|
29
30
|
attrs
|
30
31
|
end
|
31
|
-
|
32
|
+
|
32
33
|
def add_attributes_for_belongs_to_association(reflection, attrs)
|
33
34
|
key = :"add_attributes_for_belongs_to_association_#{reflection.name}"
|
34
35
|
@_already_called ||= {}
|
35
36
|
return if @_already_called[key]
|
36
37
|
@_already_called[key]=true
|
37
38
|
@_already_called[:"autosave_associated_records_for_#{reflection.name}"] = true
|
38
|
-
|
39
|
+
|
39
40
|
association = association_instance_get(reflection.name)
|
40
41
|
record = association && association.load_target
|
41
42
|
if record && !record.destroyed?
|
@@ -51,14 +52,14 @@ module ActiveRecord
|
|
51
52
|
end
|
52
53
|
end
|
53
54
|
end
|
54
|
-
|
55
|
+
|
55
56
|
def add_attributes_for_has_one_association(reflection, attrs)
|
56
57
|
key = :"add_attributes_for_has_one_association#{reflection.name}"
|
57
58
|
@_already_called ||= {}
|
58
59
|
return if @_already_called[key]
|
59
60
|
@_already_called[key]=true
|
60
61
|
@_already_called[:"autosave_associated_records_for_#{reflection.name}"] = true
|
61
|
-
|
62
|
+
|
62
63
|
association = association_instance_get(reflection.name)
|
63
64
|
record = association && association.load_target
|
64
65
|
|
@@ -80,7 +81,7 @@ module ActiveRecord
|
|
80
81
|
end
|
81
82
|
end
|
82
83
|
end
|
83
|
-
|
84
|
+
|
84
85
|
def add_attributes_for_collection_association(reflection, attrs, arel_table=nil)
|
85
86
|
key = :"add_attributes_for_collection_association#{reflection.name}"
|
86
87
|
@_already_called ||= {}
|
@@ -106,7 +107,7 @@ module ActiveRecord
|
|
106
107
|
)
|
107
108
|
end
|
108
109
|
end
|
109
|
-
|
110
|
+
|
110
111
|
association.instance_variable_set(:@sunstone_changed, false)
|
111
112
|
end
|
112
113
|
|
@@ -114,6 +115,6 @@ module ActiveRecord
|
|
114
115
|
association.reset_scope if association.respond_to?(:reset_scope)
|
115
116
|
end
|
116
117
|
end
|
117
|
-
|
118
|
+
|
118
119
|
end
|
119
120
|
end
|
@@ -1,14 +1,24 @@
|
|
1
|
+
# The last ref that this code was synced with Rails
|
2
|
+
# ref: 9269f634d471ad6ca46752421eabd3e1c26220b5
|
3
|
+
|
1
4
|
module ActiveRecord
|
2
5
|
class PredicateBuilder # :nodoc:
|
3
6
|
|
4
7
|
def expand_from_hash(attributes, &block)
|
5
8
|
return ["1=0"] if attributes.empty?
|
6
|
-
|
9
|
+
|
7
10
|
attributes.flat_map do |key, value|
|
8
|
-
if
|
11
|
+
if key.is_a?(Array)
|
12
|
+
queries = Array(value).map do |ids_set|
|
13
|
+
raise ArgumentError, "Expected corresponding value for #{key} to be an Array" unless ids_set.is_a?(Array)
|
14
|
+
expand_from_hash(key.zip(ids_set).to_h)
|
15
|
+
end
|
16
|
+
grouping_queries(queries)
|
17
|
+
elsif value.is_a?(Hash) && !table.has_column?(key)
|
9
18
|
ka = table.associated_table(key, &block)
|
10
|
-
|
11
|
-
|
19
|
+
.predicate_builder.expand_from_hash(value.stringify_keys)
|
20
|
+
|
21
|
+
if self.table.instance_variable_get(:@klass).connection.is_a?(ActiveRecord::ConnectionAdapters::SunstoneAPIAdapter)
|
12
22
|
ka.each { |k|
|
13
23
|
if k.left.is_a?(Arel::Attributes::Attribute) || k.left.is_a?(Arel::Attributes::Relation)
|
14
24
|
k.left = Arel::Attributes::Relation.new(k.left, key)
|
@@ -6,10 +6,12 @@ module ActiveRecord
|
|
6
6
|
def rpc(name)
|
7
7
|
define_method("#{name}!") do
|
8
8
|
req = Net::HTTP::Post.new("/#{self.class.table_name}/#{CGI.escape(id.to_s)}/#{CGI.escape(name.to_s)}")
|
9
|
-
self.class.connection.
|
10
|
-
|
11
|
-
|
12
|
-
|
9
|
+
self.class.connection.send(:with_raw_connection) do |conn|
|
10
|
+
conn.send_request(req) do |response|
|
11
|
+
JSON.parse(response.body).each do |k,v|
|
12
|
+
if self.class.column_names.include?(k)
|
13
|
+
@attributes.write_from_database(k, v)
|
14
|
+
end
|
13
15
|
end
|
14
16
|
end
|
15
17
|
end
|
@@ -17,7 +19,25 @@ module ActiveRecord
|
|
17
19
|
end
|
18
20
|
end
|
19
21
|
end
|
20
|
-
|
22
|
+
|
23
|
+
# Updates the attributes of the model from the passed-in hash and saves the
|
24
|
+
# record, all wrapped in a transaction. If the object is invalid, the saving
|
25
|
+
# will fail and false will be returned.
|
26
|
+
def update(attributes)
|
27
|
+
@sunstone_updating = :updating
|
28
|
+
Thread.current[:sunstone_updating_model] = self
|
29
|
+
|
30
|
+
# The following transaction covers any possible database side-effects of the
|
31
|
+
# attributes assignment. For example, setting the IDs of a child collection.
|
32
|
+
with_transaction_returning_status do
|
33
|
+
assign_attributes(attributes)
|
34
|
+
save
|
35
|
+
end
|
36
|
+
ensure
|
37
|
+
@sunstone_updating = false
|
38
|
+
Thread.current[:sunstone_updating_model] = nil
|
39
|
+
end
|
40
|
+
|
21
41
|
def update!(attributes)
|
22
42
|
@no_save_transaction = true
|
23
43
|
with_transaction_returning_status do
|
@@ -33,14 +53,14 @@ module ActiveRecord
|
|
33
53
|
def create_or_update(**, &block)
|
34
54
|
_raise_readonly_record_error if readonly?
|
35
55
|
return false if destroyed?
|
36
|
-
|
37
|
-
@
|
56
|
+
|
57
|
+
@sunstone_updating = new_record? ? :creating : :updating
|
38
58
|
Thread.current[:sunstone_updating_model] = self
|
39
59
|
|
40
60
|
result = new_record? ? _create_record(&block) : _update_record(&block)
|
41
61
|
|
42
62
|
if self.class.connection.is_a?(ActiveRecord::ConnectionAdapters::SunstoneAPIAdapter) && result != 0
|
43
|
-
row_hash = result
|
63
|
+
row_hash = result[0]
|
44
64
|
|
45
65
|
seen = Hash.new { |h, parent_klass|
|
46
66
|
h[parent_klass] = Hash.new { |i, parent_id|
|
@@ -51,10 +71,13 @@ module ActiveRecord
|
|
51
71
|
model_cache = Hash.new { |h,klass| h[klass] = {} }
|
52
72
|
parents = model_cache[self.class.base_class]
|
53
73
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
74
|
+
row_hash.each do |key, value|
|
75
|
+
if self.class.column_names.include?(key.to_s)
|
76
|
+
_write_attribute(key, value)
|
77
|
+
else
|
78
|
+
assc = association(key.to_sym)
|
79
|
+
assc.reset if assc.reflection.collection? # TODO: can load here if included
|
80
|
+
end
|
58
81
|
end
|
59
82
|
|
60
83
|
construct(self, row_hash.select{|k,v| !self.class.column_names.include?(k.to_s) }, seen, model_cache)
|
@@ -72,39 +95,50 @@ module ActiveRecord
|
|
72
95
|
end
|
73
96
|
raise ActiveRecord::RecordInvalid
|
74
97
|
ensure
|
75
|
-
@
|
98
|
+
@sunstone_updating = false
|
76
99
|
Thread.current[:sunstone_updating_model] = nil
|
77
100
|
end
|
78
|
-
|
101
|
+
|
79
102
|
# Creates a record with values matching those of the instance attributes
|
80
103
|
# and returns its id.
|
81
104
|
def _create_record(attribute_names = self.attribute_names)
|
82
105
|
attribute_names = attributes_for_create(attribute_names)
|
83
|
-
|
84
|
-
|
85
|
-
|
106
|
+
attribute_values = attributes_with_values(attribute_names)
|
107
|
+
|
108
|
+
returning_columns = self.class._returning_columns_for_insert
|
109
|
+
|
110
|
+
returning_values = self.class._insert_record(
|
111
|
+
attribute_values,
|
112
|
+
returning_columns
|
113
|
+
)
|
114
|
+
|
115
|
+
if !self.class.connection.is_a?(ActiveRecord::ConnectionAdapters::SunstoneAPIAdapter)
|
116
|
+
returning_columns.zip(returning_values).each do |column, value|
|
117
|
+
_write_attribute(column, value) if !_read_attribute(column)
|
118
|
+
end if returning_values
|
119
|
+
end
|
86
120
|
|
87
|
-
self.id ||= new_id if @primary_key
|
88
121
|
@new_record = false
|
89
122
|
@previously_new_record = true
|
90
123
|
|
91
124
|
yield(self) if block_given?
|
92
125
|
|
93
126
|
if self.class.connection.is_a?(ActiveRecord::ConnectionAdapters::SunstoneAPIAdapter)
|
94
|
-
|
127
|
+
returning_values
|
95
128
|
else
|
96
129
|
id
|
97
130
|
end
|
98
131
|
end
|
99
|
-
|
132
|
+
|
100
133
|
def _update_record(attribute_names = self.attribute_names)
|
134
|
+
attribute_names = attributes_for_update(attribute_names)
|
101
135
|
attribute_values = attributes_with_values(attribute_names)
|
102
136
|
|
103
137
|
if attribute_values.empty?
|
104
138
|
affected_rows = 0
|
105
139
|
@_trigger_update_callback = true
|
106
140
|
else
|
107
|
-
affected_rows = self.class._update_record(
|
141
|
+
affected_rows = self.class._update_record(attribute_values, _query_constraints_hash)
|
108
142
|
@_trigger_update_callback = affected_rows == 1
|
109
143
|
end
|
110
144
|
|
@@ -1,6 +1,9 @@
|
|
1
|
+
# The last ref that this code was synced with Rails
|
2
|
+
# ref: 9269f634d471ad6ca46752421eabd3e1c26220b5
|
3
|
+
|
1
4
|
module ActiveRecord
|
2
5
|
module Calculations
|
3
|
-
|
6
|
+
|
4
7
|
def pluck(*column_names)
|
5
8
|
if loaded? && all_attributes?(column_names)
|
6
9
|
return records.pluck(*column_names)
|
@@ -36,8 +36,8 @@ module ActiveRecord
|
|
36
36
|
def with_transaction_returning_status
|
37
37
|
status = nil
|
38
38
|
connection = self.class.connection
|
39
|
-
|
40
|
-
if connection.is_a?(ActiveRecord::ConnectionAdapters::SunstoneAPIAdapter) && instance_variable_defined?(:@
|
39
|
+
|
40
|
+
if connection.is_a?(ActiveRecord::ConnectionAdapters::SunstoneAPIAdapter) && instance_variable_defined?(:@sunstone_updating) && @sunstone_updating
|
41
41
|
status = yield
|
42
42
|
else
|
43
43
|
ensure_finalize = !connection.transaction_open?
|
@@ -2,6 +2,8 @@ module ActiveRecord
|
|
2
2
|
module ConnectionAdapters
|
3
3
|
# Sunstone-specific extensions to column definitions in a table.
|
4
4
|
class SunstoneColumn < Column #:nodoc:
|
5
|
+
NONE = Object.new
|
6
|
+
|
5
7
|
attr_reader :array
|
6
8
|
|
7
9
|
def initialize(name, sql_type_metadata, options={})
|
@@ -14,12 +16,20 @@ module ActiveRecord
|
|
14
16
|
@table_name = nil
|
15
17
|
@primary_key = (options['primary_key'] == true)
|
16
18
|
@array = options['array']
|
19
|
+
@auto_populated = options.has_key?('auto_populated') ? options['auto_populated'] : NONE
|
17
20
|
end
|
18
21
|
|
19
22
|
def primary_key?
|
20
23
|
@primary_key
|
21
24
|
end
|
22
25
|
|
26
|
+
def auto_populated?
|
27
|
+
# TODO: when retuning is working we can do the following to only
|
28
|
+
# return autopulated fields from StandardAPI
|
29
|
+
# @auto_populated == NONE ? @primary_key : @auto_populated
|
30
|
+
true
|
31
|
+
end
|
32
|
+
|
23
33
|
end
|
24
34
|
end
|
25
35
|
end
|
@@ -25,22 +25,35 @@ module ActiveRecord
|
|
25
25
|
else
|
26
26
|
sar = arel_or_sar_string
|
27
27
|
end
|
28
|
+
|
28
29
|
sar.compile(binds)
|
29
30
|
end
|
30
31
|
|
31
32
|
def to_sar_and_binds(arel_or_sar_string, binds = [], preparable = nil) # :nodoc:
|
33
|
+
# Arel::TreeManager -> Arel::Node
|
32
34
|
if arel_or_sar_string.respond_to?(:ast)
|
35
|
+
arel_or_sar_string = arel_or_sar_string.ast
|
36
|
+
end
|
37
|
+
|
38
|
+
if Arel.arel_node?(arel_or_sar_string) && !(String === arel_or_sar_string)
|
33
39
|
unless binds.empty?
|
34
40
|
raise "Passing bind parameters with an arel AST is forbidden. " \
|
35
41
|
"The values must be stored on the AST directly"
|
36
42
|
end
|
37
|
-
|
43
|
+
|
44
|
+
sar = visitor.accept(arel_or_sar_string, collector)
|
38
45
|
[sar.freeze, sar.binds, false]
|
39
46
|
else
|
40
47
|
[arel_or_sar_string.dup.freeze, binds, false]
|
41
48
|
end
|
42
49
|
end
|
43
50
|
|
51
|
+
def sar_for_insert(sql, pk, binds, returning)
|
52
|
+
# TODO: when StandardAPI supports returning we can do this; it might
|
53
|
+
# already need to investigate
|
54
|
+
to_sar_and_binds(sql, binds)
|
55
|
+
end
|
56
|
+
|
44
57
|
# This is used in the StatementCache object. It returns an object that
|
45
58
|
# can be used to query the database repeatedly.
|
46
59
|
def cacheable_query(klass, arel) # :nodoc:
|
@@ -80,11 +93,26 @@ module ActiveRecord
|
|
80
93
|
def select_all(arel, name = nil, binds = [], preparable: nil, async: false)
|
81
94
|
arel = arel_from_relation(arel)
|
82
95
|
sar, binds, preparable = to_sar_and_binds(arel, binds, preparable)
|
83
|
-
|
96
|
+
|
84
97
|
select(sar, name, binds, prepare: prepared_statements && preparable, async: async && FutureResult::SelectAll)
|
98
|
+
rescue ::RangeError
|
99
|
+
ActiveRecord::Result.empty(async: async)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Executes insert +sql+ statement in the context of this connection using
|
103
|
+
# +binds+ as the bind substitutes. +name+ is logged along with
|
104
|
+
# the executed +sql+ statement.
|
105
|
+
# Some adapters support the `returning` keyword argument which allows to control the result of the query:
|
106
|
+
# `nil` is the default value and maintains default behavior. If an array of column names is passed -
|
107
|
+
# the result will contain values of the specified columns from the inserted row.
|
108
|
+
#
|
109
|
+
# TODO: Add support for returning
|
110
|
+
def exec_insert(arel, name = nil, binds = [], pk = nil, sequence_name = nil, returning: nil)
|
111
|
+
sar, binds = sar_for_insert(arel, pk, binds, returning)
|
112
|
+
internal_exec_query(sar, name, binds)
|
85
113
|
end
|
86
114
|
|
87
|
-
def
|
115
|
+
def internal_exec_query(arel, name = 'SAR', binds = [], prepare: false, async: false, allow_retry: false, materialize_transactions: true)
|
88
116
|
sars = []
|
89
117
|
multiple_requests = arel.is_a?(Arel::Collectors::Sunstone)
|
90
118
|
type_casted_binds = binds#type_casted_binds(binds)
|
@@ -112,11 +140,13 @@ module ActiveRecord
|
|
112
140
|
sars.push(sar)
|
113
141
|
log_mess = sar.path.split('?', 2)
|
114
142
|
log("#{sar.method} #{log_mess[0]} #{(log_mess[1] && !log_mess[1].empty?) ? MessagePack.unpack(CGI.unescape(log_mess[1])) : '' }", name) do
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
143
|
+
with_raw_connection do |conn|
|
144
|
+
response = conn.send_request(sar)
|
145
|
+
if response.is_a?(Net::HTTPNoContent)
|
146
|
+
nil
|
147
|
+
else
|
148
|
+
JSON.parse(response.body)
|
149
|
+
end
|
120
150
|
end
|
121
151
|
end
|
122
152
|
}
|
@@ -141,21 +171,21 @@ module ActiveRecord
|
|
141
171
|
|
142
172
|
if sars[0].instance_variable_defined?(:@sunstone_calculation) && sars[0].instance_variable_get(:@sunstone_calculation)
|
143
173
|
# this is a count, min, max.... yea i know..
|
144
|
-
ActiveRecord::Result.new(['all'], [result], {:all => type_map.lookup('integer', {})})
|
174
|
+
ActiveRecord::Result.new(['all'], [result], {:all => @type_map.lookup('integer', {})})
|
145
175
|
elsif result.is_a?(Array)
|
146
176
|
ActiveRecord::Result.new(result[0] ? result[0].keys : [], result.map{|r| r.values})
|
147
177
|
else
|
148
|
-
ActiveRecord::Result.new(result.keys, [result])
|
178
|
+
ActiveRecord::Result.new(result.keys, [result.values])
|
149
179
|
end
|
150
180
|
end
|
151
181
|
|
152
182
|
def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
|
153
183
|
sar, binds = to_sar_and_binds(arel, binds)
|
154
184
|
value = exec_insert(sar, name, binds, pk, sequence_name)
|
185
|
+
|
186
|
+
return returning_column_values(value) unless returning.nil?
|
187
|
+
|
155
188
|
id_value || last_inserted_id(value)
|
156
|
-
|
157
|
-
# value = exec_insert(arel, name, binds, pk, sequence_name)
|
158
|
-
# id_value || last_inserted_id(value)
|
159
189
|
end
|
160
190
|
|
161
191
|
def update(arel, name = nil, binds = [])
|
@@ -171,6 +201,10 @@ module ActiveRecord
|
|
171
201
|
row && row['id']
|
172
202
|
end
|
173
203
|
|
204
|
+
def returning_column_values(result)
|
205
|
+
result.rows.first
|
206
|
+
end
|
207
|
+
|
174
208
|
end
|
175
209
|
end
|
176
210
|
end
|
@@ -27,8 +27,7 @@ module ActiveRecord
|
|
27
27
|
return @definitions[table_name]
|
28
28
|
end
|
29
29
|
|
30
|
-
response =
|
31
|
-
|
30
|
+
response = with_raw_connection { |conn| conn.get("/#{table_name}/schema") }
|
32
31
|
@definitions[table_name] = JSON.parse(response.body)
|
33
32
|
rescue ::Sunstone::Exception::NotFound
|
34
33
|
raise ActiveRecord::StatementInvalid, "Table \"#{table_name}\" does not exist"
|
@@ -40,7 +39,9 @@ module ActiveRecord
|
|
40
39
|
# - format_type includes the column size constraint, e.g. varchar(50)
|
41
40
|
# - ::regclass is a function that gives the id for a table name
|
42
41
|
def column_definitions(table_name) # :nodoc:
|
43
|
-
|
42
|
+
# TODO: settle on schema, I think we've switched to attributes, so
|
43
|
+
# columns can be removed soon?
|
44
|
+
definition(table_name)['attributes'] || definition(table_name)['columns']
|
44
45
|
end
|
45
46
|
|
46
47
|
# Returns the limit definition of the table (the maximum limit that can
|
@@ -50,7 +51,7 @@ module ActiveRecord
|
|
50
51
|
end
|
51
52
|
|
52
53
|
def tables
|
53
|
-
JSON.parse(
|
54
|
+
JSON.parse(with_raw_connection { |conn| conn.get('/tables').body })
|
54
55
|
end
|
55
56
|
|
56
57
|
def views
|
@@ -63,7 +64,7 @@ module ActiveRecord
|
|
63
64
|
end
|
64
65
|
|
65
66
|
def lookup_cast_type(options)
|
66
|
-
type_map.lookup(options['type'], options.symbolize_keys)
|
67
|
+
@type_map.lookup(options['type'], options.symbolize_keys)
|
67
68
|
end
|
68
69
|
|
69
70
|
def fetch_type_metadata(options)
|
@@ -13,11 +13,11 @@ module ActiveRecord
|
|
13
13
|
#
|
14
14
|
# +value+ The raw input, as provided from the database.
|
15
15
|
def deserialize(value)
|
16
|
-
value.nil? ? nil : Base64.strict_decode64(value)
|
16
|
+
value.nil? ? nil : Base64.strict_decode64(value)
|
17
17
|
end
|
18
18
|
|
19
|
-
# Casts a value from the ruby type to a type that the database knows
|
20
|
-
# to understand. The returned value from this method should be a
|
19
|
+
# Casts a value from the ruby type to a type that the database knows
|
20
|
+
# how to understand. The returned value from this method should be a
|
21
21
|
# +String+, +Numeric+, +Date+, +Time+, +Symbol+, +true+, +false+, or
|
22
22
|
# +nil+.
|
23
23
|
def serialize(value)
|
@@ -17,27 +17,14 @@ require 'active_record/connection_adapters/sunstone/type/json'
|
|
17
17
|
module ActiveRecord
|
18
18
|
module ConnectionHandling # :nodoc:
|
19
19
|
|
20
|
-
|
21
|
-
|
20
|
+
def sunstone_adapter_class
|
21
|
+
ConnectionAdapters::SunstoneAPIAdapter
|
22
|
+
end
|
23
|
+
|
22
24
|
# Establishes a connection to the database that's used by all Active Record
|
23
25
|
# objects
|
24
26
|
def sunstone_connection(config)
|
25
|
-
|
26
|
-
conn_params.delete_if { |_, v| v.nil? }
|
27
|
-
|
28
|
-
if conn_params[:url]
|
29
|
-
uri = URI.parse(conn_params.delete(:url))
|
30
|
-
conn_params[:api_key] ||= (uri.user ? CGI.unescape(uri.user) : nil)
|
31
|
-
conn_params[:host] ||= uri.host
|
32
|
-
conn_params[:port] ||= uri.port
|
33
|
-
conn_params[:use_ssl] ||= (uri.scheme == 'https')
|
34
|
-
end
|
35
|
-
|
36
|
-
# Forward only valid config params to Sunstone::Connection
|
37
|
-
conn_params.slice!(*VALID_SUNSTONE_CONN_PARAMS)
|
38
|
-
|
39
|
-
client = ::Sunstone::Connection.new(conn_params)
|
40
|
-
ConnectionAdapters::SunstoneAPIAdapter.new(client, logger, conn_params, config)
|
27
|
+
sunstone_adapter_class.new(config)
|
41
28
|
end
|
42
29
|
end
|
43
30
|
|
@@ -54,6 +41,7 @@ module ActiveRecord
|
|
54
41
|
# <encoding></tt> call on the connection.
|
55
42
|
class SunstoneAPIAdapter < AbstractAdapter
|
56
43
|
ADAPTER_NAME = 'Sunstone'.freeze
|
44
|
+
VALID_SUNSTONE_CONN_PARAMS = [:url, :host, :port, :api_key, :use_ssl, :user_agent, :ca_cert]
|
57
45
|
|
58
46
|
NATIVE_DATABASE_TYPES = {
|
59
47
|
string: { name: "string" },
|
@@ -61,6 +49,12 @@ module ActiveRecord
|
|
61
49
|
json: { name: "json" },
|
62
50
|
boolean: { name: "boolean" }
|
63
51
|
}
|
52
|
+
|
53
|
+
class << self
|
54
|
+
def new_client(conn_params)
|
55
|
+
::Sunstone::Connection.new(conn_params)
|
56
|
+
end
|
57
|
+
end
|
64
58
|
|
65
59
|
# include PostgreSQL::Quoting
|
66
60
|
# include PostgreSQL::ReferentialIntegrity
|
@@ -72,35 +66,66 @@ module ActiveRecord
|
|
72
66
|
def supports_statement_cache?
|
73
67
|
false
|
74
68
|
end
|
69
|
+
|
70
|
+
def default_prepared_statements
|
71
|
+
false
|
72
|
+
end
|
75
73
|
|
76
|
-
def clear_cache!
|
74
|
+
def clear_cache!(new_connection: false)
|
77
75
|
# TODO move @definitions to using @schema_cache
|
78
76
|
@definitions = {}
|
79
77
|
end
|
80
78
|
|
81
79
|
# Initializes and connects a SunstoneAPI adapter.
|
82
|
-
def initialize(
|
83
|
-
super
|
80
|
+
def initialize(...)
|
81
|
+
super
|
84
82
|
|
85
|
-
|
86
|
-
|
83
|
+
conn_params = @config.compact
|
84
|
+
if conn_params[:url]
|
85
|
+
uri = URI.parse(conn_params.delete(:url))
|
86
|
+
conn_params[:api_key] ||= (uri.user ? CGI.unescape(uri.user) : nil)
|
87
|
+
conn_params[:host] ||= uri.host
|
88
|
+
conn_params[:port] ||= uri.port
|
89
|
+
conn_params[:use_ssl] ||= (uri.scheme == 'https')
|
90
|
+
end
|
87
91
|
|
88
|
-
|
89
|
-
|
92
|
+
# Forward only valid config params to Sunstone::Connection
|
93
|
+
conn_params.slice!(*VALID_SUNSTONE_CONN_PARAMS)
|
94
|
+
|
95
|
+
@connection_parameters = conn_params
|
96
|
+
|
97
|
+
@max_identifier_length = nil
|
98
|
+
@type_map = nil
|
99
|
+
@raw_connection = nil
|
100
|
+
end
|
101
|
+
|
102
|
+
def url(path=nil)
|
103
|
+
"http#{@connection_parameters[:use_ssl] ? 's' : ''}://#{@connection_parameters[:host]}#{@connection_parameters[:port] != 80 ? (@connection_parameters[:port] == 443 && @connection_parameters[:use_ssl] ? '' : ":#{@connection_parameters[:port]}") : ''}#{path}"
|
90
104
|
end
|
91
105
|
|
92
106
|
def active?
|
93
|
-
@
|
107
|
+
@raw_connection&.active?
|
94
108
|
end
|
95
109
|
|
96
|
-
def
|
110
|
+
def load_type_map
|
111
|
+
@type_map = Type::HashLookupTypeMap.new
|
112
|
+
initialize_type_map(@type_map)
|
113
|
+
end
|
114
|
+
|
115
|
+
def reconnect
|
97
116
|
super
|
98
|
-
@
|
117
|
+
@raw_connection&.reconnect!
|
99
118
|
end
|
100
119
|
|
101
120
|
def disconnect!
|
102
121
|
super
|
103
|
-
@
|
122
|
+
@raw_connection&.disconnect!
|
123
|
+
@raw_connection = nil
|
124
|
+
end
|
125
|
+
|
126
|
+
def discard! # :nodoc:
|
127
|
+
super
|
128
|
+
@raw_connection = nil
|
104
129
|
end
|
105
130
|
|
106
131
|
# Executes the delete statement and returns the number of rows affected.
|
@@ -134,11 +159,17 @@ module ActiveRecord
|
|
134
159
|
end
|
135
160
|
|
136
161
|
def server_config
|
137
|
-
|
162
|
+
with_raw_connection do |conn|
|
163
|
+
JSON.parse(conn.get("/configuration").body)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def return_value_after_insert?(column) # :nodoc:
|
168
|
+
column.auto_populated?
|
138
169
|
end
|
139
170
|
|
140
171
|
def lookup_cast_type_from_column(column) # :nodoc:
|
141
|
-
cast_type = type_map.lookup(column.sql_type, {
|
172
|
+
cast_type = @type_map.lookup(column.sql_type, {
|
142
173
|
limit: column.limit,
|
143
174
|
precision: column.precision,
|
144
175
|
scale: column.scale
|
@@ -167,55 +198,63 @@ module ActiveRecord
|
|
167
198
|
true
|
168
199
|
end
|
169
200
|
|
170
|
-
# Executes an INSERT query and returns the
|
171
|
-
#
|
172
|
-
|
173
|
-
|
174
|
-
# id and return that value.
|
175
|
-
#
|
176
|
-
# If the next id was calculated in advance (as in Oracle), it should be
|
177
|
-
# passed in as +id_value+.
|
178
|
-
def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
|
179
|
-
exec_insert(arel, name, binds, pk, sequence_name)
|
201
|
+
# Executes an INSERT query and returns a hash of the object and
|
202
|
+
# any updated relations. This is different from AR which returns an ID
|
203
|
+
def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [], returning: nil)
|
204
|
+
exec_insert(arel, name, binds, pk, sequence_name, returning: returning)
|
180
205
|
end
|
181
206
|
alias create insert
|
182
207
|
|
183
|
-
|
184
|
-
|
185
|
-
|
208
|
+
# Connects to a StandardAPI server and sets up the adapter depending
|
209
|
+
# on the connected server's characteristics.
|
210
|
+
def connect
|
211
|
+
@raw_connection = self.class.new_client(@connection_parameters)
|
186
212
|
end
|
187
213
|
|
188
|
-
|
214
|
+
def reconnect
|
215
|
+
@raw_connection&.reconnect!
|
216
|
+
connect unless @raw_connection
|
217
|
+
end
|
218
|
+
|
219
|
+
# Configures the encoding, verbosity, schema search path, and time zone of the connection.
|
220
|
+
# This is called by #connect and should not be called manually.
|
221
|
+
def configure_connection
|
222
|
+
reload_type_map
|
223
|
+
end
|
189
224
|
|
190
|
-
def
|
191
|
-
@type_map
|
192
|
-
|
225
|
+
def reload_type_map
|
226
|
+
if @type_map
|
227
|
+
type_map.clear
|
228
|
+
else
|
229
|
+
@type_map = Type::HashLookupTypeMap.new
|
193
230
|
end
|
231
|
+
|
232
|
+
initialize_type_map
|
194
233
|
end
|
234
|
+
|
235
|
+
private
|
195
236
|
|
196
|
-
def initialize_type_map(m =
|
197
|
-
self.class.initialize_type_map(m)
|
198
|
-
load_additional_types
|
237
|
+
def initialize_type_map(m = nil)
|
238
|
+
self.class.initialize_type_map(m || @type_map)
|
199
239
|
end
|
200
240
|
|
201
|
-
def initialize_type_map(m) # :nodoc:
|
202
|
-
m.register_type
|
203
|
-
m.register_type
|
204
|
-
Type::
|
205
|
-
end
|
206
|
-
m.register_type 'integer' do |_, options|
|
207
|
-
Type::Integer.new(**options.slice(:limit))
|
241
|
+
def self.initialize_type_map(m) # :nodoc:
|
242
|
+
m.register_type 'boolean', Type::Boolean.new
|
243
|
+
m.register_type 'binary' do |_, options|
|
244
|
+
Sunstone::Type::Binary.new(**options.slice(:limit))
|
208
245
|
end
|
209
|
-
m.register_type
|
246
|
+
m.register_type 'datetime', Sunstone::Type::DateTime.new
|
247
|
+
m.register_type 'decimal' do |_, options|
|
210
248
|
Type::Decimal.new(**options.slice(:precision, :scale))
|
211
249
|
end
|
212
|
-
m.register_type
|
213
|
-
|
250
|
+
m.register_type 'integer' do |_, options|
|
251
|
+
Type::Integer.new(**options.slice(:limit))
|
214
252
|
end
|
215
|
-
|
216
|
-
m.register_type
|
217
|
-
|
218
|
-
|
253
|
+
m.register_type 'json', Sunstone::Type::Json.new
|
254
|
+
m.register_type 'string' do |_, options|
|
255
|
+
Type::String.new(**options.slice(:limit))
|
256
|
+
end
|
257
|
+
m.register_type 'uuid', Sunstone::Type::Uuid.new
|
219
258
|
|
220
259
|
if defined?(Sunstone::Type::EWKB)
|
221
260
|
m.register_type 'ewkb', Sunstone::Type::EWKB.new
|
@@ -3,24 +3,24 @@ require 'arel/visitors/visitor'
|
|
3
3
|
module Arel
|
4
4
|
module Visitors
|
5
5
|
class Sunstone < Arel::Visitors::Visitor
|
6
|
-
|
6
|
+
|
7
7
|
def compile(node, collector = Arel::Collectors::Sunstone.new)
|
8
8
|
accept(node, collector).value
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
private
|
12
|
-
|
12
|
+
|
13
13
|
def visit_Arel_Nodes_SelectStatement o, collector
|
14
14
|
collector.table = o.cores.first.source.left.name
|
15
15
|
|
16
16
|
collector = o.cores.inject(collector) { |c,x|
|
17
17
|
visit_Arel_Nodes_SelectCore(x, c)
|
18
18
|
}
|
19
|
-
|
19
|
+
|
20
20
|
if !o.orders.empty?
|
21
21
|
collector.order = o.orders.map { |x| visit(x, collector) }
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
collector = maybe_visit o.limit, collector
|
25
25
|
collector = maybe_visit o.offset, collector
|
26
26
|
collector = maybe_visit o.eager_load, collector
|
@@ -61,27 +61,36 @@ module Arel
|
|
61
61
|
collector.request_type = Net::HTTP::Post
|
62
62
|
collector.table = o.relation.name
|
63
63
|
collector.operation = :insert
|
64
|
-
|
64
|
+
|
65
65
|
if o.values
|
66
66
|
if o.values.is_a?(Arel::Nodes::SqlLiteral) && o.values == 'DEFAULT VALUES'
|
67
67
|
collector.updates = {}
|
68
68
|
else
|
69
|
-
|
70
|
-
|
71
|
-
o.columns.map(&:name).zip(o.values.expr.first).to_h.each do |k, v|
|
72
|
-
collector.updates[k] = case v
|
73
|
-
when Nodes::SqlLiteral, Nodes::BindParam, ActiveModel::Attribute
|
74
|
-
visit(v, collector)
|
75
|
-
else
|
76
|
-
v
|
77
|
-
end
|
78
|
-
end
|
69
|
+
values = visit(o.values, collector)
|
70
|
+
collector.updates = o.columns.map(&:name).zip(values).to_h
|
79
71
|
end
|
80
72
|
end
|
81
|
-
|
73
|
+
|
82
74
|
collector
|
83
75
|
end
|
84
|
-
|
76
|
+
|
77
|
+
def visit_Arel_Nodes_ValuesList o, collector
|
78
|
+
o.rows[0].map do |v|
|
79
|
+
visit_subrelation v, collector
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def visit_subrelation v, collector
|
84
|
+
case v
|
85
|
+
when Array
|
86
|
+
v.map { |v2| visit_subrelation v2, collector }
|
87
|
+
when Hash
|
88
|
+
v.transform_values { |v2| visit_subrelation v2, collector }
|
89
|
+
else
|
90
|
+
visit(v, collector)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
85
94
|
def find_bottom(hash)
|
86
95
|
if hash.is_a?(Hash)
|
87
96
|
if hash.values.first.is_a?(Array) || hash.values.first.is_a?(Hash)
|
@@ -98,10 +107,10 @@ module Arel
|
|
98
107
|
end
|
99
108
|
end
|
100
109
|
end
|
101
|
-
|
110
|
+
|
102
111
|
def add_to_bottom_of_hash_or_array(hash, value)
|
103
112
|
hash = find_bottom(hash)
|
104
|
-
|
113
|
+
|
105
114
|
if hash.is_a?(Hash)
|
106
115
|
nkey = hash.keys.first
|
107
116
|
nvalue = hash.values.first
|
@@ -147,26 +156,26 @@ module Arel
|
|
147
156
|
#
|
148
157
|
def visit_Arel_Nodes_UpdateStatement o, collector
|
149
158
|
collector.request_type = Net::HTTP::Patch
|
150
|
-
|
159
|
+
|
151
160
|
collector.table = o.relation.name
|
152
161
|
collector.operation = :update
|
153
|
-
|
162
|
+
|
154
163
|
# collector.id = o.wheres.first.children.first.right
|
155
164
|
if !o.wheres.empty?
|
156
165
|
collector.where = o.wheres.map { |x| visit(x, collector) }.inject([]) { |c, w|
|
157
166
|
w.is_a?(Array) ? c += w : c << w
|
158
167
|
}
|
159
168
|
end
|
160
|
-
|
169
|
+
|
161
170
|
if collector.where.size != 1 && collector.where.first.size != 1 && !collector.where.first['id']
|
162
171
|
raise 'Upsupported'
|
163
172
|
end
|
164
|
-
|
173
|
+
|
165
174
|
collector.where = collector.where.first
|
166
|
-
|
175
|
+
|
167
176
|
if o.values
|
168
177
|
collector.updates = {}
|
169
|
-
|
178
|
+
|
170
179
|
o.values.map { |x| visit(x, collector) }.each do |value|
|
171
180
|
value.each do |key, v|
|
172
181
|
if key.is_a?(Hash)
|
@@ -191,7 +200,7 @@ module Arel
|
|
191
200
|
end
|
192
201
|
end
|
193
202
|
end
|
194
|
-
|
203
|
+
|
195
204
|
collector
|
196
205
|
end
|
197
206
|
#
|
@@ -426,7 +435,7 @@ module Arel
|
|
426
435
|
def visit_Arel_Nodes_Descending o, collector
|
427
436
|
{ visit(o.expr, collector) => :desc }
|
428
437
|
end
|
429
|
-
|
438
|
+
|
430
439
|
def visit_Arel_Nodes_RandomOrdering o, collector
|
431
440
|
:random
|
432
441
|
end
|
@@ -468,7 +477,7 @@ module Arel
|
|
468
477
|
#
|
469
478
|
def visit_Arel_Nodes_Count o, collector
|
470
479
|
collector.operation = :calculate
|
471
|
-
|
480
|
+
|
472
481
|
collector.columns ||= []
|
473
482
|
collector.columns << {:count => (o.expressions.first.is_a?(Arel::Attributes::Attribute) ? o.expressions.first.name : o.expressions.first) }
|
474
483
|
# collector.columns = visit o.expressions.first, collector
|
@@ -476,7 +485,7 @@ module Arel
|
|
476
485
|
|
477
486
|
def visit_Arel_Nodes_Sum o, collector
|
478
487
|
collector.operation = :calculate
|
479
|
-
|
488
|
+
|
480
489
|
collector.columns ||= []
|
481
490
|
collector.columns << {:sum => (o.expressions.first.is_a?(Arel::Attributes::Attribute) ? o.expressions.first.name : o.expressions.first) }
|
482
491
|
# collector.columns = visit o.expressions.first, collector
|
@@ -484,7 +493,7 @@ module Arel
|
|
484
493
|
|
485
494
|
def visit_Arel_Nodes_Max o, collector
|
486
495
|
collector.operation = :calculate
|
487
|
-
|
496
|
+
|
488
497
|
collector.columns ||= []
|
489
498
|
if o.expressions.first.is_a?(Arel::Attributes::Attribute)
|
490
499
|
relation = o.expressions.first.relation
|
@@ -686,11 +695,11 @@ module Arel
|
|
686
695
|
end
|
687
696
|
end
|
688
697
|
alias_method :visit_Arel_Nodes_HomogeneousIn, :visit_Arel_Nodes_In
|
689
|
-
|
698
|
+
|
690
699
|
def visit_Arel_Nodes_NotIn o, collector
|
691
700
|
key = visit(o.left, collector)
|
692
701
|
value = {not_in: visit(o.right, collector)}
|
693
|
-
|
702
|
+
|
694
703
|
if hash.is_a?(Hash)
|
695
704
|
add_to_bottom_of_hash_or_array(key, value)
|
696
705
|
key
|
@@ -698,10 +707,10 @@ module Arel
|
|
698
707
|
{key => value}
|
699
708
|
end
|
700
709
|
end
|
701
|
-
|
710
|
+
|
702
711
|
# You merge a into b if a keys do not colid with b keys
|
703
712
|
def mergeable?(hash_a, hash_b)
|
704
|
-
|
713
|
+
|
705
714
|
hash_a.each do |key, value_a|
|
706
715
|
#TODO: one day maybe just use symbols for all keys?
|
707
716
|
if hash_b.has_key?(key.to_sym) || hash_b.has_key?(key.to_s)
|
@@ -715,7 +724,7 @@ module Arel
|
|
715
724
|
end
|
716
725
|
true
|
717
726
|
end
|
718
|
-
|
727
|
+
|
719
728
|
def visit_Arel_Nodes_And o, collector
|
720
729
|
ors = []
|
721
730
|
|
@@ -730,16 +739,16 @@ module Arel
|
|
730
739
|
ors << value
|
731
740
|
end
|
732
741
|
end
|
733
|
-
|
742
|
+
|
734
743
|
result = []
|
735
744
|
ors.each_with_index do |c, i|
|
736
745
|
result << c
|
737
746
|
result << 'AND' if ors.size != i + 1
|
738
747
|
end
|
739
|
-
|
748
|
+
|
740
749
|
result.size == 1 ? result.first : result
|
741
750
|
end
|
742
|
-
|
751
|
+
|
743
752
|
def visit_Arel_Nodes_Or o, collector
|
744
753
|
[visit(o.left, collector), 'OR', visit(o.right, collector)]
|
745
754
|
end
|
@@ -748,10 +757,12 @@ module Arel
|
|
748
757
|
case o.left
|
749
758
|
when Arel::Nodes::UnqualifiedColumn
|
750
759
|
case o.right
|
751
|
-
when
|
752
|
-
{ visit(o.left.expr, collector) =>
|
760
|
+
when Array
|
761
|
+
{ visit(o.left.expr, collector) => o.right.map { |i| i.transform_values { |v| visit(v, collector) } } }
|
762
|
+
when Hash
|
763
|
+
{ visit(o.left.expr, collector) => o.right.transform_values { |v| visit(v, collector) } }
|
753
764
|
else
|
754
|
-
{ visit(o.left.expr, collector) => o.right }
|
765
|
+
{ visit(o.left.expr, collector) => visit(o.right, collector) }
|
755
766
|
end
|
756
767
|
when Arel::Attributes::Attribute, Arel::Nodes::BindParam, ActiveModel::Attribute
|
757
768
|
{ visit(o.left, collector) => visit(o.right, collector) }
|
@@ -770,7 +781,7 @@ module Arel
|
|
770
781
|
okey.merge!(value)
|
771
782
|
hash
|
772
783
|
end
|
773
|
-
|
784
|
+
|
774
785
|
def add_to_bottom_of_hash(hash, value)
|
775
786
|
okey = hash
|
776
787
|
while okey.is_a?(Hash) && (okey.values.first.is_a?(Hash) || okey.values.first.is_a?(Array))
|
@@ -785,11 +796,11 @@ module Arel
|
|
785
796
|
okey[nkey] = { nvalue => value }
|
786
797
|
hash
|
787
798
|
end
|
788
|
-
|
799
|
+
|
789
800
|
def visit_Arel_Nodes_Equality o, collector
|
790
801
|
key = visit(o.left, collector)
|
791
802
|
value = (o.right.nil? ? nil : visit(o.right, collector))
|
792
|
-
|
803
|
+
|
793
804
|
if key.is_a?(Hash)
|
794
805
|
add_to_bottom_of_hash(key, {eq: value})
|
795
806
|
elsif o.left.class.name == 'Arel::Attributes::Key'
|
@@ -798,7 +809,7 @@ module Arel
|
|
798
809
|
{ key => value }
|
799
810
|
end
|
800
811
|
end
|
801
|
-
|
812
|
+
|
802
813
|
def visit_Arel_Nodes_TSMatch(o, collector)
|
803
814
|
key = visit(o.left, collector)
|
804
815
|
value = { ts_match: (o.right.nil? ? nil : visit(o.right, collector)) }
|
@@ -814,11 +825,11 @@ module Arel
|
|
814
825
|
hash
|
815
826
|
end
|
816
827
|
end
|
817
|
-
|
828
|
+
|
818
829
|
def visit_Arel_Nodes_TSVector(o, collector)
|
819
830
|
visit(o.attribute, collector)
|
820
831
|
end
|
821
|
-
|
832
|
+
|
822
833
|
def visit_Arel_Nodes_TSQuery(o, collector)
|
823
834
|
if o.language
|
824
835
|
[visit(o.expression, collector), visit(o.language, collector)]
|
@@ -826,11 +837,11 @@ module Arel
|
|
826
837
|
visit(o.expression, collector)
|
827
838
|
end
|
828
839
|
end
|
829
|
-
|
840
|
+
|
830
841
|
def visit_Arel_Nodes_HasKey o, collector
|
831
842
|
key = visit(o.left, collector)
|
832
843
|
value = {has_key: (o.right.nil? ? nil : o.right.to_s)}
|
833
|
-
|
844
|
+
|
834
845
|
if key.is_a?(Hash)
|
835
846
|
okey = key
|
836
847
|
while okey.values.first.is_a?(Hash)
|
@@ -847,7 +858,7 @@ module Arel
|
|
847
858
|
def visit_Arel_Nodes_HasKeys o, collector
|
848
859
|
key = visit(o.left, collector)
|
849
860
|
value = { has_keys: visit(o.right, collector) }
|
850
|
-
|
861
|
+
|
851
862
|
if key.is_a?(Hash)
|
852
863
|
okey = key
|
853
864
|
while okey.values.first.is_a?(Hash)
|
@@ -864,7 +875,7 @@ module Arel
|
|
864
875
|
def visit_Arel_Nodes_HasAnyKey o, collector
|
865
876
|
key = visit(o.left, collector)
|
866
877
|
value = { has_any_key: visit(o.right, collector) }
|
867
|
-
|
878
|
+
|
868
879
|
if key.is_a?(Hash)
|
869
880
|
okey = key
|
870
881
|
while okey.values.first.is_a?(Hash)
|
@@ -894,11 +905,11 @@ module Arel
|
|
894
905
|
def visit_Arel_Nodes_UnqualifiedColumn o, collector
|
895
906
|
o.name
|
896
907
|
end
|
897
|
-
|
908
|
+
|
898
909
|
def visit_Arel_Attributes_Cast(o, collector)
|
899
910
|
visit(o.relation, collector) # No casting yet
|
900
911
|
end
|
901
|
-
|
912
|
+
|
902
913
|
def visit_Arel_Attributes_Key o, collector
|
903
914
|
"#{visit(o.relation, collector)}.#{o.name}"
|
904
915
|
end
|
@@ -938,7 +949,7 @@ module Arel
|
|
938
949
|
# def visit_Arel_Attributes_EmptyRelation o, collector, top=true
|
939
950
|
# o.for_write ? "#{o.name}_attributes" : o.name
|
940
951
|
# end
|
941
|
-
|
952
|
+
|
942
953
|
def visit_Arel_Attributes_Attribute o, collector
|
943
954
|
join_name = o.relation.table_alias || o.relation.name
|
944
955
|
|
@@ -963,7 +974,7 @@ module Arel
|
|
963
974
|
o.value_for_database
|
964
975
|
end
|
965
976
|
end
|
966
|
-
|
977
|
+
|
967
978
|
def visit_Arel_Nodes_BindParam o, collector
|
968
979
|
a = collector.add_bind(o.value)
|
969
980
|
o.is_a?(Arel::Nodes::BindParam) ? o : a
|
@@ -1060,11 +1071,11 @@ module Arel
|
|
1060
1071
|
collector
|
1061
1072
|
end
|
1062
1073
|
end
|
1063
|
-
|
1074
|
+
|
1064
1075
|
def maybe_visit thing, collector
|
1065
1076
|
return collector unless thing
|
1066
1077
|
visit thing, collector
|
1067
1078
|
end
|
1068
1079
|
end
|
1069
1080
|
end
|
1070
|
-
end
|
1081
|
+
end
|
data/lib/sunstone/connection.rb
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
module Sunstone
|
5
5
|
class Connection
|
6
6
|
|
7
|
-
attr_reader :api_key, :host, :port, :use_ssl
|
7
|
+
attr_reader :api_key, :host, :port, :use_ssl, :prefix
|
8
8
|
|
9
9
|
# Initialize a connection a Sunstone API server.
|
10
10
|
#
|
@@ -31,7 +31,7 @@ module Sunstone
|
|
31
31
|
self.instance_variable_set(:"@#{key}", config[key])
|
32
32
|
end
|
33
33
|
|
34
|
-
@connection = Net::HTTP.new(host, port)
|
34
|
+
@connection = Net::HTTP.new(host, port || (use_ssl ? 443 : 80))
|
35
35
|
@connection.max_retries = 0
|
36
36
|
@connection.open_timeout = 5
|
37
37
|
@connection.read_timeout = 30
|
@@ -142,7 +142,7 @@ module Sunstone
|
|
142
142
|
message << " #{request.method} #{path_and_query[0]}"
|
143
143
|
if path_and_query[1]
|
144
144
|
if request['Query-Encoding'] == 'application/msgpack'
|
145
|
-
message << " " << MessagePack.unpack(CGI.unescape(path_and_query[1]))
|
145
|
+
message << " " << MessagePack.unpack(CGI.unescape(path_and_query[1])).inspect
|
146
146
|
else
|
147
147
|
message << " " << CGI.unescape(path_and_query[1])
|
148
148
|
end
|
data/lib/sunstone/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sunstone
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 7.0.
|
4
|
+
version: 7.1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jon Bracy
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-12-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -184,14 +184,14 @@ dependencies:
|
|
184
184
|
requirements:
|
185
185
|
- - ">="
|
186
186
|
- !ruby/object:Gem::Version
|
187
|
-
version: 7.
|
187
|
+
version: 7.1.0
|
188
188
|
type: :development
|
189
189
|
prerelease: false
|
190
190
|
version_requirements: !ruby/object:Gem::Requirement
|
191
191
|
requirements:
|
192
192
|
- - ">="
|
193
193
|
- !ruby/object:Gem::Version
|
194
|
-
version: 7.
|
194
|
+
version: 7.1.0
|
195
195
|
- !ruby/object:Gem::Dependency
|
196
196
|
name: msgpack
|
197
197
|
requirement: !ruby/object:Gem::Requirement
|
@@ -226,42 +226,42 @@ dependencies:
|
|
226
226
|
requirements:
|
227
227
|
- - ">="
|
228
228
|
- !ruby/object:Gem::Version
|
229
|
-
version: 7.
|
229
|
+
version: 7.1.0
|
230
230
|
type: :runtime
|
231
231
|
prerelease: false
|
232
232
|
version_requirements: !ruby/object:Gem::Requirement
|
233
233
|
requirements:
|
234
234
|
- - ">="
|
235
235
|
- !ruby/object:Gem::Version
|
236
|
-
version: 7.
|
236
|
+
version: 7.1.0
|
237
237
|
- !ruby/object:Gem::Dependency
|
238
238
|
name: arel-extensions
|
239
239
|
requirement: !ruby/object:Gem::Requirement
|
240
240
|
requirements:
|
241
241
|
- - ">="
|
242
242
|
- !ruby/object:Gem::Version
|
243
|
-
version:
|
243
|
+
version: 7.0.1
|
244
244
|
type: :runtime
|
245
245
|
prerelease: false
|
246
246
|
version_requirements: !ruby/object:Gem::Requirement
|
247
247
|
requirements:
|
248
248
|
- - ">="
|
249
249
|
- !ruby/object:Gem::Version
|
250
|
-
version:
|
250
|
+
version: 7.0.1
|
251
251
|
- !ruby/object:Gem::Dependency
|
252
252
|
name: activerecord-filter
|
253
253
|
requirement: !ruby/object:Gem::Requirement
|
254
254
|
requirements:
|
255
255
|
- - ">="
|
256
256
|
- !ruby/object:Gem::Version
|
257
|
-
version:
|
257
|
+
version: 7.0.0
|
258
258
|
type: :runtime
|
259
259
|
prerelease: false
|
260
260
|
version_requirements: !ruby/object:Gem::Requirement
|
261
261
|
requirements:
|
262
262
|
- - ">="
|
263
263
|
- !ruby/object:Gem::Version
|
264
|
-
version:
|
264
|
+
version: 7.0.0
|
265
265
|
description: A library for interacting with REST APIs. Similar to ActiveResource
|
266
266
|
email:
|
267
267
|
- jonbracy@gmail.com
|
@@ -323,7 +323,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
323
323
|
- !ruby/object:Gem::Version
|
324
324
|
version: '0'
|
325
325
|
requirements: []
|
326
|
-
rubygems_version: 3.
|
326
|
+
rubygems_version: 3.4.13
|
327
327
|
signing_key:
|
328
328
|
specification_version: 4
|
329
329
|
summary: A library for interacting with REST APIs
|