sunstone 0.1.0 → 1.0.0

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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/lib/active_record/connection_adapters/sunstone/column.rb +19 -0
  3. data/lib/active_record/connection_adapters/sunstone/database_statements.rb +40 -0
  4. data/lib/active_record/connection_adapters/sunstone/schema_statements.rb +95 -0
  5. data/lib/active_record/connection_adapters/sunstone/type/date_time.rb +22 -0
  6. data/lib/active_record/connection_adapters/sunstone_adapter.rb +177 -0
  7. data/lib/arel/collectors/sunstone.rb +75 -0
  8. data/lib/arel/visitors/sunstone.rb +769 -0
  9. data/lib/ext/active_record/associations/builder/has_and_belongs_to_many.rb +48 -0
  10. data/lib/ext/active_record/relation.rb +26 -0
  11. data/lib/ext/active_record/statement_cache.rb +24 -0
  12. data/lib/sunstone.rb +37 -347
  13. data/lib/sunstone/connection.rb +337 -0
  14. data/sunstone.gemspec +3 -2
  15. data/test/sunstone/connection_test.rb +319 -0
  16. data/test/sunstone/parser_test.rb +21 -21
  17. metadata +30 -36
  18. data/lib/sunstone/model.rb +0 -23
  19. data/lib/sunstone/model/attributes.rb +0 -99
  20. data/lib/sunstone/model/persistence.rb +0 -168
  21. data/lib/sunstone/schema.rb +0 -38
  22. data/lib/sunstone/type/boolean.rb +0 -19
  23. data/lib/sunstone/type/date_time.rb +0 -20
  24. data/lib/sunstone/type/decimal.rb +0 -19
  25. data/lib/sunstone/type/integer.rb +0 -17
  26. data/lib/sunstone/type/mutable.rb +0 -16
  27. data/lib/sunstone/type/string.rb +0 -18
  28. data/lib/sunstone/type/value.rb +0 -97
  29. data/test/sunstone/model/associations_test.rb +0 -55
  30. data/test/sunstone/model/attributes_test.rb +0 -60
  31. data/test/sunstone/model/persistence_test.rb +0 -173
  32. data/test/sunstone/model_test.rb +0 -11
  33. data/test/sunstone/schema_test.rb +0 -25
  34. data/test/sunstone/type/boolean_test.rb +0 -24
  35. data/test/sunstone/type/date_time_test.rb +0 -31
  36. data/test/sunstone/type/decimal_test.rb +0 -27
  37. data/test/sunstone/type/integer_test.rb +0 -29
  38. data/test/sunstone/type/string_test.rb +0 -54
  39. data/test/sunstone/type/value_test.rb +0 -27
