sunstone 5.0.0.1 → 5.0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a1fc21e0215013059317cc971ba06e8a221343e2
4
- data.tar.gz: 9c9f8128129d8d9ff50e398f7982f9288573a41f
3
+ metadata.gz: 251b8b254f40eac45d625f004fe405db180eefe0
4
+ data.tar.gz: e06aa5cf5ce843ccfe6c495fd5e0e7d7ec3d5685
5
5
  SHA512:
6
- metadata.gz: ccb44a14900ab3434926246a341740026723695e868283a0d09f3b4d6e41700da0aaf2f77f61c4bc69ca6fe3cdeee5204924fc67695b5ddcc09c211ae4a466a4
7
- data.tar.gz: 2fb90af0945f9c7ae8dbd1dad2dc7c0072efa24dcd2bd3cf1b76507fb6754e4a06f6093117d2e94c5900624f7083effdcaab8d02a00ff648c28e268fa2fc46b7
6
+ metadata.gz: 1ad88a3734db407224aa3addb478b5eaabfd9b5ad1ae0e3effe18c67e783a6131c652b58cbb55508f300a630f0c3aea259904743418cc711af0f8ab356bfc546
7
+ data.tar.gz: 628039ec1b81d9f4630a85dca9decd0e455e47d45d2386721cb96765628c86f19a5372275e52e1d6bdd0ac42082da8ee3ba2e4104969731a524448e10caabdbb
@@ -101,7 +101,7 @@ module ActiveRecord
101
101
  else
102
102
  if parent.association_cached?(reflection.name)
103
103
  model = parent.association(reflection.name).target
104
- construct(model, attributes.select{|k,v| !reflection.klass.column_names.include?(k.to_s) }, seen, model_cache)
104
+ construct(model, attributes.select{|k,v| !model.class.column_names.include?(k.to_s) }, seen, model_cache)
105
105
  end
106
106
  end
107
107
 
@@ -13,20 +13,57 @@ module ActiveRecord
13
13
  end
14
14
  end
15
15
 
16
+ # Returns an ActiveRecord::Result instance.
17
+ def select_all(arel, name = nil, binds = [], preparable: nil)
18
+ arel, binds = binds_from_relation arel, binds
19
+ select(arel, name, binds)
20
+ end
21
+
16
22
  def exec_query(arel, name = 'SAR', binds = [], prepare: false)
