ruby-todoist-api 0.3

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.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.kick +18 -0
  4. data/.rspec +3 -0
  5. data/.travis.yml +6 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +45 -0
  9. data/Rakefile +2 -0
  10. data/lib/todoist.rb +27 -0
  11. data/lib/todoist/client.rb +94 -0
  12. data/lib/todoist/command.rb +24 -0
  13. data/lib/todoist/error.rb +0 -0
  14. data/lib/todoist/filter.rb +16 -0
  15. data/lib/todoist/item.rb +54 -0
  16. data/lib/todoist/label.rb +15 -0
  17. data/lib/todoist/note.rb +19 -0
  18. data/lib/todoist/project.rb +32 -0
  19. data/lib/todoist/query.rb +47 -0
  20. data/lib/todoist/queue.rb +39 -0
  21. data/lib/todoist/reminder.rb +16 -0
  22. data/lib/todoist/request.rb +37 -0
  23. data/lib/todoist/resource.rb +73 -0
  24. data/lib/todoist/service/base_service.rb +77 -0
  25. data/lib/todoist/service/filter.rb +9 -0
  26. data/lib/todoist/service/item.rb +24 -0
  27. data/lib/todoist/service/label.rb +9 -0
  28. data/lib/todoist/service/note.rb +9 -0
  29. data/lib/todoist/service/project.rb +9 -0
  30. data/lib/todoist/service/reminder.rb +9 -0
  31. data/lib/todoist/user.rb +10 -0
  32. data/lib/todoist/version.rb +3 -0
  33. data/ruby-todoist-api.gemspec +22 -0
  34. data/spec/lib/todoist/client_spec.rb +58 -0
  35. data/spec/lib/todoist/filter_spec.rb +19 -0
  36. data/spec/lib/todoist/item_spec.rb +39 -0
  37. data/spec/lib/todoist/label_spec.rb +12 -0
  38. data/spec/lib/todoist/note_spec.rb +12 -0
  39. data/spec/lib/todoist/project_spec.rb +12 -0
  40. data/spec/lib/todoist/query_spec.rb +26 -0
  41. data/spec/lib/todoist/reminder_spec.rb +12 -0
  42. data/spec/lib/todoist/service/filter_spec.rb +33 -0
  43. data/spec/lib/todoist/service/item_spec.rb +46 -0
  44. data/spec/lib/todoist/service/label_spec.rb +33 -0
  45. data/spec/lib/todoist/service/note_spec.rb +33 -0
  46. data/spec/lib/todoist/service/project_spec.rb +33 -0
  47. data/spec/lib/todoist/service/reminder_spec.rb +33 -0
  48. data/spec/lib/todoist_spec.rb +7 -0
  49. data/spec/spec_helper.rb +8 -0
  50. data/spec/support/json_response_helper.rb +17 -0
  51. data/spec/support/responses/filter.json +9 -0
  52. data/spec/support/responses/item.json +25 -0
  53. data/spec/support/responses/label.json +8 -0
  54. data/spec/support/responses/note.json +18 -0
  55. data/spec/support/responses/project.json +14 -0
  56. data/spec/support/responses/query.json +88 -0
  57. data/spec/support/responses/reminder.json +9 -0
  58. metadata +153 -0
