dst 0.0.1

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 (9) hide show
  1. data/LICENCE +19 -0
  2. data/Manifest +7 -0
  3. data/README +41 -0
  4. data/bin/dst +7 -0
  5. data/dst.gemspec +81 -0
  6. data/lib/dst.rb +63 -0
  7. data/lib/dst/models.rb +78 -0
  8. data/spec/dst_spec.rb +181 -0
  9. metadata +68 -0
data/LICENCE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2008 Simon Rozet
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,7 @@
1
+ README
2
+ spec/dst_spec.rb
3
+ LICENCE
4
+ lib/dst/models.rb
5
+ lib/dst.rb
6
+ bin/dst
7
+ Manifest
data/README ADDED
@@ -0,0 +1,41 @@
1
+ dst is a KISS GTD manager for the command line lovers.
2
+
3
+ == Licence
4
+
5
+ dst is copyright 2008 Simon Rozet. It is licensed under the MIT licence.
6
+ See the included LICENSE file for details.
7
+
8
+ == Install
9
+
10
+ sudo gem install ape
11
+
12
+ == Usage
13
+
14
+ % dst @mail john@doe.org about foo
15
+ 1 - `@mail john@doe.org about foo' created.
16
+ % dst @code :myproj fix bug 123
17
+ 2 - `@code :myproj fix bug 123' created.
18
+ % dst @code :myproj do foo and bar
19
+ 3 - `@code :myproj do foo and bar' created.
20
+ % dst
21
+ 1 - @mail john@doe.org about foo
22
+ 2 - @code :myproj fix bug
23
+ 3 - @code :myproj do foo and bar
24
+ % dst @mail
25
+ 1 - @mail john@doe.org about foo
26
+ % dst :myproj
27
+ 2 - @code :myproj fix bug
28
+ 3 - @code :myproj do foo and bar
29
+ % dst ^2
30
+ Ok, `2 - @code :myproj fix bug' marked as `completed'.
31
+ % dst
32
+ 1 - @mail john@doe.org about foo
33
+ 3 - @code :myproj do foo and bar
34
+
35
+ == Contact
36
+
37
+ Simon Rozet, simon@rozet.name
38
+
39
+ == Acknowledgements
40
+
41
+ I originally wrote dst back in July 2007 with the help of Gregory Brown to whom I am very grateful.
data/bin/dst ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift 'lib/', File.dirname(__FILE__) + '/../lib'
3
+ require 'dst'
4
+
5
+ Dst::Models.establish_connection(:database => File.expand_path('~/.dst.db'))
6
+ Dst::Models.create_tables_if_necessary
7
+ Dst.process_command(ARGV.join(' '))
@@ -0,0 +1,81 @@
1
+
2
+ # Gem::Specification for Dst-0.0.1
3
+ # Originally generated by Echoe
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = %q{dst}
7
+ s.version = "0.0.1"
8
+
9
+ s.specification_version = 2 if s.respond_to? :specification_version=
10
+
11
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
+ s.authors = ["Simon Rozet"]
13
+ s.date = %q{2008-02-11}
14
+ s.default_executable = %q{dst}
15
+ s.description = %q{a KISS GTD manager for the command line lovers.}
16
+ s.email = %q{simon@rozet.name}
17
+ s.executables = ["dst"]
18
+ s.files = ["README", "spec/dst_spec.rb", "LICENCE", "lib/dst/models.rb", "lib/dst.rb", "bin/dst", "Manifest", "dst.gemspec"]
19
+ s.has_rdoc = true
20
+ s.homepage = %q{http://atonie.org/2008/dst}
21
+ s.require_paths = ["lib"]
22
+ s.rubyforge_project = %q{dst}
23
+ s.rubygems_version = %q{1.0.1}
24
+ s.summary = %q{a KISS GTD manager for the command line lovers.}
25
+
26
+ s.add_dependency(%q<activerecord>, [">= 2.0.2"])
27
+ end
28
+
29
+
30
+ # # Original Rakefile source (requires the Echoe gem):
31
+ #
32
+ # require 'rubygems'
33
+ # require 'rake'
34
+ # require 'spec/rake/spectask'
35
+ #
36
+ # Version = '0.0.1'
37
+ #
38
+ # begin
39
+ # require 'echoe'
40
+ #
41
+ # Echoe.new('dst', Version) do |p|
42
+ # p.summary = 'a KISS GTD manager for the command line lovers.'
43
+ # p.description = 'a KISS GTD manager for the command line lovers.'
44
+ # p.url = 'http://atonie.org/2008/dst'
45
+ # p.author = 'Simon Rozet'
46
+ # p.email = 'simon@rozet.name'
47
+ # p.dependencies << 'activerecord >=2.0.2'
48
+ # p.clean_pattern << 'report.html'
49
+ # p.clean_pattern << 'coverage'
50
+ # end
51
+ # rescue LoadError => boom
52
+ # puts 'You are missing a dependency required for meta-operations on this gem.'
53
+ # puts boom.to_s.capitalize
54
+ # end
55
+ #
56
+ # desc 'Install the package as a gem, without generating documentation'
57
+ # task :install_gem_no_doc => [:clean, :package] do
58
+ # sh "#{'sudo ' unless Hoe::WINDOZE }gem install pkg/*.gem --no-rdoc --no-ri"
59
+ # end
60
+ #
61
+ # task :default => 'spec'
62
+ # desc 'Run specs'
63
+ # Spec::Rake::SpecTask.new do |t|
64
+ # t.spec_opts = ['--format', 'specdoc', '--colour', '--diff']
65
+ # end
66
+ #
67
+ # desc 'Generate coverage reports'
68
+ # Spec::Rake::SpecTask.new('spec:coverage') do |t|
69
+ # t.rcov = true
70
+ # end
71
+ #
72
+ # desc 'Generate a nice HTML report of spec results'
73
+ # Spec::Rake::SpecTask.new('spec:report') do |t|
74
+ # t.spec_opts = ['--format', 'html:report.html', '--diff']
75
+ # end
76
+ #
77
+ # task :publish_on_atonie => ['spec:coverage', 'spec:report'] do
78
+ # sh 'cp -R coverage ~/web/org.atonie./2008/dst/'
79
+ # sh 'cp report.html ~/web/org.atonie./2008/dst'
80
+ # sh '~/bin/publish_atonie'
81
+ # end
@@ -0,0 +1,63 @@
1
+ require 'rubygems'
2
+ require 'dst/models'
3
+
4
+ class Dst
5
+ include Models
6
+
7
+ def self.process_command(command)
8
+ new.process_command(command)
9
+ end
10
+
11
+ def process_command(command)
12
+ options = extract_options_from_command(command)
13
+ if command.blank? || !options.has_key?(:description)
14
+ list_tasks(options)
15
+ elsif command =~ /^\^(\d+)$/
16
+ toggle_task($1.to_i)
17
+ else
18
+ create_task(options)
19
+ end
20
+ end
21
+
22
+ def create_task(options={})
23
+ task = Task.new(:description => options[:description])
24
+ task.context = Context.find_or_create_by_name(options[:context]) if options.has_key?(:context)
25
+ task.project = Project.find_or_create_by_name(options[:project]) if options.has_key?(:project)
26
+ task.save
27
+ puts "`#{task}' created."
28
+ end
29
+
30
+ def toggle_task(task_id)
31
+ task = Task.toggle!(task_id)
32
+ puts "Ok, `#{task}' marked as `#{task.status}'."
33
+ rescue ActiveRecord::RecordNotFound
34
+ puts "Oops, task ##{task_id} not found."
35
+ end
36
+
37
+ def list_tasks(options={}, include_completed=false)
38
+ context, project = options.values_at(:context, :project)
39
+ tasks = if options.empty?
40
+ Task.unfinished
41
+ elsif context
42
+ Task.unfinished.select { |task|
43
+ task.context.name == context if task.context
44
+ }
45
+ elsif project
46
+ Task.unfinished.select { |task|
47
+ task.project.name == project if task.project
48
+ }
49
+ end || []
50
+ puts tasks.empty? ? 'No tasks found' : tasks.map(&:to_s).join("\n")
51
+ end
52
+
53
+ protected
54
+ def extract_options_from_command(command)
55
+ context = command =~ /^@(\w+)/ && $1
56
+ project = command =~ /(?!\w):(\w+)/ && $1
57
+ description = command.dup
58
+ description.gsub!("@#{context}", '') unless context.nil?
59
+ description.gsub!(":#{project}", '') unless project.nil?
60
+ options = {:context => context, :project => project, :description => description.strip}
61
+ options.reject { |k, v| v.nil? || v.blank? }
62
+ end
63
+ end
@@ -0,0 +1,78 @@
1
+ require 'rubygems'
2
+ require 'active_record'
3
+
4
+ class Dst
5
+ module Models
6
+ class << self
7
+ def schema(&block)
8
+ @@schema = block if block_given?
9
+ @@schema
10
+ end
11
+
12
+ def establish_connection(options={})
13
+ ActiveRecord::Base.establish_connection({:adapter => 'sqlite3', :database => 'dst.db'}.merge(options))
14
+ end
15
+
16
+ def create_tables_if_necessary(force=false)
17
+ ActiveRecord::Schema.define(&Dst::Models.schema) if force || !Task.table_exists?
18
+ end
19
+ end
20
+
21
+ class Task < ActiveRecord::Base
22
+ belongs_to :context
23
+ belongs_to :project
24
+
25
+ def self.unfinished(options={})
26
+ find(:all, :conditions => 'status = "f"', :include => [:context, :project], :order => 't1_r1')
27
+ end
28
+
29
+ def self.toggle!(task_id)
30
+ task = find(task_id)
31
+ task.toggle!(:status)
32
+ task
33
+ end
34
+
35
+ def status
36
+ read_attribute(:status) ? 'completed' : 'unfinished'
37
+ end
38
+
39
+ def to_s
40
+ "#{id} - #{context || ''}#{project || ''}#{description}"
41
+ end
42
+ end
43
+
44
+ class Context < ActiveRecord::Base
45
+ has_many :tasks
46
+
47
+ def to_s
48
+ name ? "@#{name} ": ""
49
+ end
50
+ end
51
+
52
+ class Project < ActiveRecord::Base
53
+ has_many :tasks
54
+
55
+ def to_s
56
+ name ? ":#{name} ": ""
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ Dst::Models.schema do
63
+ create_table :tasks do |t|
64
+ t.string :description
65
+ t.boolean :status, :default => false
66
+ t.integer :context_id
67
+ t.integer :project_id
68
+ t.timestamps
69
+ end
70
+
71
+ create_table :contexts do |t|
72
+ t.string :name
73
+ end
74
+
75
+ create_table :projects do |t|
76
+ t.string :name
77
+ end
78
+ end
@@ -0,0 +1,181 @@
1
+ $:.unshift 'lib/', File.dirname(__FILE__) + '/../lib'
2
+ require 'rubygems'
3
+ require 'spec'
4
+ require 'dst'
5
+
6
+ module Kernel; def puts(s); end; end
7
+
8
+ describe Dst do
9
+ before(:each) do
10
+ @dst = Dst.new
11
+ @task_model = Dst::Models::Task
12
+ @context_model = Dst::Models::Context
13
+ @project_model = Dst::Models::Project
14
+
15
+ @task = mock('Task', :context= => true, :project= => true, :save => true)
16
+ @task_model.stub!(:new).and_return(@task)
17
+ end
18
+
19
+ it 'should exists' do
20
+ @dst.should_not be_nil
21
+ @dst.should be_an_instance_of(Dst)
22
+ end
23
+
24
+ describe "when creating tasks: `<@context> <:project> task'" do
25
+ before(:each) do
26
+ @command = '@mail :proj john@doe.name about http://example.org'
27
+ end
28
+
29
+ describe '#process_command' do
30
+ it 'dispatchs command to create_task' do
31
+ @dst.should_receive(:create_task)
32
+ @dst.process_command(@command)
33
+ end
34
+
35
+ it 'parses description correctly' do
36
+ @dst.should_receive(:create_task) do |opts|
37
+ opts[:description].should == 'john@doe.name about http://example.org'
38
+ end
39
+ @dst.process_command(@command)
40
+ end
41
+
42
+ it 'parses context correctly' do
43
+ @dst.should_receive(:create_task) { |opts| opts[:context].should == 'mail' }
44
+ @dst.process_command(@command)
45
+ end
46
+
47
+ it 'parses project correctly' do
48
+ @dst.should_receive(:create_task) { |opts| opts[:project].should == 'proj' }
49
+ @dst.process_command(@command)
50
+ end
51
+ end
52
+
53
+ describe '#create_task' do
54
+ before(:each) do
55
+ @context = mock('Context')
56
+ @context_model.stub!(:find_or_create_by_name).and_return(@context)
57
+
58
+ @project = mock('Project')
59
+ @project_model.stub!(:find_or_create_by_name).and_return(@project)
60
+ end
61
+
62
+ it 'creates a new Task with given description' do
63
+ @task_model.should_receive(:new).with(
64
+ :description => 'john@doe.name about http://example.org'
65
+ ).and_return(@task)
66
+ @dst.process_command(@command)
67
+ end
68
+
69
+ it 'finds or creates context' do
70
+ @context_model.should_receive(:find_or_create_by_name).and_return(@context)
71
+ @dst.process_command(@command)
72
+ end
73
+
74
+ it "sets task's context if specified" do
75
+ @task.should_receive(:context=).with(@context)
76
+ @dst.process_command(@command)
77
+ end
78
+
79
+ it "doesn't set tasks's context if not specified" do
80
+ @task.should_not_receive(:context=)
81
+ @dst.process_command('task without context')
82
+ end
83
+
84
+ it "finds or creates project" do
85
+ @project_model.should_receive(:find_or_create_by_name).and_return(@project)
86
+ @dst.process_command(@command)
87
+ end
88
+
89
+ it "sets task's project if specified" do
90
+ @task.should_receive(:project=).with(@project)
91
+ @dst.process_command(@command)
92
+ end
93
+
94
+ it "doesn't set tasks's project if not specified" do
95
+ @task.should_not_receive(:project=)
96
+ @dst.process_command('task without project')
97
+ end
98
+
99
+ it 'saves new task' do
100
+ @task.should_receive(:save).and_return(true)
101
+ @dst.process_command(@command)
102
+ end
103
+
104
+ it 'notices that tasks have been successfuly created' do
105
+ @dst.should_receive(:puts).with(/ created.$/)
106
+ @dst.process_command(@command)
107
+ end
108
+ end
109
+ end
110
+
111
+ describe "when toggling a task: `^<task id>'" do
112
+ before(:each) do
113
+ @task.stub!(:status).and_return(true)
114
+ @task_model.stub!(:toggle!).and_return(@task)
115
+ end
116
+
117
+ describe '#proccess_command' do
118
+ it 'dispatchs to toggle_task' do
119
+ @dst.should_receive(:toggle_task).with(479)
120
+ @dst.process_command('^479')
121
+ end
122
+
123
+ it 'parses task id correctly' do
124
+ [1, 479, 0, 1087].each do |id|
125
+ @dst.should_receive(:toggle_task).with(id)
126
+ @dst.process_command("^#{id}")
127
+ end
128
+ end
129
+ end
130
+
131
+ describe '#toggle_task' do
132
+ it 'toggle task' do
133
+ @task_model.should_receive(:toggle!).with(3).and_return(@task)
134
+ @dst.process_command('^3')
135
+ end
136
+
137
+ it 'outputs an error message if task not found' do
138
+ @task_model.should_receive(:toggle!).and_raise(ActiveRecord::RecordNotFound)
139
+ @dst.should_receive(:puts).with("Oops, task #98 not found.")
140
+ @dst.process_command('^98')
141
+ end
142
+ end
143
+ end
144
+
145
+ describe "when listing tasks: `[<@context> <@project>] [-a]]'" do
146
+ before(:each) do
147
+ @tasks = mock('tasks', :select => [@task], :empty? => false, :map => [@task])
148
+ @task_model.stub!(:unfinished).and_return(@tasks)
149
+ end
150
+
151
+ it 'finds all unfinished tasks if no filter provided' do
152
+ @task_model.should_receive(:unfinished).and_return(@tasks)
153
+ @tasks.should_not_receive(:select)
154
+ @dst.process_command('')
155
+ end
156
+
157
+ it 'finds unfinished tasks filtered by context' do
158
+ @task_model.should_receive(:unfinished).and_return(@tasks)
159
+ @tasks.should_receive(:select)
160
+ @dst.process_command('@context')
161
+ end
162
+
163
+ it 'finds unfinished tasks filtered by project' do
164
+ @task_model.should_receive(:unfinished).and_return(@tasks)
165
+ @tasks.should_receive(:select)
166
+ @dst.process_command(':project')
167
+ end
168
+
169
+ it 'outputs tasks' do
170
+ @tasks.should_receive(:map).and_return(@tasks)
171
+ @tasks.should_receive(:join).and_return("")
172
+ @dst.process_command('')
173
+ end
174
+
175
+ it 'notices if no tasks found' do
176
+ @tasks.should_receive(:empty?).and_return(true)
177
+ @dst.should_receive(:puts).with('No tasks found')
178
+ @dst.process_command('')
179
+ end
180
+ end
181
+ end
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dst
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Simon Rozet
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-02-11 00:00:00 +01:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: activerecord
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 2.0.2
23
+ version:
24
+ description: a KISS GTD manager for the command line lovers.
25
+ email: simon@rozet.name
26
+ executables:
27
+ - dst
28
+ extensions: []
29
+
30
+ extra_rdoc_files: []
31
+
32
+ files:
33
+ - README
34
+ - spec/dst_spec.rb
35
+ - LICENCE
36
+ - lib/dst/models.rb
37
+ - lib/dst.rb
38
+ - bin/dst
39
+ - Manifest
40
+ - dst.gemspec
41
+ has_rdoc: true
42
+ homepage: http://atonie.org/2008/dst
43
+ post_install_message:
44
+ rdoc_options: []
45
+
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: "0"
53
+ version:
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: "0"
59
+ version:
60
+ requirements: []
61
+
62
+ rubyforge_project: dst
63
+ rubygems_version: 1.0.1
64
+ signing_key:
65
+ specification_version: 2
66
+ summary: a KISS GTD manager for the command line lovers.
67
+ test_files: []
68
+