sagamore-client 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +4 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +6 -0
  5. data/.yardopts +1 -0
  6. data/Gemfile +14 -0
  7. data/Gemfile.lock +66 -0
  8. data/LICENSE +21 -0
  9. data/README.md +252 -0
  10. data/Rakefile +16 -0
  11. data/lib/sagamore-client.rb +1 -0
  12. data/lib/sagamore/client.rb +263 -0
  13. data/lib/sagamore/client/body.rb +12 -0
  14. data/lib/sagamore/client/collection.rb +108 -0
  15. data/lib/sagamore/client/errors.rb +17 -0
  16. data/lib/sagamore/client/resource.rb +205 -0
  17. data/lib/sagamore/client/response.rb +85 -0
  18. data/lib/sagamore/client/uri_ext.rb +51 -0
  19. data/sagamore-client.gemspec +20 -0
  20. data/spec/sagamore/client/body_spec.rb +32 -0
  21. data/spec/sagamore/client/resource_spec.rb +250 -0
  22. data/spec/sagamore/client/response_spec.rb +100 -0
  23. data/spec/sagamore/client/uri_ext_spec.rb +51 -0
  24. data/spec/sagamore/client_spec.rb +214 -0
  25. data/spec/shared_client_context.rb +5 -0
  26. data/spec/spec_helper.rb +12 -0
  27. data/vendor/cache/addressable-2.2.8.gem +0 -0
  28. data/vendor/cache/coderay-1.0.7.gem +0 -0
  29. data/vendor/cache/coveralls-0.6.9.gem +0 -0
  30. data/vendor/cache/crack-0.3.1.gem +0 -0
  31. data/vendor/cache/diff-lcs-1.1.3.gem +0 -0
  32. data/vendor/cache/hashie-1.2.0.gem +0 -0
  33. data/vendor/cache/json-1.7.4.gem +0 -0
  34. data/vendor/cache/method_source-0.8.gem +0 -0
  35. data/vendor/cache/mime-types-1.25.gem +0 -0
  36. data/vendor/cache/multi_json-1.8.0.gem +0 -0
  37. data/vendor/cache/patron-0.4.18.gem +0 -0
  38. data/vendor/cache/pry-0.9.10.gem +0 -0
  39. data/vendor/cache/rake-0.9.2.2.gem +0 -0
  40. data/vendor/cache/rest-client-1.6.7.gem +0 -0
  41. data/vendor/cache/rspec-2.11.0.gem +0 -0
  42. data/vendor/cache/rspec-core-2.11.1.gem +0 -0
  43. data/vendor/cache/rspec-expectations-2.11.2.gem +0 -0
  44. data/vendor/cache/rspec-mocks-2.11.1.gem +0 -0
  45. data/vendor/cache/simplecov-0.7.1.gem +0 -0
  46. data/vendor/cache/simplecov-html-0.7.1.gem +0 -0
  47. data/vendor/cache/slop-3.3.2.gem +0 -0
  48. data/vendor/cache/term-ansicolor-1.2.2.gem +0 -0
  49. data/vendor/cache/thor-0.18.1.gem +0 -0
  50. data/vendor/cache/tins-0.9.0.gem +0 -0
  51. data/vendor/cache/webmock-1.8.8.gem +0 -0
  52. metadata +148 -0
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MGUyNmI0ZTlhZWUxNzNkN2Q1NGJlYjcyOGZjNWQ5ODMxMjEyZDgyMQ==
5
+ data.tar.gz: !binary |-
6
+ NDM0ZmQzMzRjNTg3ODdiMmZkMWRkMTVlYzFjNGQyNzUxY2QwYWY3MQ==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ ZWIwY2U4MGQyY2JiNGI2ZTg5OTI2ZDUxOTdmMmE5Mjc3Y2VmYTE5YjJjOGJj
10
+ YWJiNmQ3Y2ZhZGJlYzY5OWQ5NThhN2NmOGY2NGJkYzU2YWVmYWU5MjZiY2Q4
11
+ YTc0ZThmM2VjMmU5YzQ3ZjU3MjIyMWViNjRmYjJjY2JkMTJiOTU=
12
+ data.tar.gz: !binary |-
13
+ MzZjMDA5OWU0YzA4MGM5N2VjYWVjNGQ5ZDQ3NDEyODhkNDlmMGIyMTJhZGMy
14
+ YzgzNjJlM2QyNjU0MGMwMTk1ODMxNzMwNTk1ZmRlYzViNTRkOWZiOGZlNWNh
15
+ YzJhNjJiNTZhMWUxYzI2ODAwNmMxY2E4MmEyYmQxYjMyMGVkNDg=
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ pkg
2
+ .yardoc
3
+ doc
4
+ coverage
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ bundler_args: --without development
3
+ rvm:
4
+ - 1.9.3
5
+ - 2.0.0
6
+ - ruby-head
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --title "Sagamore Client"
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ group :test do
6
+ gem 'rake'
7
+ gem 'rspec', '~> 2.11'
8
+ gem 'webmock', :require => false
9
+ gem 'coveralls', :require => false
10
+ end
11
+
12
+ group :development do
13
+ gem 'pry'
14
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,66 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ sagamore-client (3.0.1)
5
+ addressable (~> 2.2.8)
6
+ hashie
7
+ json (~> 1.7.4)
8
+ patron (~> 0.4.18)
9
+
10
+ GEM
11
+ remote: https://rubygems.org/
12
+ specs:
13
+ addressable (2.2.8)
14
+ coderay (1.0.7)
15
+ coveralls (0.6.9)
16
+ multi_json (~> 1.3)
17
+ rest-client
18
+ simplecov (>= 0.7)
19
+ term-ansicolor
20
+ thor
21
+ crack (0.3.1)
22
+ diff-lcs (1.1.3)
23
+ hashie (1.2.0)
24
+ json (1.7.4)
25
+ method_source (0.8)
26
+ mime-types (1.25)
27
+ multi_json (1.8.0)
28
+ patron (0.4.18)
29
+ pry (0.9.10)
30
+ coderay (~> 1.0.5)
31
+ method_source (~> 0.8)
32
+ slop (~> 3.3.1)
33
+ rake (0.9.2.2)
34
+ rest-client (1.6.7)
35
+ mime-types (>= 1.16)
36
+ rspec (2.11.0)
37
+ rspec-core (~> 2.11.0)
38
+ rspec-expectations (~> 2.11.0)
39
+ rspec-mocks (~> 2.11.0)
40
+ rspec-core (2.11.1)
41
+ rspec-expectations (2.11.2)
42
+ diff-lcs (~> 1.1.3)
43
+ rspec-mocks (2.11.1)
44
+ simplecov (0.7.1)
45
+ multi_json (~> 1.0)
46
+ simplecov-html (~> 0.7.1)
47
+ simplecov-html (0.7.1)
48
+ slop (3.3.2)
49
+ term-ansicolor (1.2.2)
50
+ tins (~> 0.8)
51
+ thor (0.18.1)
52
+ tins (0.9.0)
53
+ webmock (1.8.8)
54
+ addressable (~> 2.2.8)
55
+ crack (>= 0.1.7)
56
+
57
+ PLATFORMS
58
+ ruby
59
+
60
+ DEPENDENCIES
61
+ coveralls
62
+ pry
63
+ rake
64
+ rspec (~> 2.11)
65
+ sagamore-client!
66
+ webmock
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 Sagamore Retail
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,252 @@
1
+ # Sagamore Retail Client
2
+
3
+ [![Build Status](https://travis-ci.org/sagamore/sagamore-client-rb.png)](https://travis-ci.org/sagamore/sagamore-client-rb)
4
+ [![Code Climate](https://codeclimate.com/github/sagamore/sagamore-client-rb.png)](https://codeclimate.com/github/sagamore/sagamore-client-rb)
5
+ [![Coverage Status](https://coveralls.io/repos/sagamore/sagamore-client-rb/badge.png)](https://coveralls.io/r/sagamore/sagamore-client-rb)
6
+ [![Dependency Status](https://gemnasium.com/sagamore/sagamore-client-rb.png)](https://gemnasium.com/sagamore/sagamore-client-rb)
7
+
8
+ This is the [Sagamore Retail](http://sagamoreretail.com/) (a point-of-sale/retail management system) client library for Ruby. It provides access to the Sagamore Retail HTTP API.
9
+
10
+ It is a wrapper around the [Patron](http://toland.github.com/patron/) HTTP client library. Supports MRI 1.9+.
11
+
12
+ You can find [documentation here](http://rdoc.info/github/sagamore/sagamore-client-rb).
13
+
14
+ ## Installation
15
+
16
+ You need a recent version of libcurl and a sane build environment.
17
+
18
+ Debian/Ubuntu:
19
+
20
+ ```
21
+ sudo apt-get install build-essential libcurl4-openssl-dev
22
+ gem install sagamore-client
23
+ ```
24
+
25
+ ## Connecting
26
+
27
+ ```ruby
28
+ sagamore = Sagamore::Client.new 'http://example.sagamore.us/api'
29
+ sagamore.auth :username => 'user', :password => 'secret'
30
+ ```
31
+
32
+ ## Resource oriented
33
+
34
+ ```ruby
35
+ resource = sagamore[:items][1234]
36
+ response = resource.get
37
+ response = resource.delete
38
+
39
+ # Query string generation:
40
+ resource1 = sagamore[:items]
41
+ resource2 = resource.query(:key1 => 'val1', 'key with spaces' => 'val with spaces')
42
+ resource2.uri.to_s
43
+ # => "/items?key%20with%20spaces=val%20with%20spaces&key1=val1"
44
+ ```
45
+
46
+ ## URI oriented
47
+
48
+ ```ruby
49
+ response = sagamore.get '/items/1234'
50
+ response = sagamore.delete '/items/1234'
51
+ item_count = sagamore.count '/items'
52
+ ```
53
+
54
+ ## Collection Resources
55
+
56
+ ### Enumerable
57
+ Resources include Ruby's Enumerable module for easy iteration over collections:
58
+
59
+ ```ruby
60
+ sagamore[:items].each do |item|
61
+ puts item['description']
62
+ end
63
+
64
+ item_count = sagamore[:items].count
65
+
66
+ usernames = sagamore[:users].map {|user| user['login']}
67
+ ```
68
+
69
+ ### Filtering
70
+ Resources have a `filter` method that support's Sagamore's advanced filter syntax:
71
+
72
+ ```ruby
73
+ active_users = sagamore[:users].filter(:active => true)
74
+ active_users.each do |user|
75
+ # do something with each active user
76
+ end
77
+
78
+ # filter returns a new resource which allows for chaining:
79
+ items = sagamore[:items]
80
+ active_items = items.filter(:active => true)
81
+ active_items.filter(:price => {'$gt' => 10}).each do |item|
82
+ # ...
83
+ end
84
+
85
+ # filtering custom fields:
86
+ sagamore[:items].filter('custom@size'=> 'XL')
87
+ ```
88
+
89
+ ### Sorting
90
+ Resources have a `sort` method that accepts any number of sort options. Note that each call to sort overwrites any previous sorts.
91
+
92
+ ```ruby
93
+ resource.sort(:id, :price)
94
+ resource.sort('created_at,desc')
95
+
96
+ # returns a new resource for chaining:
97
+ resource.sort(:description, :created_at).filter(:active => true).each do |item|
98
+ # ...
99
+ end
100
+ ```
101
+
102
+ ### Creating Resources
103
+
104
+ Create a new resource via POST:
105
+
106
+ ```ruby
107
+ collection = client[:items]
108
+ response = collection.post! :description => 'Some New Item'
109
+ response.status_line
110
+ # => "HTTP/1.1 201 Created"
111
+
112
+ # To fetch the newly created resource:
113
+ new_item_response = response.resource.get!
114
+ new_item_response[:description]
115
+ # => "Some New Item"
116
+ ```
117
+
118
+ ### Embedding Related Resources
119
+
120
+ Use the `embed` method to include the contents of related resource in the response body of each item in the collection:
121
+
122
+ ```ruby
123
+ collection = client[:sales][:orders].embed(:customer, :location)
124
+ collection.first.to_hash
125
+ # => {
126
+ "id" => 1,
127
+ "customer_id" => 2,
128
+ "customer" => {
129
+ # customer data
130
+ },
131
+ "location_id" => 3,
132
+ "location" => {
133
+ # location data
134
+ }
135
+ }
136
+ ```
137
+
138
+ The `embed` method accepts one or more arguments as symbols or strings. It supports chaining and will merge the results of multiple calls.
139
+
140
+ ### Looping while results exist
141
+
142
+ Issuing deletes while iterating over a collection resource can cause the pagination to shift resulting in unexpected behavior. Use `while_results` when you want to:
143
+
144
+ * Consume messages from a queue, deleting each message after it has been processed.
145
+ * Delete all resources in a collection that doesn't support a top-level DELETE method.
146
+
147
+ For example:
148
+
149
+ ```ruby
150
+ collection = client[:system][:messages]
151
+ collection.while_results do |message|
152
+ # process message here...
153
+ collection[message['id']].delete!
154
+ end
155
+ ```
156
+
157
+ ## Request body
158
+
159
+ If the request body is a Hash, it will automatically be serialized as JSON. Otherwise, it is
160
+ passed through untouched:
161
+
162
+ ```ruby
163
+ # this:
164
+ sagamore[:some_collection].post :a => 1, :b => 2
165
+
166
+ # is equivalent to this:
167
+ sagamore[:some_collection].post '{"a":1,"b":2}'
168
+ ```
169
+
170
+ ## Response
171
+
172
+ ```ruby
173
+ response = sagamore[:items][1].get
174
+
175
+ response.status # Response status code as an Integer
176
+ response.success? # true/false depending on whether 'status' indicates non-error
177
+ response.body # Returns a Sagamore::Client::Body object (see below)
178
+ response.raw_body # Returns the raw response body as a string
179
+ response[:some_key] # Returns the corresponding key from 'body'
180
+ response.headers # Response headers as a Hash
181
+ response.resource # Returns a Resource if the response included a "Location" header, else nil
182
+ ```
183
+
184
+ ### Response Body
185
+
186
+ Given the following JSON response from the server:
187
+
188
+ ```javascript
189
+ {
190
+ "id": 1234,
191
+ "custom": {
192
+ "color": "Blue"
193
+ }
194
+ }
195
+ ```
196
+
197
+ Here are the various ways you can access the data:
198
+
199
+ ```ruby
200
+ body = response.body
201
+
202
+ # Symbols and strings can be used interchangeably for keys
203
+ body[:id]
204
+ # => 1234
205
+
206
+ body[:custom][:color]
207
+ # => "Blue"
208
+
209
+ body['custom']['color']
210
+ # => "Blue"
211
+
212
+ body.to_hash
213
+ # => {"id"=>1234, "custom"=>{"color"=>"Blue"}}
214
+
215
+ response.raw_body
216
+ # => "{\"id\":1234,\"custom\":{\"color\":\"Blue\"}}"
217
+ ```
218
+
219
+ ## Bang variants
220
+
221
+ All HTTP request methods have a bang variant that raises an exception on failure:
222
+
223
+ ```ruby
224
+ response = sagamore[:i_dont_exist].get
225
+ response.status
226
+ # => 404
227
+
228
+ sagamore[:i_dont_exist].get!
229
+ # Raises Sagamore::Client::RequestFailed exception
230
+
231
+ # To access the response from the exception:
232
+ begin
233
+ sagamore[:i_dont_exist].get!
234
+ rescue Sagamore::Client::RequestFailed => error
235
+ puts error.response.status
236
+ end
237
+ # => 404
238
+
239
+ ```
240
+
241
+ ## Debugging
242
+
243
+ ```ruby
244
+ # Log request/response trace to stdout
245
+ client.debug = true
246
+
247
+ # Or, log to a file
248
+ client.debug = '/path/to/file.log'
249
+
250
+ # Same values can be passed via :debug option to client constructor
251
+ client = Sagamore::Client.new '<url>', :debug => true
252
+ ```
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ require 'bundler/setup'
2
+ Bundler.require(:default, :development)
3
+
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task :default => :spec
9
+
10
+ desc "Start a console with a Sagamore::Client instance"
11
+ task :console do
12
+ require 'sagamore/client'
13
+ CLIENT = Sagamore::Client.new(ENV['URI'])
14
+ CLIENT.auth :username => ENV['USER'], :password => ENV['PASSWORD']
15
+ Pry.start
16
+ end
@@ -0,0 +1 @@
1
+ require 'sagamore/client'
@@ -0,0 +1,263 @@
1
+ require 'rubygems'
2
+ require 'patron'
3
+ require 'addressable/uri'
4
+ require 'json'
5
+
6
+ require 'sagamore/client/errors'
7
+
8
+ ##
9
+ # Sagamore namespace
10
+ module Sagamore
11
+ ##
12
+ # The main point of interaction for the Sagamore Client library.
13
+ #
14
+ # Client code must successfully authenticate with the API via the {#auth}
15
+ # method before calling any HTTP methods or the API will return authorization
16
+ # errors.
17
+ #
18
+ # Provides direct access to the URI-oriented interface via the HTTP methods.
19
+ # Provides access to the URI-oriented interface via the {#[]} method.
20
+ class Client
21
+ ##
22
+ # Alias for {Addressable::URI}
23
+ URI = Addressable::URI
24
+
25
+ ##
26
+ # Default number of records per page when iterating over collection resources
27
+ DEFAULT_PER_PAGE = 20
28
+
29
+ ##
30
+ # Default request timeout in seconds
31
+ DEFAULT_TIMEOUT = 60
32
+
33
+ ##
34
+ # Default connection timeout in seconds
35
+ DEFAULT_CONNECT_TIMEOUT = 10
36
+
37
+ ##
38
+ # @return [Addressable::URI] The client's base URI
39
+ attr_reader :base_uri
40
+
41
+ ##
42
+ # @param [String] base_uri Base URI
43
+ # @option opts [Boolean, String] :debug Pass true to debug to stdout. Pass a String to debug to given filename.
44
+ # @option opts [Boolean] :insecure Disable SSL certificate verification
45
+ def initialize(base_uri, opts={})
46
+ @base_uri = URI.parse(base_uri)
47
+ configure_session(base_uri, opts)
48
+ end
49
+
50
+ ##
51
+ # Returns the underlying Patron session
52
+ #
53
+ # @see http://patron.rubyforge.org/Patron/Session.html Patron::Session docs
54
+ #
55
+ # @return [Patron::Session]
56
+ def session
57
+ @session ||= Patron::Session.new
58
+ end
59
+
60
+ ##
61
+ # Set to true to enable debugging to STDOUT or a string to write to the file
62
+ # at that path.
63
+ #
64
+ # @param [String, Boolean] debug
65
+ #
66
+ # @return [String, Boolean] The debug argument
67
+ def debug=(debug)
68
+ session.enable_debug(debug == true ? nil : debug)
69
+ end
70
+
71
+ ##
72
+ # Passes the given credentials to the server, storing the session token on success.
73
+ #
74
+ # @raise [AuthFailed] If the credentials were invalid or the server returned an error
75
+ #
76
+ # @return [true]
77
+ #
78
+ # @option opts [String] :username Sagamore username
79
+ # @option opts [String] :password Sagamore password
80
+ def auth(opts={})
81
+ unless opts[:username] && opts[:password]
82
+ raise "Must specify :username and :password"
83
+ end
84
+ body = URI.form_encode \
85
+ :auth_key => opts[:username],
86
+ :password => opts[:password]
87
+ response = post '/auth/identity/callback', body,
88
+ 'Content-Type' => 'application/x-www-form-urlencoded'
89
+ response.success? or raise AuthFailed, "Sagamore auth failed"
90
+ end
91
+
92
+ ##
93
+ # Performs a HEAD request against the given URI and returns the {Response}.
94
+ #
95
+ # @return [Response]
96
+ def head(uri, headers=false); make_request(:head, uri, headers); end
97
+
98
+ ##
99
+ # Performs a HEAD request against the given URI. Returns the {Response}
100
+ # on success and raises a {RequestFailed} on failure.
101
+ #
102
+ # @raise [RequestFailed] On error response
103
+ #
104
+ # @return [Response]
105
+ def head!(uri, headers=false); raise_on_fail head(uri, headers); end
106
+
107
+ ##
108
+ # Performs a GET request against the given URI and returns the {Response}.
109
+ #
110
+ # @return [Response]
111
+ def get(uri, headers=false); make_request(:get, uri, headers); end
112
+
113
+ ##
114
+ # Performs a GET request against the given URI. Returns the {Response}
115
+ # on success and raises a {RequestFailed} on failure.
116
+ #
117
+ # @raise [RequestFailed] On error response
118
+ #
119
+ # @return [Response]
120
+ def get!(uri, headers=false); raise_on_fail get(uri, headers); end
121
+
122
+ ##
123
+ # Performs a DELETE request against the given URI and returns the {Response}.
124
+ #
125
+ # @return [Response]
126
+ def delete(uri, headers=false); make_request(:delete, uri, headers); end
127
+
128
+ ##
129
+ # Performs a DELETE request against the given URI. Returns the {Response}
130
+ # on success and raises a {RequestFailed} on failure.
131
+ #
132
+ # @raise [RequestFailed] On error response
133
+ #
134
+ # @return [Response]
135
+ def delete!(uri, headers=false); raise_on_fail delete(uri, headers); end
136
+
137
+ ##
138
+ # Performs a PUT request against the given URI and returns the {Response}.
139
+ #
140
+ # @return [Response]
141
+ def put(uri, body, headers=false); make_request(:put, uri, headers, body); end
142
+
143
+ ##
144
+ # Performs a PUT request against the given URI. Returns the {Response}
145
+ # on success and raises a {RequestFailed} on failure.
146
+ #
147
+ # @raise [RequestFailed] On error response
148
+ #
149
+ # @return [Response]
150
+ def put!(uri, body, headers=false); raise_on_fail put(uri, body, headers); end
151
+
152
+ ##
153
+ # Performs a POST request against the given URI and returns the {Response}.
154
+ #
155
+ # @return [Response]
156
+ def post(uri, body, headers=false); make_request(:post, uri, headers, body); end
157
+
158
+ ##
159
+ # Performs a POST request against the given URI. Returns the {Response}
160
+ # on success and raises a {RequestFailed} on failure.
161
+ #
162
+ # @raise [RequestFailed] On error response
163
+ #
164
+ # @return [Response]
165
+ def post!(uri, body, headers=false); raise_on_fail post(uri, body, headers); end
166
+
167
+ ##
168
+ # Returns a Resource for the given URI path.
169
+ #
170
+ # @return [Resource]
171
+ def [](uri)
172
+ Resource.new(self, uri)
173
+ end
174
+
175
+ ##
176
+ # Iterates over each page of subordinate resources of the given collection
177
+ # resource URI and yields the {Response} to the block.
178
+ def each_page(uri)
179
+ uri = URI.parse(uri)
180
+ total_pages = nil
181
+ page = 1
182
+ uri.query_values = {'per_page' => DEFAULT_PER_PAGE}.merge(uri.query_values || {})
183
+ while total_pages.nil? or page <= total_pages
184
+ uri.merge_query_values! 'page' => page
185
+ response = get!(uri)
186
+ yield response
187
+ total_pages ||= response['pages']
188
+ page += 1
189
+ end
190
+ end
191
+
192
+ ##
193
+ # Iterates over each subordinate resource of the given collection resource
194
+ # URI and yields its representation to the given block.
195
+ def each(uri)
196
+ each_page(uri) do |page|
197
+ page['results'].each do |result|
198
+ yield result
199
+ end
200
+ end
201
+ end
202
+
203
+ ##
204
+ # Returns a count of subordinate resources of the given collection resource
205
+ # URI.
206
+ #
207
+ # @param [#to_s] uri
208
+ # @raise [RequestFailed] If the GET fails
209
+ # @return [Integer] The subordinate resource count
210
+ def count(uri)
211
+ uri = URI.parse(uri)
212
+ uri.merge_query_values! 'page' => 1, 'per_page' => 1
213
+ get!(uri)['total']
214
+ end
215
+
216
+ private
217
+
218
+ def prepare_request_body(body)
219
+ body.is_a?(Hash) ? JSON.dump(body) : body
220
+ end
221
+
222
+ def make_request(method, uri, headers=false, body=false)
223
+ args = [prepare_uri(uri).to_s]
224
+ args.push prepare_request_body(body) unless body === false
225
+ args.push headers unless headers === false
226
+ new_response session.__send__(method, *args)
227
+ end
228
+
229
+ def raise_on_fail(response)
230
+ if !response.success?
231
+ error = RequestFailed.new "Request failed with status: #{response.status_line}"
232
+ error.response = response
233
+ raise error
234
+ end
235
+ response
236
+ end
237
+
238
+ def prepare_uri(uri)
239
+ uri = URI.parse(uri)
240
+ uri.path = uri.path.gsub(/^#{base_uri.path}/, '')
241
+ uri
242
+ end
243
+
244
+ def new_response(patron_response)
245
+ Response.new patron_response, self
246
+ end
247
+
248
+ def configure_session(base_url, opts)
249
+ session.base_url = base_url
250
+ session.headers['Content-Type'] = 'application/json'
251
+ session.handle_cookies
252
+ session.insecure = opts[:insecure] if opts.has_key?(:insecure)
253
+ session.timeout = DEFAULT_TIMEOUT
254
+ session.connect_timeout = DEFAULT_CONNECT_TIMEOUT
255
+ self.debug = opts[:debug] if opts.has_key?(:debug)
256
+ end
257
+ end
258
+ end
259
+
260
+ require 'sagamore/client/resource'
261
+ require 'sagamore/client/response'
262
+ require 'sagamore/client/body'
263
+ require 'sagamore/client/uri_ext'