todoist-ruby 0.1.3 → 0.2.1

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: 7daad29d7cd0d65b9368bd878565885bcd2a6f32
4
- data.tar.gz: b50c774800956866d824b8ec19f65821ccde9a29
3
+ metadata.gz: 8b6ced7c5a25859fde744ff6603d95f9a0f02459
4
+ data.tar.gz: 13701d11be2371933b127389374aa7cbd4c416ad
5
5
  SHA512:
6
- metadata.gz: c9bccb4c9920626e43af53b1171f404c91c60813aaf422f77ddcebfd2a19448dad04a4599b8b993c356503c4827dde6b87bf0e0b4b00ef2c73af0d12b82bb1ab
7
- data.tar.gz: 31fa4e9e42d0ed96829fb107ad2323832f70c774f8ae0cccde9d800b5dcab9d0cb09df9999c828b4ed614be0181c7410d762c1a8be54d66c3867afb9cd1ee472
6
+ metadata.gz: a405c89d19faae1b894944d98f16af25d8f913f982723001d3bffcae53492683569231a35a6d5c3fe7f42ff8e9c0e0e76c2161117d8a994ef8300db5a22c18b1
7
+ data.tar.gz: a7bdfbc8f96f72636ff0796e81ac94cbee93a606e27d2fed8c4654875644c6c6e63b276c02aaab8b5efed5d38dc95674579ff724398198394aebec20c968d471
data/README.md CHANGED
@@ -4,7 +4,7 @@ This is an unofficial client library that interfaces with the [Todoist API](http
4
4
 
5
5
  ## What's implemented
6
6
 
7
- ### sync API
7
+ ### Sync API
8
8
 
9
9
  The "sync" API is almost fully implemented with the exception of collaboration features.
10
10
 
@@ -62,67 +62,60 @@ Or install it yourself as:
62
62
  This section provides some simple scenarios to get started. To use the library make sure you include the library as follows:
63
63
 
64
64
  ```ruby
65
- require todoist
65
+ require 'todoist'
66
66
  ```
67
67
 
68
68
  ### Logging in and setting tokens
69
69
 
70
- Before you make any API calls, you **must** login. The library supports two methods:
70
+ Before you make any API calls, you **must** create a client using one of two methods. The library supports two methods:
71
71
 
72
72
  #### Email and password
73
73
 
74
74
  ```ruby
75
- user_manager = Todoist::Misc::User.new
76
- user = user_manager.login("hello@example.com", "123")
77
- user.email
78
- => "hello@example.com"
75
+ @client = Todoist::Client.create_client_by_login("hello@example.com", "123")
79
76
  ```
80
77
 
81
- Upon calling the login method, an object is returned implemented through an [OpenStruct](http://ruby-doc.org/stdlib-2.0.0/libdoc/ostruct/rdoc/OpenStruct.html) that represents a variety of fields that may be useful to you.
82
-
83
78
  #### Token
84
79
 
85
- New tokens can be generated at the [Todoist App Management portal](https://developer.todoist.com/appconsole.html). Once a token has been acquired simply set it as so:
80
+ New tokens can be generated at the [Todoist App Management portal](https://developer.todoist.com/appconsole.html). Once a token has been acquired you can create a client by calling:
86
81
 
87
82
  ```ruby
88
- Todoist::Config.token = "my token"
83
+ @client = Todoist::Client.create_client_by_token("my token")
89
84
  ```
90
85
 
91
- ### Using the sync API
92
-
93
- The Todoist sync API enables you to mimic how the actual Todoist client retrieves information. Among other nuances, the sync API minimizes network traffic by batching a series of calls together. It supports dependencies between as-yet-created objects through temporary IDs.
86
+ ### Using the API
94
87
 
95
- Because of the way the API is designed, it is likely your application will need to use some combination of the sync api along with the other, lighterweight methods also provided.
88
+ The Todoist API enables you to mimic how the actual Todoist client retrieves information. Among other nuances, the "sync" API minimizes network traffic by batching a series of calls together. It supports dependencies between as-yet-created objects through temporary IDs. In addition to the sync API, Todoist also has several other methods that are lighterweight. For many light use cases, the lightweight methods will suffice but for more complex cases you will likely need to use both approaches.
96
89
 
97
- Managers provides in the sync API all exist in the module ```Todoist::Sync```.
90
+ All APIs can be accessed through the client. In general, the naming convention to access the service is ```[api_type]_[api]```.
98
91
 
99
92
  There are two ways to force a sync in the API:
100
93
 
101
- 1. ```manager_object.collection```: Calling collection forces the library to sync with the Todoist server to retrieve the latest. This method stores an internal in-memory copy of the result for incremental syncs but the caller should store a copy of the result in its own variable for query purposes.
102
- 2. ```Todoist::Util::CommandSynchronizer.sync```: Calling this method forcibly syncs the side-effects that have been queued.
94
+ 1. ```collection```: Calling collection forces the library to sync with the Todoist server to retrieve the latest. This method stores an internal in-memory copy of the result for incremental syncs but the caller should store a copy of the result in its own variable for query purposes. The following call syncs all items and returns a collection of the items: ```@client.sync_items.collection```.
95
+ 2. ```sync```: Calling the sync method on the client object forces a sync which can be called like ```@client.sync```
103
96
 
104
- When objects are called using the ```manager.object.add``` methods, a shallow object is created with a temporary id accessible by sending an ```id``` message. Once any of the above synchronization methods are called above, the ids are updated via a callback with their actual ids so they can be used in subsequent calls.
97
+ When objects are called using the ```add``` methods, a shallow object is created with a temporary id accessible by sending an ```id``` message. Once any of the above synchronization methods are called above, the ids are updated via a callback with their actual ids so they can be used in subsequent calls.
105
98
 
106
99
  #### Creating an item
107
100
 
108
101
  ```ruby
109
- update_item = @item_manager.add({content: "Item3"})
102
+ update_item = @client.sync_items.add({content: "Item3"})
110
103
  ## At this time update_item has a temporary id
111
104
 
112
105
  update_item.priority = 2
113
- result = @item_manager.update(update_item)
106
+ result = @client.sync_items.update(update_item)
114
107
  # Up until this point update_item has not been created yet
115
108
 
116
- items_list = @item_manager.collection
109
+ items_list = @client.sync_items.collection
117
110
  # Update item is created and a query is issued to sync up the existing items. The actual id of the newly created item is updated and so now update_item should have the actual id.
118
111
 
119
112
  queried_object = items_list[update_item.id]
120
113
  # update_item remains a shallow value object. To fully inflate the object, you will need to retrieve the item from the list. At this point, queried_object has a fully inflated copy of the object
121
114
 
122
- @item_manager.delete([update_item])
115
+ @client.sync_items.delete([update_item])
123
116
  # As is the case with other side-effects, issuing the call does not send the request immediately.
124
117
 
125
- Todoist::Util::CommandSynchronizer.sync
118
+ @client.sync
126
119
  # Manually calling sync deletes the item
127
120
  ```
128
121
 
@@ -135,8 +128,7 @@ The rest of the APIs are available in the ```Todoist::Misc``` module. For light
135
128
  #### Creating an item using the "quick" API
136
129
 
137
130
  ```ruby
138
- @misc_quick_manager = Todoist::Misc::Quick.new
139
- item = @misc_quick_manager.add_item("Test quick add content today")
131
+ item = @client.misc_quick.add_item("Test quick add content today")
140
132
  # Unlike the sync API the item is already created after this method call and fully inflated
141
133
  ```
142
134
  ### Rate limiting
@@ -192,7 +184,8 @@ Once tests pass cleanly, subsquent runs that do not change the network requests
192
184
 
193
185
  ## Version History
194
186
 
195
- * 0.1.3: Changed Todoist::Sync managers so that the update method uses a hash instead of an OpenStruct. The OpenStruct creates errors when an OpenStruct passed from a previous call is used. The hash helps the caller make fewer mistakes.
187
+ * 0.2.1: Major refactoring of library to support implementations that require multi-user support in a concurrent environment (e.g. Rails app). The previous implementation relied heavily on class singletons. Internally, the code has been cleaned up significantly. Due to the scale of changes, 0.2.1 is not compatible 0.1.x versions of the library.
188
+ * 0.1.3: Changed ```Todoist::Sync``` managers so that the update method uses a hash instead of an OpenStruct. The OpenStruct creates errors when an OpenStruct passed from a previous call is used. The hash helps the caller make fewer mistakes.
196
189
  * 0.1.2: Renamed method ```Todoist::Util::ParseHelper.make_objects_as_array``` to ```Todoist::Util::ParseHelper.make_objects_as_hash``` to reflect the fact that it was actually returning hashes. Added the aforementioned deleted method to return arrays and finally altered ```Todoist::Misc::Completed``` to return objects as arrays instead of hashes due to the fact that recurring completed items were being de-duped unintentionally and data was being lost as a result.
197
190
  * 0.1.1: Initial release.
198
191
 
data/lib/todoist.rb CHANGED
@@ -1,6 +1,10 @@
1
1
  require "todoist/version"
2
+ require "todoist/config"
2
3
  require "todoist/util/api_helper"
3
4
  require "todoist/util/network_helper"
5
+ require "todoist/service"
6
+ require "todoist/client"
7
+
4
8
  require "todoist/sync/items"
5
9
  require "todoist/sync/labels"
6
10
  require "todoist/sync/projects"
@@ -15,7 +19,6 @@ require "todoist/misc/items"
15
19
  require "todoist/misc/quick"
16
20
  require "todoist/misc/activity"
17
21
  require "todoist/misc/backups"
18
- require "todoist/misc/user"
19
22
  require "todoist/misc/query"
20
23
 
21
24
  module Todoist
@@ -0,0 +1,119 @@
1
+ module Todoist
2
+ class Client
3
+
4
+ def self.create_client_by_token(token)
5
+ client = Client.new
6
+ client.token = token
7
+ client
8
+ end
9
+
10
+ # TODO: Need to write a unit test for this
11
+ def self.create_client_by_login(email, password)
12
+ client = Client.new
13
+ result = client.api_helper.get_response(Config::TODOIST_USER_LOGIN_COMMAND, {email: email, password: password}, false)
14
+ user = Todoist::Util::ParseHelper.make_object(result)
15
+ client.token = user.token
16
+ client
17
+ end
18
+
19
+ def token=(token)
20
+ @token = token
21
+ end
22
+
23
+ def token
24
+ @token
25
+ end
26
+
27
+ def sync
28
+ @api_helper.sync
29
+ end
30
+
31
+ def misc_activity
32
+ @misc_activity = Todoist::Misc::Activity.new(self) unless @misc_activity
33
+ @misc_activity
34
+ end
35
+
36
+ def misc_backups
37
+ @misc_backups = Todoist::Misc::Backups.new(self) unless @misc_backups
38
+ @misc_backups
39
+ end
40
+
41
+ def misc_completed
42
+ @misc_completed = Todoist::Misc::Completed.new(self) unless @misc_completed
43
+ @misc_completed
44
+ end
45
+
46
+ def misc_items
47
+ @misc_items = Todoist::Misc::Items.new(self) unless @misc_items
48
+ @misc_items
49
+ end
50
+
51
+ def misc_projects
52
+ @misc_projects = Todoist::Misc::Projects.new(self) unless @misc_projects
53
+ @misc_projects
54
+ end
55
+
56
+ def misc_query
57
+ @misc_query = Todoist::Misc::Query.new(self) unless @misc_query
58
+ @misc_query
59
+ end
60
+
61
+ def misc_quick
62
+ @misc_quick = Todoist::Misc::Quick.new(self) unless @misc_quick
63
+ @misc_quick
64
+ end
65
+
66
+ def misc_templates
67
+ @misc_templates = Todoist::Misc::Templates.new(self) unless @misc_templates
68
+ @misc_templates
69
+ end
70
+
71
+ def misc_uploads
72
+ @misc_uploads = Todoist::Misc::Uploads.new(self) unless @misc_uploads
73
+ @misc_uploads
74
+ end
75
+
76
+ def sync_filters
77
+ @sync_filters = Todoist::Sync::Filters.new(self) unless @sync_filters
78
+ @sync_filters
79
+ end
80
+
81
+ def sync_items
82
+ @sync_items = Todoist::Sync::Items.new(self) unless @sync_items
83
+ @sync_items
84
+ end
85
+
86
+ def sync_labels
87
+ @sync_labels = Todoist::Sync::Labels.new(self) unless @sync_labels
88
+ @sync_labels
89
+ end
90
+
91
+ def sync_notes
92
+ @sync_notes = Todoist::Sync::Notes.new(self) unless @sync_notes
93
+ @sync_notes
94
+ end
95
+
96
+ def sync_projects
97
+ @sync_projects = Todoist::Sync::Projects.new(self) unless @sync_projects
98
+ @sync_projects
99
+ end
100
+
101
+ def sync_reminders
102
+ @sync_reminders = Todoist::Sync::Reminders.new(self) unless @sync_reminders
103
+ @sync_reminders
104
+ end
105
+
106
+ def api_helper
107
+ @api_helper
108
+ end
109
+
110
+ protected
111
+
112
+ def initialize
113
+ @api_helper = Todoist::Util::ApiHelper.new(self)
114
+ end
115
+
116
+
117
+
118
+ end
119
+ end
@@ -0,0 +1,65 @@
1
+ module Todoist
2
+ class Config
3
+ TODOIST_API_URL = "https://todoist.com/API/v7"
4
+
5
+ # List of commands supported
6
+ @@command_list = [
7
+ TODOIST_SYNC_COMMAND = "/sync",
8
+ TODOIST_QUERY_COMMAND = "/query",
9
+ TODOIST_TEMPLATES_IMPORT_INTO_PROJECT_COMMAND = "/templates/import_into_project",
10
+ TODOIST_TEMPLATES_EXPORT_AS_FILE_COMMAND = "/templates/export_as_file",
11
+ TODOIST_TEMPLATES_EXPORT_AS_URL_COMMAND = "/templates/export_as_url",
12
+ TODOIST_UPLOADS_ADD_COMMAND = "/uploads/add",
13
+ TODOIST_UPLOADS_GET_COMMAND = "/uploads/get",
14
+ TODOIST_UPLOADS_DELETE_COMMAND = "/uploads/delete",
15
+ TODOIST_COMPLETED_GET_STATS_COMMAND = "/completed/get_stats",
16
+ TODOIST_COMPLETED_GET_ALL_COMMAND = "/completed/get_all",
17
+ TODOIST_PROJECTS_GET_ARCHIVED_COMMAND = "/projects/get_archived",
18
+ TODOIST_PROJECTS_GET_COMMAND = "/projects/get",
19
+ TODOIST_PROJECTS_GET_DATA_COMMAND = "/projects/get_data",
20
+ TODOIST_ITEMS_ADD_COMMAND = "/items/add",
21
+ TODOIST_ITEMS_GET_COMMAND = "/items/get",
22
+ TODOIST_QUICK_ADD_COMMAND = "/quick/add",
23
+ TODOIST_ACTIVITY_GET_COMMAND = "/activity/get",
24
+ TODOIST_BACKUPS_GET_COMMAND = "/backups/get",
25
+ TODOIST_USER_LOGIN_COMMAND = "/user/login"
26
+ ]
27
+
28
+ # Map of commands to URIs
29
+ @@uri = nil
30
+
31
+ # Artificial delay between requests to avoid API throttling
32
+ @@delay_between_requests = 0
33
+
34
+ # Should API throttling happen (HTTP Error 429), retry_time between requests
35
+ # with exponential backoff
36
+ @@retry_time = 20
37
+
38
+ def self.retry_time=(retry_time)
39
+ @@retry_time = retry_time
40
+ end
41
+
42
+ def self.retry_time
43
+ @@retry_time
44
+ end
45
+
46
+ def self.delay_between_requests=(delay_between_requests)
47
+ @@delay_between_requests = delay_between_requests
48
+ end
49
+
50
+ def self.delay_between_requests
51
+ @@delay_between_requests
52
+ end
53
+
54
+ def self.getURI
55
+ if @@uri == nil
56
+ @@uri = {}
57
+ @@command_list.each do |command|
58
+ @@uri[command] = URI.parse(TODOIST_API_URL + command)
59
+ end
60
+ end
61
+ return @@uri
62
+ end
63
+
64
+ end
65
+ end
@@ -1,6 +1,6 @@
1
1
  module Todoist
2
2
  module Misc
3
- class Activity
3
+ class Activity < Todoist::Service
4
4
  include Todoist::Util
5
5
 
6
6
  # Returns the activity logs for a user. Full list of supported
@@ -16,11 +16,11 @@ module Todoist
16
16
 
17
17
  def get(params={})
18
18
  if params["until"]
19
- params["until"] = ParseHelper.formatTime(params["until"])
19
+ params["until"] = ParseHelper.format_time(params["until"])
20
20
  end
21
21
 
22
22
  if params["since"]
23
- params["since"] = ParseHelper.formatTime(params["since"])
23
+ params["since"] = ParseHelper.format_time(params["since"])
24
24
  end
25
25
 
26
26
  if params["object"]
@@ -43,7 +43,7 @@ module Todoist
43
43
  params.delete("initiator")
44
44
  end
45
45
 
46
- result = NetworkHelper.getResponse(Config::TODOIST_ACTIVITY_GET_COMMAND, params)
46
+ result = @client.api_helper.get_response(Config::TODOIST_ACTIVITY_GET_COMMAND, params)
47
47
  ParseHelper.make_objects_as_hash(result)
48
48
  end
49
49
  end
@@ -1,11 +1,11 @@
1
1
  module Todoist
2
2
  module Misc
3
- class Backups
3
+ class Backups < Todoist::Service
4
4
  include Todoist::Util
5
5
 
6
6
  # Returns the backups for a user.
7
7
  def get()
8
- result = NetworkHelper.getResponse(Config::TODOIST_BACKUPS_GET_COMMAND, {})
8
+ result = @client.api_helper.get_response(Config::TODOIST_BACKUPS_GET_COMMAND, {})
9
9
  ParseHelper.make_objects_as_hash(result)
10
10
  end
11
11
  end
@@ -1,12 +1,12 @@
1
1
  module Todoist
2
2
  module Misc
3
- class Completed
3
+ class Completed < Todoist::Service
4
4
  include Todoist::Util
5
5
 
6
6
  # Get productivity stats. Returns a hash of statistics as documented
7
7
  # at https://developer.todoist.com/#get-productivity-stats
8
8
  def get_productivity_stats()
9
- NetworkHelper.getResponse(Config::TODOIST_COMPLETED_GET_STATS_COMMAND)
9
+ @client.api_helper.get_response(Config::TODOIST_COMPLETED_GET_STATS_COMMAND, {})
10
10
  end
11
11
 
12
12
  # Retrieves all completed items as documented at
@@ -17,13 +17,13 @@ module Todoist
17
17
 
18
18
  def get_all_completed_items(params = {})
19
19
  if params["until"]
20
- params["until"] = ParseHelper.formatTime(params["until"])
20
+ params["until"] = ParseHelper.format_time(params["until"])
21
21
  end
22
22
  if params["since"]
23
- params["since"] = ParseHelper.formatTime(params["since"])
23
+ params["since"] = ParseHelper.format_time(params["since"])
24
24
  end
25
25
 
26
- result = NetworkHelper.getResponse(Config::TODOIST_COMPLETED_GET_ALL_COMMAND, params)
26
+ result = @client.api_helper.get_response(Config::TODOIST_COMPLETED_GET_ALL_COMMAND, params)
27
27
  items = ParseHelper.make_objects_as_array(result["items"])
28
28
  projects = ParseHelper.make_objects_as_array(result["projects"])
29
29
  return {"items" => items, "projects" => projects}
@@ -1,6 +1,6 @@
1
1
  module Todoist
2
2
  module Misc
3
- class Items
3
+ class Items < Todoist::Service
4
4
  include Todoist::Util
5
5
 
6
6
  # Add a new task to a project. Note, that this is provided as a
@@ -30,7 +30,7 @@ module Todoist
30
30
  end
31
31
 
32
32
  params.merge(optional_params)
33
- result = NetworkHelper.getResponse(Config::TODOIST_ITEMS_ADD_COMMAND, params)
33
+ result = @client.api_helper.get_response(Config::TODOIST_ITEMS_ADD_COMMAND, params)
34
34
  item = ParseHelper.make_object(result)
35
35
  return item
36
36
  end
@@ -43,7 +43,7 @@ module Todoist
43
43
  def get_item(item, all_data = true)
44
44
  params = {item_id: item.id, all_data: all_data}
45
45
 
46
- result = NetworkHelper.getResponse(Config::TODOIST_ITEMS_GET_COMMAND, params)
46
+ result = @client.api_helper.get_response(Config::TODOIST_ITEMS_GET_COMMAND, params)
47
47
  item = ParseHelper.make_object(result["item"])
48
48
  project = ParseHelper.make_object(result["project"])
49
49
  notes = result["notes"] ? ParseHelper.make_objects_as_hash(result["notes"]) : nil
@@ -1,18 +1,18 @@
1
1
  module Todoist
2
2
  module Misc
3
- class Projects
3
+ class Projects < Todoist::Service
4
4
  include Todoist::Util
5
5
 
6
6
  # Get archived projects. Returns projects as documented here.
7
7
  def get_archived_projects()
8
- result = NetworkHelper.getResponse(Config::TODOIST_PROJECTS_GET_ARCHIVED_COMMAND)
8
+ result = @client.api_helper.get_response(Config::TODOIST_PROJECTS_GET_ARCHIVED_COMMAND)
9
9
  return ParseHelper.make_objects_as_hash(result)
10
10
  end
11
11
 
12
12
  # Gets project information including all notes.
13
13
 
14
14
  def get_project_info(project, all_data = true)
15
- result = NetworkHelper.getResponse(Config::TODOIST_PROJECTS_GET_COMMAND, {project_id: project.id, all_data: true})
15
+ result = @client.api_helper.get_response(Config::TODOIST_PROJECTS_GET_COMMAND, {project_id: project.id, all_data: true})
16
16
 
17
17
  project = result["project"] ? ParseHelper.make_object(result["project"]) : nil
18
18
  notes = result["notes"] ? ParseHelper.make_objects_as_hash(result["notes"]) : nil
@@ -21,7 +21,7 @@ module Todoist
21
21
 
22
22
  # Gets a project's uncompleted items
23
23
  def get_project_data(project)
24
- result = NetworkHelper.getResponse(Config::TODOIST_PROJECTS_GET_DATA_COMMAND, {project_id: project.id})
24
+ result = @client.api_helper.get_response(Config::TODOIST_PROJECTS_GET_DATA_COMMAND, {project_id: project.id})
25
25
  project = result["project"] ? ParseHelper.make_object(result["project"]) : nil
26
26
  items = result["items"] ? ParseHelper.make_objects_as_hash(result["items"]) : nil
27
27
  return {"project" => project, "items" => items}