17
- sar = to_sar(arel, binds)
18
-
19
- log_mess = sar.path.split('?', 2)
20
- result = log("#{sar.method} #{log_mess[0]} #{(log_mess[1] && !log_mess[1].empty?) ? MessagePack.unpack(CGI.unescape(log_mess[1])) : '' }", name) {
21
- response = @connection.send_request(sar)
22
- if response.is_a?(Net::HTTPNoContent)
23
- nil
24
- else
25
- JSON.parse(response.body)
23
+
24
+ sars = []
25
+ multiple_requests = arel.is_a?(Arel::SelectManager) && arel&.limit
26
+
27
+ if multiple_requests
28
+ allowed_limit = limit_definition(arel.source.left.name)
29
+ requested_limit = binds.find { |x| x.name == 'LIMIT' }.value
30
+ multiple_requests = requested_limit > allowed_limit
31
+ end
32
+
33
+ send_request = lambda { |req_arel|
34
+ sar = to_sar(req_arel, binds)
35
+ sars.push(sar)
36
+ log_mess = sar.path.split('?', 2)
37
+ log("#{sar.method} #{log_mess[0]} #{(log_mess[1] && !log_mess[1].empty?) ? MessagePack.unpack(CGI.unescape(log_mess[1])) : '' }", name) do
38
+ response = @connection.send_request(sar)
39
+ if response.is_a?(Net::HTTPNoContent)
40
+ nil
41
+ else
42
+ JSON.parse(response.body)
43
+ end
26
44
  end
27
45
  }
28
46
 
29
- if sar.instance_variable_get(:@sunstone_calculation)
47
+ result = if multiple_requests
48
+ bind = binds.find { |x| x.name == 'LIMIT' }
49
+ binds.delete(bind)
50
+
51
+ limit, offset, results = allowed_limit, 0, []
52
+ while offset < requested_limit
53
+ split_arel = arel.clone
54
+ split_arel.limit = limit
55
+ split_arel.offset = offset
56
+ request_results = send_request.call(split_arel)
57
+ results = results + request_results
58
+ break if request_results.size < limit
59
+ offset = offset + limit
60
+ end
61
+ results
62
+ else
63
+ send_request.call(arel)
64
+ end
65
+
66
+ if !multiple_requests && sars[0].instance_variable_get(:@sunstone_calculation)
30
67
  # this is a count, min, max.... yea i know..
31
68
  ActiveRecord::Result.new(['all'], [result], {:all => type_map.lookup('integer')})
32
69
  elsif result.is_a?(Array)
@@ -35,7 +72,7 @@ module ActiveRecord
35
72
  ActiveRecord::Result.new(result.keys, [result])
36
73
  end
37
74
  end
38
-
75
+
39
76
  def last_inserted_id(result)
40
77
  row = result.rows.first
41
78
  row && row['id']
@@ -46,7 +83,3 @@ module ActiveRecord
46
83
  end
47
84
  end
48
85
 
49
-
50
-
51
-
52
-
@@ -19,17 +19,35 @@ module ActiveRecord
19
19
  end
20
20
  end
21
21
 
22
+ def definition(table_name)
23
+ response = @connection.get("/#{table_name}/schema")
24
+
25
+ version = Gem::Version.create(response['StandardAPI-Version'] || '5.0.0.4')
26
+
27
+ if (version >= Gem::Version.create('5.0.0.5'))
28
+ JSON.parse(response.body)
29
+ else
30
+ { 'columns' => JSON.parse(response.body), 'limit' => nil }
31
+ end
32
+ rescue ::Sunstone::Exception::NotFound
33
+ raise ActiveRecord::StatementInvalid, "Table \"#{table_name}\" does not exist"
34
+ end
35
+
22
36
  # Returns the list of a table's column names, data types, and default values.
23
37
  #
24
38
  # Query implementation notes:
25
39
  # - format_type includes the column size constraint, e.g. varchar(50)
26
40
  # - ::regclass is a function that gives the id for a table name
27
41
  def column_definitions(table_name) # :nodoc:
28
- JSON.parse(@connection.get("/#{table_name}/schema").body)
29
- rescue ::Sunstone::Exception::NotFound
30
- raise ActiveRecord::StatementInvalid, "Table \"#{table_name}\" does not exist"
42
+ definition(table_name)['columns']
31
43
  end
32
-
44
+
45
+ # Returns the limit definition of the table (the maximum limit that can
46
+ # be used).
47
+ def limit_definition(table_name)
48
+ definition(table_name)['limit'] || Float::INFINITY
49
+ end
50
+
33
51
  def tables
34
52
  JSON.parse(@connection.get('/tables').body)
35
53
  end
@@ -0,0 +1,20 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module Sunstone
4
+ module Type
5
+ class Json < ActiveRecord::Type::Internal::AbstractJson
6
+
7
+
8
+ def deserialize(value)
9
+ value.nil? ? nil : value.dup
10
+ end
11
+
12
+ def serialize(value)
13
+ value
14
+ end
15
+
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -10,6 +10,7 @@ require 'active_record/connection_adapters/sunstone/column'
10
10
  require 'active_record/connection_adapters/sunstone/type/date_time'
11
11
  require 'active_record/connection_adapters/sunstone/type/array'
12
12
  require 'active_record/connection_adapters/sunstone/type/uuid'
13
+ require 'active_record/connection_adapters/sunstone/type/json'
13
14
 
14
15
  module ActiveRecord
15
16
  module ConnectionHandling # :nodoc:
@@ -20,12 +21,8 @@ module ActiveRecord
20
21
  # objects
21
22
  def sunstone_connection(config)
22
23
  conn_params = config.symbolize_keys
23
-
24
24
  conn_params.delete_if { |_, v| v.nil? }
25
25
 
26
- # Map ActiveRecords param names to PGs.
27
- conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
28
- conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
29
26
  if conn_params[:url]
30
27
  uri = URI.parse(conn_params.delete(:url))
31
28
  conn_params[:api_key] ||= (uri.user ? CGI.unescape(uri.user) : nil)
@@ -34,12 +31,11 @@ module ActiveRecord
34
31
  conn_params[:use_ssl] ||= (uri.scheme == 'https')
35
32
  end
36
33
 
37
- # Forward only valid config params to PGconn.connect.
34
+ # Forward only valid config params to Sunstone::Connection
38
35
  conn_params.slice!(*VALID_SUNSTONE_CONN_PARAMS)
39
36
 
40
- # The postgres drivers don't allow the creation of an unconnected PGconn object,
41
- # so just pass a nil connection object for the time being.
42
- ConnectionAdapters::SunstoneAPIAdapter.new(nil, logger, conn_params, config)
37
+ client = ::Sunstone::Connection.new(conn_params)
38
+ ConnectionAdapters::SunstoneAPIAdapter.new(client, logger, conn_params, config)
43
39
  end
44
40
  end
45
41
 
@@ -71,44 +67,29 @@ module ActiveRecord
71
67
  include Sunstone::ColumnDumper
72
68
  # include Savepoints
73
69
 
74
- # Returns 'SunstoneAPI' as adapter name for identification purposes.
75
- def adapter_name
76
- ADAPTER_NAME
77
- end
78
-
79
70
  # Initializes and connects a SunstoneAPI adapter.
80
71
  def initialize(connection, logger, connection_parameters, config)
81
72
  super(connection, logger, config)
82
73
 
83
74
  @prepared_statements = false
84
- @visitor = Arel::Visitors::Sunstone.new
85
- @connection_parameters, @config = connection_parameters, config
75
+ @connection_parameters = connection_parameters
86
76
 
87
- connect
88
-
89
- @type_map = Type::HashLookupTypeMap.new
90
- initialize_type_map(type_map)
77
+ # @type_map = Type::HashLookupTypeMap.new
78
+ # initialize_type_map(type_map)
91
79
  end
92
80
 
93
- # Is this connection alive and ready for queries?
94
81
  def active?
95
- @connection.ping
96
- true
97
- rescue Net::HTTPExceptions
98
- false
82
+ @connection.active?
99
83
  end
100
84
 
101
- # TODO: this doesn't work yet
102
- # Close then reopen the connection.
103
85
  def reconnect!
104
86
  super
105
- @connection.reset
106
- # configure_connection
87
+ @connection.reconnect!
107
88
  end
108
-
109
- # TODO don't know about this yet
110
- def reset!
111
- # configure_connection
89
+
90
+ def disconnect!
91
+ super
92
+ @connection.disconnect!
112
93
  end
113
94
 
114
95
  # Executes the delete statement and returns the number of rows affected.
@@ -117,14 +98,6 @@ module ActiveRecord
117
98
  r.rows.first.to_i
118
99
  end
119
100
 
120
- # TODO: deal with connection.close
121
- # Disconnects from the database if already connected. Otherwise, this
122
- # method does nothing.
123
- def disconnect!
124
- super
125
- @connection.close rescue nil
126
- end
127
-
128
101
  def native_database_types #:nodoc:
129
102
  NATIVE_DATABASE_TYPES
130
103
  end
@@ -140,7 +113,11 @@ module ActiveRecord
140
113
  def update_table_definition(table_name, base) #:nodoc:
141
114
  SunstoneAPI::Table.new(table_name, base)
142
115
  end
143
-
116
+
117
+ def arel_visitor
118
+ Arel::Visitors::Sunstone.new
119
+ end
120
+
144
121
  def collector
145
122
  Arel::Collectors::Sunstone.new
146
123
  end
@@ -206,7 +183,7 @@ module ActiveRecord
206
183
  m.register_type 'integer', Type::Integer.new
207
184
  m.register_type 'decimal', Type::Decimal.new
208
185
  m.register_type 'datetime', Sunstone::Type::DateTime.new
209
- m.register_type 'json', Type::Internal::AbstractJson.new
186
+ m.register_type 'json', Sunstone::Type::Json.new
210
187
  m.register_type 'uuid', Sunstone::Type::Uuid.new
211
188
 
212
189
  if defined?(Sunstone::Type::EWKB)
@@ -214,12 +191,6 @@ module ActiveRecord
214
191
  end
215
192
  end
216
193
 
217
- # Connects to a Sunstone API server and sets up the adapter depending on
218
- # the connected server's characteristics.
219
- def connect
220
- @connection = ::Sunstone::Connection.new(@connection_parameters)
221
- end
222
-
223
194
  def create_table_definition(name, temporary, options, as = nil) # :nodoc:
224
195
  SunstoneAPI::TableDefinition.new native_database_types, name, temporary, options, as
225
196
  end
@@ -2,7 +2,9 @@ module Arel
2
2
  module Collectors
3
3
  class Sunstone < Arel::Collectors::Bind
4
4
 
5
- attr_accessor :request_type, :table, :where, :limit, :offset, :order, :operation, :columns, :updates, :eager_loads, :id
5
+ MAX_URI_LENGTH = 2083
6
+
7
+ attr_accessor :request_type, :table, :where, :limit, :offset, :order, :operation, :columns, :updates, :eager_loads, :id, :distinct, :distinct_on
6
8
 
7
9
  def cast_attribute(v)
8
10
  if (v.is_a?(ActiveRecord::Attribute))
@@ -58,49 +60,81 @@ module Arel
58
60
 
59
61
  def compile bvs, conn = nil
60
62
  path = "/#{table}"
63
+ headers = {}
64
+ request_type_override = nil
61
65
 
66
+ body = nil
62
67
  if updates
63
- body = {table.singularize => substitute_binds(updates.clone, bvs)}.to_json
68
+ body = {
69
+ table.singularize => substitute_binds(updates.clone, bvs)
70
+ }
64
71
  end
65
72
 
66
- get_params = {}
73
+ params = {}
67
74
  if where
68
- get_params[:where] = substitute_binds(where.clone, bvs)
69
- if get_params[:where].size == 1
70
- get_params[:where] = get_params[:where].pop
75
+ params[:where] = substitute_binds(where.clone, bvs)
76
+ if params[:where].size == 1
77
+ params[:where] = params[:where].pop
71
78
  end
72
79
  end
73
80
 
74
81
  if eager_loads
75
- get_params[:include] = eager_loads.clone
82
+ params[:include] = eager_loads.clone
83
+ end
84
+
85
+ if distinct_on
86
+ params[:distinct_on] = distinct_on
87
+ elsif distinct
88
+ params[:distinct] = true
89
+ end
90
+
91
+ if limit.is_a?(Arel::Nodes::BindParam)
92
+ params[:limit] = substitute_binds(limit, bvs)
93
+ elsif limit
94
+ params[:limit] = limit
95
+ end
96
+
97
+ if offset.is_a?(Arel::Nodes::BindParam)
98
+ params[:offset] = substitute_binds(offset, bvs)
99
+ elsif offset
100
+ params[:offset] = offset
76
101
  end
77
102
 
78
- get_params[:limit] = substitute_binds(limit, bvs) if limit
79
- get_params[:offset] = substitute_binds(offset, bvs) if offset
80
- get_params[:order] = substitute_binds(order, bvs) if order
103
+ params[:order] = substitute_binds(order, bvs) if order
81
104
 
82
105
  case operation
83
106
  when :count
84
107
  path += "/#{operation}"
85
108
  when :calculate
86
109
  path += "/calculate"
87
- get_params[:select] = columns
110
+ params[:select] = columns
88
111
  when :update, :delete
89
- path += "/#{get_params[:where]['id']}"
90
- get_params.delete(:where)
91
- end
92
- if get_params.size > 0
93
- path += "?#{CGI.escape(MessagePack.pack(get_params))}"
112
+ path += "/#{params[:where]['id']}"
113
+ params.delete(:where)
94
114
  end
95
115
 
96
- request = request_type.new(path)
97
- if get_params.size > 0
98
- request['Query-Encoding'] = 'application/msgpack'
116
+ if params.size > 0 && request_type == Net::HTTP::Get
117
+ newpath = path + "?#{CGI.escape(MessagePack.pack(params))}"
118
+ if newpath.length > MAX_URI_LENGTH
119
+ request_type_override = Net::HTTP::Post
120
+ headers['X-Http-Method-Override'] = 'GET'
121
+ if body
122
+ body.merge!(params)
123
+ else
124
+ body = params
125
+ end
126
+ else
127
+ path = newpath
128
+ headers['Query-Encoding'] = 'application/msgpack'
129
+ end
99
130
  end
131
+
132
+ request = (request_type_override || request_type).new(path)
133
+ headers.each { |k,v| request[k] = v }
100
134
  request.instance_variable_set(:@sunstone_calculation, true) if [:calculate, :delete].include?(operation)
101
135
 
102
- if updates
103
- request.body = body
136
+ if body
137
+ request.body = body.to_json
104
138
  end
105
139
 
106
140
  request
@@ -55,6 +55,8 @@ module Arel
55
55
  collector.operation = :select
56
56
  end
57
57
 
58
+ collector = maybe_visit o.set_quantifier, collector
59
+
58
60
  if o.source && !o.source.empty?
59
61
  collector.table = o.source.left.name
60
62
  end
@@ -296,13 +298,15 @@ module Arel
296
298
  # visit o.expr, collector
297
299
  # end
298
300
  #
299
- # def visit_Arel_Nodes_Distinct o, collector
300
- # collector << DISTINCT
301
- # end
302
- #
303
- # def visit_Arel_Nodes_DistinctOn o, collector
304
- # raise NotImplementedError, 'DISTINCT ON not implemented for this db'
305
- # end
301
+ def visit_Arel_Nodes_Distinct o, collector
302
+ collector.distinct = true
303
+ collector
304
+ end
305
+
306
+ def visit_Arel_Nodes_DistinctOn o, collector
307
+ collector.distinct_on = o.expr.map(&:name)
308
+ collector
309
+ end
306
310
  #
307
311
  # def visit_Arel_Nodes_With o, collector
308
312
  # collector << "WITH "
@@ -840,13 +844,7 @@ module Arel
840
844
  end
841
845
 
842
846
  def visit_Arel_Attributes_Cast(o, collector)
843
- key = visit(o.relation, collector)
844
- value = { :cast => o.name }
845
- if key.is_a?(Hash)
846
- add_to_bottom_of_hash(key, value)
847
- else
848
- { key => value }
849
- end
847
+ visit(o.relation, collector) # No casting yet
850
848
  end
851
849
 
852
850
  def visit_Arel_Attributes_Key o, collector
@@ -914,7 +912,7 @@ module Arel
914
912
  end
915
913
 
916
914
  def literal(o, collector)
917
- o.to_s;
915
+ o
918
916
  end
919
917
  alias :visit_Arel_Nodes_SqlLiteral :literal
920
918
  alias :visit_Bignum :literal
@@ -42,16 +42,34 @@ module Sunstone
42
42
  end
43
43
 
44
44
  # Ping the Sunstone. If everything is configured and operating correctly
45
- # <tt>"pong"</tt> will be returned. Otherwise and Sunstone::Exception should be
46
- # thrown.
45
+ # <tt>"pong"</tt> will be returned. Otherwise and Sunstone::Exception should
46
+ # be thrown.
47
47
  #
48
48
  # #!ruby
49
49
  # Sunstone.ping # => "pong"
50
50
  #
51
- # Sunstone.ping # raises Sunstone::Exception::ServiceUnavailable if a 503 is returned
51
+ # Sunstone.ping # raises Sunstone::Exception::ServiceUnavailable if a
52
+ # 503 is returned
52
53
  def ping
53
54
  get('/ping').body
54
55
  end
56
+
57
+ def connect!
58
+ @connection.start
59
+ end
60
+
61
+ def active?
62
+ @connection.active?
63
+ end
64
+
65
+ def reconnect!
66
+ disconnect!
67
+ connect!
68
+ end
69
+
70
+ def disconnect!
71
+ @connection.finish if @connection.active?
72
+ end
55
73
 
56
74
  # Returns the User-Agent of the client. Defaults to:
57
75
  # "sunstone-ruby/SUNSTONE_VERSION RUBY_VERSION-pPATCH_LEVEL PLATFORM"
@@ -150,6 +168,7 @@ module Sunstone
150
168
  return_value = nil
151
169
  retry_count = 0
152
170
  begin
171
+ close_connection = false
153
172
  @connection.request(request) do |response|
154
173
  if response['Deprecation-Notice']
155
174
  ActiveSupport::Deprecation.warn(response['Deprecation-Notice'])
@@ -159,17 +178,23 @@ module Sunstone
159
178
 
160
179
  # Get the cookies
161
180
  response.each_header do |key, value|
162
- if key.downcase == 'set-cookie' && Thread.current[:sunstone_cookie_store]
163
- Thread.current[:sunstone_cookie_store].set_cookie(request_uri, value)
181
+ case key.downcase
182
+ when 'set-cookie'
183
+ if Thread.current[:sunstone_cookie_store]
184
+ Thread.current[:sunstone_cookie_store].set_cookie(request_uri, value)
185
+ end
186
+ when 'connection'
187
+ close_connection = (value == 'close')
164
188
  end
165
189
  end
166
190
 
167
191
  if block_given?
168
- return_value =yield(response)
192
+ return_value = yield(response)
169
193
  else
170
- return_value =response
194
+ return_value = response
171
195
  end
172
196
  end
197
+ @connection.finish if close_connection
173
198
  rescue ActiveRecord::ConnectionNotEstablished
174
199
  retry_count += 1
175
200
  retry_count == 1 ? retry : raise
@@ -322,6 +347,7 @@ module Sunstone
322
347
  headers['Accept'] = 'application/json'
323
348
  headers['User-Agent'] = user_agent
324
349
  headers['Api-Version'] = '0.1.0'
350
+ headers['Connection'] = 'keep-alive'
325
351
 
326
352
  request_api_key = Thread.current[:sunstone_api_key] || api_key
327
353
  headers['Api-Key'] = request_api_key if request_api_key
@@ -1,3 +1,3 @@
1
1
  module Sunstone
2
- VERSION = '5.0.0.1'
2
+ VERSION = '5.0.0.2'
3
3
  end
@@ -28,6 +28,7 @@ Gem::Specification.new do |s|
28
28
  s.add_development_dependency 'sdoc-templates-42floors'
29
29
  s.add_development_dependency 'rgeo'
30
30
  s.add_development_dependency 'simplecov'
31
+ s.add_development_dependency 'byebug'
31
32
 
32
33
  # Runtime
33
34
  s.add_runtime_dependency 'msgpack'
@@ -52,15 +52,23 @@ class ActiveRecord::PersistanceTest < Minitest::Test
52
52
  test '#save attempts another request while in transaction' do
53
53
  webmock(:get, '/test_model_bs/schema').to_return(
54
54
  body: {
55
- id: {type: 'integer', primary_key: true, null: false, array: false},
56
- name: {type: 'string', primary_key: false, null: true, array: false}
57
- }.to_json
55
+ columns: {
56
+ id: {type: 'integer', primary_key: true, null: false, array: false},
57
+ name: {type: 'string', primary_key: false, null: true, array: false}
58
+ },
59
+ limit: 100
60
+ }.to_json,
61
+ headers: { 'StandardAPI-Version' => '5.0.0.5' }
58
62
  )
