sunstone 5.0.0.1 → 5.0.0.2

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 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