todoist-ruby 0.1.3 → 0.2.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.
@@ -1,27 +1,27 @@
1
1
  module Todoist
2
2
  module Sync
3
- class Labels
3
+ class Labels < Todoist::Service
4
4
  include Todoist::Util
5
5
 
6
6
  # Return a Hash of labels where key is the id of a label and value is a label
7
7
  def collection
8
- return ApiHelper.collection("labels")
8
+ return @client.api_helper.collection("labels")
9
9
  end
10
10
 
11
11
  # Add a label with a given hash of attributes and returns the label id
12
12
  def add(args)
13
- return ApiHelper.add(args, "label_add")
13
+ return @client.api_helper.add(args, "label_add")
14
14
  end
15
15
 
16
16
  # Update label given a hash of attributes
17
17
  def update(args)
18
- return ApiHelper.command(args, "label_update")
18
+ return @client.api_helper.command(args, "label_update")
19
19
  end
20
20
 
21
21
  # Delete a label given a label
22
22
  def delete(label)
23
23
  args = {id: label.id}
24
- return ApiHelper.command(args, "label_delete")
24
+ return @client.api_helper.command(args, "label_delete")
25
25
  end
26
26
 
27
27
  # Update orders for an array of labels
@@ -31,7 +31,7 @@ module Todoist
31
31
  args[label.id] = label.item_order
32
32
  end
33
33
  args = {id_order_mapping: args.to_json}
34
- return ApiHelper.command(args, "label_update_orders")
34
+ return @client.api_helper.command(args, "label_update_orders")
35
35
  end
36
36
 
37
37
  end
@@ -1,29 +1,29 @@
1
1
  module Todoist
2
2
  module Sync
3
- class Notes
3
+ class Notes < Todoist::Service
4
4
  include Todoist::Util
5
5
 
6
6
  # Return a Hash of notes where key is the id of a note and value is a note
7
7
  def collection
8
- return ApiHelper.collection("notes")
8
+ return @client.api_helper.collection("notes")
9
9
  end
10
10
 
11
11
  # Add a note with a given hash of attributes and returns the note id.
12
12
  # Please note that item_id or project_id key is required. In addition,
13
13
  # content is also a required key in the hash.
14
14
  def add(args)
15
- return ApiHelper.add(args, "note_add")
15
+ return @client.api_helper.add(args, "note_add")
16
16
  end
17
17
 
18
18
  # Update a note given a hash of attributes
19
19
  def update(args)
20
- return ApiHelper.command(args, "note_update")
20
+ return @client.api_helper.command(args, "note_update")
21
21
  end
22
22
 
23
23
  # Delete notes given an a note
24
24
  def delete(note)
25
25
  args = {id: note.id}
26
- return ApiHelper.command(args, "note_delete")
26
+ return @client.api_helper.command(args, "note_delete")
27
27
  end
28
28
 
29
29
 
@@ -2,43 +2,43 @@ module Todoist
2
2
  module Sync
3
3
 
4
4
 
5
- class Projects
5
+ class Projects < Todoist::Service
6
6
  include Todoist::Util
7
7
 
8
8
  # Return a Hash of projects where key is the id of a project and value is a project
9
9
  def collection
10
- return ApiHelper.collection("projects")
10
+ return @client.api_helper.collection("projects")
11
11
  end
12
12
 
13
13
  # Add a project with a given hash of attributes and returns the project id
14
14
  def add(args)
15
- return ApiHelper.add(args, "project_add")
15
+ return @client.api_helper.add(args, "project_add")
16
16
  end
17
17
 
18
18
  # Delete projects given an array of projects
19
19
  def delete(projects)
20
20
  project_ids = projects.collect { |project| project.id }
21
21
  args = {ids: project_ids.to_json}
22
- return ApiHelper.command(args, "project_delete")
22
+ return @client.api_helper.command(args, "project_delete")
23
23
  end
24
24
 
25
25
  # Archive projects given an array of projects
26
26
  def archive(projects)
27
27
  project_ids = projects.collect { |project| project.id }
28
28
  args = {ids: project_ids.to_json}
29
- return ApiHelper.command(args, "project_archive")
29
+ return @client.api_helper.command(args, "project_archive")
30
30
  end
31
31
 
32
32
  # Unarchive projects given an array of projects
33
33
  def unarchive(projects)
34
34
  project_ids = projects.collect { |project| project.id }
35
35
  args = {ids: project_ids.to_json}
36
- return ApiHelper.command(args, "project_unarchive")
36
+ return @client.api_helper.command(args, "project_unarchive")
37
37
  end
38
38
 
39
39
  # Update project given a hash of attributes
40
40
  def update(args)