59
63
  webmock(:get, '/test_model_as/schema').to_return(
60
64
  body: {
61
- id: {type: 'integer', primary_key: true, null: false, array: false},
62
- name: {type: 'string', primary_key: false, null: true, array: false}
63
- }.to_json
65
+ columns: {
66
+ id: {type: 'integer', primary_key: true, null: false, array: false},
67
+ name: {type: 'string', primary_key: false, null: true, array: false}
68
+ },
69
+ limit: 100
70
+ }.to_json,
71
+ headers: { 'StandardAPI-Version' => '5.0.0.5' }
64
72
  )
65
73
 
66
74
  assert_raises ActiveRecord::StatementInvalid do
@@ -2,9 +2,31 @@ require 'test_helper'
2
2
 
3
3
  class ActiveRecord::QueryTest < Minitest::Test
4
4
 
5
- test '::find' do
5
+ test '::find w/ old schema definition' do
6
+ replaced_stub = WebMock::StubRegistry.instance.global_stubs.find { |x|
7
+ x.request_pattern.uri_pattern.to_s == 'http://example.com/ships/schema'
8
+ }
9
+ WebMock::StubRegistry.instance.global_stubs.delete(replaced_stub)
10
+
11
+ new_stub = WebMock::API.stub_request(:get, "http://example.com/ships/schema").to_return(
12
+ body: {
13
+ id: {type: 'integer', primary_key: true, null: false, array: false},
14
+ fleet_id: {type: 'integer', primary_key: false, null: true, array: false},
15
+ name: {type: 'string', primary_key: false, null: true, array: false}
16
+ }.to_json
17
+ )
18
+
6
19
  webmock(:get, "/ships", { where: {id: 42}, limit: 1 }).to_return(body: [{id: 42}].to_json)
