nylas 0.15.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +363 -0
- data/lib/account.rb +32 -0
- data/lib/api_thread.rb +56 -0
- data/lib/calendar.rb +15 -0
- data/lib/contact.rb +10 -0
- data/lib/draft.rb +32 -0
- data/lib/event.rb +34 -0
- data/lib/file.rb +31 -0
- data/lib/inbox.rb +141 -0
- data/lib/message.rb +41 -0
- data/lib/namespace.rb +147 -0
- data/lib/parameters.rb +26 -0
- data/lib/restful_model.rb +80 -0
- data/lib/restful_model_collection.rb +149 -0
- data/lib/rfc2882.rb +15 -0
- data/lib/tag.rb +9 -0
- data/lib/time_attr_accessor.rb +12 -0
- data/lib/version.rb +3 -0
- metadata +206 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 5155ab9113d983b6c3dc83818524d2f5f547475d
|
4
|
+
data.tar.gz: 8091ecf517e6eabb67e7fbefc7d4058c9f1db2b1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a8f0c9418ec9872bb112b522777e27dcac89fdaa246b7cb2d359bb7c38eea96de1dde5a4e3f49b84f34ee056efc58a6b270bf34ad382a0be7b422582771aff13
|
7
|
+
data.tar.gz: 0c6ad33a84d4a890e88e4b3d3dbd4d1cbf56b8b3261e64817124e9694fe5ec8307473ee678f2dc9948511aec639bb2778c3390dd433164b8fa62fbe3322cdd6e
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
----
|
3
|
+
|
4
|
+
Copyright (c) 2014 InboxApp, Inc. and Contributors
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
8
|
+
in the Software without restriction, including without limitation the rights
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
11
|
+
furnished to do so, subject to the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be included in
|
14
|
+
all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,363 @@
|
|
1
|
+
# Nylas REST API Ruby bindings
|
2
|
+
|
3
|
+
## Installation
|
4
|
+
|
5
|
+
Add this line to your application's Gemfile:
|
6
|
+
|
7
|
+
gem 'inbox'
|
8
|
+
|
9
|
+
And then execute:
|
10
|
+
|
11
|
+
bundle
|
12
|
+
|
13
|
+
You don't need to use this repo unless you're planning to modify the gem. If you just want to use the Inbox SDK with Ruby bindings, you should run:
|
14
|
+
|
15
|
+
gem install inbox
|
16
|
+
|
17
|
+
|
18
|
+
##Requirements
|
19
|
+
|
20
|
+
- Ruby 1.8.7 or above. (Ruby 1.8.6 may work if you load ActiveSupport.)
|
21
|
+
|
22
|
+
- rest-client, json
|
23
|
+
|
24
|
+
|
25
|
+
## Example Rails App
|
26
|
+
|
27
|
+
A small example Rails app is included in the `example` directory. You can run the sample app to see how an authentication flow might be implemented.
|
28
|
+
|
29
|
+
`cd example`
|
30
|
+
|
31
|
+
`RESTCLIENT_LOG=stdout rails s`
|
32
|
+
|
33
|
+
*Note that you will need to replace the Inbox App ID and Secret in `config/environments/development.rb` to use the sample app.*
|
34
|
+
|
35
|
+
## Usage
|
36
|
+
|
37
|
+
### App ID and Secret
|
38
|
+
|
39
|
+
Before you can interact with the Inbox API, you need to register for the Nylas Developer Program at [https://www.nylas.com/](https://www.nylas.com/). After you've created a developer account, you can create a new application to generate an App ID / Secret pair.
|
40
|
+
|
41
|
+
Generally, you should store your App ID and Secret into environment variables to avoid adding them to source control. That said, in the example project and code snippets below, the values were added to `config/environments/development.rb` for convenience.
|
42
|
+
|
43
|
+
|
44
|
+
### Authentication
|
45
|
+
|
46
|
+
The Nylas REST API uses server-side (three-legged) OAuth, and the Ruby gem provides convenience methods that simplify the OAuth process. For more information about authenticating with Nylas, visit the [Developer Documentation](https://www.nylas.com/docs/knowledgebase#authentication).
|
47
|
+
|
48
|
+
**Step 1: Redirect the user to Nylas:**
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
require 'inbox'
|
52
|
+
|
53
|
+
def login
|
54
|
+
inbox = Nylas::API.new(config.inbox_app_id, config.inbox_app_secret, nil)
|
55
|
+
# The email address of the user you want to authenticate
|
56
|
+
user_email = 'ben@nylas.com'
|
57
|
+
|
58
|
+
# This URL must be registered with your application in the developer portal
|
59
|
+
callback_url = url_for(:action => 'login_callback')
|
60
|
+
|
61
|
+
redirect_to inbox.url_for_authentication(callback_url, user_email)
|
62
|
+
end
|
63
|
+
```
|
64
|
+
|
65
|
+
**Step 2: Handle the Authentication Response:**
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
def login_callback
|
69
|
+
inbox = Nylas::API.new(config.inbox_app_id, config.inbox_app_secret, nil)
|
70
|
+
inbox_token = inbox.token_for_code(params[:code])
|
71
|
+
|
72
|
+
# Save the inbox_token to the current session, save it to the user model, etc.
|
73
|
+
end
|
74
|
+
```
|
75
|
+
|
76
|
+
### Managing Billing
|
77
|
+
|
78
|
+
If you're using the open-source version of the Nylas Sync Engine or have fewer than 10 accounts associated with your developer app, you don't need to worry about billing. However, if you've requested production access to the Sync Engine, you are billed monthly based on the number of email accounts you've connected to Inbox. You can choose to start accounts in "trial" state and sync slowly at a rate of one message per minute so users can try out your app. If you use trial mode, you need to upgrade accounts (and start paying for them) within 30 days or they will automatically expire. You may wish to upgrade accounts earlier to dramatically speed up the mail sync progress depending on your app's needs.
|
79
|
+
|
80
|
+
**Starting an Account in Trial Mode**
|
81
|
+
|
82
|
+
When you're redirecting the user to Nylas to authenticate with their email provider,
|
83
|
+
pass the additional `trial: true` option to start their account in trial mode.
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
redirect_to inbox.url_for_authentication(callback_url, user_email, {trial: true})
|
87
|
+
```
|
88
|
+
|
89
|
+
**Upgrading an Account**
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
inbox = Nylas::API.new(config.inbox_app_id, config.inbox_app_secret, nil)
|
93
|
+
account = inbox.accounts.find(account_id)
|
94
|
+
account.upgrade!
|
95
|
+
```
|
96
|
+
|
97
|
+
**Cancelling an Account**
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
inbox = Nylas::API.new(config.inbox_app_id, config.inbox_app_secret, nil)
|
101
|
+
account = inbox.accounts.find(account_id)
|
102
|
+
account.downgrade!
|
103
|
+
|
104
|
+
# Your Inbox API token will be revoked, you will not be charged
|
105
|
+
```
|
106
|
+
|
107
|
+
### Account Status
|
108
|
+
|
109
|
+
````ruby
|
110
|
+
# Query the status of every account linked to the app
|
111
|
+
inbox = Nylas::API.new(config.inbox_app_id, config.inbox_app_secret, inbox_token)
|
112
|
+
accounts = inbox.accounts
|
113
|
+
accounts.each { |a| [a.account_id, a.sync_state] } # Available fields are: account_id, sync_state, trial, trial_expires, billing_state and namespace_id. See lib/account.rb for more details.
|
114
|
+
```
|
115
|
+
|
116
|
+
### Fetching Namespaces
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
inbox = Nylas::API.new(config.inbox_app_id, config.inbox_app_secret, inbox_token)
|
120
|
+
|
121
|
+
# Get the first namespace
|
122
|
+
namespace = inbox.namespaces.first
|
123
|
+
|
124
|
+
# Print out the email address and provider (Gmail, Exchange)
|
125
|
+
puts namespace.email_address
|
126
|
+
puts namespace.provider
|
127
|
+
```
|
128
|
+
|
129
|
+
|
130
|
+
### Fetching Threads
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
# Fetch the first thread
|
134
|
+
thread = namespace.threads.first
|
135
|
+
|
136
|
+
# Fetch a specific thread
|
137
|
+
thread = namespace.threads.find('ac123acd123ef123')
|
138
|
+
|
139
|
+
# List all threads tagged `inbox`
|
140
|
+
# (paginating 50 at a time until no more are returned.)
|
141
|
+
namespace.threads.where(:tag => 'inbox').each do |thread|
|
142
|
+
puts thread.subject
|
143
|
+
end
|
144
|
+
|
145
|
+
# List the 5 most recent unread threads
|
146
|
+
namespace.threads.where(:tag => 'unread').range(0,4).each do |thread|
|
147
|
+
puts thread.subject
|
148
|
+
end
|
149
|
+
|
150
|
+
# List all threads with 'ben@nylas.com'
|
151
|
+
namespace.threads.where(:any_email => 'ben@nylas.com').each do |thread|
|
152
|
+
puts thread.subject
|
153
|
+
end
|
154
|
+
|
155
|
+
# Get number of all threads
|
156
|
+
count = namespace.threads.count
|
157
|
+
|
158
|
+
# Get number of threads with 'ben@inboxapp.com'
|
159
|
+
count = namespace.threads.where(:any_email => 'ben@inboxapp.com').count
|
160
|
+
|
161
|
+
# Collect all threads with 'ben@nylas.com' into an array.
|
162
|
+
# Note: for large numbers of threads, this is not advised.
|
163
|
+
threads = namespace.threads.where(:any_email => 'ben@nylas.com').all
|
164
|
+
```
|
165
|
+
|
166
|
+
|
167
|
+
### Working with Threads
|
168
|
+
|
169
|
+
```ruby
|
170
|
+
# List thread participants
|
171
|
+
thread.participants.each do |participant|
|
172
|
+
puts participant['email']
|
173
|
+
end
|
174
|
+
|
175
|
+
# Mark as read
|
176
|
+
thread.mark_as_read!
|
177
|
+
|
178
|
+
# Archive
|
179
|
+
thread.archive!
|
180
|
+
|
181
|
+
# Add or remove arbitrary tags
|
182
|
+
tagsToAdd = ['inbox', 'cfa1233ef123acd12']
|
183
|
+
tagsToRemove = []
|
184
|
+
thread.update_tags!(tagsToAdd, tagsToRemove)
|
185
|
+
|
186
|
+
# List messages
|
187
|
+
thread.messages.each do |message|
|
188
|
+
puts message.subject
|
189
|
+
end
|
190
|
+
```
|
191
|
+
|
192
|
+
|
193
|
+
### Working with Files
|
194
|
+
|
195
|
+
```ruby
|
196
|
+
# List files
|
197
|
+
namespace.files.each do |file|
|
198
|
+
puts file.filename
|
199
|
+
end
|
200
|
+
|
201
|
+
# Create a new file
|
202
|
+
file = namespace.files.build(:file => File.new("./public/favicon.ico", 'rb'))
|
203
|
+
file.save!
|
204
|
+
```
|
205
|
+
|
206
|
+
### Working with Messages, Contacts, etc.
|
207
|
+
|
208
|
+
Each of the primary collections (contacts, messages, etc.) behave the same way as `threads`. For example, finding messages with a filter is similar to finding threads:
|
209
|
+
|
210
|
+
```ruby
|
211
|
+
messages = namespace.messages.where(:to => 'ben@nylas.com`).all
|
212
|
+
```
|
213
|
+
|
214
|
+
The `where` method accepts a hash of filters, as documented in the [Inbox Filters Documentation](https://www.nylas.com/docs/api#filters).
|
215
|
+
|
216
|
+
### Getting the raw contents of a message
|
217
|
+
|
218
|
+
It's possible to access the unprocessed contents of a message using the raw method:
|
219
|
+
|
220
|
+
```ruby
|
221
|
+
raw_contents = message.raw.rfc2822
|
222
|
+
```
|
223
|
+
|
224
|
+
|
225
|
+
### Creating and Sending Drafts
|
226
|
+
|
227
|
+
```ruby
|
228
|
+
# Create a new draft
|
229
|
+
draft = namespace.drafts.build(
|
230
|
+
:to => [{:name => 'Ben Gotow', :email => 'ben@nylas.com'}],
|
231
|
+
:subject => "Sent by Ruby",
|
232
|
+
:body => "Hi there!<strong>This is HTML</strong>"
|
233
|
+
)
|
234
|
+
|
235
|
+
# Modify attributes as necessary
|
236
|
+
draft.cc = [{:name => 'Michael', :email => 'mg@nylas.com'}]
|
237
|
+
|
238
|
+
# Add the file we uploaded as an attachment
|
239
|
+
draft.attach(file)
|
240
|
+
|
241
|
+
# Save the draft
|
242
|
+
draft.save!
|
243
|
+
|
244
|
+
# Send the draft.
|
245
|
+
draft.send!
|
246
|
+
```
|
247
|
+
|
248
|
+
### Creating an event
|
249
|
+
|
250
|
+
````ruby
|
251
|
+
# Every event is attached to a calendar -- get the id of the first calendar
|
252
|
+
calendar_id = inbox.namespaces.first.calendars.first.id
|
253
|
+
new_event = inbox.namespaces.first.events.build(:calendar_id => calendar_id, :title => 'Coffee?')
|
254
|
+
|
255
|
+
# Modify attributes as necessary
|
256
|
+
new_event.location = "L'excelsior"
|
257
|
+
|
258
|
+
# Dates are expressed by the Inbox API as UTC timestamps
|
259
|
+
new_event.when = {:start_time => 1407542195, :end_time => 1407543195}
|
260
|
+
|
261
|
+
# Persist the event --- it's automatically synced back to the Google or Exchange calendar
|
262
|
+
new_event.save!
|
263
|
+
```
|
264
|
+
|
265
|
+
## Using the Delta sync API
|
266
|
+
|
267
|
+
The delta sync API allows fetching all the changes that occured since a specified time. [Read this](https://nylas.com/docs/api#sync-protocol) for more details about the API.
|
268
|
+
|
269
|
+
````ruby
|
270
|
+
# Get all the messages starting from timestamp
|
271
|
+
#
|
272
|
+
# we first need to get a cursor object a cursor is simply the id of
|
273
|
+
# an individual change.
|
274
|
+
cursor = inbox.namespaces.first.get_cursor(1407543195)
|
275
|
+
|
276
|
+
last_cursor = nil
|
277
|
+
inbox.namespaces.first.deltas(cursor) do |event, object|
|
278
|
+
if event == "create" or event == "modify"
|
279
|
+
if object.is_a?(Nylas::Contact)
|
280
|
+
puts "#{object.name} - #{object.email}"
|
281
|
+
elsif object.is_a?(Nylas::Event)
|
282
|
+
puts "Event!"
|
283
|
+
end
|
284
|
+
elsif event == "delete"
|
285
|
+
# In the case of a deletion, the API only returns the ID of the object.
|
286
|
+
# In this case, the Ruby SDK returns a dummy object with only the id field
|
287
|
+
# set.
|
288
|
+
puts "Deleting from collection #{object.class.name}, id: #{object}"
|
289
|
+
end
|
290
|
+
last_cursor = object.cursor
|
291
|
+
end
|
292
|
+
|
293
|
+
# Don't forget to save the last cursor so that we can pick up changes
|
294
|
+
# from where we left.
|
295
|
+
save_to_db(last_cursor)
|
296
|
+
```
|
297
|
+
|
298
|
+
### Exclude changes from a specific type --- get only messages
|
299
|
+
````ruby
|
300
|
+
inbox.namespaces.first.deltas(cursor, exclude=[Nylas::Contact,
|
301
|
+
Nylas::Event,
|
302
|
+
Nylas::File,
|
303
|
+
Nylas::Tag,
|
304
|
+
Nylas::Thread]) do |event, object|
|
305
|
+
if event == 'create' or event == 'modify'
|
306
|
+
puts object.subject
|
307
|
+
end
|
308
|
+
end
|
309
|
+
```
|
310
|
+
|
311
|
+
|
312
|
+
### Handling Errors
|
313
|
+
The Nylas API uses conventional HTTP response codes to indicate success or failure of an API request. The ruby gem raises these as native exceptions.
|
314
|
+
|
315
|
+
Code | Error Type | Description
|
316
|
+
--- | --- | ---
|
317
|
+
400 | InvalidRequest | Your request has invalid parameters.
|
318
|
+
403 | AccessDenied | You don't have authorization to access the requested resource or perform the requested action. You may need to re-authenticate the user.
|
319
|
+
404 | ResourceNotFound | The requested resource doesn't exist.
|
320
|
+
500 | APIError | There was an internal error with the Nylas server.
|
321
|
+
|
322
|
+
A few additional exceptions are raised by the `draft.send!` method if your draft couldn't be sent.
|
323
|
+
|
324
|
+
Code | Error Type | Description
|
325
|
+
--- | --- | ---
|
326
|
+
402 | MessageRejected| The message was syntactically valid, but rejected for delivery by the mail server.
|
327
|
+
429 | SendingQuotaExceeded | The user has exceeded their daily sending quota.
|
328
|
+
503 | ServiceUnavailable | There was a temporary error establishing a connection to the user's mail server.
|
329
|
+
|
330
|
+
|
331
|
+
|
332
|
+
## Open-Source Sync Engine
|
333
|
+
|
334
|
+
The [Nylas Sync Engine](http://github.com/inboxapp/inbox) is open-source, and you can also use the Ruby gem with the open-source API. Since the open-source API provides no authentication or security, connecting to it is simple. When you instantiate the Inbox object, provide `nil` for the App ID, App Secret, and API Token, and pass the fully-qualified address to your copy of the sync engine:
|
335
|
+
|
336
|
+
```ruby
|
337
|
+
require 'inbox'
|
338
|
+
inbox = Nylas::API.new(nil, nil, nil, 'http://localhost:5555/')
|
339
|
+
```
|
340
|
+
|
341
|
+
|
342
|
+
## Contributing
|
343
|
+
|
344
|
+
We'd love your help making the Nylas ruby gem better. Join the Google Group for project updates and feature discussion. We also hang out in `#Nylas` on [irc.freenode.net](http://irc.freenode.net), or you can email [support@nylas.com](mailto:support@nylas.com).
|
345
|
+
|
346
|
+
Please sign the [Contributor License Agreement](https://www.nylas.com/cla.html) before submitting pull requests. (It's similar to other projects, like NodeJS or Meteor.)
|
347
|
+
|
348
|
+
Tests can be run with:
|
349
|
+
|
350
|
+
rspec spec
|
351
|
+
|
352
|
+
|
353
|
+
## Deployment
|
354
|
+
|
355
|
+
The Nylas ruby gem uses [Jeweler](https://github.com/technicalpickles/jeweler) for release management. Jeweler should be installed automatically when you call `bundle`, and extends `rake` to include a few more commands. When you're ready to release a new version, edit `lib/version.rb` and then build:
|
356
|
+
|
357
|
+
rake build
|
358
|
+
|
359
|
+
Test your new version (found in `pkg/`) locally, and then release with:
|
360
|
+
|
361
|
+
rake release
|
362
|
+
|
363
|
+
If it's your first time updating the ruby gem, you may be prompted for the username/password for rubygems.org. Members of the Nylas team can find that by doing `fetch-password rubygems`.
|
data/lib/account.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'restful_model'
|
2
|
+
|
3
|
+
module Inbox
|
4
|
+
class Account < RestfulModel
|
5
|
+
|
6
|
+
parameter :account_id
|
7
|
+
parameter :trial
|
8
|
+
parameter :trial_expires
|
9
|
+
parameter :sync_state
|
10
|
+
parameter :billing_state
|
11
|
+
|
12
|
+
def _perform_account_action!(action)
|
13
|
+
raise UnexpectedAccountAction.new unless action == "upgrade" || action == "downgrade"
|
14
|
+
|
15
|
+
collection = ManagementModelCollection.new(Account, @_api, @namespace_id, {:account_id=>@account_id})
|
16
|
+
::RestClient.post("#{collection.url}/#{@account_id}/#{action}",{}) do |response, request, result|
|
17
|
+
# Throw any exceptions
|
18
|
+
json = Inbox.interpret_response(result, response, :expected_class => Object)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def upgrade!
|
23
|
+
_perform_account_action!('upgrade')
|
24
|
+
end
|
25
|
+
|
26
|
+
def downgrade!
|
27
|
+
_perform_account_action!('downgrade')
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
data/lib/api_thread.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'restful_model'
|
2
|
+
require 'time_attr_accessor'
|
3
|
+
|
4
|
+
module Inbox
|
5
|
+
class Thread < RestfulModel
|
6
|
+
extend TimeAttrAccessor
|
7
|
+
|
8
|
+
parameter :subject
|
9
|
+
parameter :participants
|
10
|
+
parameter :snippet
|
11
|
+
parameter :tags
|
12
|
+
parameter :message_ids
|
13
|
+
parameter :draft_ids
|
14
|
+
time_attr_accessor :last_message_timestamp
|
15
|
+
time_attr_accessor :first_message_timestamp
|
16
|
+
|
17
|
+
def messages
|
18
|
+
@messages ||= RestfulModelCollection.new(Message, @_api, @namespace_id, {:thread_id=>@id})
|
19
|
+
end
|
20
|
+
|
21
|
+
def drafts
|
22
|
+
@drafts ||= RestfulModelCollection.new(Draft, @_api, @namespace_id, {:thread_id=>@id})
|
23
|
+
end
|
24
|
+
|
25
|
+
def update_tags!(tags_to_add = [], tags_to_remove = [])
|
26
|
+
update('PUT', '', {
|
27
|
+
:add_tags => tags_to_add,
|
28
|
+
:remove_tags => tags_to_remove
|
29
|
+
})
|
30
|
+
end
|
31
|
+
|
32
|
+
def mark_as_read!
|
33
|
+
update_tags!([], ['unread'])
|
34
|
+
end
|
35
|
+
|
36
|
+
def mark_as_seen!
|
37
|
+
update_tags!([], ['unseen'])
|
38
|
+
end
|
39
|
+
|
40
|
+
def archive!
|
41
|
+
update_tags!(['archive'], ['inbox'])
|
42
|
+
end
|
43
|
+
|
44
|
+
def unarchive!
|
45
|
+
update_tags!(['inbox'], ['archive'])
|
46
|
+
end
|
47
|
+
|
48
|
+
def star!
|
49
|
+
update_tags!(['starred'], [''])
|
50
|
+
end
|
51
|
+
|
52
|
+
def unstar!
|
53
|
+
update_tags!([], ['starred'])
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|