@@ -0,0 +1,48 @@
1
+ module ActiveRecord::Associations::Builder
2
+ class HasAndBelongsToMany # :nodoc:
3
+
4
+ def through_model
5
+ habtm = JoinTableResolver.build lhs_model, association_name, options
6
+
7
+ join_model = Class.new(lhs_model.base_class.superclass) {
8
+ class << self;
9
+ attr_accessor :class_resolver
10
+ attr_accessor :name
11
+ attr_accessor :table_name_resolver
12
+ attr_accessor :left_reflection
13
+ attr_accessor :right_reflection
14
+ end
15
+
16
+ def self.table_name
17
+ table_name_resolver.join_table
18
+ end
19
+
20
+ def self.compute_type(class_name)
21
+ class_resolver.compute_type class_name
22
+ end
23
+
24
+ def self.add_left_association(name, options)
25
+ belongs_to name, options
26
+ self.left_reflection = _reflect_on_association(name)
27
+ end
28
+
29
+ def self.add_right_association(name, options)
30
+ rhs_name = name.to_s.singularize.to_sym
31
+ belongs_to rhs_name, options
32
+ self.right_reflection = _reflect_on_association(rhs_name)
33
+ end
34
+
35
+ }
36
+
37
+ join_model.name = "HABTM_#{association_name.to_s.camelize}"
38
+ join_model.table_name_resolver = habtm
39
+ join_model.class_resolver = lhs_model
40
+ join_model.primary_key = nil
41
+
42
+ join_model.add_left_association :left_side, class: lhs_model
43
+ join_model.add_right_association association_name, belongs_to_options(options)
44
+ join_model
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,26 @@
1
+ module ActiveRecord
2
+ class Relation
3
+ def to_sql
4
+ @to_sql ||= begin
5
+ relation = self
6
+ connection = klass.connection
7
+ visitor = connection.visitor
8
+ collector = connection.collector
9
+
10
+ if eager_loading?
11
+ find_with_associations { |rel| relation = rel }
12
+ end
13
+
14
+ arel = relation.arel
15
+ binds = (arel.bind_values + relation.bind_values).dup
16
+ binds.map! { |bv| connection.quote(*bv.reverse) }
17
+ collect = visitor.accept(arel.ast, collector)
18
+ if collector.is_a?(Arel::Collectors::Sunstone)
19
+ collect.compile(binds)
20
+ else
21
+ collect.substitute_binds(binds).join
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,24 @@
1
+ module ActiveRecord
2
+ class StatementCache
3
+ class PartialQuery
4
+
5
+ def initialize collector
6
+ @collector = collector
7
+ @indexes = collector.value.find_all { |thing|
8
+ Arel::Nodes::BindParam === thing
9
+ }
10
+ end
11
+
12
+ def sql_for(binds, connection)
13
+ binds = binds.dup
14
+ @collector.compile(binds)
15
+ end
16
+ end
17
+
18
+ def self.partial_query(visitor, ast, collector)
19
+ collected = visitor.accept(ast, collector)
20
+ PartialQuery.new collected
21
+ end
22
+
23
+ end
24
+ end
data/lib/sunstone.rb CHANGED
@@ -1,361 +1,51 @@
1
- require 'set'
2
- require 'uri'
3
- require 'net/https'
4
-
5
1
  require 'wankel'
6
2
  require 'cookie_store'
7
- require 'connection_pool'
8
3
 
9
4
  require 'active_support'
10
5
  require 'active_support/core_ext'
11
6
 
12
7
  require 'active_model'
13
8
 
9
+ require 'active_record'
10
+
11
+ require 'sunstone/connection'
14
12
  require 'sunstone/exception'
15
- require 'sunstone/schema'
16
- require 'sunstone/model'
17
- require 'sunstone/parser'
13
+ require 'ext/active_record/statement_cache'
14
+ require 'ext/active_record/relation'
15
+ require 'ext/active_record/associations/builder/has_and_belongs_to_many'
16
+
17
+ # require 'sunstone/parser'
18
18
 
19
- # _Sunstone_ is a low-level API. It provides basic HTTP #get, #post, #put, and
20
- # #delete calls to the Sunstone Server. It can also provides basic error
21
- # checking of responses.
22
19
  module Sunstone
23
20
  VERSION = 0.1