41
- return ApiHelper.command(args, "project_update")
41
+ return @client.api_helper.command(args, "project_update")
42
42
  end
43
43
 
44
44
  # Update orders and indents for an array of projects
@@ -48,7 +48,7 @@ module Todoist
48
48
  tuples[project.id] = [project.item_order, project.indent]
49
49
  end
50
50
  args = {ids_to_orders_indents: tuples.to_json}
51
- return ApiHelper.command(args, "project_update_orders_indents")
51
+ return @client.api_helper.command(args, "project_update_orders_indents")
52
52
  end
53
53
 
54
54
  end
@@ -1,35 +1,35 @@
1
1
  module Todoist
2
2
  module Sync
3
- class Reminders
3
+ class Reminders < Todoist::Service
4
4
  include Todoist::Util
5
5
 
6
6
  # Return a Hash of reminders where key is the id of a reminder and value is a reminder
7
7
  def collection
8
- return ApiHelper.collection("reminders")
8
+ return @client.api_helper.collection("reminders")
9
9
  end
10
10
 
11
11
  # Add a reminder with a given hash of attributes and returns the reminder id.
12
12
  # Please note that item_id is required as is a date as specific in the
13
13
  # documentation. This method can be tricky to all.
14
14
  def add(args)
15
- return ApiHelper.add(args, "reminder_add")
15
+ return @client.api_helper.add(args, "reminder_add")
16
16
  end
17
17
 
18
18
  # Update a reminder given a hash of attributes
19
19
  def update(args)
20
- return ApiHelper.command(args, "reminder_update")
20
+ return @client.api_helper.command(args, "reminder_update")
21
21
  end
22
22
 
23
23
  # Delete reminder given an array of reminders
24
24
  def delete(reminder)
25
25
  args = {id: reminder.id}
26
- return ApiHelper.command(args, "reminder_delete")
26
+ return @client.api_helper.command(args, "reminder_delete")
27
27
  end
28
28
 
29
29
  # Clear locations which is used for location reminders
30
30
  def clear_locations
31
31
  args = {}
32
- return ApiHelper.command(args, "clear_locations")
32
+ return @client.api_helper.command(args, "clear_locations")
33
33
  end
34
34
  end
35
35
  end
@@ -1,10 +1,9 @@
1
1
  require "net/http"
2
2
  require "json"
3
- require "todoist/util/config"
3
+ require "todoist/config"
4
4
  require "todoist/util/network_helper"
5
5
  require "todoist/util/parse_helper"
6
6
  require "todoist/util/uuid"
7
- require "todoist/util/command_synchronizer"
8
7
  require "ostruct"
9
8
  require 'concurrent'
10
9
 
@@ -15,18 +14,21 @@ module Todoist
15
14
  module Util
16
15
 
17
16
  class ApiHelper
18
-
19
- @@object_cache = {"projects" => Concurrent::Hash.new({}), "labels" => Concurrent::Hash.new({}),
17
+ def initialize(client)
18
+ @client = client
19
+ @object_cache = {"projects" => Concurrent::Hash.new({}), "labels" => Concurrent::Hash.new({}),
20
20
  "items" => Concurrent::Hash.new({}), "notes" => Concurrent::Hash.new({}),
21
21
  "reminders" => Concurrent::Hash.new({}), "filters" => Concurrent::Hash.new({})
22
- }
23
- @@sync_token_cache = Concurrent::Hash.new({"projects" => "*", "labels" => "*",
24
- "items" => "*", "notes" => "*", "reminders" => "*", "filters" => "*"})
25
-
26
- def self.collection(type)
27
- CommandSynchronizer.sync
22
+ }
23
+ @sync_token_cache = Concurrent::Hash.new({"projects" => "*", "labels" => "*",
24
+ "items" => "*", "notes" => "*", "reminders" => "*", "filters" => "*"})
25
+ @network_helper = NetworkHelper.new(client)
26
+ end
27
+
28
+ def collection(type)
29
+ @network_helper.sync
28
30
 
29
- response = getSyncResponse({sync_token: sync_token(type), resource_types: "[\"#{type}\"]"})
31
+ response = @network_helper.get_sync_response({sync_token: sync_token(type), resource_types: "[\"#{type}\"]"})
30
32
  response[type].each do |object_data|
31
33
  object = OpenStruct.new(object_data)
32
34
  objects(type)[object.id] = object
@@ -35,23 +37,24 @@ module Todoist
35
37
  return objects(type)
36
38
  end
37
39
 
38
- def self.exec(args, command, temporary_resource_id)
40
+ def exec(args, command, temporary_resource_id)
39
41
  command_uuid = Uuid.command_uuid