20
+
21
+ assert_equal 42, Ship.find(42).id
7
22
 
23
+ WebMock::API.remove_request_stub(new_stub)
24
+ WebMock::StubRegistry.instance.global_stubs.push(replaced_stub)
25
+ end
26
+
27
+ test '::find' do
28
+ webmock(:get, "/ships", { where: {id: 42}, limit: 1 }).to_return(body: [{id: 42}].to_json)
29
+
8
30
  assert_equal 42, Ship.find(42).id
9
31
  end
10
32
 
@@ -21,7 +43,8 @@ class ActiveRecord::QueryTest < Minitest::Test
21
43
  end
22
44
 
23
45
  test '::find_each' do
24
- webmock(:get, "/ships", { limit: 1000, order: [{id: :asc}] }).to_return(body: [].to_json)
46
+ webmock(:get, "/ships", { limit: 100, offset: 0, order: [{id: :asc}] }).to_return(body: Array.new(100, { id: 1 }).to_json)
47
+ webmock(:get, "/ships", { limit: 100, offset: 100, order: [{id: :asc}] }).to_return(body: Array.new(10, { id: 2 }).to_json)
25
48
 
26
49
  assert_nil Ship.find_each { |s| s }
27
50
  end
@@ -63,6 +86,20 @@ class ActiveRecord::QueryTest < Minitest::Test
63
86
  assert_equal [], Ship.where(nations: {id: 1}).limit(10).to_a
