todoist_querynaut 0.1.0
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.
- checksums.yaml +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +47 -0
- data/lib/todoist_querynaut.rb +9 -0
- data/lib/todoist_querynaut/client.rb +36 -0
- data/lib/todoist_querynaut/parser.rb +35 -0
- data/lib/todoist_querynaut/project_not_found_error.rb +2 -0
- data/lib/todoist_querynaut/todoist_query/node_extensions.rb +72 -0
- data/lib/todoist_querynaut/todoist_query_parser.treetop +55 -0
- data/spec/lib/todoist_querynaut/client_spec.rb +68 -0
- data/spec/lib/todoist_querynaut/parser_spec.rb +207 -0
- data/spec/lib/todoist_querynaut/todoist_query/intersection_spec.rb +37 -0
- data/spec/lib/todoist_querynaut/todoist_query/literal_query_spec.rb +42 -0
- data/spec/lib/todoist_querynaut/todoist_query/negated_query_spec.rb +22 -0
- data/spec/lib/todoist_querynaut/todoist_query/priority_query_spec.rb +30 -0
- data/spec/lib/todoist_querynaut/todoist_query/project_name_query_spec.rb +44 -0
- data/spec/lib/todoist_querynaut/todoist_query/union_spec.rb +37 -0
- data/spec/spec_helper.rb +8 -0
- metadata +169 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c812ff2de231d377f592d944c80e8e090803ce39
|
4
|
+
data.tar.gz: 54f155642e5e2f205eb8b18011495568ab6efb7c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5b9f67cdd4ad545a160c8094bd3613b996780d4c55580c2e3225d44aa938af98f08b62a0b078f938a2aa2b9c1fde6119c1d7ee1164914495a78f61f45981ddec
|
7
|
+
data.tar.gz: ab61e52a42a093363e88ca21f7ed1d26838f36401feae953e0fc8cc73ffb3cedfb4d660d284f8c35fbdafe28906e030502f41af1d5efea924fda9b5d69ebb40d
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2017 Stefan Siegl
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# TodoistQuerynaut
|
2
|
+
|
3
|
+
Todoist Querynaut is a Ruby Gem that reimplements the [filter query language](https://support.todoist.com/hc/en-us/articles/205248842-Filters)
|
4
|
+
of Todoist.
|
5
|
+
|
6
|
+
[Todoist](https://todoist.com) has a powerful feature named filters,
|
7
|
+
yet they are implemented on client side (i.e. within their web application).
|
8
|
+
It even has a REST API that allows to do searches, yet they are not as powerful,
|
9
|
+
e.g. they don't allow for boolean operator logic.
|
10
|
+
|
11
|
+
This is where Todoist Querynaut kicks in, it has a parser for the query
|
12
|
+
language and does one or more calls to the REST API to collect the correct set
|
13
|
+
of result items.
|
14
|
+
|
15
|
+
Querynaut isn't yet fully fledged, some search capabilities are missing
|
16
|
+
|
17
|
+
* search for items within sub-projects
|
18
|
+
* filtering based on exact due dates
|
19
|
+
* filtering based on creation date
|
20
|
+
* "no labels" query
|
21
|
+
|
22
|
+
## Installation
|
23
|
+
|
24
|
+
Add this line to your application's Gemfile
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
gem 'todoist_querynaut'
|
28
|
+
```
|
29
|
+
|
30
|
+
And then execute:
|
31
|
+
|
32
|
+
$ bundle install
|
33
|
+
|
34
|
+
## Usage
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
querynaut = TodoistQuerynaut::Client.new(Todoist::Client.new("your_api_token"))
|
38
|
+
result = @client.run("(overdue | today) & #work")
|
39
|
+
```
|
40
|
+
|
41
|
+
## Contributing
|
42
|
+
|
43
|
+
1. Fork it ( https://github.com/stesie/todoist_querynaut/fork )
|
44
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
45
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
46
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
47
|
+
5. Create a new Pull Request
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module TodoistQuerynaut
|
2
|
+
class Client
|
3
|
+
def initialize(todoist)
|
4
|
+
@todoist = todoist
|
5
|
+
end
|
6
|
+
|
7
|
+
def run(query)
|
8
|
+
TodoistQuerynaut::Parser.parse(query).run_query self
|
9
|
+
end
|
10
|
+
|
11
|
+
def search(query)
|
12
|
+
# Todoist doesn't always set "query" on the results, hence we cannot
|
13
|
+
# search(value)[value] here, as ruby-todoist-api Gem then assigns "nil"
|
14
|
+
# as the hash's key. Instead we do .first.last, to get the value of the
|
15
|
+
# first hash entry
|
16
|
+
@todoist.query.search(query).first.last.data
|
17
|
+
end
|
18
|
+
|
19
|
+
def all_items
|
20
|
+
search "view all"
|
21
|
+
end
|
22
|
+
|
23
|
+
def project_name_to_id(name)
|
24
|
+
projects = all_projects.select{|p| p["name"].casecmp(name) == 0}
|
25
|
+
raise ProjectNotFoundError if projects.empty?
|
26
|
+
projects.first["id"].to_i
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def all_projects()
|
32
|
+
@projects ||= @todoist.projects.retrieve(["projects"])["Projects"]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module TodoistQuerynaut
|
2
|
+
class Parser
|
3
|
+
Treetop.load(File.join(File.dirname(__FILE__), 'todoist_query_parser.treetop'))
|
4
|
+
@@parser = TodoistQueryParser.new
|
5
|
+
|
6
|
+
def self.parse(data)
|
7
|
+
tree = @@parser.parse(data)
|
8
|
+
|
9
|
+
if tree.nil?
|
10
|
+
raise Exception, "Parse error at offset: #{@@parser.index}"
|
11
|
+
end
|
12
|
+
|
13
|
+
#return tree
|
14
|
+
return self.clean_tree(tree)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def self.clean_tree(node)
|
20
|
+
if node.class.name == "Treetop::Runtime::SyntaxNode"
|
21
|
+
if node.elements.nil? || node.elements.length == 0
|
22
|
+
return node.empty? ? [] : node
|
23
|
+
else
|
24
|
+
return node.elements.map{|n| self.clean_tree n}.flatten(1)
|
25
|
+
end
|
26
|
+
else
|
27
|
+
if !node.elements.nil?
|
28
|
+
node.elements.replace node.elements.map{|n| self.clean_tree n}.flatten(1)
|
29
|
+
return node.children[0] if node.is_a?(TodoistQuery::SetExpressionNode) && node.sole?
|
30
|
+
end
|
31
|
+
return node
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module TodoistQuerynaut
|
2
|
+
module TodoistQuery
|
3
|
+
class SetExpressionNode < Treetop::Runtime::SyntaxNode
|
4
|
+
def children
|
5
|
+
elements.reject{|a| a.class.name == 'Treetop::Runtime::SyntaxNode'}
|
6
|
+
end
|
7
|
+
|
8
|
+
def sole?
|
9
|
+
children.size == 1
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class Union < SetExpressionNode
|
14
|
+
def run_query(todoist)
|
15
|
+
children.inject [] {|acc,child| acc | (child.run_query todoist)}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class Intersection < SetExpressionNode
|
20
|
+
def run_query(todoist)
|
21
|
+
acc = children[0].run_query todoist
|
22
|
+
children.drop(1).inject(acc) {|acc,child| acc & (child.run_query todoist)}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class LiteralQuery < Treetop::Runtime::SyntaxNode
|
27
|
+
def value
|
28
|
+
text_value
|
29
|
+
end
|
30
|
+
|
31
|
+
def run_query(todoist)
|
32
|
+
todoist.search(value)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class NDaysQuery < Treetop::Runtime::SyntaxNode
|
37
|
+
end
|
38
|
+
|
39
|
+
class PriorityQuery < Treetop::Runtime::SyntaxNode
|
40
|
+
def value
|
41
|
+
text_value[-1].to_i
|
42
|
+
end
|
43
|
+
|
44
|
+
def run_query(todoist)
|
45
|
+
todoist.search "p#{value}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class ProjectNameQuery < Treetop::Runtime::SyntaxNode
|
50
|
+
def value
|
51
|
+
text_value[2..-1]
|
52
|
+
end
|
53
|
+
|
54
|
+
def run_query(todoist)
|
55
|
+
project_id = todoist.project_name_to_id value
|
56
|
+
todoist.all_items.select{|item| item["project_id"] == project_id}
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class LabelQuery < Treetop::Runtime::SyntaxNode
|
61
|
+
end
|
62
|
+
|
63
|
+
class NoLabelsQuery < Treetop::Runtime::SyntaxNode
|
64
|
+
end
|
65
|
+
|
66
|
+
class NegatedQuery < Treetop::Runtime::SyntaxNode
|
67
|
+
def run_query(todoist)
|
68
|
+
todoist.all_items - query.run_query(todoist)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module TodoistQuerynaut
|
2
|
+
grammar TodoistQuery
|
3
|
+
rule union
|
4
|
+
intersection ( '|' intersection )* <Union>
|
5
|
+
end
|
6
|
+
|
7
|
+
rule intersection
|
8
|
+
primary ( '&' primary )* <Intersection>
|
9
|
+
end
|
10
|
+
|
11
|
+
rule primary
|
12
|
+
space? ( '(' union ')' / query_part ) space?
|
13
|
+
end
|
14
|
+
|
15
|
+
rule query_part
|
16
|
+
negated_part / literal_query / n_days_query / priority_query / project_name_query / label_query / no_labels_query
|
17
|
+
end
|
18
|
+
|
19
|
+
rule negated_part
|
20
|
+
'!' query:query_part <NegatedQuery>
|
21
|
+
end
|
22
|
+
|
23
|
+
rule literal_query
|
24
|
+
'over due' <LiteralQuery> / 'overdue' <LiteralQuery> / 'today' <LiteralQuery> / 'tomorrow' <LiteralQuery> / 'view all' <LiteralQuery>
|
25
|
+
end
|
26
|
+
|
27
|
+
rule n_days_query
|
28
|
+
[0-9]+ space "days" <NDaysQuery> {
|
29
|
+
def value
|
30
|
+
text_value.to_i
|
31
|
+
end
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
rule priority_query
|
36
|
+
("priority" space / "p") [1-4] <PriorityQuery>
|
37
|
+
end
|
38
|
+
|
39
|
+
rule project_name_query
|
40
|
+
'p:' [\w]+ <ProjectNameQuery>
|
41
|
+
end
|
42
|
+
|
43
|
+
rule label_query
|
44
|
+
'@' [\w]+ <LiteralQuery>
|
45
|
+
end
|
46
|
+
|
47
|
+
rule no_labels_query
|
48
|
+
'no labels' <NoLabelsQuery>
|
49
|
+
end
|
50
|
+
|
51
|
+
rule space
|
52
|
+
[\s]+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe TodoistQuerynaut::Client do
|
4
|
+
describe "#search" do
|
5
|
+
it "delegates search query to todoist api and returns result" do
|
6
|
+
todoist_api = double()
|
7
|
+
allow(todoist_api).to receive(:query) {
|
8
|
+
query_obj = double()
|
9
|
+
allow(query_obj).to receive(:search) { |query|
|
10
|
+
{ query => Todoist::Result.new({ "query" => "foo", "data" => [ "content_here" ] }) }
|
11
|
+
}
|
12
|
+
query_obj
|
13
|
+
}
|
14
|
+
|
15
|
+
result = TodoistQuerynaut::Client.new(todoist_api).search("foobar")
|
16
|
+
expect(result).to eq(["content_here"])
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "#all_items" do
|
21
|
+
it "should run a 'view all' query" do
|
22
|
+
stub_request(:post, "https://todoist.com/API/v6/query").
|
23
|
+
with(:body => {"queries" => "[\"view all\"]", "token" => "some_token"}).
|
24
|
+
to_return(:status => 200, :body => json_response_raw("query_view_all"), :headers => {})
|
25
|
+
result = TodoistQuerynaut::Client.new(Todoist::Client.new("some_token")).all_items
|
26
|
+
|
27
|
+
expect(result.size).to eq(4)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "#project_name_to_id" do
|
32
|
+
before :each do
|
33
|
+
stub_request(:post, "https://todoist.com/API/v6/sync").
|
34
|
+
with(:body => { "seq_no" => "0", "seq_no_global" => "0", "resource_types" => '["projects"]', "token" => "some_token" }).
|
35
|
+
to_return(:status => 200, :body => json_response_raw("sync_projects_all"), :headers => {})
|
36
|
+
@client = TodoistQuerynaut::Client.new(Todoist::Client.new("some_token"))
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should resolve 'Someday Maybe' to 185594700" do
|
40
|
+
result = @client.project_name_to_id("Someday Maybe")
|
41
|
+
expect(result).to eq(185594700)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should resolve project names without case-sensitivity" do
|
45
|
+
result = @client.project_name_to_id("SomeDAY MayBE")
|
46
|
+
expect(result).to eq(185594700)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should raise ProjectNotFoundError for 'No Such Project'" do
|
50
|
+
expect{ @client.project_name_to_id("No Such Project") }.to raise_error(ProjectNotFoundError)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "#run" do
|
55
|
+
before :each do
|
56
|
+
stub_request(:post, "https://todoist.com/API/v6/query").
|
57
|
+
with(:body => {"queries" => "[\"view all\"]", "token" => "some_token"}).
|
58
|
+
to_return(:status => 200, :body => json_response_raw("query_view_all"), :headers => {})
|
59
|
+
@client = TodoistQuerynaut::Client.new(Todoist::Client.new("some_token"))
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should parse the query, execute it and return the results" do
|
63
|
+
result = @client.run("view all")
|
64
|
+
expect(result.size).to eq(4)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,207 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "#parse" do
|
4
|
+
describe "query literals" do
|
5
|
+
it "should parse 'today'" do
|
6
|
+
tree = TodoistQuerynaut::Parser.parse("today")
|
7
|
+
expect(tree).to be_a(TodoistQuerynaut::TodoistQuery::LiteralQuery)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should ignore trailing whitespace" do
|
11
|
+
tree = TodoistQuerynaut::Parser.parse("today ")
|
12
|
+
expect(tree).to be_a(TodoistQuerynaut::TodoistQuery::LiteralQuery)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should ignore leading whitespace" do
|
16
|
+
tree = TodoistQuerynaut::Parser.parse(" today")
|
17
|
+
expect(tree).to be_a(TodoistQuerynaut::TodoistQuery::LiteralQuery)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should parse 'tomorrow'" do
|
21
|
+
tree = TodoistQuerynaut::Parser.parse("tomorrow")
|
22
|
+
expect(tree).to be_a(TodoistQuerynaut::TodoistQuery::LiteralQuery)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should parse 'overdue'" do
|
26
|
+
tree = TodoistQuerynaut::Parser.parse("overdue")
|
27
|
+
expect(tree).to be_a(TodoistQuerynaut::TodoistQuery::LiteralQuery)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should parse 'over due'" do
|
31
|
+
tree = TodoistQuerynaut::Parser.parse("over due")
|
32
|
+
expect(tree).to be_a(TodoistQuerynaut::TodoistQuery::LiteralQuery)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should parse 'view all'" do
|
36
|
+
tree = TodoistQuerynaut::Parser.parse("view all")
|
37
|
+
expect(tree).to be_a(TodoistQuerynaut::TodoistQuery::LiteralQuery)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "negated queries" do
|
42
|
+
it "should parse '!today'" do
|
43
|
+
tree = TodoistQuerynaut::Parser.parse("!today")
|
44
|
+
expect(tree).to be_a(TodoistQuerynaut::TodoistQuery::NegatedQuery)
|
45
|
+
expect(tree.query).to be_a(TodoistQuerynaut::TodoistQuery::LiteralQuery)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "n-days queries" do
|
50
|
+
it "should parse '4 days'" do
|
51
|
+
tree = TodoistQuerynaut::Parser.parse("4 days")
|
52
|
+
expect(tree).to be_a(TodoistQuerynaut::TodoistQuery::NDaysQuery)
|
53
|
+
expect(tree.value).to eq(4)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should parse '23 days'" do
|
57
|
+
tree = TodoistQuerynaut::Parser.parse("23 days")
|
58
|
+
expect(tree).to be_a(TodoistQuerynaut::TodoistQuery::NDaysQuery)
|
59
|
+
expect(tree.value).to eq(23)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should parse '42 days'" do
|
63
|
+
tree = TodoistQuerynaut::Parser.parse("42 days")
|
64
|
+
expect(tree).to be_a(TodoistQuerynaut::TodoistQuery::NDaysQuery)
|
65
|
+
expect(tree.value).to eq(42)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "priority queries" do
|
70
|
+
it "should parse 'p1'" do
|
71
|
+
tree = TodoistQuerynaut::Parser.parse("p1")
|
72
|
+
expect(tree).to be_a(TodoistQuerynaut::TodoistQuery::PriorityQuery)
|
73
|
+
expect(tree.value).to eq(1)
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should parse 'priority 3'" do
|
77
|
+
tree = TodoistQuerynaut::Parser.parse("priority 3")
|
78
|
+
expect(tree).to be_a(TodoistQuerynaut::TodoistQuery::PriorityQuery)
|
79
|
+
expect(tree.value).to eq(3)
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should parse 'priority 2'" do
|
83
|
+
tree = TodoistQuerynaut::Parser.parse("priority 2")
|
84
|
+
expect(tree).to be_a(TodoistQuerynaut::TodoistQuery::PriorityQuery)
|
85
|
+
expect(tree.value).to eq(2)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "project name queries" do
|
90
|
+
it "should parse 'p:foo'" do
|
91
|
+
tree = TodoistQuerynaut::Parser.parse("p:foo")
|
92
|
+
expect(tree).to be_a(TodoistQuerynaut::TodoistQuery::ProjectNameQuery)
|
93
|
+
expect(tree.value).to eq("foo")
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should parse 'p:some_long_project23'" do
|
97
|
+
tree = TodoistQuerynaut::Parser.parse("p:some_long_project23")
|
98
|
+
expect(tree).to be_a(TodoistQuerynaut::TodoistQuery::ProjectNameQuery)
|
99
|
+
expect(tree.value).to eq("some_long_project23")
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe "label queries" do
|
104
|
+
it "should parse '@foo'" do
|
105
|
+
tree = TodoistQuerynaut::Parser.parse("@foo")
|
106
|
+
expect(tree).to be_a(TodoistQuerynaut::TodoistQuery::LiteralQuery)
|
107
|
+
expect(tree.value).to eq("@foo")
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should parse '@rspec'" do
|
111
|
+
tree = TodoistQuerynaut::Parser.parse("@rspec")
|
112
|
+
expect(tree).to be_a(TodoistQuerynaut::TodoistQuery::LiteralQuery)
|
113
|
+
expect(tree.value).to eq("@rspec")
|
114
|
+
end
|
115
|
+
|
116
|
+
it "should parse 'no labels'" do
|
117
|
+
tree = TodoistQuerynaut::Parser.parse("no labels")
|
118
|
+
expect(tree).to be_a(TodoistQuerynaut::TodoistQuery::NoLabelsQuery)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe "logical operations" do
|
123
|
+
describe "intersections" do
|
124
|
+
it "should support intersections of two labels'" do
|
125
|
+
tree = TodoistQuerynaut::Parser.parse("@foo & @bar")
|
126
|
+
expect(tree).to be_a(TodoistQuerynaut::TodoistQuery::Intersection)
|
127
|
+
expect(tree.children[0]).to be_a(TodoistQuerynaut::TodoistQuery::LiteralQuery)
|
128
|
+
expect(tree.children[0].value).to eq("@foo")
|
129
|
+
expect(tree.children[1]).to be_a(TodoistQuerynaut::TodoistQuery::LiteralQuery)
|
130
|
+
expect(tree.children[1].value).to eq("@bar")
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should support intersections of two different query parts'" do
|
134
|
+
tree = TodoistQuerynaut::Parser.parse("p4 & today")
|
135
|
+
expect(tree).to be_a(TodoistQuerynaut::TodoistQuery::Intersection)
|
136
|
+
expect(tree.children.size).to eq(2)
|
137
|
+
expect(tree.children[0]).to be_a(TodoistQuerynaut::TodoistQuery::PriorityQuery)
|
138
|
+
expect(tree.children[0].value).to eq(4)
|
139
|
+
expect(tree.children[1]).to be_a(TodoistQuerynaut::TodoistQuery::LiteralQuery)
|
140
|
+
expect(tree.children[1].value).to eq("today")
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
it "should support intersections of multiple parts" do
|
145
|
+
tree = TodoistQuerynaut::Parser.parse("@foo & @bar & @here & @there")
|
146
|
+
expect(tree).to be_a(TodoistQuerynaut::TodoistQuery::Intersection)
|
147
|
+
expect(tree.children.size).to eq(4)
|
148
|
+
tree.children.each { |node| expect(node).to be_a(TodoistQuerynaut::TodoistQuery::LiteralQuery) }
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
describe "unions" do
|
153
|
+
it "should support unions of two labels'" do
|
154
|
+
tree = TodoistQuerynaut::Parser.parse("@foo | @bar")
|
155
|
+
expect(tree).to be_a(TodoistQuerynaut::TodoistQuery::Union)
|
156
|
+
expect(tree.children[0]).to be_a(TodoistQuerynaut::TodoistQuery::LiteralQuery)
|
157
|
+
expect(tree.children[0].value).to eq("@foo")
|
158
|
+
expect(tree.children[1]).to be_a(TodoistQuerynaut::TodoistQuery::LiteralQuery)
|
159
|
+
expect(tree.children[1].value).to eq("@bar")
|
160
|
+
end
|
161
|
+
|
162
|
+
it "should support unions of two different query parts'" do
|
163
|
+
tree = TodoistQuerynaut::Parser.parse("p4 | today")
|
164
|
+
expect(tree).to be_a(TodoistQuerynaut::TodoistQuery::Union)
|
165
|
+
expect(tree.children.size).to eq(2)
|
166
|
+
expect(tree.children[0]).to be_a(TodoistQuerynaut::TodoistQuery::PriorityQuery)
|
167
|
+
expect(tree.children[0].value).to eq(4)
|
168
|
+
expect(tree.children[1]).to be_a(TodoistQuerynaut::TodoistQuery::LiteralQuery)
|
169
|
+
expect(tree.children[1].value).to eq("today")
|
170
|
+
end
|
171
|
+
|
172
|
+
it "should support unions of multiple parts" do
|
173
|
+
tree = TodoistQuerynaut::Parser.parse("@foo | @bar | @here | @there")
|
174
|
+
expect(tree).to be_a(TodoistQuerynaut::TodoistQuery::Union)
|
175
|
+
expect(tree.children.size).to eq(4)
|
176
|
+
tree.children.each { |node| expect(node).to be_a(TodoistQuerynaut::TodoistQuery::LiteralQuery) }
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
describe "operator precedence" do
|
181
|
+
it "should give higher precedence to intersection" do
|
182
|
+
tree = TodoistQuerynaut::Parser.parse("p1 & p1 | p4")
|
183
|
+
expect(tree).to be_a(TodoistQuerynaut::TodoistQuery::Union)
|
184
|
+
expect(tree.children[0]).to be_a(TodoistQuerynaut::TodoistQuery::Intersection)
|
185
|
+
end
|
186
|
+
|
187
|
+
it "should give higher precedence to intersection (2)" do
|
188
|
+
tree = TodoistQuerynaut::Parser.parse("p4 | p1 & p1")
|
189
|
+
expect(tree).to be_a(TodoistQuerynaut::TodoistQuery::Union)
|
190
|
+
expect(tree.children[1]).to be_a(TodoistQuerynaut::TodoistQuery::Intersection)
|
191
|
+
end
|
192
|
+
|
193
|
+
it "should give even higher precedence to parentheses" do
|
194
|
+
tree = TodoistQuerynaut::Parser.parse("p1 & (p1 | p4)")
|
195
|
+
expect(tree).to be_a(TodoistQuerynaut::TodoistQuery::Intersection)
|
196
|
+
expect(tree.children[1]).to be_a(TodoistQuerynaut::TodoistQuery::Union)
|
197
|
+
end
|
198
|
+
|
199
|
+
it "should give even higher precedence to parentheses (2)" do
|
200
|
+
tree = TodoistQuerynaut::Parser.parse("(p1 | p4) & p2")
|
201
|
+
expect(tree).to be_a(TodoistQuerynaut::TodoistQuery::Intersection)
|
202
|
+
expect(tree.children[0]).to be_a(TodoistQuerynaut::TodoistQuery::Union)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
end
|
207
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe TodoistQuerynaut::TodoistQuery::Intersection do
|
4
|
+
describe "#run_query" do
|
5
|
+
it "should return items of 'today' for 'today & today'" do
|
6
|
+
intersection_query = TodoistQuerynaut::TodoistQuery::Intersection.new("today&today", 0...13, [
|
7
|
+
TodoistQuerynaut::TodoistQuery::LiteralQuery.new("today", 0...5),
|
8
|
+
TodoistQuerynaut::TodoistQuery::LiteralQuery.new("today", 0...5)
|
9
|
+
])
|
10
|
+
|
11
|
+
stub_request(:post, "https://todoist.com/API/v6/query").
|
12
|
+
with(:body => {"queries" => "[\"today\"]", "token" => "some_token"}).
|
13
|
+
to_return(:status => 200, :body => json_response_raw("query_today"), :headers => {})
|
14
|
+
result = intersection_query.run_query TodoistQuerynaut::Client.new(Todoist::Client.new("some_token"))
|
15
|
+
|
16
|
+
expect(result.size).to eq(1)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should run both queries and return the intersection" do
|
20
|
+
intersection_query = TodoistQuerynaut::TodoistQuery::Intersection.new("overdue & overdue_one", 0...21, [
|
21
|
+
TodoistQuerynaut::TodoistQuery::LiteralQuery.new("overdue", 0...7),
|
22
|
+
TodoistQuerynaut::TodoistQuery::LiteralQuery.new("overdue_one", 0...11)
|
23
|
+
])
|
24
|
+
|
25
|
+
stub_request(:post, "https://todoist.com/API/v6/query").
|
26
|
+
with(:body => {"queries" => "[\"overdue_one\"]", "token" => "some_token"}).
|
27
|
+
to_return(:status => 200, :body => json_response_raw("query_overdue_one"), :headers => {})
|
28
|
+
stub_request(:post, "https://todoist.com/API/v6/query").
|
29
|
+
with(:body => {"queries" => "[\"overdue\"]", "token" => "some_token"}).
|
30
|
+
to_return(:status => 200, :body => json_response_raw("query_overdue"), :headers => {})
|
31
|
+
result = intersection_query.run_query TodoistQuerynaut::Client.new(Todoist::Client.new("some_token"))
|
32
|
+
|
33
|
+
expect(result.size).to eq(1)
|
34
|
+
expect(result[0]["content"]).to eq("overdue_two")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe TodoistQuerynaut::TodoistQuery::LiteralQuery do
|
4
|
+
describe "#value" do
|
5
|
+
it "should return the literal query string 'today'" do
|
6
|
+
literal_query = TodoistQuerynaut::TodoistQuery::LiteralQuery.new("today", 0...5)
|
7
|
+
expect(literal_query.value).to eq("today")
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should return the literal query string 'overdue'" do
|
11
|
+
literal_query = TodoistQuerynaut::TodoistQuery::LiteralQuery.new("overdue", 0...7)
|
12
|
+
expect(literal_query.value).to eq("overdue")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "#run_query" do
|
17
|
+
it "should run a 'today' query" do
|
18
|
+
literal_query = TodoistQuerynaut::TodoistQuery::LiteralQuery.new("today", 0...5)
|
19
|
+
|
20
|
+
stub_request(:post, "https://todoist.com/API/v6/query").
|
21
|
+
with(:body => {"queries" => "[\"today\"]", "token" => "some_token"}).
|
22
|
+
to_return(:status => 200, :body => json_response_raw("query_today"), :headers => {})
|
23
|
+
result = literal_query.run_query TodoistQuerynaut::Client.new(Todoist::Client.new("some_token"))
|
24
|
+
|
25
|
+
expect(result.size).to eq(1)
|
26
|
+
expect(result[0]["content"]).to eq("query_today_item_content")
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should run a 'overdue' query" do
|
30
|
+
literal_query = TodoistQuerynaut::TodoistQuery::LiteralQuery.new("overdue", 0...7)
|
31
|
+
|
32
|
+
stub_request(:post, "https://todoist.com/API/v6/query").
|
33
|
+
with(:body => {"queries" => "[\"overdue\"]", "token" => "some_token"}).
|
34
|
+
to_return(:status => 200, :body => json_response_raw("query_overdue"), :headers => {})
|
35
|
+
result = literal_query.run_query TodoistQuerynaut::Client.new(Todoist::Client.new("some_token"))
|
36
|
+
|
37
|
+
expect(result.size).to eq(2)
|
38
|
+
expect(result[0]["content"]).to eq("overdue_one")
|
39
|
+
expect(result[1]["content"]).to eq("overdue_two")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe TodoistQuerynaut::TodoistQuery::NegatedQuery do
|
4
|
+
describe "#run_query" do
|
5
|
+
it "should negate a 'today' query" do
|
6
|
+
negated_query = TodoistQuerynaut::Parser.parse("!today")
|
7
|
+
|
8
|
+
# will run two queries: today and "view all"
|
9
|
+
stub_request(:post, "https://todoist.com/API/v6/query").
|
10
|
+
with(:body => {"queries" => "[\"today\"]", "token" => "some_token"}).
|
11
|
+
to_return(:status => 200, :body => json_response_raw("query_today"), :headers => {})
|
12
|
+
stub_request(:post, "https://todoist.com/API/v6/query").
|
13
|
+
with(:body => {"queries" => "[\"view all\"]", "token" => "some_token"}).
|
14
|
+
to_return(:status => 200, :body => json_response_raw("query_view_all"), :headers => {})
|
15
|
+
result = negated_query.run_query TodoistQuerynaut::Client.new(Todoist::Client.new("some_token"))
|
16
|
+
|
17
|
+
# all items (4) excluding today (1) --> 3
|
18
|
+
expect(result.size).to eq(3)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe TodoistQuerynaut::TodoistQuery::PriorityQuery do
|
4
|
+
describe "#value" do
|
5
|
+
it "should return the priority of query string 'p4'" do
|
6
|
+
priority_query = TodoistQuerynaut::TodoistQuery::PriorityQuery.new("p4", 0...2)
|
7
|
+
expect(priority_query.value).to eq(4)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should return the priority of query string 'priority 3'" do
|
11
|
+
priority_query = TodoistQuerynaut::TodoistQuery::PriorityQuery.new("priority 3", 0...10)
|
12
|
+
expect(priority_query.value).to eq(3)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "#run_query" do
|
17
|
+
it "should run a 'p4' query" do
|
18
|
+
priority_query = TodoistQuerynaut::TodoistQuery::PriorityQuery.new("p4", 0...2)
|
19
|
+
|
20
|
+
stub_request(:post, "https://todoist.com/API/v6/query").
|
21
|
+
with(:body => {"queries" => "[\"p4\"]", "token" => "some_token"}).
|
22
|
+
to_return(:status => 200, :body => json_response_raw("query_p4"), :headers => {})
|
23
|
+
result = priority_query.run_query TodoistQuerynaut::Client.new(Todoist::Client.new("some_token"))
|
24
|
+
|
25
|
+
expect(result.size).to eq(1)
|
26
|
+
expect(result[0]["content"]).to eq("weekly_p4_item")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe TodoistQuerynaut::TodoistQuery::ProjectNameQuery do
|
4
|
+
describe "#value" do
|
5
|
+
it "should return the project name (foo) of query string 'p:foo'" do
|
6
|
+
project_name_query = TodoistQuerynaut::TodoistQuery::ProjectNameQuery.new("p:foo", 0...5)
|
7
|
+
expect(project_name_query.value).to eq("foo")
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should return the project name (blarg) of query string 'p:blarg'" do
|
11
|
+
project_name_query = TodoistQuerynaut::TodoistQuery::ProjectNameQuery.new("p:blarg", 0...7)
|
12
|
+
expect(project_name_query.value).to eq("blarg")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "#run_query" do
|
17
|
+
before :each do
|
18
|
+
stub_request(:post, "https://todoist.com/API/v6/query").
|
19
|
+
with(:body => {"queries" => "[\"view all\"]", "token" => "some_token"}).
|
20
|
+
to_return(:status => 200, :body => json_response_raw("query_view_all"), :headers => {})
|
21
|
+
stub_request(:post, "https://todoist.com/API/v6/sync").
|
22
|
+
with(:body => { "seq_no" => "0", "seq_no_global" => "0", "resource_types" => '["projects"]', "token" => "some_token" }).
|
23
|
+
to_return(:status => 200, :body => json_response_raw("sync_projects_all"), :headers => {})
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should run a 'p:Inbox' query" do
|
27
|
+
project_name_query = TodoistQuerynaut::TodoistQuery::ProjectNameQuery.new("p:Inbox", 0...7)
|
28
|
+
result = project_name_query.run_query TodoistQuerynaut::Client.new(Todoist::Client.new("some_token"))
|
29
|
+
|
30
|
+
expect(result.size).to eq(1)
|
31
|
+
expect(result[0]["content"]).to eq("query_today_item_content")
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should match project names without case-sensitivity" do
|
35
|
+
project_name_query = TodoistQuerynaut::TodoistQuery::ProjectNameQuery.new("p:INBoX", 0...7)
|
36
|
+
result = project_name_query.run_query TodoistQuerynaut::Client.new(Todoist::Client.new("some_token"))
|
37
|
+
|
38
|
+
expect(result.size).to eq(1)
|
39
|
+
expect(result[0]["content"]).to eq("query_today_item_content")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe TodoistQuerynaut::TodoistQuery::Union do
|
4
|
+
describe "#run_query" do
|
5
|
+
it "should run both queries and return the union" do
|
6
|
+
union_query = TodoistQuerynaut::TodoistQuery::Union.new("today|overdue", 0...13, [
|
7
|
+
TodoistQuerynaut::TodoistQuery::LiteralQuery.new("today", 0...5),
|
8
|
+
TodoistQuerynaut::TodoistQuery::LiteralQuery.new("overdue", 0...7)
|
9
|
+
])
|
10
|
+
|
11
|
+
stub_request(:post, "https://todoist.com/API/v6/query").
|
12
|
+
with(:body => {"queries" => "[\"today\"]", "token" => "some_token"}).
|
13
|
+
to_return(:status => 200, :body => json_response_raw("query_today"), :headers => {})
|
14
|
+
stub_request(:post, "https://todoist.com/API/v6/query").
|
15
|
+
with(:body => {"queries" => "[\"overdue\"]", "token" => "some_token"}).
|
16
|
+
to_return(:status => 200, :body => json_response_raw("query_overdue"), :headers => {})
|
17
|
+
result = union_query.run_query TodoistQuerynaut::Client.new(Todoist::Client.new("some_token"))
|
18
|
+
|
19
|
+
expect(result.size).to eq(3)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should remove duplicate items" do
|
23
|
+
union_query = TodoistQuerynaut::TodoistQuery::Union.new("today|overdue", 0...13, [
|
24
|
+
TodoistQuerynaut::TodoistQuery::LiteralQuery.new("today", 0...5),
|
25
|
+
TodoistQuerynaut::TodoistQuery::LiteralQuery.new("today", 0...5),
|
26
|
+
])
|
27
|
+
|
28
|
+
stub_request(:post, "https://todoist.com/API/v6/query").
|
29
|
+
with(:body => {"queries" => "[\"today\"]", "token" => "some_token"}).
|
30
|
+
to_return(:status => 200, :body => json_response_raw("query_today"), :headers => {})
|
31
|
+
result = union_query.run_query TodoistQuerynaut::Client.new(Todoist::Client.new("some_token"))
|
32
|
+
|
33
|
+
expect(result.size).to eq(1)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,169 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: todoist_querynaut
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Stefan Siegl
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-03-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.4'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.4'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: simplecov
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.11.2
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.11.2
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: webmock
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.17'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.17'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: ruby-todoist-api
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0.3'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0.3'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: treetop
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '1.6'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '1.6'
|
111
|
+
description: Todoist implements its filter and query language client side, this Gem
|
112
|
+
reimplements the language features against Todoist's REST API.
|
113
|
+
email:
|
114
|
+
- stesie@brokenpipe.de
|
115
|
+
executables: []
|
116
|
+
extensions: []
|
117
|
+
extra_rdoc_files: []
|
118
|
+
files:
|
119
|
+
- LICENSE.txt
|
120
|
+
- README.md
|
121
|
+
- lib/todoist_querynaut.rb
|
122
|
+
- lib/todoist_querynaut/client.rb
|
123
|
+
- lib/todoist_querynaut/parser.rb
|
124
|
+
- lib/todoist_querynaut/project_not_found_error.rb
|
125
|
+
- lib/todoist_querynaut/todoist_query/node_extensions.rb
|
126
|
+
- lib/todoist_querynaut/todoist_query_parser.treetop
|
127
|
+
- spec/lib/todoist_querynaut/client_spec.rb
|
128
|
+
- spec/lib/todoist_querynaut/parser_spec.rb
|
129
|
+
- spec/lib/todoist_querynaut/todoist_query/intersection_spec.rb
|
130
|
+
- spec/lib/todoist_querynaut/todoist_query/literal_query_spec.rb
|
131
|
+
- spec/lib/todoist_querynaut/todoist_query/negated_query_spec.rb
|
132
|
+
- spec/lib/todoist_querynaut/todoist_query/priority_query_spec.rb
|
133
|
+
- spec/lib/todoist_querynaut/todoist_query/project_name_query_spec.rb
|
134
|
+
- spec/lib/todoist_querynaut/todoist_query/union_spec.rb
|
135
|
+
- spec/spec_helper.rb
|
136
|
+
homepage: https://github.com/stesie/todoist_querynaut
|
137
|
+
licenses:
|
138
|
+
- MIT
|
139
|
+
metadata: {}
|
140
|
+
post_install_message:
|
141
|
+
rdoc_options: []
|
142
|
+
require_paths:
|
143
|
+
- lib
|
144
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
145
|
+
requirements:
|
146
|
+
- - ">="
|
147
|
+
- !ruby/object:Gem::Version
|
148
|
+
version: '0'
|
149
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
150
|
+
requirements:
|
151
|
+
- - ">="
|
152
|
+
- !ruby/object:Gem::Version
|
153
|
+
version: '0'
|
154
|
+
requirements: []
|
155
|
+
rubyforge_project:
|
156
|
+
rubygems_version: 2.5.1
|
157
|
+
signing_key:
|
158
|
+
specification_version: 4
|
159
|
+
summary: Todoist Query Language implementation
|
160
|
+
test_files:
|
161
|
+
- spec/lib/todoist_querynaut/todoist_query/literal_query_spec.rb
|
162
|
+
- spec/lib/todoist_querynaut/todoist_query/priority_query_spec.rb
|
163
|
+
- spec/lib/todoist_querynaut/todoist_query/union_spec.rb
|
164
|
+
- spec/lib/todoist_querynaut/todoist_query/project_name_query_spec.rb
|
165
|
+
- spec/lib/todoist_querynaut/todoist_query/negated_query_spec.rb
|
166
|
+
- spec/lib/todoist_querynaut/todoist_query/intersection_spec.rb
|
167
|
+
- spec/lib/todoist_querynaut/parser_spec.rb
|
168
|
+
- spec/lib/todoist_querynaut/client_spec.rb
|
169
|
+
- spec/spec_helper.rb
|