40
42
  commands = {type: command, temp_id: temporary_resource_id, uuid: command_uuid, args: args}
41
- response = getSyncResponse({commands: "[#{commands.to_json}]"})
43
+ response = @network_helper.get_sync_response({commands: "[#{commands.to_json}]"})
42
44
  raise RuntimeError, "Response returned is not ok" unless response["sync_status"][command_uuid] == "ok"
43
45
  return response
44
46
  end
45
47
 
46
- def self.command(args, command)
48
+ def command(args, command)
47
49
  temporary_resource_id = Uuid.temporary_resource_id
48
50
  command_uuid = Uuid.command_uuid
49
51
  command = {type: command, temp_id: temporary_resource_id, uuid: command_uuid, args: args}
50
- CommandSynchronizer.queue(command)
52
+
53
+ @network_helper.queue(command)
51
54
  return true
52
55
  end
53
56
 
54
- def self.add(args, command)
57
+ def add(args, command)
55
58
  temporary_resource_id = Uuid.temporary_resource_id
56
59
  command_uuid = Uuid.command_uuid
57
60
  command = {type: command, temp_id: temporary_resource_id, uuid: command_uuid, args: args}
@@ -60,28 +63,38 @@ module Todoist
60
63
  object.id = temp_id_mappings[temporary_resource_id] if temp_id_mappings[temporary_resource_id]
61
64
  end
62
65
 
63
- CommandSynchronizer.queue(command, temp_id_callback)
66
+ @network_helper.queue(command, temp_id_callback)
64
67
  return object
65
68
  end
66
69
 
67
- def self.getSyncResponse(params)
68
- NetworkHelper.getResponse(Config::TODOIST_SYNC_COMMAND, params)
70
+ def get_response(command, params = {}, token = true)
71
+ @network_helper.get_response(command, params, token)
69
72
  end
70
73
 
71
- protected
74
+ def get_multipart_response(command, params)
75
+ @network_helper.get_multipart_response(command, params)
76
+ end
72
77
 
78
+ def multipart_file(file)
79
+ @network_helper.multipart_file(file)
80
+ end
73
81
 
82
+ def sync
83
+ @network_helper.sync
84
+ end
85
+
86
+ protected
74
87
 
75
- def self.objects(type)
76
- @@object_cache[type]
88
+ def objects(type)
89
+ @object_cache[type]
77
90
  end
78
91
 
79
- def self.sync_token(type)
80
- @@sync_token_cache[type]
92
+ def sync_token(type)
93
+ @sync_token_cache[type]
81
94
  end
82
95
 
83
- def self.set_sync_token(type, value)
84
- @@sync_token_cache[type] = value
96
+ def set_sync_token(type, value)
97
+ @sync_token_cache[type] = value
85
98
  end
86
99
 
87
100
  end
@@ -1,23 +1,33 @@
1
1
  require "net/http"
2
2
  require "json"
3
- require "todoist/util/config"
3
+ require "todoist/config"
4
4
  require 'net/http/post/multipart'
5
5
  require 'mimemagic'
6
+ require 'openssl'
6
7
 
7
8
  module Todoist
8
9
  module Util
9
10
  class NetworkHelper
10
11
 
11
- @@last_request_time = 0.0
12
12
 
13
- def self.configureHTTP(command)
13
+
14
+ def initialize(client)
15
+ @client = client
16
+ @command_cache = Concurrent::Array.new([])
17
+ @command_mutex = Mutex.new
18
+ @temp_id_callback_cache = Concurrent::Array.new([])
19
+ @last_request_time = 0.0
20
+
21
+ end
22
+
23
+ def configure_http(command)
14
24
  http = Net::HTTP.new(Config.getURI()[command].host, Config.getURI()[command].port)
15
25
  http.use_ssl = true
16
26
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE
17
27
  return http
18
28
  end
19
29
 
20
- def self.configureRequest(command, params)
30
+ def configure_request(command, params)
21
31
  request = Net::HTTP::Post.new(Config.getURI()[command].request_uri)
22
32
  request.set_form_data(params)
23
33
  return request
@@ -25,9 +35,9 @@ module Todoist
25
35
 
26
36
  # Files need to be of class UploadIO
27
37
 
28
- def self.getMultipartResponse(command, params={})
29
- token = {token: Todoist::Util::Config.token}
30
- http = configureHTTP(command)
38
+ def get_multipart_response(command, params={})
39
+ token = {token: @client.token}
40
+ http = configure_http(command)
31
41
  url = Config.getURI()[command]
32
42
  http.start do
33
43
  req = Net::HTTP::Post::Multipart.new(url, token.merge(params))
@@ -40,11 +50,11 @@ module Todoist
40
50
  end
41
51
  end
42
52
 