64
87
  end
65
88
  ### end polymorphic test
89
+
90
+ # Distinct
91
+ test '::distinct query' do
92
+ webmock(:get, "/ships", distinct: true).to_return(body: [].to_json)
93
+
94
+ assert_equal [], Ship.distinct
95
+ end
96
+
97
+ # TODO: i need arel-extensions....
98
+ # test '::distinct_on query' do
99
+ # webmock(:get, "/ships", distinct_on: ['id']).to_return(body: [].to_json)
100
+ #
101
+ # assert_equal [], Ship.distinct_on(:id)
102
+ # end
66
103
 
67
104
  test '::count' do
68
105
  webmock(:get, "/ships/calculate", select: [{count: "*"}]).to_return(body: [10].to_json)
@@ -82,6 +119,16 @@ class ActiveRecord::QueryTest < Minitest::Test
82
119
  assert_equal 10, Ship.sum(:weight)
83
120
  end
84
121
 
122
+ test '::where(....big get request turns into post...)' do
123
+ name = 'q' * 3000
124
+ webmock(:post, "/ships").with(
125
+ headers: {'X-Http-Method-Override' => 'GET'},
126
+ body: {where: {name: name}}.to_json
127
+ ).to_return(body: [{id: 42}].to_json)
128
+
129
+ assert_equal 42, Ship.where(name: name)[0].id
130
+ end
131
+
85
132
  # Relation test
