taskwarrior-web 1.0.6 → 1.0.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,15 @@
1
+ ## v1.0.7 (11/17/12)
2
+
3
+ * Moving `task` version parsing to Verionomy library. Should be much more
4
+ flexible and stable.
5
+ * Removing tag input widget from new task form, and changed parsing to split
6
+ tags on spaces, commas, or any combination thereof. So something like
7
+ "these,will all, be , tags now" should result in tags of "these", "will",
8
+ "all", "be", "tags", and "now". Hopefully this is easier to use and less
9
+ confusing.
10
+ * Adding basic support for `task` < 1.9.2
11
+ * Moved to thin gem as a server for speed and stability.
12
+
1
13
  ## v1.0.6 (11/15/12)
2
14
 
3
15
  * Fixed escaping issue when viewing or adding projects with spaces.
data/README.md CHANGED
@@ -3,12 +3,17 @@
3
3
  A lightweight, Sinatra-based web interface for the
4
4
  wonderful [Taskwarrior](http://taskwarrior.org/) todo application.
5
5
 
6
- **Now compatible with ALL versions of Taskwarrior, including the new 2.0.0**
6
+ **Now compatible with all versions of Taskwarrior, including 2.x**
7
7
  **Also, now including a NEW theme based on Twitter Bootstrap. Mobile version
8
8
  forthcoming!**
9
9
 
10
10
  [![Build Status](https://secure.travis-ci.org/theunraveler/taskwarrior-web.png)](http://travis-ci.org/theunraveler/taskwarrior-web)
11
11
 
12
+ ## Requirements
13
+
14
+ * `ruby` >= 1.9 (support for `ruby` < 1.9 is very unlikely, but pull requests
15
+ are gladly accepted).
16
+
12
17
  ## Installation
13
18
 
14
19
  `gem install taskwarrior-web`
@@ -38,10 +43,17 @@ The current featureset includes:
38
43
  * Optional Basic HTTP Auth protection. To enable, set `task-web.user` and
39
44
  `task-web.passwd` in your `.taskrc` file.
40
45
 
41
- ## Known Issues
46
+ ## Reporting Bugs
47
+
48
+ To report a bug, use the [Github issue tracker][1]. Since `taskwarrior-web`
49
+ works with several different versions of `task`, using many different
50
+ configurations, please include the output from `task _version` and either the
51
+ output of `task show` or a copy of your `.taskrc` file when filing a bug. This helps me reproduce bugs easier.
52
+
53
+ Here is an example of a [good bug report][2].
42
54
 
43
- * taskwarrior-web requires Ruby >= 1.9. It will not work with 1.8 and lower.
44
- Support for 1.8 will happen at some point.
55
+ [1]: http://github.com/theunraveler/taskwarrior-web/issues
56
+ [2]: http://github.com/theunraveler/taskwarrior-web/issues/26
45
57
 
46
58
  ## Marginalia
47
59
 
@@ -4,7 +4,7 @@ module TaskwarriorWeb
4
4
  module CommandBuilder
5
5
  def self.included(class_name)
6
6
  class_name.class_eval do
7
- case TaskwarriorWeb::Config.task_major_version
7
+ case TaskwarriorWeb::Config.version.major
8
8
  when 2
9
9
  require 'taskwarrior-web/command_builders/v2'
10
10
  include TaskwarriorWeb::CommandBuilder::V2
@@ -5,7 +5,7 @@ module TaskwarriorWeb::CommandBuilder
5
5
 
6
6
  TASK_COMMANDS = {
7
7
  :add => 'add',
8
- :query => '_query',
8
+ :query => TaskwarriorWeb::Config.version > Versionomy.parse('1.9.2') ? '_query' : 'export',
9
9
  :count => 'count',
10
10
  :complete => ':id done',
11
11
  :projects => '_projects',
@@ -42,8 +42,7 @@ module TaskwarriorWeb::CommandBuilder
42
42
  string = ''
43
43
  string << %Q( #{@params.delete(:description).shellescape}) if @params.has_key?(:description)
44
44
 
45
- if @params.has_key?(:tags)
46
- tags = @params.delete(:tags)
45
+ if tags = @params.delete(:tags)
47
46
  tag_indicator = TaskwarriorWeb::Config.property('tag.indicator') || '+'
48
47
  tags.each { |tag| string << %Q( #{tag_indicator}#{tag.to_s.shellescape}) }
49
48
  end
@@ -1,14 +1,11 @@
1
1
  require 'parseconfig'
2
+ require 'versionomy'
2
3
 
3
4
  module TaskwarriorWeb
4
5
  class Config
5
6
 
6
- def self.task_version
7
- @task_version ||= `task _version`
8
- end
9
-
10
- def self.task_major_version
11
- self.task_version[0,1].to_i
7
+ def self.version
8
+ @version ||= Versionomy.parse(`task _version`.strip)
12
9
  end
13
10
 
14
11
  def self.file
@@ -0,0 +1,16 @@
1
+ require 'taskwarrior-web/config'
2
+ require 'versionomy'
3
+
4
+ module TaskwarriorWeb
5
+ module Parser
6
+ def self.parse(results)
7
+ if TaskwarriorWeb::Config.version > Versionomy.parse('1.9.2')
8
+ require 'taskwarrior-web/parser/json'
9
+ TaskwarriorWeb::Parser::Json.parse(results)
10
+ else
11
+ require 'taskwarrior-web/parser/csv'
12
+ TaskwarriorWeb::Parser::Csv.parse(results)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,11 @@
1
+ require 'csv'
2
+
3
+ module TaskwarriorWeb::Parser::Csv
4
+ def self.parse(csv)
5
+ rows = []
6
+ CSV.parse(csv, :headers => true, :quote_char => "'", :header_converters => :symbol, :converters => :all) do |row|
7
+ rows << Hash[row.headers.zip(row.fields)]
8
+ end
9
+ rows
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ require 'json'
2
+
3
+ module TaskwarriorWeb::Parser::Json
4
+ def self.parse(json)
5
+ json.strip!
6
+ json = '[' + json + ']'
7
+ json == '[No matches.]' ? [] : ::JSON.parse(json)
8
+ end
9
+ end
@@ -28,34 +28,25 @@ var initAutocomplete = function() {
28
28
  $('#task-project').typeahead({
29
29
  source: function (query, process) {
30
30
  return $.get('/ajax/projects/', {query: query}, function (data) {
31
- data = $.parseJSON(data);
32
- process(data);
31
+ process($.parseJSON(data));
33
32
  });
34
33
  }
35
34
  });
36
-
37
- $('#task-tags').tokenInput('/ajax/tags', {
38
- theme: 'facebook',
39
- queryParam: 'query',
40
- noResultsText: 'No matching tags',
41
- allowFreeTagging: true
42
- });
43
35
  };
44
36
 
45
37
  var initTaskCompletion = function() {
46
38
  $('input.complete').click(function() {
47
39
  // Cache the checkbox in case we need to restore it.
48
- var container = $(this).parent();
49
- var checkbox = container.html();
50
- var row = $(this).closest('tr');
40
+ var container = $(this).parent(),
41
+ checkbox = container.html(),
42
+ row = $(this).closest('tr');
51
43
  container.html('<img src="/img/ajax-loader.gif" />');
52
44
  $.ajax({
53
45
  url: '/ajax/task-complete/' + $(this).data('task-id'),
54
46
  type: 'POST',
55
47
  success: function(data) {
56
48
  refreshPageContents();
57
- var message = (data === '') ? 'Task marked as completed.' : data;
58
- set_message(message, 'alert-success');
49
+ set_message(data === '' ? 'Task marked as completed.' : data, 'alert-success');
59
50
  row.fadeOut();
60
51
  },
61
52
  error: function(data) {
@@ -1,5 +1,6 @@
1
- require 'json'
2
1
  require 'taskwarrior-web/command'
2
+ require 'taskwarrior-web/parser'
3
+ require 'versionomy'
3
4
 
4
5
  module TaskwarriorWeb
5
6
 
@@ -32,7 +33,7 @@ module TaskwarriorWeb
32
33
 
33
34
  # Make sure that the tags are an array.
34
35
  def tags=(value)
35
- @tags = value.is_a?(String) ? value.gsub(', ', ',').split(',') : value
36
+ @tags = value.is_a?(String) ? value.split(/\W+/).reject(&:empty?) : value
36
37
  end
37
38
 
38
39
  def to_hash
@@ -46,20 +47,18 @@ module TaskwarriorWeb
46
47
  # Run queries on tasks.
47
48
  def self.query(*args)
48
49
  tasks = []
49
-
50
- # Process the JSON data.
51
- json = Command.new(:query, nil, *args).run
52
- json.strip!
53
- json = '[' + json + ']'
54
- results = json == '[No matches.]' ? [] : ::JSON.parse(json)
55
-
56
- results.each { |result| tasks << Task.new(result) }
50
+ results = Command.new(:query, nil, *args).run
51
+ Parser.parse(results).each { |result| tasks << Task.new(result) }
57
52
  tasks
58
53
  end
59
54
 
60
55
  # Get the number of tasks for some paramters
61
56
  def self.count(*args)
62
- Command.new(:count, nil, *args).run.to_s.strip!
57
+ if TaskwarriorWeb::Config.version > Versionomy.parse('1.9.2')
58
+ Command.new(:count, nil, *args).run.to_s.strip!
59
+ else
60
+ self.query(*args).count
61
+ end
63
62
  end
64
63
 
65
64
  # Define method_missing to implement dynamic finder methods
@@ -1,19 +1,16 @@
1
1
  <html>
2
2
  <head>
3
- <title><%= @title %> | Taskwarrior</title>
3
+ <title><%= @title %> | TaskwarriorWeb</title>
4
4
 
5
5
  <!-- Styles -->
6
6
  <link rel="stylesheet" href="/css/bootstrap.min.css">
7
7
  <link rel="stylesheet" href="/css/datepicker.css">
8
- <link rel="stylesheet" href="/css/token-input.css">
9
- <link rel="stylesheet" href="/css/token-input-facebook.css">
10
8
  <link rel="stylesheet" href="/css/styles.css">
11
9
 
12
10
  <!-- Scripts -->
13
11
  <script type="text/javascript" src="/js/jquery.min.js"></script>
14
12
  <script type="text/javascript" src="/js/bootstrap.min.js"></script>
15
13
  <script type="text/javascript" src="/js/bootstrap-datepicker.js"></script>
16
- <script type="text/javascript" src="/js/jquery.tokeninput.js"></script>
17
14
  <script type="text/javascript" src="/js/application.js"></script>
18
15
  </head>
19
16
 
@@ -10,7 +10,7 @@
10
10
  <div class="control-group">
11
11
  <label for="task-project" class="control-label">Project</label>
12
12
  <div class="controls">
13
- <input type="textfield" id="task-project" name="task[project]" value="<%= @task[:project] unless @task.nil? %>" />
13
+ <input type="textfield" id="task-project" name="task[project]" value="<%= @task[:project] unless @task.nil? %>" autocomplete="off" />
14
14
  </div>
15
15
  </div>
16
16
 
@@ -24,8 +24,8 @@
24
24
  <div class="control-group">
25
25
  <label for="task-tags" class="control-label">Tags</label>
26
26
  <div class="controls">
27
- <input type="textfield" id="task-tags" name="task[tags]" value="<%= @task[:tags] unless @task.nil? %>" />
28
- <div class="description">Enter tags separated by commas</div>
27
+ <input type="textfield" id="task-tags" name="task[tags]" value="<%= @task[:tags] unless @task.nil? %>" autocomplete="off" />
28
+ <div class="description">Enter tags separated by commas or spaces (e.g. <em>each, word will,be a tag</em>)</div>
29
29
  </div>
30
30
  </div>
31
31
 
@@ -44,4 +44,16 @@ describe "My App" do
44
44
  last_response.body.should eq('15')
45
45
  end
46
46
  end
47
+
48
+ describe 'not_found' do
49
+ it 'should set the title to "Not Found"' do
50
+ get '/page-not-found'
51
+ last_response.body.should =~ /<title>Page Not Found/
52
+ end
53
+
54
+ it 'should have a status code of 404' do
55
+ get '/page-not-found'
56
+ last_response.status.should eq(404)
57
+ end
58
+ end
47
59
  end
@@ -7,7 +7,7 @@ describe TaskwarriorWeb::CommandBuilder do
7
7
  describe '.included' do
8
8
  context 'when v2 is reported' do
9
9
  it 'should include CommandBuilder V2 module' do
10
- TaskwarriorWeb::Config.should_receive(:task_version).and_return('2.0.1')
10
+ TaskwarriorWeb::Config.should_receive(:version).and_return(Versionomy.parse('2.0.0'))
11
11
  TestCommandClass.class_eval { include TaskwarriorWeb::CommandBuilder }
12
12
  TestCommandClass.should include(TaskwarriorWeb::CommandBuilder::V2)
13
13
  end
@@ -15,7 +15,7 @@ describe TaskwarriorWeb::CommandBuilder do
15
15
 
16
16
  context 'when v1 is reported' do
17
17
  it 'should include CommandBuilder V1 module' do
18
- TaskwarriorWeb::Config.should_receive(:task_version).and_return('1.9.4')
18
+ TaskwarriorWeb::Config.should_receive(:version).and_return(Versionomy.parse('1.0.0'))
19
19
  TestCommandClass.class_eval { include TaskwarriorWeb::CommandBuilder }
20
20
  TestCommandClass.should include(TaskwarriorWeb::CommandBuilder::V1)
21
21
  end
@@ -23,7 +23,7 @@ describe TaskwarriorWeb::CommandBuilder do
23
23
 
24
24
  context 'when an invalid version number is reported' do
25
25
  it 'should throw an UnrecognizedTaskVersion exception' do
26
- TaskwarriorWeb::Config.should_receive(:task_version).and_return('95.583.3')
26
+ TaskwarriorWeb::Config.should_receive(:version).and_return(Versionomy.parse('9.0.0'))
27
27
  expect {
28
28
  TestCommandClass.class_eval { include TaskwarriorWeb::CommandBuilder }
29
29
  }.to raise_exception(TaskwarriorWeb::UnrecognizedTaskVersion)
@@ -17,7 +17,7 @@ describe TaskwarriorWeb::CommandBuilder::Base do
17
17
  it 'should create a string from the passed paramters' do
18
18
  command = TaskwarriorWeb::Command.new(:query, nil, :test => 14, :none => :none, :hello => :hi)
19
19
  command.parse_params
20
- command.params.should eq(' test:14 none:none hello:hi')
20
+ command.params.should eq(' test:\"14\" none:\"none\" hello:\"hi\"')
21
21
  end
22
22
 
23
23
  it 'should prefix tags with the tag.indicator if specified' do
@@ -37,7 +37,7 @@ describe TaskwarriorWeb::CommandBuilder::Base do
37
37
  it 'should pull out the description parameter' do
38
38
  command = TaskwarriorWeb::Command.new(:add, nil, :description => 'Hello', :status => :pending)
39
39
  command.parse_params
40
- command.params.should eq(" Hello status:pending")
40
+ command.params.should eq(' Hello status:\"pending\"')
41
41
  end
42
42
  end
43
43
  end
@@ -53,8 +53,8 @@ describe TaskwarriorWeb::Task do
53
53
 
54
54
  describe '#tags=' do
55
55
  it 'should convert a string to an array when initializing' do
56
- task = TaskwarriorWeb::Task.new(:tags => 'hi there, twice')
57
- task.tags.should eq(['hi there', 'twice'])
56
+ task = TaskwarriorWeb::Task.new(:tags => 'hi, twice')
57
+ task.tags.should eq(['hi', 'twice'])
58
58
  end
59
59
 
60
60
  it 'should convert a string to an array when setting explicitly' do
@@ -62,6 +62,15 @@ describe TaskwarriorWeb::Task do
62
62
  task.tags = 'hello, twice,thrice'
63
63
  task.tags.should eq(['hello', 'twice', 'thrice'])
64
64
  end
65
+
66
+ it 'should break on any combination of spaces and commas' do
67
+ try = ['hi twice and again', 'hi,twice,and,again', 'hi twice,and again', 'hi, twice and , again']
68
+
69
+ try.each do |tags|
70
+ task = TaskwarriorWeb::Task.new(:tags => tags)
71
+ task.tags.should eq(['hi', 'twice', 'and', 'again'])
72
+ end
73
+ end
65
74
  end
66
75
 
67
76
  describe '#to_hash' do
@@ -3,7 +3,7 @@ $:.push File.expand_path("../lib", __FILE__)
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "taskwarrior-web"
6
- s.version = '1.0.6'
6
+ s.version = '1.0.7'
7
7
  s.platform = Gem::Platform::RUBY
8
8
  s.authors = ["Jake Bell"]
9
9
  s.email = ["jake@theunraveler.com"]
@@ -16,9 +16,11 @@ Gem::Specification.new do |s|
16
16
  s.required_ruby_version = '>= 1.9.0'
17
17
 
18
18
  s.add_dependency('sinatra')
19
+ s.add_dependency('thin')
19
20
  s.add_dependency('parseconfig')
20
21
  s.add_dependency('vegas')
21
22
  s.add_dependency('rinku')
23
+ s.add_dependency('versionomy')
22
24
 
23
25
  s.add_development_dependency('rake')
24
26
  s.add_development_dependency('rack-test')
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: taskwarrior-web
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.6
4
+ version: 1.0.7
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-14 00:00:00.000000000 Z
12
+ date: 2012-11-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sinatra
16
- requirement: &70175087714260 !ruby/object:Gem::Requirement
16
+ requirement: &70252814870220 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,21 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70175087714260
24
+ version_requirements: *70252814870220
25
+ - !ruby/object:Gem::Dependency
26
+ name: thin
27
+ requirement: &70252814869540 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70252814869540
25
36
  - !ruby/object:Gem::Dependency
26
37
  name: parseconfig
27
- requirement: &70175087713800 !ruby/object:Gem::Requirement
38
+ requirement: &70252814868880 !ruby/object:Gem::Requirement
28
39
  none: false
29
40
  requirements:
30
41
  - - ! '>='
@@ -32,10 +43,10 @@ dependencies:
32
43
  version: '0'
33
44
  type: :runtime
34
45
  prerelease: false
35
- version_requirements: *70175087713800
46
+ version_requirements: *70252814868880
36
47
  - !ruby/object:Gem::Dependency
37
48
  name: vegas
38
- requirement: &70175087713200 !ruby/object:Gem::Requirement
49
+ requirement: &70252814868200 !ruby/object:Gem::Requirement
39
50
  none: false
40
51
  requirements:
41
52
  - - ! '>='
@@ -43,10 +54,21 @@ dependencies:
43
54
  version: '0'
44
55
  type: :runtime
45
56
  prerelease: false
46
- version_requirements: *70175087713200
57
+ version_requirements: *70252814868200
47
58
  - !ruby/object:Gem::Dependency
48
59
  name: rinku
49
- requirement: &70175087712780 !ruby/object:Gem::Requirement
60
+ requirement: &70252814867560 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :runtime
67
+ prerelease: false
68
+ version_requirements: *70252814867560
69
+ - !ruby/object:Gem::Dependency
70
+ name: versionomy
71
+ requirement: &70252814866800 !ruby/object:Gem::Requirement
50
72
  none: false
51
73
  requirements:
52
74
  - - ! '>='
@@ -54,10 +76,10 @@ dependencies:
54
76
  version: '0'
55
77
  type: :runtime
56
78
  prerelease: false
57
- version_requirements: *70175087712780
79
+ version_requirements: *70252814866800
58
80
  - !ruby/object:Gem::Dependency
59
81
  name: rake
60
- requirement: &70175087712300 !ruby/object:Gem::Requirement
82
+ requirement: &70252814866040 !ruby/object:Gem::Requirement
61
83
  none: false
62
84
  requirements:
63
85
  - - ! '>='
@@ -65,10 +87,10 @@ dependencies:
65
87
  version: '0'
66
88
  type: :development
67
89
  prerelease: false
68
- version_requirements: *70175087712300
90
+ version_requirements: *70252814866040
69
91
  - !ruby/object:Gem::Dependency
70
92
  name: rack-test
71
- requirement: &70175087711700 !ruby/object:Gem::Requirement
93
+ requirement: &70252814865320 !ruby/object:Gem::Requirement
72
94
  none: false
73
95
  requirements:
74
96
  - - ! '>='
@@ -76,10 +98,10 @@ dependencies:
76
98
  version: '0'
77
99
  type: :development
78
100
  prerelease: false
79
- version_requirements: *70175087711700
101
+ version_requirements: *70252814865320
80
102
  - !ruby/object:Gem::Dependency
81
103
  name: rspec
82
- requirement: &70175087711220 !ruby/object:Gem::Requirement
104
+ requirement: &70252814864780 !ruby/object:Gem::Requirement
83
105
  none: false
84
106
  requirements:
85
107
  - - ! '>='
@@ -87,7 +109,7 @@ dependencies:
87
109
  version: '0'
88
110
  type: :development
89
111
  prerelease: false
90
- version_requirements: *70175087711220
112
+ version_requirements: *70252814864780
91
113
  description: This gem provides a graphical frontend for the Taskwarrior task manager.
92
114
  It is based on Sinatra.
93
115
  email:
@@ -116,13 +138,13 @@ files:
116
138
  - lib/taskwarrior-web/command_builders/v2.rb
117
139
  - lib/taskwarrior-web/config.rb
118
140
  - lib/taskwarrior-web/helpers.rb
141
+ - lib/taskwarrior-web/parser.rb
142
+ - lib/taskwarrior-web/parser/csv.rb
143
+ - lib/taskwarrior-web/parser/json.rb
119
144
  - lib/taskwarrior-web/public/css/bootstrap-responsive.min.css
120
145
  - lib/taskwarrior-web/public/css/bootstrap.min.css
121
146
  - lib/taskwarrior-web/public/css/datepicker.css
122
- - lib/taskwarrior-web/public/css/jquery.tagsinput.css
123
147
  - lib/taskwarrior-web/public/css/styles.css
124
- - lib/taskwarrior-web/public/css/token-input-facebook.css
125
- - lib/taskwarrior-web/public/css/token-input.css
126
148
  - lib/taskwarrior-web/public/favicon.ico
127
149
  - lib/taskwarrior-web/public/img/ajax-loader.gif
128
150
  - lib/taskwarrior-web/public/img/glyphicons-halflings-white.png
@@ -131,7 +153,6 @@ files:
131
153
  - lib/taskwarrior-web/public/js/bootstrap-datepicker.js
132
154
  - lib/taskwarrior-web/public/js/bootstrap.min.js
133
155
  - lib/taskwarrior-web/public/js/jquery.min.js
134
- - lib/taskwarrior-web/public/js/jquery.tokeninput.js
135
156
  - lib/taskwarrior-web/runner.rb
136
157
  - lib/taskwarrior-web/task.rb
137
158
  - lib/taskwarrior-web/views/404.erb
@@ -175,7 +196,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
175
196
  version: '0'
176
197
  segments:
177
198
  - 0
178
- hash: -3999339187514199251
199
+ hash: -2453576857035471181
179
200
  requirements: []
180
201
  rubyforge_project: taskwarrior-web
181
202
  rubygems_version: 1.8.11