24
-
25
- extend self
26
-
27
- attr_reader :site
28
-
29
- # Set the User-Agent of the client. Will be joined with other User-Agent info
30
- attr_writer :user_agent
31
- attr_accessor :api_key, :host, :port, :use_ssl
32
-
33
- # Sets the Protocol, API Token, and Host and Port of the API Server
34
- #
35
- # #!ruby
36
- # Sunstone.site = "https://API_KEY@host.com"
37
- def site=(url)
38
- @site = url
39
- uri = URI.parse(url)
40
- @api_key = uri.user ? CGI.unescape(uri.user) : nil
41
- @host, @port = uri.host, uri.port
42
- @use_ssl = (uri.scheme == 'https')
43
- end
44
-
45
- # Returns the User-Agent of the client. Defaults to:
46
- # "sunstone-ruby/SUNSTONE_VERSION RUBY_VERSION-pPATCH_LEVEL PLATFORM"
47
- def user_agent
48
- [
49
- @user_agent,
50
- "Sunstone/#{Sunstone::VERSION}",
51
- "Ruby/#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}",
52
- RUBY_PLATFORM
53
- ].compact.join(' ')
54
- end
55
-
56
- # Set a cookie jar to use during request sent during the
57
- def with_cookie_store(store, &block)
58
- Thread.current[:sunstone_cookie_store] = store
59
- yield
60
- ensure
61
- Thread.current[:sunstone_cookie_store] = nil
62
- end
63
-
64
- # Sends a Net::HTTPRequest to the server. The headers returned from
65
- # Sunestone#headers are automatically added to the request. The appropriate
66
- # error is raised if the response is not in the 200..299 range.
67
- #
68
- # Paramaters::
69
- #
70
- # * +request+ - A Net::HTTPRequest to send to the server
71
- # * +body+ - Optional, a String, IO Object, or a Ruby object which is
72
- # converted into JSON and sent as the body
73
- # * +block+ - An optional block to call with the +Net::HTTPResponse+ object.
74
- #
75
- # Return Value::
76
- #
77
- # Returns the return value of the <tt>&block</tt> if given, otherwise the
78
- # response object (a Net::HTTPResponse)
79
- #
80
- # Examples:
81
- #
82
- # #!ruby
83
- # Sunstone.send_request(#<Net::HTTP::Get>) # => #<Net::HTTP::Response>
84
- #
85
- # Sunstone.send_request(#<Net::HTTP::Get @path="/404">) # => raises Sunstone::Exception::NotFound
86
- #
87
- # # this will still raise an exception if the response_code is not valid
88
- # # and the block will not be called
89
- # Sunstone.send_request(#<Net::HTTP::Get>) do |response|
90
- # # ...
91
- # end
92
- #
93
- # # The following example shows how to stream a response:
94
- # Sunstone.send_request(#<Net::HTTP::Get>) do |response|
95
- # response.read_body do |chunk|
96
- # io.write(chunk)
97
- # end
98
- # end
99
- def send_request(request, body=nil, &block)
100
- request_headers.each { |k, v| request[k] = v }
101
-
102
- if body.is_a?(IO)
103
- request['Transfer-Encoding'] = 'chunked'
104
- request.body_stream = body
105
- elsif body.is_a?(String)
106
- request.body = body
107
- elsif body
108
- request.body = Wankel.encode(body)
109
- end
110
-
111
- return_value = nil
112
- with_connection do |connection|
113
- connection.request(request) do |response|
114
-
115
- if response['X-42Floors-API-Version-Deprecated']
116
- logger.warn("DEPRECATION WARNING: API v#{API_VERSION} is being phased out")
117
- end
118
-
119
- validate_response_code(response)
120
-
121
- # Get the cookies
122
- response.each_header do |key, value|
123
- if key.downcase == 'set-cookie' && Thread.current[:sunstone_cookie_store]
124
- Thread.current[:sunstone_cookie_store].set_cookie("#{site}#{request.path}", value)
125
- end
126
- end
127
-
128
- if block_given?
129
- return_value =yield(response)
130
- else
131
- return_value =response
132
- end
133
- end
134
- end
135
-
136
- return_value
137
- end
138
-
139
- # Send a GET request to +path+ on the Sunstone Server via +Sunstone#send_request+.
140
- # See +Sunstone#send_request+ for more details on how the response is handled.
141
- #
142
- # Paramaters::
143
- #
144
- # * +path+ - The +path+ on the server to GET to.
145
- # * +params+ - Either a String, Hash, or Ruby Object that responds to
146
- # #to_param. Appended on the URL as query params
147
- # * +block+ - An optional block to call with the +Net::HTTPResponse+ object.
148
- #
149
- # Return Value::
150
- #
151
- # See +Sunstone#send_request+
152
- #
153
- # Examples:
154
- #
155
- # #!ruby
156
- # Sunstone.get('/example') # => #<Net::HTTP::Response>
157
- #
158
- # Sunstone.get('/example', 'query=stuff') # => #<Net::HTTP::Response>
159
- #
160
- # Sunstone.get('/example', {:query => 'stuff'}) # => #<Net::HTTP::Response>
161
- #
162
- # Sunstone.get('/404') # => raises Sunstone::Exception::NotFound
163
- #
164
- # Sunstone.get('/act') do |response|
165
- # # ...
166
- # end
167
- def get(path, params='', &block)
168
- params ||= ''
169
- request = Net::HTTP::Get.new(path + '?' + params.to_param)
170
-
171
- send_request(request, nil, &block)
172
- end
173
21
 
