todos 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/bin/todos +14 -0
  2. data/lib/todos_cli.rb +235 -0
  3. data/lib/todos_database.rb +52 -0
  4. metadata +76 -0
data/bin/todos ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+
4
+ require 'rubygems'
5
+ require 'sequel'
6
+
7
+ DB_PATH = File.join(ENV['HOME'], 'todos.sqlite3')
8
+ DB = Sequel.connect("sqlite://#{DB_PATH}")
9
+
10
+ require 'todos_database.rb'
11
+ require 'todos_cli.rb'
12
+
13
+ Todos::Commands::GeneralCommands.run
14
+
data/lib/todos_cli.rb ADDED
@@ -0,0 +1,235 @@
1
+ module Todos
2
+
3
+ module Asker
4
+ def self.multi(fields)
5
+ params = {}
6
+ fields.each do |field|
7
+ params.merge! single(field)
8
+ end
9
+ params
10
+ end
11
+ def self.single(field)
12
+ {field => ask(field)}
13
+ end
14
+ def self.ask(field)
15
+ print "#{field.to_s.capitalize.tr('_', ' ')}:_ "
16
+ gets.strip
17
+ end
18
+ end
19
+
20
+ module Menu
21
+ def self.append_features(klass)
22
+ def klass.menu
23
+ @commands ||= constants.reject{ |command| command =~ /basic/i }.collect { |command| const_get(command).extend(MenuItem) }.sort { |a, b| a::KEY <=> b::KEY }
24
+ print_menu
25
+ process_user_input
26
+ end
27
+ def klass.print_menu
28
+ puts
29
+ @commands.each do |command|
30
+ puts command.menu_item
31
+ end
32
+ end
33
+ def klass.process_user_input
34
+ print 'Command:_ '
35
+ user_input = gets.strip.downcase
36
+ puts
37
+ command = @commands.detect { |command| command::KEY.eql? user_input }
38
+ call_command(command) if command
39
+ end
40
+ end
41
+ end
42
+
43
+ module MenuItem
44
+ def menu_item
45
+ "#{self::KEY}\t#{self::DESCRIPTION}"
46
+ end
47
+ end
48
+
49
+ module Commands
50
+
51
+ module BasicList
52
+ def print_list(items)
53
+ items.each do |item|
54
+ puts item
55
+ end
56
+ end
57
+ end
58
+
59
+ class GeneralCommands
60
+ class BasicTaskList
61
+ extend BasicList
62
+ def self.task_list_body(tasks)
63
+ unless tasks.empty?
64
+ print_list tasks
65
+ else
66
+ puts 'There are no tasks'
67
+ end
68
+ end
69
+
70
+ def self.execute(conditions)
71
+ tasks = Task.with_all.filter(conditions)
72
+ task_list_body(tasks)
73
+ unless tasks.empty?
74
+ task = Task.filter(conditions.merge(Asker.single(:id)))
75
+ unless task.empty?
76
+ BasicTaskManipulation.run(task)
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ class ListDoneTasks < BasicTaskList
83
+ KEY = 'd'
84
+ DESCRIPTION = 'List done tasks'
85
+ def self.execute
86
+ puts 'Done tasks'
87
+ task_list_body(Task.with_all.filter(:done => true))
88
+ end
89
+ end
90
+
91
+ class ListUndoneTasks < BasicTaskList
92
+ KEY = 'l'
93
+ DESCRIPTION = 'List undone tasks'
94
+ def self.execute
95
+ puts 'Tasks to do'
96
+ super(:done => false)
97
+ end
98
+ end
99
+
100
+ class AddTask
101
+ KEY = 'a'
102
+ DESCRIPTION = 'Add new task'
103
+ REQUIRED_ATTRIBUTES = [:description, :priority]
104
+ SUCCESS_MESSAGE = 'Task created'
105
+ FAIL_MESSAGE = 'Could not create task'
106
+
107
+ def self.execute
108
+ params = Asker.multi(REQUIRED_ATTRIBUTES)
109
+ Task.association_reflections.each do |k, v|
110
+ params.merge!(v[:key] => const_get(v[:class_name]).find_or_create(:name => Asker.ask(k)).id)
111
+ end
112
+ puts Task.insert(params).zero? ? FAIL_MESSAGE: SUCCESS_MESSAGE
113
+ end
114
+ end
115
+
116
+ class Exit
117
+ KEY = 'x'
118
+ DESCRIPTION = 'Exit the program'
119
+ def self.execute
120
+ exit
121
+ end
122
+ end
123
+
124
+ class ListContexts
125
+ KEY = 'c'
126
+ DESCRIPTION = 'List contexts'
127
+ extend BasicList
128
+ def self.execute
129
+ puts 'List of contests'
130
+ contexts = Context.active
131
+ unless contexts.empty?
132
+ print_list(contexts)
133
+ conditions = { :done => false }.merge(Asker.single(:context_id))
134
+ BasicTaskList.execute(conditions)
135
+ else
136
+ puts 'There are no contexts'
137
+ end
138
+ end
139
+ end
140
+
141
+ class ListProjects
142
+ KEY = 'p'
143
+ DESCRIPTION = 'List projects'
144
+ extend BasicList
145
+ def self.execute
146
+ puts 'List of projects'
147
+ projects = Project.active
148
+ unless projects.empty?
149
+ print_list(projects)
150
+ conditions = { :done => false }.merge(Asker.single(:project_id))
151
+ BasicTaskList.execute(conditions)
152
+ else
153
+ puts 'There are no projects'
154
+ end
155
+ end
156
+ end
157
+
158
+ include Menu
159
+
160
+ def self.run
161
+ loop { menu }
162
+ end
163
+
164
+ def self.call_command(command)
165
+ command.execute
166
+ end
167
+ end
168
+
169
+ class BasicTaskManipulation
170
+
171
+ module BasicMessages
172
+ FAIL_MESSAGE = 'Could not update task'
173
+ SUCCESS_MESSAGE = 'Task successfuly updated'
174
+ end
175
+ class RemoveTask
176
+ KEY = 'r'
177
+ DESCRIPTION = 'Remove task'
178
+ FAIL_MESSAGE = 'Could not remove task'
179
+ SUCCESS_MESSAGE = 'Task successfuly removed'
180
+ def self.execute(task)
181
+ task.delete
182
+ end
183
+ end
184
+
185
+ class MarkTaskDone
186
+ KEY = 'd'
187
+ DESCRIPTION = 'Mark task as done'
188
+ include BasicMessages
189
+ def self.execute(task)
190
+ task.update(:done => true)
191
+ end
192
+ end
193
+
194
+ class ChangeTaskPriority
195
+ KEY = 'i'
196
+ DESCRIPTION = 'Change priority of the task'
197
+ include BasicMessages
198
+ def self.execute(task)
199
+ task.update(Asker.single(:priority))
200
+ end
201
+ end
202
+
203
+ class ChangeTaskProject
204
+ KEY = 'p'
205
+ DESCRIPTION = 'Change project of the task'
206
+ include BasicMessages
207
+ def self.execute(task)
208
+ project_id = Project.find_or_create(:name => Asker.ask(:project)).id
209
+ task.update(:project_id => project_id)
210
+ end
211
+ end
212
+
213
+ class ChangeTaskContext
214
+ KEY = 'c'
215
+ DESCRIPTION = 'Change context of the task'
216
+ include BasicMessages
217
+ def self.execute(task)
218
+ context_id = Context.find_or_create(:name => Asker.ask(:context)).id
219
+ task.update(:context_id => context_id)
220
+ end
221
+ end
222
+
223
+ include Menu
224
+
225
+ def self.run(task)
226
+ @task = task
227
+ menu
228
+ end
229
+
230
+ def self.call_command(command)
231
+ puts command.execute(@task).to_i.zero? ? command::FAIL_MESSAGE : command::SUCCESS_MESSAGE
232
+ end
233
+ end
234
+ end
235
+ end
@@ -0,0 +1,52 @@
1
+ unless DB.table_exists? :tasks
2
+ DB.create_table :tasks do
3
+ primary_key :id
4
+ TrueClass :done, :default => false
5
+ Integer :project_id, :default => 0, :null => false
6
+ Integer :context_id, :default => 0, :null => false
7
+ String :description, :default => '', :null => false, :size => 200
8
+ String :priority, :default => '', :null => false, :size => 1
9
+ end
10
+ DB.create_table :projects do
11
+ primary_key :id
12
+ String :name, :default => '', :null => false, :size => 50
13
+ end
14
+ DB.create_table :contexts do
15
+ primary_key :id
16
+ String :name, :default => '', :null => false, :size => 50
17
+ end
18
+ end
19
+
20
+ class Project < Sequel::Model
21
+ one_to_many :tasks
22
+ def to_s
23
+ "#{id}\t#{name.empty? ? 'n/a' : name}"
24
+ end
25
+ def self.active
26
+ select('projects.*'.lit).join(:tasks, :project_id => :id).group(:project_id).order('lower(name)'.lit).filter(:tasks__done => false)
27
+ end
28
+ end
29
+
30
+ class Context < Sequel::Model
31
+ one_to_many :tasks
32
+ def to_s
33
+ "#{id}\t#{name.empty? ? 'n/a' : name}"
34
+ end
35
+ def self.active
36
+ select('contexts.*'.lit).join(:tasks, :context_id => :id).group(:context_id).order('lower(name)'.lit).filter(:tasks__done => false)
37
+ end
38
+ end
39
+
40
+ class Task < Sequel::Model
41
+ many_to_one :project
42
+ many_to_one :context
43
+ def to_s
44
+ "#{pk}\t#{description}\t" +
45
+ "#{' Project: ' + project.name unless project.name.empty?}" +
46
+ "#{' Context: ' + context.name unless context.name.empty?}" +
47
+ "#{' Priority: ' + priority unless priority.empty?}"
48
+ end
49
+ def self.with_all
50
+ select('tasks.*, projects.name, contexts.name'.lit).join(:projects, :id => :project_id).join(:contexts, :id => :tasks__context_id).order('done, length(priority) desc, lower(priority)'.lit)
51
+ end
52
+ end
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: todos
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Iwakura Taro
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-12-03 00:00:00 +02:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: sqlite3-ruby
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.2.4
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: sequel
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 3.6.0
34
+ version:
35
+ description: " Interactive command line application for managing list of tasks.\n Allow create new tasks, remove tasks, mark task as done and\n filter tasks on project or context.\n"
36
+ email: taro@mail333.com
37
+ executables:
38
+ - todos
39
+ extensions: []
40
+
41
+ extra_rdoc_files: []
42
+
43
+ files:
44
+ - lib/todos_cli.rb
45
+ - lib/todos_database.rb
46
+ - bin/todos
47
+ has_rdoc: true
48
+ homepage:
49
+ licenses: []
50
+
51
+ post_install_message:
52
+ rdoc_options: []
53
+
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: "0"
61
+ version:
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: "0"
67
+ version:
68
+ requirements:
69
+ - sqlite3-3.6.19 or greater
70
+ rubyforge_project:
71
+ rubygems_version: 1.3.5
72
+ signing_key:
73
+ specification_version: 3
74
+ summary: Interactive CLI tool for managing list of tasks
75
+ test_files: []
76
+