43
- def self.getResponse(command, params ={}, token = true)
44
- token = token ? {token: Todoist::Util::Config.token} : {}
45
- http = configureHTTP(command)
46
- request = configureRequest(command, token.merge(params))
47
- retry_after_secs = Todoist::Util::Config.retry_time
53
+ def get_response(command, params ={}, token = true)
54
+ token = token ? {token: @client.token} : {}
55
+ http = configure_http(command)
56
+ request = configure_request(command, token.merge(params))
57
+ retry_after_secs = Todoist::Config.retry_time
48
58
  # Hack to fix encoding issues with Net:HTTP for login case
49
59
  request.body = request.body.gsub '%40', '@' unless token
50
60
  while true
@@ -67,7 +77,7 @@ module Todoist
67
77
  when 429
68
78
  puts("Encountered 429 - retry after #{retry_after_secs}")
69
79
  sleep(retry_after_secs)
70
- retry_after_secs *= Todoist::Util::Config.retry_time
80
+ retry_after_secs *= Todoist::Config.retry_time
71
81
  when 500
72
82
  raise StandardError, "HTTP 500 Error - The request failed due to a server error."
73
83
  when 503
@@ -77,24 +87,50 @@ module Todoist
77
87
 
78
88
  end
79
89
 
80
- def self.throttle_request(http, request)
81
- time_since_last_request = Time.now.to_f - @@last_request_time
90
+ def throttle_request(http, request)
91
+ time_since_last_request = Time.now.to_f - @last_request_time
82
92
 
83
- if (time_since_last_request < Todoist::Util::Config.delay_between_requests)
84
- wait = Todoist::Util::Config.delay_between_requests - time_since_last_request
93
+ if (time_since_last_request < Todoist::Config.delay_between_requests)
94
+ wait = Todoist::Config.delay_between_requests - time_since_last_request
85
95
  puts("Throttling request by: #{wait}")
86
96
  sleep(wait)
87
97
  end
88
- @@last_request_time = Time.now.to_f
98
+ @last_request_time = Time.now.to_f
89
99
  http.request(request)
90
100
  end
91
101
 
92
102
  # Prepares a file for multipart upload
93
- def self.multipart_file(file)
103
+ def multipart_file(file)
94
104
  filename = File.basename(file)
95
105
  mime_type = MimeMagic.by_path(filename).type
96
106
  return UploadIO.new(file, mime_type, filename)
97
107
  end
108
+
109
+ def queue(command, callback = nil)
110
+ @command_mutex.synchronize do
111
+ @command_cache.push(command)
112
+ @temp_id_callback_cache.push(callback) if callback
113
+ end
114
+
115
+ end
116
+
117
+ def sync
118
+ @command_mutex.synchronize do
119
+ response = get_sync_response({commands: @command_cache.to_json})
120
+ @command_cache.clear
121
+ # Process callbacks here
122
+ temp_id_mappings = response["temp_id_mapping"]
123
+ @temp_id_callback_cache.each do |callback|
124
+ callback.(temp_id_mappings)
125
+ end
126
+ @temp_id_callback_cache.clear
127
+ end
128
+ end
129
+
130
+ def get_sync_response(params)
131
+ get_response(Config::TODOIST_SYNC_COMMAND, params)
132
+ end
133
+
98
134
  end
99
135
  end
100
136
  end
@@ -4,11 +4,11 @@ module Todoist
4
4
  module Util
5
5
  class ParseHelper
6
6
 
7
- def self.utcOffsetHours
7
+ def self.utc_offset_hours
8
8
  return Time.now.utc_offset/60/60
9
9
  end
10
10
 
11
- def self.parseTodoistDate(item, key)
11
+ def self.parse_todoist_date(item, key)
12
12
  if item[key]
13
13
  time = Time.parse(item[key])
14
14
  return time.to_datetime
@@ -18,17 +18,17 @@ module Todoist
18
18
  end
19
19
 
20
20
 
21
- def self.filterToday(item, key)
21
+ def self.filter_today(item, key)
22
22
 
23
23
  now = DateTime.now
24
- if parseTodoistDate(item, key) && parseTodoistDate(item, key) <= DateTime.new(now.year, now.month, now.day, -utcOffsetHours) + 1
24
+ if parse_todoist_date(item, key) && parse_todoist_date(item, key) <= DateTime.new(now.year, now.month, now.day, -utc_offset_hours) + 1
25
25
  return true
26
26
  else
27
27
  return false
28
28
  end
29
29
  end
30
30
 
31
- def self.formatTime(datetime)
31
+ def self.format_time(datetime)
32
32
  datetime.strftime("%Y-%m-%dT%H:%M")
33
33
  end
34
34