sunstone 0.1.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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