sunstone 7.0.0 → 7.1.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/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
|