syncano 4.0.0.alpha1 → 4.0.0.alpha2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +8 -157
- data/circle.yml +1 -1
- data/lib/syncano.rb +38 -11
- data/lib/syncano/api.rb +20 -2
- data/lib/syncano/api/endpoints.rb +17 -0
- data/lib/syncano/connection.rb +46 -54
- data/lib/syncano/poller.rb +55 -0
- data/lib/syncano/resources.rb +48 -16
- data/lib/syncano/resources/base.rb +46 -56
- data/lib/syncano/resources/paths.rb +48 -0
- data/lib/syncano/resources/resource_invalid.rb +15 -0
- data/lib/syncano/response.rb +55 -0
- data/lib/syncano/schema.rb +10 -29
- data/lib/syncano/schema/attribute_definition.rb +2 -2
- data/lib/syncano/schema/endpoints_whitelist.rb +40 -0
- data/lib/syncano/schema/resource_definition.rb +5 -0
- data/lib/syncano/upload_io.rb +7 -0
- data/lib/syncano/version.rb +1 -1
- data/spec/integration/syncano_spec.rb +220 -15
- data/spec/spec_helper.rb +3 -1
- data/spec/unit/connection_spec.rb +34 -97
- data/spec/unit/resources/paths_spec.rb +21 -0
- data/spec/unit/resources_base_spec.rb +77 -16
- data/spec/unit/response_spec.rb +75 -0
- data/spec/unit/schema/resource_definition_spec.rb +10 -0
- data/spec/unit/schema_spec.rb +5 -55
- data/syncano.gemspec +4 -0
- metadata +69 -13
- data/lib/active_attr/dirty.rb +0 -26
- data/lib/active_attr/typecasting/hash_typecaster.rb +0 -34
- data/lib/active_attr/typecasting_override.rb +0 -29
- data/lib/syncano/model/associations.rb +0 -121
- data/lib/syncano/model/associations/base.rb +0 -38
- data/lib/syncano/model/associations/belongs_to.rb +0 -30
- data/lib/syncano/model/associations/has_many.rb +0 -75
- data/lib/syncano/model/associations/has_one.rb +0 -22
- data/lib/syncano/model/base.rb +0 -257
- data/lib/syncano/model/callbacks.rb +0 -49
- data/lib/syncano/model/scope_builder.rb +0 -158
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bd053f463d602ab8927be7cd6f1da7080cd50e03
|
4
|
+
data.tar.gz: 672affd48e958f2387e1e01a3b74e5a8324a95d2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 35fc5c588f6b5d90a0b8cd318bf9be218a432985cd7703fe0ab98d1f4c9d01838685e2b137f5e39e3bbd6424f72de3f611e654f01c95d31b3b6c86e0f569f8d9
|
7
|
+
data.tar.gz: 7b4373ec897884764c0a70c0ea1a03504eb72d8841dad4f64653b01a47c4c2b1186a9bf16b74b16d3ca067ffbb679d3602b12321a22fdff84a6c8a0f3033f3ba
|
data/README.md
CHANGED
@@ -1,167 +1,18 @@
|
|
1
|
-
# Syncano 4.0
|
1
|
+
# Syncano 4.0 Ruby Gem
|
2
2
|
|
3
|
+
## Ruby QuickStart Guide
|
4
|
+
---
|
3
5
|
|
4
|
-
|
6
|
+
Syncano ruby gem provides communication with Syncano platform - ([www.syncano.io](http://www.syncano.io/?utm_source=github&utm_medium=readme&utm_campaign=syncano-ruby))
|
5
7
|
|
6
|
-
|
8
|
+
You can find quick start on installing and using Syncano's Ruby library in our [documentation](http://docs.syncano.io/docs/ruby/?utm_source=github&utm_medium=readme&utm_campaign=syncano-js).
|
7
9
|
|
8
|
-
|
9
|
-
$ gem install syncano --pre
|
10
|
-
```
|
10
|
+
For more detailed information on how to use Syncano and its features - our [Developer Manual](http://docs.syncano.io/docs/getting-started-with-syncano/?utm_source=github&utm_medium=readme&utm_campaign=syncano-js) should be very helpful.
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
After installation, you have to set a path for api root for syncano.
|
15
|
-
|
16
|
-
If you want to use staging, export:
|
17
|
-
|
18
|
-
```bash
|
19
|
-
$ export API_ROOT=https://api.syncano.rocks
|
20
|
-
```
|
21
|
-
|
22
|
-
If you're less adventurous, use our production api servers:
|
23
|
-
|
24
|
-
```bash
|
25
|
-
$ export=API_ROOT=https://api.syncano.io
|
26
|
-
```
|
27
|
-
|
28
|
-
Ok, now we can start coding!
|
29
|
-
|
30
|
-
```ruby
|
31
|
-
require 'syncano'
|
32
|
-
|
33
|
-
syncano = Syncano.connect(api_key: 'your-api-key')
|
34
|
-
|
35
|
-
syncano.instances.all.each { |instance| puts instance }
|
36
|
-
```
|
37
|
-
|
38
|
-
You can either pass your API key to the connect method as above or set
|
39
|
-
`ENV['SYNCANO_API_KEY']` and call just `Syncano.connect`.
|
40
|
-
|
41
|
-
## API basics
|
42
|
-
|
43
|
-
Syncano API is a nested API - all the endpointes are scoped by an instances, ex.
|
44
|
-
codeboxes path is `/instance/your_instance_name/codeboxes/`. Syncano instances
|
45
|
-
is more less a schema is in relation databases. **Your instance name must be
|
46
|
-
unique across all existing Syncano instnaces, not only limitted to your account.**
|
47
|
-
|
48
|
-
|
49
|
-
# Instances
|
50
|
-
|
51
|
-
In order to do anything with Syncano, you have to create an instances. Choose a
|
52
|
-
globally unique name and call:
|
53
|
-
|
54
|
-
```ruby
|
55
|
-
instances = syncano.instances.create name: 'my_instances_name'
|
56
|
-
instance.first
|
57
|
-
|
58
|
-
#=> #<Syncano::Resources::Instance created_at: Sun, 26 Apr 2015 18:09:46 +0000, description: "", metadata: {}, name: "my_instance_name", owner: nil, role: "full", updated_at: Sun, 26 Apr 2015 18:09:46 +0000>
|
59
|
-
```
|
60
|
-
|
61
|
-
# Classes and objects
|
62
|
-
|
63
|
-
In order to save objects in Syncano, first you need to create a class. A class
|
64
|
-
defines objects' attributes in the class' schema. The attribute definition has two
|
65
|
-
mandatory (`name`, `type`) and two optional fields (`filter_index`, `order_index`).
|
66
|
-
What these fields are for is rather obvious - `name` defines objects' attribute
|
67
|
-
name, and `type` defines type (you can read more about available types in the
|
68
|
-
[API docs](http://docs.syncano.com/v0.1/docs/instancesinstanceclasses-2)). `*_index`
|
69
|
-
fields are indexing. `order_index` allows you order returned collections,
|
70
|
-
`filter_index` allows filtering in a various ways. There will be a few examples
|
71
|
-
in this README, but you can read in the
|
72
|
-
[API docs](http://docs.syncano.com/v0.1/docs/filtering-data-objects).
|
73
|
-
|
74
|
-
```ruby
|
75
|
-
stock = instance.classes.create name: 'stock_items',
|
76
|
-
schema: [{ name: 'name', type: 'string',
|
77
|
-
filter_index: true },
|
78
|
-
{ name: 'amount', type: 'integer',
|
79
|
-
filter_index: true,
|
80
|
-
order_index: true }]
|
81
|
-
```
|
82
|
-
|
83
|
-
Once we have a class, we can start creating objects.
|
84
|
-
|
85
|
-
```ruby
|
86
|
-
chorizo = stock.objects.create name: 'Chorizo', amount: 100
|
87
|
-
black_pudding = stock.objects.create name: 'Black pudding', amount: 200
|
88
|
-
curry_wurts = stock.objects.create name: 'Curry wurst', amount: 150
|
89
|
-
kabanos = stock.objects.create name: 'Kabanos'
|
90
|
-
something = stock.objects.create amount: 3
|
91
|
-
```
|
92
|
-
|
93
|
-
Now we have a few items in stock, let's try filtering.
|
94
|
-
|
95
|
-
```ruby
|
96
|
-
stock.objects.all(order_by: '-amount', query: { amount: { _lte: 150 }, name: { _exists: true } })
|
97
|
-
#=> #<Syncano::Resources::Collection:0x007fc18b9c7698 @next=false, @prev=false, @collection=[#<Syncano::Resources::Object amount: 150, channel: nil, channel_room: nil, created_at: Mon, 27 Apr 2015 05:21:31 +0000, group: nil, group_permissions: "none", id: 12, name: "Curry wurst", other_permissions: "none", owner: nil, owner_permissions: "none", revision: 1, updated_at: Mon, 27 Apr 2015 05:21:31 +0000>, #<Syncano::Resources::Object amount: 100, channel: nil, channel_room: nil, created_at: Mon, 27 Apr 2015 05:21:30 +0000, group: nil, group_permissions: "none", id: 10, name: "Chorizo", other_permissions: "none", owner: nil, owner_permissions: "none", revision: 1, updated_at: Mon, 27 Apr 2015 05:21:30 +0000>]>
|
98
|
-
```
|
99
|
-
|
100
|
-
Let's give `something` a name and try again.
|
101
|
-
|
102
|
-
```ruby
|
103
|
-
something.name = 'Unidentified sausage'
|
104
|
-
something.save
|
105
|
-
|
106
|
-
stock.objects.all(order_by: '-amount', query: { amount: { _lte: 150 }, name: { _exists: true } })
|
107
|
-
#=> #<Syncano::Resources::Collection:0x007fc18d58a628 @next=false, @prev=false, @collection=[#<Syncano::Resources::Object amount: 150, channel: nil, channel_room: nil, created_at: Mon, 27 Apr 2015 05:21:31 +0000, group: nil, group_permissions: \"none\", id: 12, name: \"Curry wurst\", other_permissions: \"none\", owner: nil, owner_permissions: \"none\", revision: 1, updated_at: Mon, 27 Apr 2015 05:21:31 +0000>, #<Syncano::Resources::Object amount: 100, channel: nil, channel_room: nil, created_at: Mon, 27 Apr 2015 05:21:30 +0000, group: nil, group_permissions: \"none\", id: 10, name: \"Chorizo\", other_permissions: \"none\", owner: nil, owner_permissions: \"none\", revision: 1, updated_at: Mon, 27 Apr 2015 05:21:30 +0000>, #<Syncano::Resources::Object amount: 3, channel: nil, channel_room: nil, created_at: Mon, 27 Apr 2015 05:30:18 +0000, group: nil, group_permissions: \"none\", id: 15, name: \"Unidentified sausage\", other_permissions: \"none\", owner: nil, owner_permissions: \"none\", revision: 2, updated_at: Mon, 27 Apr 2015 05:30:48 +0000>]>
|
108
|
-
```
|
109
|
-
|
110
|
-
Now it matches the query and appears in the result.
|
111
|
-
|
112
|
-
# Codeboxes
|
113
|
-
|
114
|
-
Codeboxes are small pieces of code that run on Syncano servers. You can run them
|
115
|
-
manually using the API, you can create a schedule to run them periodically, you
|
116
|
-
can create a Webhook (and optionally make it public) to run them from the web,
|
117
|
-
you can create a trigger to run one after a class' object is created, updated or
|
118
|
-
deleted. There are three runtimes available: Ruby, Python and Node. This gem is
|
119
|
-
available in Ruby runtime (just needs to be required). Let's create a simple
|
120
|
-
codebox and run it.
|
121
|
-
|
122
|
-
```ruby
|
123
|
-
clock = instance.codeboxes.create(name: 'clock', source: 'puts Time.now', runtime_name: 'ruby')
|
124
|
-
#=> #<Syncano::Resources::CodeBox config: {}, created_at: Thu, 30 Apr 2015 05:50:09 +0000, description: "", id: 1, name: "clock", runtime_name: "ruby", source: "puts Time.now", updated_at: Thu, 30 Apr 2015 05:50:09 +0000>
|
125
|
-
clock.run
|
126
|
-
#=> {"status"=>"pending", "links"=>{"self"=>"gv1/instances/a523b7e842dea927d8c306ec0a9a7a4ac30191c2cd034b11d/codeboxes/1/traces/1/"}, "executed_at"=>nil, "result"=>"", "duration"=>nil, "id"=>1}
|
127
|
-
```
|
128
|
-
|
129
|
-
When you schedule a codebox run, it returns the trace. Immediately after the
|
130
|
-
call it's status is pending, so you need to check the trace.
|
131
|
-
|
132
|
-
```ruby
|
133
|
-
clock.traces.first
|
134
|
-
=> #<Syncano::Resources::CodeBoxTrace duration: 526, executed_at: Thu, 30 Apr 2015 05:25:14 +0000, id: 1, result: "2015-04-30 05:25:14 +0000", status: "success">
|
135
|
-
```
|
136
|
-
|
137
|
-
The run method is asynchronous and returns immediately. You should use this to
|
138
|
-
run codeboxes when you don't care about results at this very moment. If you
|
139
|
-
want to run the codebox and get results in one call, you should use webhooks.
|
140
|
-
|
141
|
-
# Webhooks
|
142
|
-
|
143
|
-
You can use webhooks to run codeboxes synchronously. Webhooks can be either
|
144
|
-
public or private. You have to provide your API key when calling private ones,
|
145
|
-
public are public, you can call them with curl, connect with third party
|
146
|
-
services, etc. Ruby:
|
147
|
-
|
148
|
-
|
149
|
-
```ruby
|
150
|
-
webhook = @instance.webhooks.create slug: 'clock-webhook', codebox: clock.primary_key, public: true
|
151
|
-
#=> #<Syncano::Resources::Webhook codebox: 1, public: true, public_link: "a20b0ae122b53b2f2c445f6a7a202b274c3631ad", slug: "clock-webhook">
|
152
|
-
|
153
|
-
webhook.run['result']
|
154
|
-
#=> "2015-04-30 05:51:45 +0000"
|
155
|
-
```
|
156
|
-
|
157
|
-
and curl
|
158
|
-
|
159
|
-
```bash
|
160
|
-
$ curl "https://api.syncano.rocks/v1/instances//af248d3e8b92e6e7aaa42dfc41de80c66c90d620cbe3fcd19/webhooks/p/a20b0ae122b53b2f2c445f6a7a202b274c3631ad/"
|
161
|
-
{"status": "success", "duration": 270, "result": "2015-04-30 06:11:08 +0000", "executed_at": "2015-04-30T06:11:08.607389Z"}
|
162
|
-
```
|
12
|
+
In case you need help working with the library - email us at libraries@syncano.com - we will be happy to help!
|
163
13
|
|
164
14
|
## Contributing
|
15
|
+
---
|
165
16
|
|
166
17
|
1. Fork it
|
167
18
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
data/circle.yml
CHANGED
data/lib/syncano.rb
CHANGED
@@ -1,30 +1,42 @@
|
|
1
1
|
$: << Dir.pwd
|
2
2
|
|
3
|
-
require '
|
4
|
-
require '
|
5
|
-
require '
|
6
|
-
require 'active_attr/typecasting_override'
|
7
|
-
require 'active_support/core_ext/hash/indifferent_access'
|
3
|
+
require 'active_attr'
|
4
|
+
require 'active_model'
|
5
|
+
require 'active_support/concern'
|
8
6
|
require 'active_support/core_ext/class/attribute.rb'
|
7
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
9
8
|
require 'active_support/inflector'
|
9
|
+
require 'celluloid/future'
|
10
|
+
require 'celluloid/io'
|
11
|
+
require 'faraday'
|
12
|
+
require 'http'
|
13
|
+
require 'yaml'
|
14
|
+
require 'mimetype_fu'
|
15
|
+
|
16
|
+
require 'syncano/query_builder'
|
10
17
|
require 'syncano/version'
|
11
18
|
require 'syncano/api'
|
19
|
+
require 'syncano/api/endpoints'
|
12
20
|
require 'syncano/connection'
|
13
21
|
require 'syncano/schema'
|
14
22
|
require 'syncano/scope'
|
23
|
+
require 'syncano/poller'
|
15
24
|
require 'syncano/resources'
|
16
25
|
require 'syncano/resources/base'
|
17
26
|
require 'syncano/resources/collection'
|
27
|
+
require 'syncano/resources/paths'
|
28
|
+
require 'syncano/resources/resource_invalid'
|
18
29
|
require 'syncano/resources/space'
|
30
|
+
require 'syncano/response'
|
31
|
+
require 'syncano/upload_io'
|
19
32
|
require 'syncano/query_builder'
|
20
|
-
require 'syncano/model/base'
|
21
33
|
|
22
34
|
module Syncano
|
23
35
|
class << self
|
24
36
|
def connect(options = {})
|
25
37
|
connection = Connection.new(
|
26
38
|
options.reverse_merge(api_key: ENV['SYNCANO_API_KEY']))
|
27
|
-
connection.authenticate
|
39
|
+
connection.authenticate unless connection.authenticated?
|
28
40
|
|
29
41
|
API.new connection
|
30
42
|
end
|
@@ -35,6 +47,23 @@ module Syncano
|
|
35
47
|
class RuntimeError < StandardError; end
|
36
48
|
|
37
49
|
class HTTPError < StandardError
|
50
|
+
end
|
51
|
+
|
52
|
+
class NotFound < HTTPError
|
53
|
+
attr_accessor :path, :method_name
|
54
|
+
|
55
|
+
def initialize(path, method_name)
|
56
|
+
self.path = path
|
57
|
+
self.method_name = method_name
|
58
|
+
end
|
59
|
+
|
60
|
+
def inspect
|
61
|
+
%{#{self.class.name} path: "#{path}" method: "#{method_name}"}
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class HTTPErrorWithBody < HTTPError
|
66
|
+
|
38
67
|
attr_accessor :body, :original_response
|
39
68
|
|
40
69
|
def initialize(body, original_response)
|
@@ -49,8 +78,8 @@ module Syncano
|
|
49
78
|
alias :to_s :inspect
|
50
79
|
end
|
51
80
|
|
52
|
-
class ClientError <
|
53
|
-
class ServerError <
|
81
|
+
class ClientError < HTTPErrorWithBody; end
|
82
|
+
class ServerError < HTTPErrorWithBody; end
|
54
83
|
|
55
84
|
class UnsupportedStatusError < StandardError
|
56
85
|
attr_accessor :original_response
|
@@ -62,7 +91,5 @@ module Syncano
|
|
62
91
|
def inspect
|
63
92
|
"The server returned unsupported status code #{original_response.status}"
|
64
93
|
end
|
65
|
-
|
66
|
-
alias :to_s :inspect
|
67
94
|
end
|
68
95
|
end
|
data/lib/syncano/api.rb
CHANGED
@@ -1,13 +1,31 @@
|
|
1
1
|
module Syncano
|
2
2
|
class API
|
3
|
+
|
3
4
|
def initialize(connection)
|
4
5
|
self.connection = connection
|
5
|
-
|
6
|
-
|
6
|
+
|
7
|
+
self.class.initialize(connection) unless self.class.initialized?
|
8
|
+
end
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def initialize(connection)
|
12
|
+
endpoints = Schema::EndpointsWhitelist.new(Schema.new(connection))
|
13
|
+
|
14
|
+
resources_definitions = Resources.build_definitions(endpoints)
|
15
|
+
|
16
|
+
include Syncano::API::Endpoints.definition(resources_definitions)
|
17
|
+
|
18
|
+
self.initialized = true
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialized?
|
22
|
+
initialized
|
23
|
+
end
|
7
24
|
end
|
8
25
|
|
9
26
|
private
|
10
27
|
|
11
28
|
attr_accessor :connection
|
29
|
+
cattr_accessor :initialized
|
12
30
|
end
|
13
31
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Syncano
|
2
|
+
class API
|
3
|
+
module Endpoints
|
4
|
+
def self.definition(resources_definition)
|
5
|
+
Module.new do
|
6
|
+
resources_definition.each do |resource_definition|
|
7
|
+
resource_class = ::Syncano::Resources.define_resource_class(resource_definition)
|
8
|
+
|
9
|
+
define_method(resource_definition.name.tableize) do
|
10
|
+
::Syncano::QueryBuilder.new(connection, resource_class)
|
11
|
+
end if resource_definition.top_level?
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/syncano/connection.rb
CHANGED
@@ -6,89 +6,81 @@ module Syncano
|
|
6
6
|
AUTH_PATH = 'account/auth/'
|
7
7
|
METHODS = Set.new [:get, :post, :put, :delete, :head, :patch, :options]
|
8
8
|
|
9
|
-
|
10
|
-
|
9
|
+
attr_accessor :api_key
|
10
|
+
attr_accessor :user_key
|
11
|
+
|
12
|
+
class << self
|
13
|
+
def api_root
|
14
|
+
ENV['API_ROOT']
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def http_fetcher
|
19
|
+
HttpFetcher.new api_key, user_key
|
11
20
|
end
|
12
21
|
|
13
22
|
def initialize(options = {})
|
14
23
|
self.api_key = options[:api_key]
|
15
24
|
self.email = options[:email]
|
16
25
|
self.password = options[:password]
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
26
|
+
self.user_key = options[:user_key]
|
27
|
+
self.conn = Faraday.new(self.class.api_root) do |faraday|
|
28
|
+
faraday.path_prefix = API_VERSION
|
29
|
+
faraday.request :multipart
|
30
|
+
faraday.request :url_encoded
|
31
|
+
faraday.adapter Faraday.default_adapter
|
32
|
+
end
|
22
33
|
end
|
23
34
|
|
24
35
|
def authenticated?
|
25
36
|
!api_key.nil?
|
26
37
|
end
|
27
38
|
|
28
|
-
def authenticate
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
def authenticate!
|
35
|
-
response = conn.post(AUTH_PATH, email: email, password: password)
|
36
|
-
body = parse_response(response)
|
37
|
-
|
38
|
-
case response
|
39
|
-
when Status.successful
|
40
|
-
self.api_key = body['account_key']
|
41
|
-
when Status.client_error
|
42
|
-
raise ClientError.new(body, response)
|
43
|
-
end
|
39
|
+
def authenticate
|
40
|
+
api_key = request(:post, AUTH_PATH,
|
41
|
+
email: email,
|
42
|
+
password: password)['account_key']
|
43
|
+
self.api_key = api_key
|
44
44
|
end
|
45
45
|
|
46
46
|
def request(method, path, params = {})
|
47
47
|
raise %{Unsupported method "#{method}"} unless METHODS.include? method
|
48
|
-
|
48
|
+
|
49
|
+
conn.headers['X-API-KEY'] = api_key if api_key
|
50
|
+
conn.headers['X-USER-KEY'] = user_key if user_key
|
49
51
|
conn.headers['User-Agent'] = "Syncano Ruby Gem #{Syncano::VERSION}"
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
when Status.successful
|
55
|
-
parse_response response
|
56
|
-
when Status.client_error # TODO figure out if we want to raise an exception on not found or not
|
57
|
-
raise ClientError.new(response.body, response)
|
58
|
-
when Status.server_error
|
59
|
-
raise ServerError.new(response.body, response)
|
60
|
-
else
|
61
|
-
raise UnsupportedStatusError.new(response)
|
62
|
-
end
|
52
|
+
|
53
|
+
raw_response = conn.send(method, path, params)
|
54
|
+
|
55
|
+
Syncano::Response.handle ResponseWrapper.new(raw_response)
|
63
56
|
end
|
64
57
|
|
65
58
|
private
|
66
59
|
|
67
|
-
|
68
|
-
|
69
|
-
|
60
|
+
class ResponseWrapper < BasicObject
|
61
|
+
def initialize(response)
|
62
|
+
@response = response
|
63
|
+
end
|
70
64
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
->(response) { (200...300).include? response.status }
|
75
|
-
end
|
65
|
+
def method_missing(name, *args, &block)
|
66
|
+
@response.__send__(name, *args, &block)
|
67
|
+
end
|
76
68
|
|
77
|
-
|
78
|
-
|
79
|
-
|
69
|
+
def status
|
70
|
+
Status.new @response.status
|
71
|
+
end
|
80
72
|
|
81
|
-
|
82
|
-
|
83
|
-
|
73
|
+
private
|
74
|
+
|
75
|
+
class Status
|
76
|
+
attr_accessor :code
|
84
77
|
|
85
|
-
def
|
86
|
-
|
78
|
+
def initialize(code)
|
79
|
+
self.code = code
|
87
80
|
end
|
88
81
|
end
|
89
82
|
end
|
90
83
|
|
91
|
-
attr_accessor :api_key
|
92
84
|
attr_accessor :api_root
|
93
85
|
attr_accessor :email
|
94
86
|
attr_accessor :password
|