86
133
 
87
134
  test '#to_sql' do
@@ -1,56 +1,80 @@
1
-
2
-
3
1
  WebMock::StubRegistry.instance.global_stubs.push(
4
2
  WebMock::RequestStub.new(:get, "http://example.com/ping").to_return(
5
- body: "pong"
3
+ body: "pong",
4
+ headers: { 'StandardAPI-Version' => '5.0.0.5' }
6
5
  ),
7
6
 
8
7
  WebMock::RequestStub.new(:get, "http://example.com/tables").to_return(
9
- body: %w(ships fleets sailors).to_json
8
+ body: %w(ships fleets sailors).to_json,
9
+ headers: { 'StandardAPI-Version' => '5.0.0.5' }
10
10
  ),
11
11
 
12
12
  WebMock::RequestStub.new(:get, "http://example.com/ships/schema").to_return(
13
13
  body: {
14
- id: {type: 'integer', primary_key: true, null: false, array: false},
15
- fleet_id: {type: 'integer', primary_key: false, null: true, array: false},
16
- name: {type: 'string', primary_key: false, null: true, array: false}
17
- }.to_json
14
+ columns: {
15
+ id: {type: 'integer', primary_key: true, null: false, array: false},
16
+ fleet_id: {type: 'integer', primary_key: false, null: true, array: false},
17
+ name: {type: 'string', primary_key: false, null: true, array: false}
18
+ },
19
+ limit: 100
20
+ }.to_json,
21
+ headers: { 'StandardAPI-Version' => '5.0.0.5' }
18
22
  ),
