contextio 1.5.0 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 028f4ed25415c6d3512c35374b324199b18131e6
4
- data.tar.gz: 4369526ee522539a85b89ea065523c1b7f0ce4a4
3
+ metadata.gz: f3726a18eda9fc6ebe4ca26a055cefc0ab8c21c6
4
+ data.tar.gz: 70f0f22c3c52460d4e81648f2839c05e5f88514f
5
5
  SHA512:
6
- metadata.gz: edce3fb7fe4a13c6e1388cc41e2f0369de581553cc3453ca84b2b239693ca4066fa06d8a7edb216cfee24d0614af509c1c47af22c9fe224b5406732e30776620
7
- data.tar.gz: 5a024a9a50af47dec49a6dd032e288032af1b1dcd06e5f28e9b2d863e2c15785550d148a2b7fbd87ebad856568ee065e5cb78acaedfe622ceebbbfc5bfdbe487
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 [Usage
15
- section](#usage)).
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
- ## Usage
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.email_addresses # ['some@email.com', 'another@email.com']
58
- account.first_name # 'Bruno'
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`. This is most helpful for
68
- accounts, but works for any resource collection.
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['1234'] # No request made here.
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 [API
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.
@@ -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.4'
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'
@@ -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
- "/#{API.version}/#{API.strip_resource_path(resource_path)}#{API.hash_to_url_params(params)}"
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), params, headers)
146
+ token.request(method, path(resource_path), normalized_params, headers)
139
147
  else # GET, DELETE, HEAD, etc.
140
- token.request(method, path(resource_path, params), nil, headers)
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 self.strip_resource_path(resource_path)
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: API.base_url))
186
+ @consumer ||= OAuth::Consumer.new(key, secret, @opts.merge(site: base_url))
179
187
  end
180
188
 
181
189
  # @!attribute [r] token
@@ -48,7 +48,7 @@ class ContextIO
48
48
  end
49
49
 
50
50
  def content
51
- @content ||= api.request(:get, "#{resource_url}/content")
51
+ @content ||= api.raw_request(:get, "#{resource_url}/content")
52
52
  end
53
53
 
54
54
  def content_link