contextio 1.5.0 → 1.6.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.
- checksums.yaml +4 -4
- data/CHANGES.md +11 -0
- data/README.md +254 -24
- data/contextio.gemspec +1 -1
- data/lib/contextio/api.rb +13 -5
- data/lib/contextio/file.rb +1 -1
- data/lib/contextio/message.rb +2 -1
- data/lib/contextio/source.rb +0 -10
- data/lib/contextio/source_collection.rb +6 -6
- data/lib/contextio/version.rb +1 -1
- data/spec/spec_helper.rb +4 -0
- data/spec/unit/contextio/account_collection_spec.rb +7 -7
- data/spec/unit/contextio/account_spec.rb +3 -3
- data/spec/unit/contextio/api/resource_collection_spec.rb +16 -16
- data/spec/unit/contextio/api/resource_spec.rb +22 -22
- data/spec/unit/contextio/api_spec.rb +60 -3
- data/spec/unit/contextio/connect_token_collection_spec.rb +7 -7
- data/spec/unit/contextio/connect_token_spec.rb +2 -2
- data/spec/unit/contextio/email_settings_spec.rb +10 -10
- data/spec/unit/contextio/oauth_provider_collection_spec.rb +3 -3
- data/spec/unit/contextio/oauth_provider_spec.rb +7 -7
- data/spec/unit/contextio/source_collection_spec.rb +7 -7
- data/spec/unit/contextio/source_spec.rb +11 -5
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f3726a18eda9fc6ebe4ca26a055cefc0ab8c21c6
|
4
|
+
data.tar.gz: 70f0f22c3c52460d4e81648f2839c05e5f88514f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2651ef5e3d6ea6276eaaebf33761120b9e68b42be0a35e8a2cdbe5d8b04481bd1dd55b857b65ec265f601c48c07d3004b4d66e346401553b0b202c22fc57cee5
|
7
|
+
data.tar.gz: 6258631b9b65b5b624fbb545d8175b768997f84b10268fa05bc31617b2bba921f486934ae469bdb537874f273d27069a79a01c169379a403e1218671b894c0fa
|
data/CHANGES.md
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
# Changes
|
2
2
|
|
3
|
+
## 1.6.0
|
4
|
+
|
5
|
+
* Add `version` and `base_url` instance variables to API. - Dominik Gehl
|
6
|
+
* Don't try to JSON parse raw attachments. - Dominik Gehl
|
7
|
+
* Use symbols for options internally to avoid OAuth gem failure. - Ben Hamill
|
8
|
+
* Add `in_reply_to` to `Message`'s lazy attributes. - Asa Wilson
|
9
|
+
* Fix bug where `Hash`es with mixed `String`/`Symbol` keys would cause the OAuth
|
10
|
+
gem to explode. - Ben Hamill
|
11
|
+
* Add missing files association to `Message` class. - Ben Hamill
|
12
|
+
* Add a ton of examples to the README to help new users. - Johnny Goodman
|
13
|
+
|
3
14
|
## 1.5.0
|
4
15
|
|
5
16
|
* Make `Source#sync!` and `Account#sync!` take an options hash that will be
|
data/README.md
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
* [Homepage](https://github.com/contextio/contextio-ruby#readme)
|
4
4
|
* [API Documentation](http://context.io/docs/2.0/)
|
5
5
|
* [Gem Documentation](http://rubydoc.info/gems/contextio/frames)
|
6
|
+
* [API Explorer](https://console.context.io/#explore)
|
6
7
|
* [Sign Up](http://context.io)
|
7
8
|
|
8
9
|
|
@@ -11,8 +12,17 @@
|
|
11
12
|
Provides a Ruby interface to [Context.IO](http://context.io). The general design
|
12
13
|
was inspired by the wonderful [aws-sdk](https://github.com/aws/aws-sdk-ruby)
|
13
14
|
gem. You start with an object that represents your account with Context.IO and
|
14
|
-
then you deal with collections within that going forward (see the [
|
15
|
-
section](#
|
15
|
+
then you deal with collections within that going forward (see the [Quick Start
|
16
|
+
section](#quick start)).
|
17
|
+
|
18
|
+
|
19
|
+
## Install
|
20
|
+
|
21
|
+
$ gem install contextio
|
22
|
+
|
23
|
+
Or, of course, put this in your Gemfile:
|
24
|
+
|
25
|
+
gem 'contextio'
|
16
26
|
|
17
27
|
|
18
28
|
## A Note On Method Names
|
@@ -28,15 +38,6 @@ docs](http://rubydoc.info/gems/contextio/frames) for a specific class when in
|
|
28
38
|
doubt.
|
29
39
|
|
30
40
|
|
31
|
-
## Install
|
32
|
-
|
33
|
-
$ gem install contextio
|
34
|
-
|
35
|
-
Or, of course, put this in your Gemfile:
|
36
|
-
|
37
|
-
gem 'contextio'
|
38
|
-
|
39
|
-
|
40
41
|
## Version Numbers
|
41
42
|
|
42
43
|
This gem adheres to [SemVer](http://semver.org/). So you should be pretty safe
|
@@ -45,7 +46,11 @@ bump. When the major version bumps, be warned; upgrading will take some kind of
|
|
45
46
|
effort.
|
46
47
|
|
47
48
|
|
48
|
-
##
|
49
|
+
## Examples
|
50
|
+
|
51
|
+
### Quick Start
|
52
|
+
|
53
|
+
Print the subject of the first five messages in the some@email.com account.
|
49
54
|
|
50
55
|
```ruby
|
51
56
|
require 'contextio'
|
@@ -54,29 +59,254 @@ contextio = ContextIO.new('your_api_key', 'your_api_secret')
|
|
54
59
|
|
55
60
|
account = contextio.accounts.where(email: 'some@email.com').first
|
56
61
|
|
57
|
-
account.
|
58
|
-
|
59
|
-
account.suspended? # False
|
60
|
-
|
61
|
-
account.messages.where(folder: '\Drafts').each do |m|
|
62
|
-
puts m.subject
|
62
|
+
account.messages.where(limit: 5).each do |message|
|
63
|
+
puts message.subject
|
63
64
|
end
|
64
65
|
```
|
65
66
|
|
67
|
+
|
68
|
+
### Primary Key Queries
|
69
|
+
|
66
70
|
To grab some object you already know the primary key for, you'll use the `[]`
|
67
|
-
method like you would for a `Hash` or `Array`.
|
68
|
-
|
71
|
+
method like you would for a `Hash` or `Array`.
|
72
|
+
|
73
|
+
This is most helpful for accounts, but works for any resource collection.
|
69
74
|
|
70
75
|
```ruby
|
71
76
|
require 'contextio'
|
72
77
|
|
73
78
|
contextio = ContextIO.new('your_api_key', 'your_api_secret')
|
74
79
|
|
80
|
+
some_account_id = "exampleaccountid12345678"
|
81
|
+
|
75
82
|
account = contextio.accounts[some_account_id]
|
76
83
|
message = account.messages[some_message_id]
|
77
84
|
```
|
78
85
|
|
79
86
|
|
87
|
+
#### Message By Key
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
message = account.messages[some_message_id]
|
91
|
+
|
92
|
+
message.message_id #=> "examplemessageid12345678"
|
93
|
+
message.subject #=> "My Email's Subject"
|
94
|
+
message.from.class #=> Hash
|
95
|
+
message.from['email'] #=> "some@email.com"
|
96
|
+
message.from['name'] #=> "John Doe"
|
97
|
+
|
98
|
+
message.delete #=> true
|
99
|
+
```
|
100
|
+
|
101
|
+
Body content must be accessed through each body part (eg: html, text, etc.).
|
102
|
+
Context.io does not store body content and so each call will source it
|
103
|
+
directly from the mail box associated with the account. This will be slow
|
104
|
+
relative to the context.io API.
|
105
|
+
|
106
|
+
You can specify and receive a single body content type.
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
message = account.messages[some_message_id]
|
110
|
+
|
111
|
+
message_part = message.body_parts.where(type: 'text/plain').first
|
112
|
+
|
113
|
+
message_part.class #=> ContextIO::BodyPart
|
114
|
+
message_part.type #=> "text/plain"
|
115
|
+
message_part.content #=> "body content of text/plain body_part"
|
116
|
+
```
|
117
|
+
|
118
|
+
You can determine how many body parts are available and iterate over each one.
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
message = account.messages[some_message_id]
|
122
|
+
|
123
|
+
message.body_parts.class #=> ContextIO::BodyPartCollection
|
124
|
+
message.body_parts.count #=> 2
|
125
|
+
|
126
|
+
message.body_parts.each do |part|
|
127
|
+
puts part.class #=> ContextIO::BodyPart
|
128
|
+
puts part.type #=> "text/html"
|
129
|
+
puts part.content #=> "body content of text/html body_part"
|
130
|
+
end
|
131
|
+
```
|
132
|
+
|
133
|
+
|
134
|
+
#### Account By Key
|
135
|
+
|
136
|
+
You can specify an account id and get back information about that account.
|
137
|
+
|
138
|
+
```ruby
|
139
|
+
account = contextio.accounts[some_account_id]
|
140
|
+
|
141
|
+
account.email_addresses #=> ['some@email.com', 'another@email.com']
|
142
|
+
account.id #=> "exampleaccountid12345678"
|
143
|
+
account.first_name #=> "Bruno"
|
144
|
+
account.suspended? #=> False
|
145
|
+
```
|
146
|
+
|
147
|
+
|
148
|
+
### Message Collections
|
149
|
+
|
150
|
+
#### Query Basics
|
151
|
+
|
152
|
+
Queries to Messages return ContextIO::MessageCollection objects which can be
|
153
|
+
iterated on.
|
154
|
+
|
155
|
+
The `where` method allows you to refine search results based on
|
156
|
+
[available filters](http://context.io/docs/2.0/accounts/messages#get).
|
157
|
+
|
158
|
+
```ruby
|
159
|
+
#the 25 most recent messages by default, you can specify a higher limit
|
160
|
+
account.messages
|
161
|
+
|
162
|
+
#the 50 most recent messages
|
163
|
+
account.messages.where(limit: 50)
|
164
|
+
|
165
|
+
#recent messages sent to the account by some@email.com
|
166
|
+
account.messages.where(from: 'some@email.com')
|
167
|
+
|
168
|
+
#multiple parameters accepted in hash format
|
169
|
+
account.messages.where(from: 'some@email.com', subject: 'hello')
|
170
|
+
|
171
|
+
#regexp accepted as a string like '/regexp/'
|
172
|
+
account.messages.where(from: 'some@email.com', subject: '/h.llo/')
|
173
|
+
|
174
|
+
#regexp options are supported, the /i case insensitive is often useful
|
175
|
+
account.messages.where(from: 'some@email.com', subject: '/h.llo/i')
|
176
|
+
```
|
177
|
+
|
178
|
+
|
179
|
+
#### Querying Dates
|
180
|
+
|
181
|
+
Pass dates in to message queries as Unix Epoch integers.
|
182
|
+
|
183
|
+
```ruby
|
184
|
+
require 'active_support/all'
|
185
|
+
|
186
|
+
account.messages.where(date_before: 3.hours.ago.to_i, date_after: 5.hours.ago.to_i).each do |message|
|
187
|
+
puts "(#{message.date}) #{message.subject}"
|
188
|
+
end
|
189
|
+
```
|
190
|
+
|
191
|
+
You can mix date and non-date parameters.
|
192
|
+
|
193
|
+
```ruby
|
194
|
+
account.messages.where(
|
195
|
+
date_before: 3.days.ago.to_i,
|
196
|
+
date_after: 4.weeks.ago.to_i,
|
197
|
+
subject: 'subject of email',
|
198
|
+
from: 'foo@email.com'
|
199
|
+
)
|
200
|
+
```
|
201
|
+
|
202
|
+
|
203
|
+
### Individual Messages
|
204
|
+
|
205
|
+
#### Message Basics
|
206
|
+
|
207
|
+
```ruby
|
208
|
+
account.messages.where(limit: 1).class # ContextIO::MessageCollection
|
209
|
+
|
210
|
+
message = account.messages.where(limit: 1).first
|
211
|
+
|
212
|
+
message.class #=> ContextIO::Message
|
213
|
+
message.subject #=> "subject of message"
|
214
|
+
message.from #=> {"email"=>"some@email.com", "name"=>"John Doe"}
|
215
|
+
message.to #=> [{"email"=>"some@email.com", "name"=>"'John Doe'"}, {"email"=>"another@email.com", "name"=>"Jeff Mangum"}]
|
216
|
+
```
|
217
|
+
|
218
|
+
#### Message Dates
|
219
|
+
|
220
|
+
##### received_at
|
221
|
+
|
222
|
+
`received_at` is the time when your IMAP account records the message
|
223
|
+
having arrived. It is returned as a Time object.
|
224
|
+
|
225
|
+
```ruby
|
226
|
+
m.received_at #=> 2013-04-19 08:12:04 -0500
|
227
|
+
m.received_at.class #=> Time
|
228
|
+
```
|
229
|
+
|
230
|
+
##### indexed_at
|
231
|
+
|
232
|
+
`indexed_at` is the time when the message was processed and indexed
|
233
|
+
by the Context.io sync process.
|
234
|
+
|
235
|
+
```ruby
|
236
|
+
m.indexed_at #=> 2013-04-29 01:14:39 -0500
|
237
|
+
m.received_at.class #=> Time
|
238
|
+
```
|
239
|
+
|
240
|
+
##### date
|
241
|
+
|
242
|
+
A message's date is set by the sender, extracted from the message's Date
|
243
|
+
header and is returned as a FixNum which can be converted into a Time
|
244
|
+
object.
|
245
|
+
|
246
|
+
```ruby
|
247
|
+
message.date #=> 1361828599
|
248
|
+
Time.at(message.date) #=> 2013-04-19 08:11:33 -0500
|
249
|
+
Time.at(message.date).class #=> Time
|
250
|
+
```
|
251
|
+
|
252
|
+
`message.date` is not reliable as it is easily spoofed. While it is made
|
253
|
+
available, `received_at` and `indexed_at` are better choices.
|
254
|
+
|
255
|
+
|
256
|
+
#### Messages with Body Data
|
257
|
+
|
258
|
+
By default, Context.io's API does not return message queries with body data.
|
259
|
+
|
260
|
+
You can include the body attribute in each individual message returned by
|
261
|
+
adding `include_body: 1` to your `messages.where` query options.
|
262
|
+
|
263
|
+
```ruby
|
264
|
+
account.messages.where(include_body: 1, limit: 1).each do |message|
|
265
|
+
puts "#{message.subject} #{message.date} #{message.body[0]['content']}"
|
266
|
+
end
|
267
|
+
```
|
268
|
+
|
269
|
+
Emails that contain two or more body parts are called [multipart messages](https://en.wikipedia.org/wiki/MIME#Alternative).
|
270
|
+
|
271
|
+
'text/plain' and 'text/html' are common body part types for multipart
|
272
|
+
messages.
|
273
|
+
|
274
|
+
In the case a user is viewing email in a client that does not support HTML
|
275
|
+
markup, the 'text/plain' body part type will render instead.
|
276
|
+
|
277
|
+
If you are working with multipart messages, you may want to check each
|
278
|
+
body part's content in turn.
|
279
|
+
|
280
|
+
```ruby
|
281
|
+
account.messages.where(include_body: 1, limit: 1).each do |message|
|
282
|
+
message.body_parts.each do |body_part|
|
283
|
+
puts body_part.content
|
284
|
+
end
|
285
|
+
end
|
286
|
+
```
|
287
|
+
|
288
|
+
The `include_body` method queries the source IMAP box directly, which results
|
289
|
+
in slower return times.
|
290
|
+
|
291
|
+
|
292
|
+
### Files
|
293
|
+
|
294
|
+
#### Files Per Message ID
|
295
|
+
|
296
|
+
```ruby
|
297
|
+
message = account.messages[message_id]
|
298
|
+
|
299
|
+
message.files.class #=> ContextIO::FileCollection
|
300
|
+
message.files.count #=> 2
|
301
|
+
message.files.map { |f| f.file_name } #=> ["at_icon.png", "argyle_slides.png"]
|
302
|
+
message.files.first.resource_url #=> https://contextio_to_s3_redirect_url.io
|
303
|
+
```
|
304
|
+
|
305
|
+
The file['resource_url'] url is a S3 backed temporary link. It is intended
|
306
|
+
to be used promptly after being called. Do not store off this link. Instead,
|
307
|
+
store off the message_id and request on demand.
|
308
|
+
|
309
|
+
|
80
310
|
### On Laziness
|
81
311
|
|
82
312
|
This gem is architected to be as lazy as possible. It won't make an HTTP request
|
@@ -88,7 +318,7 @@ require 'contextio'
|
|
88
318
|
|
89
319
|
contextio = ContextIO.new('your_api_key', 'your_api_secret')
|
90
320
|
|
91
|
-
account = contextio.accounts['
|
321
|
+
account = contextio.accounts['exampleaccountid12345678'] # No request made here.
|
92
322
|
account.last_name # Request made here.
|
93
323
|
account.first_name # No request made here.
|
94
324
|
```
|
@@ -101,8 +331,8 @@ docs](http://rubydoc.info/gems/contextio/frames) will trigger the request.
|
|
101
331
|
|
102
332
|
### On Requests and Methods
|
103
333
|
|
104
|
-
There are some consistent mappings between the requests documented in the
|
105
|
-
docs](http://context.io/docs/2.0/) and the methods implemented in the gem.
|
334
|
+
There are some consistent mappings between the requests documented in the
|
335
|
+
[API docs](http://context.io/docs/2.0/) and the methods implemented in the gem.
|
106
336
|
|
107
337
|
**For collections of resources:**
|
108
338
|
|
@@ -143,6 +373,6 @@ failing test can be helpful. If in doubt, make an Issue to discuss.
|
|
143
373
|
|
144
374
|
## Copyright
|
145
375
|
|
146
|
-
Copyright (c) 2012 Context.IO
|
376
|
+
Copyright (c) 2012-2013 Context.IO
|
147
377
|
|
148
378
|
This gem is distributed under the MIT License. See LICENSE.md for details.
|
data/contextio.gemspec
CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |gem|
|
|
21
21
|
|
22
22
|
gem.add_development_dependency 'bundler'
|
23
23
|
gem.add_development_dependency 'rubygems-tasks', '~> 0.2'
|
24
|
-
gem.add_development_dependency 'rspec', '~> 2.
|
24
|
+
gem.add_development_dependency 'rspec', '~> 2.14'
|
25
25
|
gem.add_development_dependency 'rake'
|
26
26
|
gem.add_development_dependency 'yard'
|
27
27
|
gem.add_development_dependency 'redcarpet'
|
data/lib/contextio/api.rb
CHANGED
@@ -52,6 +52,8 @@ class ContextIO
|
|
52
52
|
self.class.user_agent_string
|
53
53
|
end
|
54
54
|
|
55
|
+
attr_accessor :base_url, :version
|
56
|
+
|
55
57
|
# @!attribute [r] key
|
56
58
|
# @return [String] The OAuth key for the user's Context.IO account.
|
57
59
|
# @!attribute [r] secret
|
@@ -67,6 +69,8 @@ class ContextIO
|
|
67
69
|
@key = key
|
68
70
|
@secret = secret
|
69
71
|
@opts = opts || {}
|
72
|
+
@base_url = self.class.base_url
|
73
|
+
@version = self.class.version
|
70
74
|
end
|
71
75
|
|
72
76
|
# Generates the path for a resource_path and params hash for use with the API.
|
@@ -76,7 +80,7 @@ class ContextIO
|
|
76
80
|
# @param [{String, Symbol => String, Symbol, Array<String, Symbol>}] params
|
77
81
|
# A Hash of the query parameters for the action represented by this path.
|
78
82
|
def path(resource_path, params = {})
|
79
|
-
"/#{
|
83
|
+
"/#{version}/#{strip_resource_path(resource_path)}#{API.hash_to_url_params(params)}"
|
80
84
|
end
|
81
85
|
|
82
86
|
# Makes a request against the Context.IO API.
|
@@ -129,15 +133,19 @@ class ContextIO
|
|
129
133
|
# @return [Net::HTTP*] The response object from the request.
|
130
134
|
def oauth_request(method, resource_path, params, headers=nil)
|
131
135
|
headers ||= { 'Accept' => 'application/json', 'User-Agent' => user_agent_string }
|
136
|
+
normalized_params = params.inject({}) do |normalized_params, (key, value)|
|
137
|
+
normalized_params[key.to_sym] = value
|
138
|
+
normalized_params
|
139
|
+
end
|
132
140
|
|
133
141
|
# The below array used to include put, too, but there is a weirdness in
|
134
142
|
# the oauth gem with PUT and signing requests. See
|
135
143
|
# https://github.com/oauth/oauth-ruby/pull/34#issuecomment-5862199 for
|
136
144
|
# some discussion on the matter. This is a work-around.
|
137
145
|
if %w(post).include? method.to_s.downcase
|
138
|
-
token.request(method, path(resource_path),
|
146
|
+
token.request(method, path(resource_path), normalized_params, headers)
|
139
147
|
else # GET, DELETE, HEAD, etc.
|
140
|
-
token.request(method, path(resource_path,
|
148
|
+
token.request(method, path(resource_path, normalized_params), nil, headers)
|
141
149
|
end
|
142
150
|
end
|
143
151
|
|
@@ -147,7 +155,7 @@ class ContextIO
|
|
147
155
|
# @param [#to_s] resource_path The full URL or path for a resource.
|
148
156
|
#
|
149
157
|
# @return [String] The resource path.
|
150
|
-
def
|
158
|
+
def strip_resource_path(resource_path)
|
151
159
|
resource_path.to_s.gsub("#{base_url}/#{version}/", '')
|
152
160
|
end
|
153
161
|
|
@@ -175,7 +183,7 @@ class ContextIO
|
|
175
183
|
# @return [OAuth::Consumer] An Oauth consumer object for credentials
|
176
184
|
# purposes.
|
177
185
|
def consumer
|
178
|
-
@consumer ||= OAuth::Consumer.new(key, secret, @opts.merge(site:
|
186
|
+
@consumer ||= OAuth::Consumer.new(key, secret, @opts.merge(site: base_url))
|
179
187
|
end
|
180
188
|
|
181
189
|
# @!attribute [r] token
|