orchestrate 0.5

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d745f6432de35d38a03090bad7e072c5050f2660
4
+ data.tar.gz: b48814208a0b00a4c4d80bad1ea1e0a7d9c3e51c
5
+ SHA512:
6
+ metadata.gz: 99d1b6e4fffed62524458cd0862fc71d0d6f3d6cdc2187ce8882007159bbddfda38cac003f304ffb53e75fa8fceda1a8fe389963c6ff50448db84f796c112e16
7
+ data.tar.gz: c5f59656dc41f5b4eab6f9840a838b69938e20ac50d9d3c8cf44d0e337a946de98e4290260dd2e64b19c43b56351a86e0dc59515035f6554f2673e2d555a9419
data/.gitignore ADDED
@@ -0,0 +1,20 @@
1
+ *.gem
2
+ *.lock
3
+ *.rbc
4
+ *.log
5
+ .bundle
6
+ .config
7
+ coverage
8
+ InstalledFiles
9
+ lib/bundler/man
10
+ pkg
11
+ rdoc
12
+ spec/reports
13
+ test/tmp
14
+ test/version_tmp
15
+ tmp
16
+
17
+ # YARD artifacts
18
+ .yardoc
19
+ _yardoc
20
+ doc/
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.1
4
+ - 2.1.0
5
+ - 2.0.0
6
+ - 1.9.3
7
+ before_install: gem install bundler -v 1.6.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in orchestrate-rails.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 jimcar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ 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, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,115 @@
1
+ Orchestrate API for Ruby
2
+ ========================
3
+ [![Build Status](https://travis-ci.org/orchestrate-io/orchestrate-ruby.png?branch=master)](https://travis-ci.org/orchestrate-io/orchestrate-ruby)
4
+
5
+ Ruby client interface for the [Orchestrate.io](http://orchestrate.io) REST API.
6
+
7
+ [rDoc Documentation](http://rdoc.info/github/orchestrate-io/orchestrate-ruby/master/frames)
8
+
9
+ ## Getting Started
10
+
11
+ Provide your API key:
12
+
13
+ ``` ruby
14
+ Orchestrate::Configuration.api_key = '8ce391a...'
15
+ ```
16
+
17
+ Create a client:
18
+
19
+ ``` ruby
20
+ client = Orchestrate::Client.new
21
+ ```
22
+
23
+ and start making requests:
24
+
25
+ ``` ruby
26
+ client.list(:my_collection)
27
+ ```
28
+
29
+ ## Swapping out the HTTP backend
30
+
31
+ This gem uses [Faraday][] for its HTTP needs -- and Faraday allows you to change the underlying HTTP client used. It defaults to `Net::HTTP` but if you wanted to use [Typhoeus][] or [EventMachine HTTP][em-http], doing so would be easy. Alternate Faraday backends enable using callbacks or parallel request support.
32
+
33
+ In your Orchestrate configuration, simply provide a `faraday` key with a block that will be called with the `Faraday::Connection` object. You may decorate it with middleware or change the adapter as described in the Faraday README. Examples are below.
34
+
35
+ You may use Faraday's `test` adapter to stub out calls to the Orchestrate API in your tests. See `tests/test_helper.rb` and the tests in `tests/orchestrate/api/*_test.rb` for examples.
36
+
37
+ [Faraday]: https://github.com/lostisland/faraday/
38
+ [Typhoeus]: https://github.com/typhoeus/typhoeus#readme
39
+ [em-http]: https://github.com/igrigorik/em-http-request#readme
40
+
41
+ ### Parallel HTTP requests
42
+
43
+ If you're using a Faraday backend that enables parallelization, such as Typhoeus, EM-HTTP-Request, or EM-Synchrony you can use `Orchestrate::Client#in_parallel` to fire off multiple requests at once. If your Faraday backend does not support this, the method will still work as expected, but Faraday will output a warning to STDERR and the requests will be performed in series.
44
+
45
+ ``` ruby
46
+ client = Orchestrate::Client.new
47
+
48
+ responses = client.in_parallel do |r|
49
+ r[:list] = client.list(:my_collection)
50
+ r[:user] = client.get(:users, current_user_id)
51
+ r[:user_events] = client.list_events(:users, current_user_id, :notices)
52
+ end
53
+ # will return when all requests have completed
54
+
55
+ responses[:user] = #<Faraday::Response:0x00...>
56
+ ```
57
+
58
+ ### Callback Support
59
+
60
+ If you're using a Faraday backend that enables callbacks, such as EM-HTTP-Request or EM-Synchrony, you may use the callback interface to designate actions to perform when the request completes.
61
+
62
+ ``` ruby
63
+ client = Orchestrate::Client.new
64
+ response = client.list(:my_collection)
65
+ response.finished? # false
66
+ response.on_complete do
67
+ # do stuff with the response as normal
68
+ end
69
+ ```
70
+
71
+ If the Faraday backend adapter does not support callbacks, the block provided will be executed when `Orchestrate::Client#on_complete` is called.
72
+
73
+
74
+ ### Using with Typhoeus
75
+
76
+ Typhoeus is backed by libcurl and enables parallelization.
77
+
78
+ ``` ruby
79
+ require 'typhoeus'
80
+ require 'typhoeus/adapters/faraday'
81
+
82
+ Orchestrate.configure do |config|
83
+ config.faraday = {|f| f.adapter :typhoeus }
84
+ config.api_key = "my_api_key"
85
+ end
86
+ ```
87
+
88
+ ### Using with EM-HTTP-Request
89
+
90
+ EM-HTTP-Request is an HTTP client for Event Machine. It enables callback support and parallelization.
91
+
92
+
93
+ ``` ruby
94
+ require 'em-http-request'
95
+
96
+ Orchestrate.configure do |config|
97
+ config.faraday = {|f| f.adapter :em_http }
98
+ config.api_key = "my_api_key"
99
+ end
100
+ ```
101
+
102
+ ### Using with EM-Syncrony
103
+
104
+ EM-Synchrony is a collection of utility classes for EventMachine to help untangle evented code. It enables parallelization.
105
+
106
+ ``` ruby
107
+ require 'em-synchrony'
108
+
109
+ Orchestrate.configure do |config|
110
+ config.faraday = {|f| f.adapter :em_synchrony }
111
+ config.api_key = "my_api_key"
112
+ end
113
+ ```
114
+
115
+
data/Rakefile ADDED
@@ -0,0 +1,20 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+ require "rdoc/task"
4
+
5
+ task default: :test
6
+
7
+ Rake::TestTask.new do |test|
8
+ test.libs << "test"
9
+ test.test_files = FileList["test/**/*_test.rb"]
10
+ test.verbose = true
11
+ end
12
+
13
+ Rake::RDocTask.new(rdoc: "doc", clobber_rdoc: "doc:clean", rerdoc: "doc:force") do |rdoc|
14
+ rdoc.main = "README.md"
15
+ rdoc.title = "Orchestrate API Documentation"
16
+ rdoc.options << "--line-numbers"
17
+ rdoc.rdoc_files.include("README.md", "lib/**/*.rb")
18
+ rdoc.rdoc_dir = "doc"
19
+ end
20
+
@@ -0,0 +1,46 @@
1
+ require "orchestrate/api"
2
+ require "orchestrate/client"
3
+ require "orchestrate/configuration"
4
+ require "orchestrate/helpers"
5
+ require "orchestrate/version"
6
+
7
+ #
8
+ # A library for supporting connections to the \Orchestrate API.
9
+ #
10
+ module Orchestrate
11
+
12
+ # Configuration ------------------------------------------------------------
13
+
14
+ class << self
15
+
16
+ #
17
+ # An instance of Configuration containing the current library
18
+ # configuration options.
19
+ #
20
+ attr_accessor :config
21
+
22
+ #
23
+ # Lazily initialize and return the Configuration instance.
24
+ #
25
+ def config # :nodoc:
26
+ @config ||= Configuration.new
27
+ end
28
+
29
+ #
30
+ # Configure the Orchestrate library. This method should be called during
31
+ # application or script initialization. At a bare minimum, the Orchestrate
32
+ # library will require that an API key is provided.
33
+ #
34
+ # ==== Example
35
+ #
36
+ # Orchestrate.configure do |config|
37
+ # config.api_key = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
38
+ # end
39
+ #
40
+ def configure
41
+ yield config
42
+ end
43
+
44
+ end
45
+
46
+ end
@@ -0,0 +1,15 @@
1
+ require "orchestrate"
2
+ require "orchestrate/api/extensions"
3
+
4
+ module Orchestrate
5
+
6
+ #
7
+ # Ruby gem +orchestrate-api+ provides an interface to the
8
+ # [Orchestrate](http://orchestrate.io) API.
9
+ #
10
+ # The Client class is used to setup the client and make HTTP requests.
11
+ #
12
+ module API
13
+ end
14
+
15
+ end
@@ -0,0 +1,57 @@
1
+ module Orchestrate::API
2
+
3
+ # Not used. But it does contain nice summary of orchestrate.io api errors.
4
+ #
5
+ module Error
6
+ def errors
7
+ @@errors ||= [
8
+ { :status => 400,
9
+ :code => :api_bad_request,
10
+ :desc => 'The API request is malformed.'
11
+ },
12
+ { :status => 500,
13
+ :code => :security_authentication,
14
+ :desc => 'An error occurred while trying to authenticate.'
15
+ },
16
+ { :status => 401,
17
+ :code => :security_unauthorized,
18
+ :desc => 'Valid credentials are required.'
19
+ },
20
+ { :status => 400,
21
+ :code => :search_param_invalid,
22
+ :desc => 'A provided search query param is invalid.'
23
+ },
24
+ { :status => 500,
25
+ :code => :search_index_not_found,
26
+ :desc => 'Index could not be queried for this application.'
27
+ },
28
+ { :status => 500,
29
+ :code => :internal_error,
30
+ :desc => 'Internal Error.'
31
+ },
32
+ { :status => 404,
33
+ :code => :items_not_found,
34
+ :desc => 'The requested items could not be found.'
35
+ },
36
+ { :status => 412,
37
+ :code => :item_version_mismatch,
38
+ :desc => 'The version of the item does not match.'
39
+ },
40
+ { :status => 412,
41
+ :code => :item_already_present,
42
+ :desc => 'The item is already present.'
43
+ },
44
+ { :status => 400,
45
+ :code => :item_ref_malformed,
46
+ :desc => 'The provided Item Ref is malformed.'
47
+ },
48
+ { :status => 409,
49
+ :code => :indexing_conflict,
50
+ :desc => 'The item has been stored but conflicts were detected ' +
51
+ 'when indexing. Conflicting fields have not been indexed.'
52
+ },
53
+ ]
54
+ end
55
+ end
56
+
57
+ end
@@ -0,0 +1,14 @@
1
+ module Orchestrate::API
2
+
3
+ # Implement the blank? helpers here, instead of requiring active_support/core_ext.
4
+ class ::Object
5
+ def blank?
6
+ respond_to?(:empty?) ? empty? : !self
7
+ end
8
+
9
+ def present?
10
+ !blank?
11
+ end
12
+ end
13
+
14
+ end
@@ -0,0 +1,410 @@
1
+ require 'faraday'
2
+ require 'faraday_middleware'
3
+
4
+ module Orchestrate
5
+
6
+ # ==== Ruby Client for the Orchestrate REST *API*.
7
+ #
8
+ # The primary entry point is the #send_request method, which generates a
9
+ # Request, and returns the Response to the caller.
10
+ #
11
+ class Client
12
+
13
+ # Orchestrate::Configuration instance for the client. If not explicitly
14
+ # provided during initialization, will default to Orchestrate.config
15
+ attr_accessor :config
16
+
17
+ # The Faraday HTTP "connection" for the client.
18
+ attr_accessor :http
19
+
20
+ # Initialize and return a new Client instance. Optionally, configure
21
+ # options for the instance by passing a Configuration object. If no
22
+ # custom configuration is provided, the configuration options from
23
+ # Orchestrate.config will be used.
24
+ def initialize(config = Orchestrate.config)
25
+ @config = config
26
+
27
+ @http = Faraday.new(config.base_url) do |faraday|
28
+ if config.faraday.respond_to?(:call)
29
+ config.faraday.call(faraday)
30
+ else
31
+ faraday.adapter Faraday.default_adapter
32
+ end
33
+
34
+ # faraday seems to want you do specify these twice.
35
+ faraday.request :basic_auth, config.api_key, ''
36
+ faraday.basic_auth config.api_key, ''
37
+
38
+ # parses JSON responses
39
+ faraday.response :json, :content_type => /\bjson$/
40
+ end
41
+ end
42
+
43
+ # -------------------------------------------------------------------------
44
+ # collection
45
+
46
+ # call-seq:
47
+ # client.list(collection_name, parameters = {}) -> response
48
+ #
49
+ # Performes a List query against the given collection. Results are sorted
50
+ # lexicographically by key name.
51
+ #
52
+ # +collection_name+:: A String or Symbol representing the name of the collection.
53
+ # +parameters+:: a Hash object containing query parameters:
54
+ # - +:limit+ - integer, number of results to return. Defaults to 10, Max 100.
55
+ # - +:start+ - string, start key of query range, including value.
56
+ # - +:after+ - string, start key of query range, excluding value.
57
+ # - +:before+ - string, end key of query range, excluding value.
58
+ # - +:end+ - string, end key of query range, including value.
59
+ #
60
+ # Note, you cannot provide *both* 'start' and 'after', or 'before' and 'end'
61
+ #
62
+ def list(collection, options={})
63
+ Orchestrate::Helpers.range_keys!('key', options)
64
+ send_request :get, [collection], { query: options }
65
+ end
66
+
67
+ # call-seq:
68
+ # client.list(collection_name, query, parameters = {}) -> response
69
+ #
70
+ # Performs a Search query against the given collection.
71
+ #
72
+ # +collection_name+:: a String or Symbol representing the name of the collection.
73
+ # +query+:: a String containing a Lucene query
74
+ # +parameters+:: a Hash object containing additional parameters:
75
+ # - +limit+: - integer, number of results to return. Defaults to 10, Max 100.
76
+ # - +offset+: - ingeger, the starting position of the results. Defaults to 0.
77
+ #
78
+ def search(collection, query, parameters={})
79
+ send_request :get, [collection], { query: parameters.merge({query: query})}
80
+ end
81
+
82
+ # call-seq:
83
+ # client.delete_collection(collection_name) -> response
84
+ #
85
+ # Deletes the given collection.
86
+ #
87
+ # +collection_name+:: a String or Symbol representing the name of the collection.
88
+ #
89
+ def delete_collection(collection)
90
+ send_request :delete, [collection], { query: {force:true} }
91
+ end
92
+
93
+ # -------------------------------------------------------------------------
94
+ # Key/Value
95
+
96
+ # call-seq:
97
+ # client.get(collection_name, key) -> response
98
+ # client.get(collection_name, key, ref) -> response
99
+ #
100
+ # Retreieves a value assigned to a key.
101
+ #
102
+ # +collection_name+:: a String or Symbol representing the name of the collection.
103
+ # +key+:: a String or Symbol representing the key for the value.
104
+ # +ref+:: if given, returns the value for the key at the specified ref. If omitted, returns the latest value for the key.
105
+ #
106
+ def get(collection, key, ref=nil)
107
+ if ref
108
+ send_request :get, [collection, key, 'refs', ref]
109
+ else
110
+ send_request :get, [collection, key]
111
+ end
112
+ end
113
+
114
+ # call-seq:
115
+ # client.put(collection_name, key, body) -> response
116
+ # client.put(collection_name, key, body, condition) -> response
117
+ #
118
+ # Creates or Updates the value at the specified key.
119
+ #
120
+ # +collection_name+:: a String or Symbol representing the name of the collection.
121
+ # +key+:: a String or Symbol representing the key for the value.
122
+ # +body+:: a Hash object representing the value for the key.
123
+ # +condition+::
124
+ # - +nil+ - the value for the specified key will be updated regardless.
125
+ # - String - used as 'If-Match'. The value will only be updated if the Key's current Value's Ref matches the given condition.
126
+ # - false - used as 'If-None-Match'. The value will only be created for the key if the key currently has no value.
127
+ #
128
+ def put(collection, key, body, condition=nil)
129
+ headers={}
130
+ if condition.is_a?(String)
131
+ headers['If-Match'] = format_ref(condition)
132
+ elsif condition == false
133
+ headers['If-None-Match'] = '*'
134
+ end
135
+ send_request :put, [collection, key], { body: body, headers: headers }
136
+ end
137
+ alias :put_if_unmodified :put
138
+
139
+ # call-seq:
140
+ # client.put_if_absent(collection_name, key, body) -> response
141
+ #
142
+ # Will create the value at the specified key only if there is no existing value for the specified key.
143
+ #
144
+ def put_if_absent(collection, key, body)
145
+ put collection, key, body, false
146
+ end
147
+
148
+ # call-seq:
149
+ # client.delete(collection_name, key) -> response
150
+ # client.delete(collection_name, key, ref) -> response
151
+ #
152
+ # Deletes the value of the specified key. Historical values for given refs are still available.
153
+ #
154
+ # +collection_name+:: a String or Symbol representing the name of the collection.
155
+ # +key+:: a String or Symbol representing the key for the value.
156
+ # +ref+:: if provided, used as 'If-Match', will only delete the key if the current value is the provided ref.
157
+ #
158
+ def delete(collection, key, ref=nil)
159
+ headers = {}
160
+ headers['If-Match'] = format_ref(ref) if ref
161
+ send_request :delete, [collection, key], { headers: headers }
162
+ end
163
+
164
+ # call-seq:
165
+ # client.purge(collection_name, key) -> response
166
+ #
167
+ # Deletes the value for the specified key as well as all historical values. Cannot be undone.
168
+ #
169
+ # +collection_name+:: a String or Symbol representing the name of the collection.
170
+ # +key+:: a String or Symbol representing the key for the value.
171
+ #
172
+ def purge(collection, key)
173
+ send_request :delete, [collection, key], { query: { purge: true } }
174
+ end
175
+
176
+ # -------------------------------------------------------------------------
177
+ # Events
178
+
179
+ # call-seq:
180
+ # client.get_event(collection_name, key, event_type, timestamp, ordinal) -> response
181
+ #
182
+ # Gets the event for the specified arguments.
183
+ #
184
+ # +collection_name+:: a String or Symbol representing the name of the collection.
185
+ # +key+:: a String or Symbol representing the key for the value.
186
+ # +event_type+:: a String or Symbol representing the category for the event.
187
+ # +timestamp+:: an Integer or String representing a time.
188
+ # - Integers are Milliseconds since Unix Epoch.
189
+ # - Strings must be formatted as per http://orchestrate.io/docs/api/#events/timestamps
190
+ # - A future version will support ruby Time objects.
191
+ # +ordinal+:: an Integer representing the order of the event for this timestamp.
192
+ #
193
+ def get_event(collection, key, event_type, timestamp, ordinal)
194
+ send_request :get, [collection, key, 'events', event_type, timestamp, ordinal]
195
+ end
196
+
197
+ # call-seq:
198
+ # client.post_event(collection_name, key, event_type) -> response
199
+ # client.post_event(collection_name, key, event_type, timestamp) -> response
200
+ #
201
+ # Creates an event.
202
+ #
203
+ # +collection_name+:: a String or Symbol representing the name of the collection.
204
+ # +key+:: a String or Symbol representing the key for the value.
205
+ # +event_type+:: a String or Symbol representing the category for the event.
206
+ # +body+:: a Hash object representing the value for the event.
207
+ # +timestamp+:: an Integer or String representing a time.
208
+ # - nil - Timestamp value will be created by Orchestrate.
209
+ # - Integers are Milliseconds since Unix Epoch.
210
+ # - Strings must be formatted as per http://orchestrate.io/docs/api/#events/timestamps
211
+ # - A future version will support ruby Time objects.
212
+ #
213
+ def post_event(collection, key, event_type, body, timestamp=nil)
214
+ path = [collection, key, 'events', event_type, timestamp].compact
215
+ send_request :post, path, { body: body }
216
+ end
217
+
218
+ # call-seq:
219
+ # client.put_event(collection_name, key, event_type, timestamp, ordinal, body) -> response
220
+ # client.put_event(collection_name, key, event_type, timestamp, ordinal, body, ref) -> response
221
+ #
222
+ # Puts the event for the specified arguments.
223
+ #
224
+ # +collection_name+:: a String or Symbol representing the name of the collection.
225
+ # +key+:: a String or Symbol representing the key for the value.
226
+ # +event_type+:: a String or Symbol representing the category for the event.
227
+ # +timestamp+:: an Integer or String representing a time.
228
+ # - Integers are Milliseconds since Unix Epoch.
229
+ # - Strings must be formatted as per http://orchestrate.io/docs/api/#events/timestamps
230
+ # - A future version will support ruby Time objects.
231
+ # +ordinal+:: an Integer representing the order of the event for this timestamp.
232
+ # +body+:: a Hash object representing the value for the event.
233
+ # +ref+::
234
+ # - +nil+ - The event will update regardless.
235
+ # - String - used as 'If-Match'. The event will only update if the event's current value matches this ref.
236
+ #
237
+ def put_event(collection, key, event_type, timestamp, ordinal, body, ref=nil)
238
+ path = [collection, key, 'events', event_type, timestamp, ordinal]
239
+ headers = {}
240
+ headers['If-Match'] = format_ref(ref) if ref
241
+ send_request :put, path, { body: body, headers: headers }
242
+ end
243
+
244
+ # call-seq:
245
+ # client.purge_event(collection, key, event_type, timestamp, ordinal) -> response
246
+ # client.purge_event(collection, key, event_type, timestamp, ordinal, ref) -> response
247
+ #
248
+ # Deletes the event for the specified arguments.
249
+ #
250
+ # +collection_name+:: a String or Symbol representing the name of the collection.
251
+ # +key+:: a String or Symbol representing the key for the value.
252
+ # +event_type+:: a String or Symbol representing the category for the event.
253
+ # +timestamp+:: an Integer or String representing a time.
254
+ # - Integers are Milliseconds since Unix Epoch.
255
+ # - Strings must be formatted as per http://orchestrate.io/docs/api/#events/timestamps
256
+ # - A future version will support ruby Time objects.
257
+ # +ordinal+:: an Integer representing the order of the event for this timestamp.
258
+ # +ref+::
259
+ # - +nil+ - The event will be deleted regardless.
260
+ # - String - used as 'If-Match'. The event will only be deleted if the event's current value matches this ref.
261
+ #
262
+ def purge_event(collection, key, event_type, timestamp, ordinal, ref=nil)
263
+ path = [collection, key, 'events', event_type, timestamp, ordinal]
264
+ headers = {}
265
+ headers['If-Match'] = format_ref(ref) if ref
266
+ send_request :delete, path, { query: { purge: true }, headers: headers }
267
+ end
268
+
269
+ # call-seq:
270
+ # client.list_events(collection_name, key, event_type) -> response
271
+ # client.list_events(collection_name, key, event_type, parameters = {}) -> response
272
+ #
273
+ # Puts the event for the specified arguments.
274
+ #
275
+ # +collection_name+:: a String or Symbol representing the name of the collection.
276
+ # +key+:: a String or Symbol representing the key for the value.
277
+ # +event_type+:: a String or Symbol representing the category for the event.
278
+ # +parameters+::
279
+ # - +:limit+ - integer, number of results to return. Defaults to 10, Max 100.
280
+ # - +:start+ - Integer/String representing the inclusive start to a range.
281
+ # - +:after+ - Integer/String representing the exclusive start to a range.
282
+ # - +:before+ - Integer/String representing the exclusive end to a range.
283
+ # - +:end+ - Integer/String representing the inclusive end to a range.
284
+ #
285
+ # Range parameters are formatted as ":timestamp/:ordinal":
286
+ # +timestamp+:: an Integer or String representing a time.
287
+ # - Integers are Milliseconds since Unix Epoch.
288
+ # - Strings must be formatted as per http://orchestrate.io/docs/api/#events/timestamps
289
+ # - A future version will support ruby Time objects.
290
+ # +ordinal+:: is optional.
291
+ #
292
+ def list_events(collection, key, event_type, parameters={})
293
+ Orchestrate::Helpers.range_keys!('event', parameters)
294
+ send_request :get, [collection, key, 'events', event_type], { query: parameters }
295
+ end
296
+
297
+ # -------------------------------------------------------------------------
298
+ # Graph
299
+
300
+ # call-seq:
301
+ # client.get_relations(collection_name, key, *kinds) -> response
302
+ #
303
+ # Returns the relation's collection, key and ref values.
304
+ #
305
+ # +collection_name+:: a String or Symbol representing the name of the collection.
306
+ # +key+:: a String or Symbol representing the key for the value.
307
+ # +kinds+:: one or more String or Symbol values representing the relations and depth to walk.
308
+ #
309
+ def get_relations(collection, key, *kinds)
310
+ path = [collection, key, 'relations'].concat(kinds)
311
+ send_request :get, path
312
+ end
313
+
314
+ # call-seq:
315
+ # client.put_relation(collection_name, key, kind, to_collection_name, to_key) -> response
316
+ #
317
+ # Stores a relationship between two Key/Value items. They do not need to be in the same collection.
318
+ #
319
+ # +collection_name+:: a String or Symbol representing the name of the collection.
320
+ # +key+:: a String or Symbol representing the key for the value.
321
+ # +kind+:: a String or Symbol value representing the relation type.
322
+ # +to_collection_name+:: a String or Symbol representing the name of the collection the related item belongs.
323
+ # +to_key+:: a String or Symbol representing the key for the related item.
324
+ #
325
+ def put_relation(collection, key, kind, to_collection, to_key)
326
+ send_request :put, [collection, key, 'relation', kind, to_collection, to_key]
327
+ end
328
+
329
+ # call-seq:
330
+ # client.delete_relation(collection_name, key, kind, to_collection, to_key) -> response
331
+ #
332
+ # Deletes a relationship between two Key/Value items.
333
+ #
334
+ # +collection_name+:: a String or Symbol representing the name of the collection.
335
+ # +key+:: a String or Symbol representing the key for the value.
336
+ # +kind+:: a String or Symbol value representing the relation type.
337
+ # +to_collection_name+:: a String or Symbol representing the name of the collection the related item belongs.
338
+ # +to_key+:: a String or Symbol representing the key for the related item.
339
+ #
340
+ def delete_relation(collection, key, kind, to_collection, to_key)
341
+ path = [collection, key, 'relation', kind, to_collection, to_key]
342
+ send_request :delete, path, { query: {purge: true} }
343
+ end
344
+
345
+ # call-seq:
346
+ # client.in_parallel {|responses| block } -> Hash
347
+ #
348
+ # Performs any requests generated inside the block in parallel. If the
349
+ # client isn't using a Faraday adapter that supports parallelization, will
350
+ # output a warning to STDERR.
351
+ #
352
+ # Example:
353
+ # responses = client.in_parallel do |r|
354
+ # r[:some_items] = client.list(:site_globals)
355
+ # r[:user] = client.get(:users, current_user_key)
356
+ # r[:user_feed] = client.list_events(:users, current_user_key, :notices)
357
+ # end
358
+ #
359
+ def in_parallel(&block)
360
+ accumulator = {}
361
+ http.in_parallel do
362
+ block.call(accumulator)
363
+ end
364
+ accumulator
365
+ end
366
+
367
+ # call-seq:
368
+ # client.send_request(method, url, opts={}) -> response
369
+ #
370
+ # Performs the HTTP request against the API and returns a Faraday::Response
371
+ #
372
+ # +method+ - the HTTP method, one of [ :get, :post, :put, :delete ]
373
+ # +url+ - an Array of segments to be joined with '/'
374
+ # +opts+
375
+ # - +:query+ - a Hash for the request query string
376
+ # - +:body+ - a Hash for the :put or :post request body
377
+ # - +:headers+ - a Hash the request headers
378
+ #
379
+ def send_request(method, url, opts={})
380
+ url = "/v0/#{url.join('/')}"
381
+ query_string = opts.fetch(:query, {})
382
+ body = opts.fetch(:body, '')
383
+ headers = opts.fetch(:headers, {})
384
+ headers['User-Agent'] = "ruby/orchestrate/#{Orchestrate::VERSION}"
385
+ headers['Accept'] = 'application/json' if method == :get
386
+
387
+ http.send(method) do |request|
388
+ config.logger.debug "Performing #{method.to_s.upcase} request to \"#{url}\""
389
+ request.url url, query_string
390
+ if [:put, :post].include?(method)
391
+ headers['Content-Type'] = 'application/json'
392
+ request.body = body.to_json
393
+ end
394
+ headers.each {|header, value| request[header] = value }
395
+ end
396
+ end
397
+
398
+ # ------------------------------------------------------------------------
399
+
400
+ private
401
+
402
+ # Formats the provided 'ref' to be quoted per API specification. If
403
+ # already quoted, does not add additional quotes.
404
+ def format_ref(ref)
405
+ "\"#{ref.gsub(/"/,'')}\""
406
+ end
407
+
408
+ end
409
+
410
+ end