174
- # Send a POST request to +path+ on the Sunstone Server via +Sunstone#send_request+.
175
- # See +Sunstone#send_request+ for more details on how the response is handled.
176
- #
177
- # Paramaters::
178
- #
179
- # * +path+ - The +path+ on the server to POST to.
180
- # * +body+ - Optional, See +Sunstone#send_request+.
181
- # * +block+ - Optional, See +Sunstone#send_request+
182
- #
183
- # Return Value::
184
- #
185
- # See +Sunstone#send_request+
186
- #
187
- # Examples:
188
- #
189
- # #!ruby
190
- # Sunstone.post('/example') # => #<Net::HTTP::Response>
191
- #
192
- # Sunstone.post('/example', 'body') # => #<Net::HTTP::Response>
193
- #
194
- # Sunstone.post('/example', #<IO Object>) # => #<Net::HTTP::Response>
195
- #
196
- # Sunstone.post('/example', {:example => 'data'}) # => #<Net::HTTP::Response>
197
- #
198
- # Sunstone.post('/404') # => raises Sunstone::Exception::NotFound
199
- #
200
- # Sunstone.post('/act') do |response|
201
- # # ...
202
- # end
203
- def post(path, body=nil, &block)
204
- request = Net::HTTP::Post.new(path)
205
-
206
- send_request(request, body, &block)
207
- end
208
-
209
- # Send a PUT request to +path+ on the Sunstone Server via +Sunstone#send_request+.
210
- # See +Sunstone#send_request+ for more details on how the response is handled.
211
- #
212
- # Paramaters::
213
- #
214
- # * +path+ - The +path+ on the server to POST to.
215
- # * +body+ - Optional, See +Sunstone#send_request+.
216
- # * +block+ - Optional, See +Sunstone#send_request+
217
- #
218
- # Return Value::
219
- #
220
- # See +Sunstone#send_request+
221
- #
222
- # Examples:
223
- #
224
- # #!ruby
225
- # Sunstone.put('/example') # => #<Net::HTTP::Response>
226
- #
227
- # Sunstone.put('/example', 'body') # => #<Net::HTTP::Response>
228
- #
229
- # Sunstone.put('/example', #<IO Object>) # => #<Net::HTTP::Response>
230
- #
231
- # Sunstone.put('/example', {:example => 'data'}) # => #<Net::HTTP::Response>
232
- #
233
- # Sunstone.put('/404') # => raises Sunstone::Exception::NotFound
234
- #
235
- # Sunstone.put('/act') do |response|
236
- # # ...
237
- # end
238
- def put(path, body=nil, *valid_response_codes, &block)
239
- request = Net::HTTP::Put.new(path)
240
-
241
- send_request(request, body, &block)
242
- end
243
-
244
- # Send a DELETE request to +path+ on the Sunstone Server via +Sunstone#send_request+.
245
- # See +Sunstone#send_request+ for more details on how the response is handled
246
- #
247
- # Paramaters::
248
- #
249
- # * +path+ - The +path+ on the server to POST to.
250
- # * +block+ - Optional, See +Sunstone#send_request+
251
- #
252
- # Return Value::
253
- #
254
- # See +Sunstone#send_request+
255
- #
256
- # Examples:
257
- #
258
- # #!ruby
259
- # Sunstone.delete('/example') # => #<Net::HTTP::Response>
260
- #
261
- # Sunstone.delete('/404') # => raises Sunstone::Exception::NotFound
262
- #
263
- # Sunstone.delete('/act') do |response|
264
- # # ...
265
- # end
266
- def delete(path, &block)
267
- request = Net::HTTP::Delete.new(path)
268
-
269
- send_request(request, nil, &block)
270
- end
271
-
272
- # Get a connection from the connection pool and perform the block with
273
- # the connection
274
- def with_connection(&block)
275
- connection_pool.with({}, &block)
276
- end
277
-
278
- # Ping the Sunstone. If everything is configured and operating correctly
279
- # <tt>"pong"</tt> will be returned. Otherwise and Sunstone::Exception should be
280
- # thrown.
281
- #
282
- # #!ruby
283
- # Sunstone.ping # => "pong"
284
- #
285
- # Sunstone.ping # raises Sunstone::Exception::ServiceUnavailable if a 503 is returned
286
- def ping
287
- get('/ping').body
288
- end
289
-
290
- def config
291
- @config ||= Wankel.parse(get('/config').body, :symbolize_keys => true)
292
- end
293
-
294
- private
295
-
296
- def request_headers
297
- headers = {
298
- 'Content-Type' => 'application/json',
299
- 'User-Agent' => user_agent
300
- }
301
-
302
- headers['Api-Key'] = api_key if api_key
303
-
304
- headers
305
- end
306
-
307
- # Raise an Sunstone::Exception based on the response_code, unless the response_code
308
- # is include in the valid_response_codes Array
309
- #
310
- # Paramaters::
311
- #
312
- # * +response+ - The Net::HTTP::Response object
313
- #
314
- # Return Value::
315
- #
316
- # If an exception is not raised the +response+ is returned
317
- #
318
- # Examples:
319
- #
320
- # #!ruby
321
- # Sunstone.validate_response_code(<Net::HTTP::Response @code=200>) # => <Net::HTTP::Response @code=200>
322
- #
323
- # Sunstone.validate_response_code(<Net::HTTP::Response @code=404>) # => raises Sunstone::Exception::NotFound
324
- #
325
- # Sunstone.validate_response_code(<Net::HTTP::Response @code=500>) # => raises Sunstone::Exception
326
- def validate_response_code(response)
327
- code = response.code.to_i
328
-
329
- if !(200..299).include?(code)
330
- case code
331
- when 400
332
- raise Sunstone::Exception::BadRequest, response
333
- when 401
334
- raise Sunstone::Exception::Unauthorized, response
335
- when 404
336
- raise Sunstone::Exception::NotFound, response
337
- when 410
338
- raise Sunstone::Exception::Gone, response
339
- when 422
340
- raise Sunstone::Exception::ApiVersionUnsupported, response
341
- when 503
342
- raise Sunstone::Exception::ServiceUnavailable, response
343
- when 301
344
- raise Sunstone::Exception::MovedPermanently, response
345
- when 300..599
346
- raise Sunstone::Exception, response
347
- else
348
- raise Sunstone::Exception, response
349
- end
350
- end
351
- end
352
-
353
- def connection_pool
354
- @connection_pool ||= ConnectionPool.new(size: 5, timeout: 5) do
355
- connection = Net::HTTP.new(@host, @port)
356
- connection.use_ssl = @ssl
357
- connection
358
- end
359
- end
360
-
22
+ # TODO:
23
+ # # Set a cookie jar to use during request sent during the
24
+ # def with_cookie_store(store, &block)
25
+ # Thread.current[:sunstone_cookie_store] = store
26
+ # yield
27
+ # ensure
28
+ # Thread.current[:sunstone_cookie_store] = nil
29
+ # end
30
+ #
31
+ #
32
+ # # Get a connection from the connection pool and perform the block with
33
+ # # the connection
34
+ # def with_connection(&block)
35
+ # connection_pool.with({}, &block)
36
+ # end
37
+ #
38
+ # private
39
+ #
40
+ # def request_headers
41
+ # headers = {
42
+ # 'Content-Type' => 'application/json',
43
+ # 'User-Agent' => user_agent
44
+ # }
45
+ #
46
+ # headers['Api-Key'] = api_key if api_key
47
+ #
48
+ # headers
49
+ # end
50
+ #
361
51
  end