todoist-ruby 0.1.3 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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