@@ -0,0 +1,39 @@
1
+ module Todoist
2
+ class Queue
3
+ extend Forwardable
4
+
5
+ def_delegators :@queue, :clear, :first, :last, :shift, :size, :length
6
+
7
+ def initialize(client)
8
+ @client = client
9
+ end
10
+
11
+ def queue
12
+ @queue ||= []
13
+ end
14
+
15
+ def path
16
+ "/API/v6/sync"
17
+ end
18
+
19
+ def length
20
+ queue.length
21
+ end
22
+
23
+ def process!
24
+ return if queue == []
25
+ commands = queue
26
+ clear_queue!
27
+ @client.post(path, { commands: commands.map(&:to_hash).to_json})
28
+ end
29
+
30
+ def clear_queue!
31
+ @queue = []
32
+ end
33
+
34
+ def add(command)
35
+ @queue ||= []
36
+ @queue << command
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,16 @@
1
+ module Todoist
2
+ class Reminder
3
+ include Todoist::Resource
4
+ extend Todoist::Resource::ClassMethods
5
+
6
+ define_attributes %w(
7
+ id
8
+ user_id
9
+ name
10
+ query
11
+ color
12
+ item_order
13
+ is_deleted
14
+ )
15
+ end
16
+ end
@@ -0,0 +1,37 @@
1
+ require 'json'
2
+
3
+ module Todoist
4
+ class Request
5
+ SUCCES_STATUS_CODE = 200
6
+
7
+ attr_accessor :path, :net_http_method
8
+
9
+ def initialize(path, net_http_method)
10
+ self.path = path
11
+ self.net_http_method = net_http_method
12
+ end
13
+
14
+ def self.post(path, form_data, request_class = Net::HTTP::Post)
15
+ request = request_class.new(path)
16
+ request.set_form_data(form_data)
17
+
18
+ new(path, request)
19
+ end
20
+
21
+ def execute(base_url)
22
+ uri = URI.parse("#{base_url}#{path}")
23
+
24
+ response = http(uri).request(net_http_method)
25
+ end
26
+
27
+
28
+ def http(uri)
29
+ net = Net::HTTP.new(uri.host, uri.port)
30
+ net.use_ssl = true
31
+ net.verify_mode = OpenSSL::SSL::VERIFY_NONE
32
+ net.read_timeout = 90
33
+ net.open_timeout = 30
34
+ net
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,73 @@
1
+ require 'logger'
2
+ module Todoist
3
+ module Resource
4
+ module ClassMethods
5
+ attr_reader :attributes, :read_only_attributes
6
+
7
+ def define_attributes(attributes)
8
+ attr_accessor(*@attributes = attributes)
9
+ end
10
+
11
+ def define_read_only_attributes(attributes)
12
+ @read_only_attributes = attributes
13
+ end
14
+ end
15
+
16
+ attr_accessor :temp_id
17
+
18
+ def initialize(attributes={})
19
+ self.attributes = attributes || {}
20
+ end
21
+
22
+ def attributes
23
+ self.class.attributes.inject({}) do |attributes, attribute|
24
+ value = send(attribute)
25
+ if !value.nil?
26
+ attributes[attribute] = value
27
+ end; attributes
28
+ end
29
+ end
30
+
31
+ def attributes=(attributes)
32
+ attributes.each do |attribute, value|
33
+ if respond_to?(writer = attribute.to_s + '=')
34
+ send(writer, value)
35
+ else
36
+ logger.warn "#{self.class} does not have an `#{attribute}' attribute"
37
+ end
38
+ end
39
+ end
40
+
41
+ def to_hash
42
+ attributes
43
+ end
44
+
45
+ def logger
46
+ @logger ||= Logger.new
47
+ end
48
+
49
+ def create_command(name, arguments, tmp_id = nil)
50
+ command_class.new(name, arguments, tmp_id)
51
+ end
52
+
53
+ def command_class
54
+ @command_class ||= Command
55
+ end
56
+
57
+ def to_submittable_hash
58
+ if self.class.read_only_attributes
59
+ attributes.delete_if { |key, value| self.class.read_only_attributes.include?(key)}
60
+ else
61
+ attributes
62
+ end
63
+ end
64
+
65
+ def resource_type
66
+ self.class.name.split('::').last.downcase
67
+ end
68
+
69
+ def persisted?
70
+ !id.nil?
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,77 @@
1
+ module Todoist
2
+ module Service
3
+ class BaseService
4
+ attr_writer :command_class
5
+ attr_reader :client
6
+ attr_accessor :data
7
+
8
+ def initialize(client)
9
+ @client = client
10
+ end
11
+
12
+ def process
13
+ @client.queue.process
14
+ end
15
+
16
+ def create_command(name, arguments, tmp_id = nil)
17
+ command_class.new(name, arguments, tmp_id)
18
+ end
19
+
20
+ def command_class
21
+ @command_class ||= Command
22
+ end
23
+
24
+ def resource_type
25
+ self.class.name.split('::').last.downcase
26
+ end
27
+
28
+ def resource_type_plural
29
+ "#{resource_type}s"
30
+ end
31
+
32
+ def path
33
+ '/API/v6/sync'
34
+ end
35
+
36
+ def seq_no
37
+ data.nil? ? (@seq_no || 0) : client.seq_no
38
+ end
39
+
40
+ def seq_no_global
41
+ data.nil? ? 0 : client.seq_no_global
42
+ end
43
+
44
+ def retrieve(types = ["all"])
45
+ response = client.post(path, { seq_no: seq_no, seq_no_global: seq_no_global, resource_types: types.to_json })
46
+ @seq_no = response['seq_no'] if response['seq_no']
47
+ response
48
+ end
49
+
50
+ def all
51
+ response = retrieve([resource_type_plural])
52
+ self.data = response[resource_type_plural.capitalize].map { |resource| collection_class.new(resource) }
53
+ end
54
+
55
+ def create(params, temp_id = nil)
56
+ instance = build(params, temp_id)
57
+ save(instance)
58
+ end
59
+
60
+ def build(params, temp_id = nil)
61
+ instance = collection_class.new(params)
62
+ instance.temp_id ||= temp_id
63
+ instance
64
+ end
65
+
66
+ def save(instance)
67
+ if instance.persisted?
68
+ @client.queue.add(create_command("#{resource_type}_update", instance))
69
+ else
70
+ instance.temp_id ||= SecureRandom.uuid
71
+ @client.queue.add(create_command("#{resource_type}_add", instance))
72
+ end
73
+ instance
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,9 @@
1
+ module Todoist
2
+ module Service
3
+ class Filter < BaseService
4
+ def collection_class
5
+ Todoist::Filter
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,24 @@
1
+ module Todoist
2
+ module Service
3
+ class Item < BaseService
4
+ def update(id, arguments)
5
+ @client.add_to_queue(create_command('item_update', arguments.merge({id: id})))
6
+ end
7
+
8
+ def delete(ids)
9
+ if ids.is_a?(FixNum)
10
+ ids = [ids]
11
+ end
12
+ @client.add_to_queue(create_command('item_delete', {ids: ids}))
13
+ end
14
+
15
+ def close(id)
16
+ @client.add_to_queue(create_command('item_close', {id: id}))
17
+ end
18
+
19
+ def collection_class
20
+ Todoist::Item
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,9 @@
1
+ module Todoist
2
+ module Service
3
+ class Label < BaseService
4
+ def collection_class
5
+ Todoist::Label
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Todoist
2
+ module Service
3
+ class Note < BaseService
4
+ def collection_class
5
+ Todoist::Note
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Todoist
2
+ module Service
3
+ class Project < BaseService
4
+ def collection_class
5
+ Todoist::Project
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Todoist
2
+ module Service
3
+ class Reminder < BaseService
4
+ def collection_class
5
+ Todoist::Reminder
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ module Todoist
2
+ class User
3
+ include Todoist::Resource
4
+ extend Todoist::Resource::ClassMethods
5
+
6
+ define_attributes %w(
7
+ id
8
+ )
9
+ end
10
+ end
@@ -0,0 +1,3 @@
1
+ module Todoist
2
+ VERSION = 0.3
3
+ end
@@ -0,0 +1,22 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'todoist/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "ruby-todoist-api"
8
+ spec.version = Todoist::VERSION
9
+ spec.authors = ["Maarten van Vliet"]
10
+ spec.email = ["maartenvanvliet@gmail.com"]
11
+ spec.summary = %q{Wrapper for todoist api.}
12
+ spec.description = %q{Wrapper for todoist api v6.}
13
+ spec.homepage = "https://github.com/maartenvanvliet/ruby-todoist-api"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.test_files = spec.files.grep(%r{^(spec)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "rspec", "~> 3"
21
+ spec.add_development_dependency "webmock", "~> 1.21"
22
+ end
@@ -0,0 +1,58 @@
1
+ require "spec_helper"
2
+
3
+ describe Todoist::Client do
4
+
5
+ let(:client) { described_class.new("api_token") }
6
+
7
+ it "sets a api token when given one string argument" do
8
+ expect(client.token).to eq 'api_token'
9
+ end
10
+
11
+ it "has a baseurl" do
12
+ expect(client.base_url).to_not be_nil
13
+ end
14
+
15
+ describe "request handling" do
16
+ let!(:stub) {
17
+ stub_request(:post, "https://todoist.com/path").
18
+ with(:body => {"payload"=>"payload", "token"=>"api_token"}).
19
+ to_return(:status => 200, :body => '{}')
20
+
21
+ }
22
+
23
+ it "can post a request" do
24
+ client.post('/path', { payload: 'payload'})
25
+
26
+ expect(stub).to have_been_requested
27
+ end
28
+ end
29
+
30
+
31
+ describe "queue handling" do
32
+ let(:command) { { 'type' => 'test_command'} }
33
+ before do
34
+ client.queue.add(command)
35
+ end
36
+
37
+ it "can add a command to the queue" do
38
+
39
+ expect(client.queue.first).to eq(command)
40
+ end
41
+
42
+ describe "sends the queue in a request" do
43
+ let!(:stub) {
44
+ stub_request(:post, "https://todoist.com/API/v6/sync").
45
+ with(:body => {"commands"=>"[{\"type\":\"test_command\"}]", "token"=>"api_token"}).
46
+ to_return(:status => 200, :body => '{}')
47
+
48
+ }
49
+
50
+ it "can post a request" do
51
+ client.process!
52
+
53
+ expect(client.queue.length).to eq 0
54
+ expect(stub).to have_been_requested
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,19 @@
1
+ require "spec_helper"
2
+
3
+ describe Todoist::Filter do
4
+
5
+ it "can be built from json" do
6
+ instance = described_class.new(json_response('filter'))
7
+
8
+ expect(instance.attributes).to eq({
9
+ "id"=>4638878,
10
+ "user_id"=>1855589,
11
+ "name"=>"Priority 1",
12
+ "query"=>"priority 1",
13
+ "color"=>6,
14
+ "item_order"=>3,
15
+ "is_deleted"=>0
16
+ }
17
+ )
18
+ end
19
+ end