19
23
 
20
24
  WebMock::RequestStub.new(:get, "http://example.com/fleets/schema").to_return(
21
25
  body: {
22
- id: {type: 'integer', primary_key: true, null: false, array: false},
23
- name: {type: 'string', primary_key: false, null: true, array: false}
24
- }.to_json
26
+ columns: {
27
+ id: {type: 'integer', primary_key: true, null: false, array: false},
28
+ name: {type: 'string', primary_key: false, null: true, array: false}
29
+ },
30
+ limit: 100
31
+ }.to_json,
32
+ headers: { 'StandardAPI-Version' => '5.0.0.5' }
25
33
  ),
26
34
 
27
35
  WebMock::RequestStub.new(:get, "http://example.com/sailors/schema").to_return(
28
36
  body: {
29
- id: {type: 'integer', primary_key: true, null: false, array: false},
30
- name: {type: 'string', primary_key: false, null: true, array: false}
31
- }.to_json
37
+ columns: {
38
+ id: {type: 'integer', primary_key: true, null: false, array: false},
39
+ name: {type: 'string', primary_key: false, null: true, array: false}
40
+ },
41
+ limit: 100
42
+ }.to_json,
43
+ headers: { 'StandardAPI-Version' => '5.0.0.5' }
32
44
  ),
33
45
 
34
46
  WebMock::RequestStub.new(:get, "http://example.com/sailors_ships/schema").to_return(
35
47
  body: {
36
- sailor_id: {type: 'integer', primary_key: false, null: false, array: false},
37
- ship_id: {type: 'integer', primary_key: false, null: true, array: false}
38
- }.to_json
48
+ columns: {
49
+ sailor_id: {type: 'integer', primary_key: false, null: false, array: false},
50
+ ship_id: {type: 'integer', primary_key: false, null: true, array: false}
51
+ },
52
+ limit: 100
53
+ }.to_json,
54
+ headers: { 'StandardAPI-Version' => '5.0.0.5' }
39
55
  ),
