rmilk 0.1.6
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.
- data/README.rdoc +38 -0
- data/Rakefile +59 -0
- data/bin/rmilk +1 -0
- data/lib/import_rmilk.rb +13 -0
- data/lib/rmilk.rb +13 -0
- data/lib/rmilk/console_ui.rb +161 -0
- data/lib/rmilk/context_storage.rb +61 -0
- data/lib/rmilk/input_parser.rb +32 -0
- data/lib/rmilk/output_formatter.rb +61 -0
- data/lib/rmilk/rmilk_runner.rb +27 -0
- data/lib/rmilk/rtm_list_repository.rb +34 -0
- data/lib/rmilk/rtm_task_repository.rb +42 -0
- data/lib/rmilk/task.rb +72 -0
- data/lib/rmilk/version.rb +13 -0
- data/test/test_helper.rb +8 -0
- data/test/unit/context_storage_test.rb +51 -0
- data/test/unit/input_parser_test.rb +54 -0
- data/test/unit/output_formatter_test_test.rb +32 -0
- data/test/unit/rtm_list_repository_test.rb +37 -0
- data/test/unit/rtm_task_repository_test.rb +26 -0
- metadata +104 -0
data/README.rdoc
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
= Rmilk
|
2
|
+
|
3
|
+
== Description
|
4
|
+
|
5
|
+
Describe your gem here ...
|
6
|
+
|
7
|
+
== Installation
|
8
|
+
|
9
|
+
sudo gem install rmilk
|
10
|
+
|
11
|
+
== Usage
|
12
|
+
|
13
|
+
require 'rmilk'
|
14
|
+
|
15
|
+
== License
|
16
|
+
|
17
|
+
Copyright (c) <year> <copyright holders>
|
18
|
+
|
19
|
+
Permission is hereby granted, free of charge, to any person
|
20
|
+
obtaining a copy of this software and associated documentation
|
21
|
+
files (the "Software"), to deal in the Software without
|
22
|
+
restriction, including without limitation the rights to use,
|
23
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
24
|
+
copies of the Software, and to permit persons to whom the
|
25
|
+
Software is furnished to do so, subject to the following
|
26
|
+
conditions:
|
27
|
+
|
28
|
+
The above copyright notice and this permission notice shall be
|
29
|
+
included in all copies or substantial portions of the Software.
|
30
|
+
|
31
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
32
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
33
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
34
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
35
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
36
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
37
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
38
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake/gempackagetask'
|
3
|
+
require 'rake/testtask'
|
4
|
+
|
5
|
+
require 'lib/rmilk/version'
|
6
|
+
|
7
|
+
spec = Gem::Specification.new do |s|
|
8
|
+
s.name = 'rmilk'
|
9
|
+
s.version = ConsoleRtm::Version.to_s
|
10
|
+
s.has_rdoc = true
|
11
|
+
s.extra_rdoc_files = %w(README.rdoc)
|
12
|
+
s.rdoc_options = %w(--main README.rdoc)
|
13
|
+
s.summary = "This gem provides console ui for RMilk service "
|
14
|
+
s.author = 'Victor Savkin'
|
15
|
+
s.email = 'avix1000@gmail.com'
|
16
|
+
s.homepage = 'http://my-site.net'
|
17
|
+
s.files = %w(README.rdoc Rakefile) + Dir.glob("{bin,lib,test}/**/*")
|
18
|
+
s.bindir = "bin"
|
19
|
+
s.executables = ['rmilk']
|
20
|
+
|
21
|
+
s.add_dependency('flexmock')
|
22
|
+
s.add_dependency('rufus-rtm')
|
23
|
+
s.add_dependency('require_all')
|
24
|
+
end
|
25
|
+
|
26
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
27
|
+
pkg.gem_spec = spec
|
28
|
+
end
|
29
|
+
|
30
|
+
Rake::TestTask.new do |t|
|
31
|
+
t.libs << 'test'
|
32
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
33
|
+
t.verbose = true
|
34
|
+
end
|
35
|
+
|
36
|
+
begin
|
37
|
+
require 'rcov/rcovtask'
|
38
|
+
|
39
|
+
Rcov::RcovTask.new(:coverage) do |t|
|
40
|
+
t.libs = ['test']
|
41
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
42
|
+
t.verbose = true
|
43
|
+
t.rcov_opts = ['--text-report', "-x #{Gem.path}", '-x /Library/Ruby', '-x /usr/lib/ruby']
|
44
|
+
end
|
45
|
+
|
46
|
+
task :default => :coverage
|
47
|
+
|
48
|
+
rescue LoadError
|
49
|
+
warn "\n**** Install rcov (sudo gem install relevance-rcov) to get coverage stats ****\n"
|
50
|
+
task :default => :test
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
desc 'Generate the gemspec to serve this Gem from Github'
|
55
|
+
task :github do
|
56
|
+
file = File.dirname(__FILE__) + "/#{spec.name}.gemspec"
|
57
|
+
File.open(file, 'w') {|f| f << spec.to_ruby }
|
58
|
+
puts "Created gemspec: #{file}"
|
59
|
+
end
|
data/bin/rmilk
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../lib/rmilk.rb'
|
data/lib/import_rmilk.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#this shit is required to init rufus lib
|
2
|
+
if !ENV["RTM_API_KEY"] then
|
3
|
+
ENV["RTM_API_KEY"] = "stub"
|
4
|
+
ENV["RTM_SHARED_SECRET"] = "stub"
|
5
|
+
ENV["RTM_FROB"] = "stub"
|
6
|
+
ENV["RTM_AUTH_TOKEN"] = "stub"
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'rubygems'
|
10
|
+
require 'rufus/rtm'
|
11
|
+
require "require_all"
|
12
|
+
require 'pstore'
|
13
|
+
require_all File.dirname(__FILE__) + '/rmilk/*.rb'
|
data/lib/rmilk.rb
ADDED
@@ -0,0 +1,161 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../import_rmilk.rb'
|
2
|
+
|
3
|
+
module ConsoleRtm
|
4
|
+
|
5
|
+
class HelpMessage
|
6
|
+
def initialize
|
7
|
+
@messages = []
|
8
|
+
@messages << '-l Inbox -d today -s completed'
|
9
|
+
@messages << '-next'
|
10
|
+
@messages << '-res'
|
11
|
+
@messages << '-a New Task -t Inbox'
|
12
|
+
@messages << '-history'
|
13
|
+
@messages << '-hd 0'
|
14
|
+
@messages << '-complete 0'
|
15
|
+
@messages << '-uncomplete 0'
|
16
|
+
@messages << '-postpone 0'
|
17
|
+
@messages << '-delete 0'
|
18
|
+
@messages << '-m 0 -l Home'
|
19
|
+
@messages << '-e 0 -p 1 -d today'
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
@messages.join("\n")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class Action
|
28
|
+
def initialize(task_repo, list_repo, is_apply, &apply)
|
29
|
+
@task_repo = task_repo
|
30
|
+
@list_repo = list_repo
|
31
|
+
@is_apply = is_apply
|
32
|
+
@apply = apply
|
33
|
+
end
|
34
|
+
|
35
|
+
def apply?
|
36
|
+
@is_apply.call
|
37
|
+
end
|
38
|
+
|
39
|
+
def apply!
|
40
|
+
@apply.call
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class ConsoleUi
|
45
|
+
def initialize(store, out)
|
46
|
+
@store, @out = store, out
|
47
|
+
@list_repo = RtmListRepository.new(store)
|
48
|
+
@task_repo = RtmTaskRepository.new(@list_repo)
|
49
|
+
|
50
|
+
@actions = []
|
51
|
+
@input_parse = InputParser.new
|
52
|
+
@help = HelpMessage.new
|
53
|
+
|
54
|
+
rule_when lambda {command?('-l') || command?('-d')} do
|
55
|
+
criteria = {:status => 'incomplete'}
|
56
|
+
if @args['-l'] then criteria[:list] = @args['-l'] end
|
57
|
+
if @args['-d'] then criteria[:due] = @args['-d'] end
|
58
|
+
if @args['-s'] then criteria[:status] = @args['-s'] end
|
59
|
+
tasks = @task_repo.get_tasks(criteria)
|
60
|
+
@store.save_tasks tasks
|
61
|
+
@out.format_tasks tasks
|
62
|
+
end
|
63
|
+
|
64
|
+
rule_when lambda {command?('-next')} do
|
65
|
+
criteria = {:status => 'incomplete', :list => ENV["NEXT_ACTIONS"]}
|
66
|
+
tasks = @task_repo.get_tasks(criteria)
|
67
|
+
@store.save_tasks tasks
|
68
|
+
@out.format_tasks tasks
|
69
|
+
end
|
70
|
+
|
71
|
+
rule_when lambda {command?('-res')} do
|
72
|
+
tasks = @store.get_tasks
|
73
|
+
@out.format_tasks tasks
|
74
|
+
end
|
75
|
+
|
76
|
+
rule_when lambda {command?('-a')} do
|
77
|
+
text = @args['-a']
|
78
|
+
list_name = @args['-t'] ? @args['-t'] : ENV["DEFAULT_LIST"]
|
79
|
+
@task_repo.add! Task.new(nil, text, list_name)
|
80
|
+
end
|
81
|
+
|
82
|
+
rule_when lambda {command?('-history')} do
|
83
|
+
h = @store.get_history
|
84
|
+
@out.format_history h
|
85
|
+
end
|
86
|
+
|
87
|
+
rule_when lambda {command?('-hd')} do
|
88
|
+
h = @store.get_history
|
89
|
+
index = @args['-hd'].to_i
|
90
|
+
process h[index]
|
91
|
+
end
|
92
|
+
|
93
|
+
rule_when lambda {command?('-complete')} do
|
94
|
+
task('-complete').complete!
|
95
|
+
end
|
96
|
+
|
97
|
+
rule_when lambda {command?('-uncomplete')} do
|
98
|
+
task('-complete').uncomplete!
|
99
|
+
end
|
100
|
+
|
101
|
+
rule_when lambda {command?('-postpone')} do
|
102
|
+
task('-postpone').postpone!
|
103
|
+
end
|
104
|
+
|
105
|
+
rule_when lambda {command?('-delete')} do
|
106
|
+
task('-delete').delete!
|
107
|
+
end
|
108
|
+
|
109
|
+
rule_when lambda {command?('-m')} do
|
110
|
+
task = task('-m')
|
111
|
+
@task_repo.move_task task, @args['-l']
|
112
|
+
end
|
113
|
+
|
114
|
+
rule_when lambda {command?('-e')} do
|
115
|
+
task = task('-e')
|
116
|
+
if @args['-p'] then
|
117
|
+
task.priority = @args['-p']
|
118
|
+
end
|
119
|
+
if @args['-d'] then
|
120
|
+
task.due = @args['-d']
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
rule_when lambda {command?('-help')} do
|
125
|
+
@out.output @help.to_s
|
126
|
+
end
|
127
|
+
|
128
|
+
rule_when lambda {true} do
|
129
|
+
@out.error 'unprocessed input'
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def process(input)
|
134
|
+
@deep ||= 1
|
135
|
+
@deep += 1
|
136
|
+
return "infinite recursion" if @deep > 10
|
137
|
+
|
138
|
+
@out.clean_output!
|
139
|
+
@store.add_to_history input
|
140
|
+
@args = @input_parse.parse(input)
|
141
|
+
action = @actions.find {|a| a.apply?}
|
142
|
+
action.apply! rescue return $!
|
143
|
+
@out.result
|
144
|
+
end
|
145
|
+
|
146
|
+
def rule_when(is_apply, &apply)
|
147
|
+
@actions << Action.new(@task_repo, @list_repo, is_apply, &apply)
|
148
|
+
end
|
149
|
+
|
150
|
+
def task(args_key)
|
151
|
+
index = @args[args_key].to_i
|
152
|
+
h = @store.get_tasks
|
153
|
+
h[index]
|
154
|
+
end
|
155
|
+
|
156
|
+
def command?(name)
|
157
|
+
@args['command'] == name
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../import_rmilk.rb'
|
2
|
+
|
3
|
+
module ConsoleRtm
|
4
|
+
class ContextStorage
|
5
|
+
def initialize(file_name, history_size = 10)
|
6
|
+
@history_size = history_size
|
7
|
+
@store = PStore.new(file_name)
|
8
|
+
end
|
9
|
+
|
10
|
+
def save_list_name(list_id, list_name)
|
11
|
+
@store.transaction do
|
12
|
+
@store["list_id_#{list_id}"] = list_name
|
13
|
+
@store["list_name_#{list_name}"] = list_id
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def get_list_name(list_id)
|
18
|
+
@store.transaction do
|
19
|
+
@store["list_id_#{list_id}"]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def get_list_id(list_name)
|
24
|
+
@store.transaction do
|
25
|
+
@store["list_name_#{list_name}"]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def save_tasks(tasks)
|
30
|
+
@store.transaction do
|
31
|
+
@store["tasks"] = tasks
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def get_tasks
|
36
|
+
@store.transaction do
|
37
|
+
@store["tasks"]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def add_to_history(text)
|
42
|
+
@store.transaction do
|
43
|
+
h = @store["history"]
|
44
|
+
(h ||= []) << text
|
45
|
+
@store["history"] = h.last(@history_size)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def get_history
|
50
|
+
@store.transaction do
|
51
|
+
@store["history"].reverse
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def recreate
|
56
|
+
path = @store.path
|
57
|
+
File.delete path if File.exists? path
|
58
|
+
@store = PStore.new(path)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module ConsoleRtm
|
2
|
+
|
3
|
+
class InputParser
|
4
|
+
def parse(input)
|
5
|
+
parts = input.split(' ').map {|e| preprocess e}
|
6
|
+
res, last_command, first_command = {}, 'first', nil
|
7
|
+
parts.each do |e|
|
8
|
+
if command? e then
|
9
|
+
res[e] = true
|
10
|
+
last_command = e
|
11
|
+
first_command ||= e
|
12
|
+
else
|
13
|
+
t = res[last_command]
|
14
|
+
value_is_not_set = (t == true || t.nil?)
|
15
|
+
res[last_command] = value_is_not_set ? e : "#{t} #{e}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
res['command'] = first_command if !first_command.nil?
|
19
|
+
res
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
def command?(str)
|
24
|
+
str[0, 1] == '-'
|
25
|
+
end
|
26
|
+
|
27
|
+
def preprocess(str)
|
28
|
+
str[0, 1] == '"' && str[-1, 1] == '"' ? str[1..-2] : str
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module ConsoleRtm
|
2
|
+
|
3
|
+
class OutputFormatter
|
4
|
+
attr_reader :result
|
5
|
+
|
6
|
+
def initialize(complex_formatter = true)
|
7
|
+
@complex_formatter = complex_formatter
|
8
|
+
@colors = {:red => "\033[31m",
|
9
|
+
:blue => "\033[34m",
|
10
|
+
:lightBlue => "\033[36m"}
|
11
|
+
end
|
12
|
+
|
13
|
+
def clean_output!
|
14
|
+
@result = ''
|
15
|
+
end
|
16
|
+
|
17
|
+
def format_tasks(tasks)
|
18
|
+
temp = []
|
19
|
+
tasks.each_with_index do |e, i|
|
20
|
+
temp << "#{get_priority_color e}#{i} #{e.text}#{get_end_color}"
|
21
|
+
end
|
22
|
+
if @complex_formatter then
|
23
|
+
@result = "------\n" + temp.join("\n") + "\n------"
|
24
|
+
else
|
25
|
+
@result = temp.join("\n")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def format_history(history)
|
30
|
+
temp = []
|
31
|
+
history.each_with_index do |e, i|
|
32
|
+
temp << "#{i} #{e}"
|
33
|
+
end
|
34
|
+
@result = temp.join("\n")
|
35
|
+
end
|
36
|
+
|
37
|
+
def error(text)
|
38
|
+
@result = text
|
39
|
+
end
|
40
|
+
|
41
|
+
def output(text)
|
42
|
+
@result = text
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
def get_priority_color(task)
|
47
|
+
return "" unless @complex_formatter
|
48
|
+
p = task.priority
|
49
|
+
if p == "1" then return @colors[:red] end
|
50
|
+
if p == "2" then return @colors[:blue] end
|
51
|
+
if p == "3" then return @colors[:lightBlue] end
|
52
|
+
return ''
|
53
|
+
end
|
54
|
+
|
55
|
+
def get_end_color
|
56
|
+
return "" unless @complex_formatter
|
57
|
+
"\033[0m"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../import_rmilk.rb'
|
2
|
+
|
3
|
+
module ConsoleRtm
|
4
|
+
class RmilkRunner
|
5
|
+
def init_environment
|
6
|
+
ENV["RTM_API_KEY"] = "4486f9224a4a8fa8fef850482fc26160"
|
7
|
+
ENV["RTM_SHARED_SECRET"] = "03b3b6dba031d1b2"
|
8
|
+
ENV["RTM_FROB"] = "1de9da4bdf1dfad6882864c1a0694dd7807aed1e"
|
9
|
+
ENV["RTM_AUTH_TOKEN"] = "079c19ec8b7e364860288624da7a9cf416d310d1"
|
10
|
+
|
11
|
+
ENV["DEFAULT_LIST"] = "Next"
|
12
|
+
ENV["NEXT_ACTIONS"] = "Next"
|
13
|
+
end
|
14
|
+
|
15
|
+
def init_storage
|
16
|
+
@storage = ContextStorage.new('/tmp/rtm_local.store')
|
17
|
+
end
|
18
|
+
|
19
|
+
def init_ui
|
20
|
+
@ui = ConsoleUi.new(@storage, OutputFormatter.new)
|
21
|
+
end
|
22
|
+
|
23
|
+
def process(str)
|
24
|
+
puts @ui.process(str)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../import_rmilk.rb'
|
2
|
+
|
3
|
+
module ConsoleRtm
|
4
|
+
class RtmListRepository
|
5
|
+
def initialize(context)
|
6
|
+
@context = context
|
7
|
+
end
|
8
|
+
|
9
|
+
def get_list_name_by_id(id)
|
10
|
+
if n = @context.get_list_name(id) then
|
11
|
+
n
|
12
|
+
else
|
13
|
+
list = all_lists.find {|list| list.list_id == id}
|
14
|
+
return nil if list == nil
|
15
|
+
@context.save_list_name id, list.name and return list.name
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def get_list_id_by_name(name)
|
20
|
+
if id = @context.get_list_id(name) then
|
21
|
+
id
|
22
|
+
else
|
23
|
+
list = all_lists.find {|list| list.name == name}
|
24
|
+
return nil if list == nil
|
25
|
+
@context.save_list_name list.list_id, name and return list.list_id
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
def all_lists
|
31
|
+
Rufus::RTM::List.find
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../import_rmilk.rb'
|
2
|
+
|
3
|
+
module ConsoleRtm
|
4
|
+
|
5
|
+
class RtmTaskRepository
|
6
|
+
def initialize(list_repository)
|
7
|
+
@list_repository = list_repository
|
8
|
+
end
|
9
|
+
|
10
|
+
def get_tasks(filter)
|
11
|
+
filter_str = filter.inject("") do |r, (k, v)|
|
12
|
+
str = "#{k}:#{v}"
|
13
|
+
r.empty? ? str : "#{r} and #{str}"
|
14
|
+
end
|
15
|
+
tasks = all_tasks_filtered(filter_str).collect do |t|
|
16
|
+
list_name = @list_repository.get_list_name_by_id(t.list_id)
|
17
|
+
Task.new(t.task_id, t.name, list_name, t)
|
18
|
+
end
|
19
|
+
tasks.sort {|a,b| a.priority <=> b.priority}
|
20
|
+
end
|
21
|
+
|
22
|
+
def add!(task)
|
23
|
+
list_id = @list_repository.get_list_id_by_name(task.list_name)
|
24
|
+
raise "invalid list name" if list_id.nil?
|
25
|
+
new_added_task = Rufus::RTM::Task.add! task.text, list_id
|
26
|
+
task.milk_task = new_added_task
|
27
|
+
task
|
28
|
+
end
|
29
|
+
|
30
|
+
def move_task(task, list_name)
|
31
|
+
list_id = @list_repository.get_list_id_by_name(list_name)
|
32
|
+
raise "invalid list name" if list_id.nil?
|
33
|
+
task.move_to list_id
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
def all_tasks_filtered(filter_str)
|
38
|
+
Rufus::RTM::Task.find(:filter => filter_str) rescue []
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
data/lib/rmilk/task.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../import_rmilk.rb'
|
2
|
+
|
3
|
+
module ConsoleRtm
|
4
|
+
|
5
|
+
class Task
|
6
|
+
attr_reader :id, :text, :list_name, :tags
|
7
|
+
attr_accessor :milk_task
|
8
|
+
|
9
|
+
def initialize(id, text, list_name, milk_task = nil, tags = {})
|
10
|
+
@id = id
|
11
|
+
@text = text
|
12
|
+
@list_name = list_name
|
13
|
+
@milk_task = milk_task
|
14
|
+
@tags = tags
|
15
|
+
end
|
16
|
+
|
17
|
+
def complete!
|
18
|
+
@milk_task.complete!
|
19
|
+
end
|
20
|
+
|
21
|
+
def delete!
|
22
|
+
@milk_task.delete!
|
23
|
+
end
|
24
|
+
|
25
|
+
def postpone!
|
26
|
+
execute_milk "rtm.tasks.postpone"
|
27
|
+
end
|
28
|
+
|
29
|
+
def uncomplete!
|
30
|
+
execute_milk "rtm.tasks.uncomplete"
|
31
|
+
end
|
32
|
+
|
33
|
+
def priority=(priority)
|
34
|
+
execute_milk "rtm.tasks.setPriority", {:priority => priority}
|
35
|
+
end
|
36
|
+
|
37
|
+
def priority
|
38
|
+
milk_task.priority
|
39
|
+
end
|
40
|
+
|
41
|
+
def due=(due)
|
42
|
+
execute_milk "rtm.tasks.setDueDate", {:due => due}
|
43
|
+
end
|
44
|
+
|
45
|
+
def move_to(to_list_id)
|
46
|
+
execute_milk "rtm.tasks.moveTo",
|
47
|
+
{:from_list_id => milk_task.list_id,
|
48
|
+
:to_list_id => to_list_id}
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_s
|
52
|
+
"#{@list_name}: #{@id} - #{@text}"
|
53
|
+
end
|
54
|
+
|
55
|
+
def ==(obj)
|
56
|
+
id = obj.id && text == obj.text &&
|
57
|
+
list_name == obj.list_name && tags == obj.tags
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
def execute_milk(method, args = {})
|
62
|
+
args[:method] = method
|
63
|
+
args[:timeline] = Rufus::RTM::MilkResource.timeline
|
64
|
+
args[:list_id] = milk_task.list_id
|
65
|
+
args[:taskseries_id] = milk_task.taskseries_id
|
66
|
+
args[:task_id] = milk_task.task_id
|
67
|
+
res = Rufus::RTM.milk(args)
|
68
|
+
raise res['err']['msg'] if !res['err'].nil?
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
2
|
+
|
3
|
+
class ContextStorageTest < Test::Unit::TestCase
|
4
|
+
include ConsoleRtm
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@st = ContextStorage.new('rtm_console_tmp.store', 2)
|
8
|
+
end
|
9
|
+
|
10
|
+
def teardown
|
11
|
+
File.delete 'rtm_console_tmp.store'
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_can_save_and_get_list_name
|
15
|
+
@st.save_list_name(1, "Home")
|
16
|
+
@st.save_list_name(2, "Work")
|
17
|
+
assert_equal "Home", @st.get_list_name(1)
|
18
|
+
assert_equal "Work", @st.get_list_name(2)
|
19
|
+
assert_equal nil, @st.get_list_name(3)
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_can_save_and_get_list_id
|
23
|
+
@st.save_list_name(1, "Home")
|
24
|
+
@st.save_list_name(2, "Work")
|
25
|
+
assert_equal 1, @st.get_list_id("Home")
|
26
|
+
assert_equal 2, @st.get_list_id("Work")
|
27
|
+
assert_equal nil, @st.get_list_id("Income")
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_shares_state_between_instances
|
31
|
+
@st.save_list_name(1, "Home")
|
32
|
+
st2 = ContextStorage.new('rtm_console_tmp.store')
|
33
|
+
assert_equal 1, st2.get_list_id("Home")
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_can_save_and_get_list_of_tasks
|
37
|
+
tasks = [Task.new(1, "text", "Home"), Task.new(2, "text2", "Work")]
|
38
|
+
@st.save_tasks tasks
|
39
|
+
tasks_restored = @st.get_tasks
|
40
|
+
assert_equal tasks, tasks_restored
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_can_save_command_history
|
44
|
+
@st.add_to_history "one"
|
45
|
+
@st.add_to_history "two"
|
46
|
+
assert_equal ['two', 'one'], @st.get_history
|
47
|
+
|
48
|
+
@st.add_to_history "three"
|
49
|
+
assert_equal ['three', 'two'], @st.get_history
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
2
|
+
|
3
|
+
class InputParserTest < Test::Unit::TestCase
|
4
|
+
include ConsoleRtm
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@parser = InputParser.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_returns_empty_hash_if_input_is_empty
|
11
|
+
@res = @parser.parse("")
|
12
|
+
assert_equal 0, @res.size
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_only_values
|
16
|
+
@res = @parser.parse("Dog")
|
17
|
+
assert_equal 'Dog', @res['first']
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_skip_pars
|
21
|
+
@res = @parser.parse('"Dog"')
|
22
|
+
assert_equal 'Dog', @res['first']
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_commands_without_values
|
26
|
+
@res = @parser.parse("-a -bb")
|
27
|
+
assert_not_nil @res['-a']
|
28
|
+
assert_not_nil @res['-bb']
|
29
|
+
assert_nil @res['-ccc']
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_commands_with_values
|
33
|
+
@res = @parser.parse("-a Hello -b Bye")
|
34
|
+
assert_equal 'Hello', @res['-a']
|
35
|
+
assert_equal 'Bye', @res['-b']
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_commands_with_complex_values
|
39
|
+
@res = @parser.parse("-a Hello Darling")
|
40
|
+
assert_equal 'Hello Darling', @res['-a']
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_sets_first_command
|
44
|
+
@res = @parser.parse("-a Hello -b Bye")
|
45
|
+
assert_equal '-a', @res['command']
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_complex_example
|
49
|
+
@res = @parser.parse("Dog -a Cats Hate Dogs -b")
|
50
|
+
assert_equal 'Dog', @res['first']
|
51
|
+
assert_equal 'Cats Hate Dogs', @res['-a']
|
52
|
+
assert_not_nil @res['-b']
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
2
|
+
|
3
|
+
class OutputFormatterTest < Test::Unit::TestCase
|
4
|
+
include ConsoleRtm
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@f = OutputFormatter.new(false)
|
8
|
+
@tasks = [OpenStruct.new(:text => 'text1'), OpenStruct.new(:text => 'text2')]
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_output_is_empty_line_if_task_list_is_empty
|
12
|
+
@f.format_tasks([])
|
13
|
+
assert_equal "", @f.result
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_output_of_not_empty_task_list
|
17
|
+
@f.format_tasks(@tasks)
|
18
|
+
assert_equal "0 text1\n1 text2", @f.result
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_can_clean_result
|
22
|
+
@f.format_tasks(@tasks)
|
23
|
+
assert_equal "0 text1\n1 text2", @f.result
|
24
|
+
@f.clean_output!
|
25
|
+
assert_equal "", @f.result
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_format_history
|
29
|
+
@f.format_history(["aa", "bb"])
|
30
|
+
assert_equal "0 aa\n1 bb", @f.result
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
2
|
+
|
3
|
+
class RtmListRepositoryTest < Test::Unit::TestCase
|
4
|
+
include ConsoleRtm
|
5
|
+
|
6
|
+
def setup
|
7
|
+
st = ContextStorage.new('rtm_console_tmp.store')
|
8
|
+
@repo = flexmock(RtmListRepository.new(st))
|
9
|
+
|
10
|
+
@lists = [OpenStruct.new(:list_id => 1, :name => 'Home'),
|
11
|
+
OpenStruct.new(:list_id => 2, :name => 'Work')]
|
12
|
+
end
|
13
|
+
|
14
|
+
def teardown
|
15
|
+
File.delete 'rtm_console_tmp.store'
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_caching_list_when_query_by_id
|
19
|
+
@repo.should_receive(:all_lists).once.and_return(@lists)
|
20
|
+
assert_equal "Home", @repo.get_list_name_by_id(1)
|
21
|
+
#we must execute it second time to check that we'll use cache
|
22
|
+
assert_equal "Home", @repo.get_list_name_by_id(1)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_caching_list_when_query_by_name
|
26
|
+
@repo.should_receive(:all_lists).once.and_return(@lists)
|
27
|
+
assert_equal 1, @repo.get_list_id_by_name('Home')
|
28
|
+
#we must execute it second time to check that we'll use cache
|
29
|
+
assert_equal 1, @repo.get_list_id_by_name('Home')
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_returns_nil_if_cant_find_list
|
33
|
+
@repo.should_receive(:all_lists).twice.and_return(@lists)
|
34
|
+
assert_equal nil, @repo.get_list_id_by_name('Bla')
|
35
|
+
assert_equal nil, @repo.get_list_id_by_name('Blo')
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
2
|
+
|
3
|
+
class RtmTaskRepositoryTest < Test::Unit::TestCase
|
4
|
+
include ConsoleRtm
|
5
|
+
|
6
|
+
def setup
|
7
|
+
list_repo = flexmock("list_repo")
|
8
|
+
list_repo.should_receive(:get_list_name_by_id).and_return("Home")
|
9
|
+
list_repo.should_receive(:get_list_id_by_name).and_return(1)
|
10
|
+
@repo = flexmock(RtmTaskRepository.new(list_repo))
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_repo_creates_string_filter_expression
|
14
|
+
@repo.should_receive(:all_tasks_filtered).with("status:completed and due:today").and_return([])
|
15
|
+
@repo.get_tasks(:status => 'completed', :due => 'today')
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_repo_returns_the_list_of_tasks
|
19
|
+
t = OpenStruct.new(:task_id => 17, :name => "test", :list_id => 1)
|
20
|
+
@repo.should_receive(:all_tasks_filtered).and_return([t])
|
21
|
+
|
22
|
+
tasks = @repo.get_tasks(:list => 'Home')
|
23
|
+
assert_equal 1,tasks.size
|
24
|
+
assert_equal Task.new(17, 'test', 'Home'), tasks[0]
|
25
|
+
end
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rmilk
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.6
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Victor Savkin
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-11-24 00:00:00 +10:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: flexmock
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rufus-rtm
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: require_all
|
37
|
+
type: :runtime
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: "0"
|
44
|
+
version:
|
45
|
+
description:
|
46
|
+
email: avix1000@gmail.com
|
47
|
+
executables:
|
48
|
+
- rmilk
|
49
|
+
extensions: []
|
50
|
+
|
51
|
+
extra_rdoc_files:
|
52
|
+
- README.rdoc
|
53
|
+
files:
|
54
|
+
- README.rdoc
|
55
|
+
- Rakefile
|
56
|
+
- bin/rmilk
|
57
|
+
- lib/import_rmilk.rb
|
58
|
+
- lib/rmilk
|
59
|
+
- lib/rmilk/rmilk_runner.rb
|
60
|
+
- lib/rmilk/context_storage.rb
|
61
|
+
- lib/rmilk/version.rb
|
62
|
+
- lib/rmilk/task.rb
|
63
|
+
- lib/rmilk/rtm_task_repository.rb
|
64
|
+
- lib/rmilk/console_ui.rb
|
65
|
+
- lib/rmilk/output_formatter.rb
|
66
|
+
- lib/rmilk/input_parser.rb
|
67
|
+
- lib/rmilk/rtm_list_repository.rb
|
68
|
+
- lib/rmilk.rb
|
69
|
+
- test/unit
|
70
|
+
- test/unit/context_storage_test.rb
|
71
|
+
- test/unit/input_parser_test.rb
|
72
|
+
- test/unit/rtm_list_repository_test.rb
|
73
|
+
- test/unit/rtm_task_repository_test.rb
|
74
|
+
- test/unit/output_formatter_test_test.rb
|
75
|
+
- test/test_helper.rb
|
76
|
+
has_rdoc: true
|
77
|
+
homepage: http://my-site.net
|
78
|
+
post_install_message:
|
79
|
+
rdoc_options:
|
80
|
+
- --main
|
81
|
+
- README.rdoc
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: "0"
|
89
|
+
version:
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: "0"
|
95
|
+
version:
|
96
|
+
requirements: []
|
97
|
+
|
98
|
+
rubyforge_project:
|
99
|
+
rubygems_version: 1.3.1
|
100
|
+
signing_key:
|
101
|
+
specification_version: 2
|
102
|
+
summary: This gem provides console ui for RMilk service
|
103
|
+
test_files: []
|
104
|
+
|