40
56
 
41
57
  WebMock::RequestStub.new(:get, "http://example.com/countries/schema").to_return(
42
58
  body: {
43
- id: {type: 'integer', primary_key: true, null: false, array: false},
44
- name: {type: 'string', primary_key: false, null: true, array: false}
45
- }.to_json
59
+ columns: {
60
+ id: {type: 'integer', primary_key: true, null: false, array: false},
61
+ name: {type: 'string', primary_key: false, null: true, array: false}
62
+ },
63
+ limit: 100
64
+ }.to_json,
65
+ headers: { 'StandardAPI-Version' => '5.0.0.5' }
46
66
  ),
47
67
 
48
68
  WebMock::RequestStub.new(:get, "http://example.com/ownerships/schema").to_return(
49
69
  body: {
50
- country_id: {type: 'integer', primary_key: false, null: false, array: false},
51
- asset_type: {type: 'string', primary_key: false, null: false, array: false},
52
- asset_id: {type: 'integer', primary_key: false, null: true, array: false}
53
- }.to_json
70
+ columns: {
71
+ country_id: {type: 'integer', primary_key: false, null: false, array: false},
72
+ asset_type: {type: 'string', primary_key: false, null: false, array: false},
73
+ asset_id: {type: 'integer', primary_key: false, null: true, array: false}
74
+ },
75
+ limit: 100
76
+ }.to_json,
77
+ headers: { 'StandardAPI-Version' => '5.0.0.5' }
54
78
  )
55
79
  )
56
80
 
@@ -9,6 +9,7 @@ SimpleCov.start do
9
9
  end
10
10
 
11
11
  require 'rgeo'
12
+ require 'byebug'
12
13
  require "minitest/autorun"
13
14
  require 'minitest/unit'
14
15
  require 'minitest/reporters'
@@ -18,13 +19,6 @@ require 'mocha/mini_test'
18
19
  require 'sunstone'
19
20
  require File.expand_path('../models.rb', __FILE__)
20
21
 
21
-
22
-
23
- # require 'faker'
24
- # require "mocha"
25
- # require "mocha/mini_test"
26
- # require 'active_support/testing/time_helpers'
27
-
28
22
  Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new
29
23
 
30
24
  # File 'lib/active_support/testing/declarative.rb', somewhere in rails....
@@ -70,7 +64,7 @@ class Minitest::Test
70
64
 
71
65
  def webmock(method, path, query=nil)
72
66
  query = deep_transform_query(query) if query
73
-
67
+
74
68
  stub_request(method, /^#{ExampleRecord.connection.instance_variable_get(:@connection).url}/).with do |req|
75
69
  if query
76
70
  req&.uri&.path == path && req.uri.query && unpack(req.uri.query.sub(/=true$/, '')) == query
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: 5.0.0.1
4
+ version: 5.0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jon Bracy
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-09-26 00:00:00.000000000 Z
11
+ date: 2016-11-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -192,6 +192,20 @@ dependencies:
192
192
  - - ">="
193
193
  - !ruby/object:Gem::Version
194
194
  version: '0'
195
+ - !ruby/object:Gem::Dependency
196
+ name: byebug
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - ">="
200
+ - !ruby/object:Gem::Version
201
+ version: '0'
202
+ type: :development
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - ">="
207
+ - !ruby/object:Gem::Version
208
+ version: '0'
195
209
  - !ruby/object:Gem::Dependency
196
210
  name: msgpack
197
211
  requirement: !ruby/object:Gem::Requirement
@@ -286,6 +300,7 @@ files:
286
300
  - lib/active_record/connection_adapters/sunstone/type/array.rb
287
301
  - lib/active_record/connection_adapters/sunstone/type/date_time.rb
288
302
  - lib/active_record/connection_adapters/sunstone/type/ewkb.rb
303
+ - lib/active_record/connection_adapters/sunstone/type/json.rb
289
304
  - lib/active_record/connection_adapters/sunstone/type/uuid.rb
290
305
  - lib/active_record/connection_adapters/sunstone/type_metadata.rb
291
306
  - lib/active_record/connection_adapters/sunstone_adapter.rb
@@ -329,7 +344,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
329
344
  version: '0'
330
345
  requirements: []
331
346
  rubyforge_project:
332
- rubygems_version: 2.5.1
347
+ rubygems_version: 2.6.4
333
348
  signing_key:
334
349
  specification_version: 4
335
350
  summary: A library for interacting with REST APIs