medo 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/Gemfile +12 -0
  2. data/Gemfile.lock +48 -0
  3. data/LICENSE.txt +20 -0
  4. data/README.md +14 -0
  5. data/Rakefile +29 -0
  6. data/VERSION +1 -0
  7. data/bin/medo +59 -0
  8. data/features/add_notes.feature +18 -0
  9. data/features/add_task.feature +12 -0
  10. data/features/list_tasks.feature +25 -0
  11. data/features/step_definitions/add_task_steps.rb +3 -0
  12. data/features/step_definitions/list_tasks_steps.rb +6 -0
  13. data/features/support/env.rb +14 -0
  14. data/lib/medo.rb +6 -0
  15. data/lib/medo/commands/clear.rb +9 -0
  16. data/lib/medo/commands/delete.rb +13 -0
  17. data/lib/medo/commands/done.rb +13 -0
  18. data/lib/medo/commands/list.rb +16 -0
  19. data/lib/medo/commands/new.rb +15 -0
  20. data/lib/medo/commands/note.rb +18 -0
  21. data/lib/medo/commands/show.rb +19 -0
  22. data/lib/medo/file_task_storage.rb +53 -0
  23. data/lib/medo/json_task_reader.rb +31 -0
  24. data/lib/medo/json_task_writer.rb +33 -0
  25. data/lib/medo/support.rb +6 -0
  26. data/lib/medo/support/decorator.rb +40 -0
  27. data/lib/medo/task.rb +63 -0
  28. data/lib/medo/task_reader.rb +8 -0
  29. data/lib/medo/task_writer.rb +21 -0
  30. data/lib/medo/terminal.rb +6 -0
  31. data/lib/medo/text_task_writer.rb +137 -0
  32. data/lib/medo/text_task_writer/decorators/colors_decorator.rb +28 -0
  33. data/lib/medo/text_task_writer/decorators/numbers_decorator.rb +37 -0
  34. data/spec/lib/file_task_storage_spec.rb +59 -0
  35. data/spec/lib/json_task_reader_spec.rb +26 -0
  36. data/spec/lib/json_task_writer_spec.rb +32 -0
  37. data/spec/lib/task_reader_spec.rb +9 -0
  38. data/spec/lib/task_spec.rb +101 -0
  39. data/spec/lib/task_writer_spec.rb +18 -0
  40. data/spec/lib/text_task_writer_spec.rb +106 -0
  41. data/spec/spec_helper.rb +24 -0
  42. data/spec/support/task_stubs_spec_helper.rb +41 -0
  43. metadata +189 -0
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "gli", ">= 2.0.0.rc4"
4
+ gem "rainbow", "~> 1.1"
5
+
6
+ group :development do
7
+ gem "rspec", "~> 2.10"
8
+ gem "rake", "~> 0.9"
9
+ gem "aruba", "~> 0.4"
10
+ gem "simplecov", "~> 0.6", :require => false
11
+ end
12
+
@@ -0,0 +1,48 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ aruba (0.4.11)
5
+ childprocess (>= 0.2.3)
6
+ cucumber (>= 1.1.1)
7
+ ffi (>= 1.0.11)
8
+ rspec (>= 2.7.0)
9
+ builder (3.0.0)
10
+ childprocess (0.3.2)
11
+ ffi (~> 1.0.6)
12
+ cucumber (1.2.0)
13
+ builder (>= 2.1.2)
14
+ diff-lcs (>= 1.1.3)
15
+ gherkin (~> 2.10.0)
16
+ json (>= 1.4.6)
17
+ diff-lcs (1.1.3)
18
+ ffi (1.0.11)
19
+ gherkin (2.10.0)
20
+ json (>= 1.4.6)
21
+ gli (2.0.0.rc4)
22
+ json (1.7.3)
23
+ multi_json (1.3.6)
24
+ rainbow (1.1.4)
25
+ rake (0.9.2.2)
26
+ rspec (2.10.0)
27
+ rspec-core (~> 2.10.0)
28
+ rspec-expectations (~> 2.10.0)
29
+ rspec-mocks (~> 2.10.0)
30
+ rspec-core (2.10.1)
31
+ rspec-expectations (2.10.0)
32
+ diff-lcs (~> 1.1.3)
33
+ rspec-mocks (2.10.1)
34
+ simplecov (0.6.4)
35
+ multi_json (~> 1.0)
36
+ simplecov-html (~> 0.5.3)
37
+ simplecov-html (0.5.3)
38
+
39
+ PLATFORMS
40
+ ruby
41
+
42
+ DEPENDENCIES
43
+ aruba (~> 0.4)
44
+ gli (>= 2.0.0.rc4)
45
+ rainbow (~> 1.1)
46
+ rake (~> 0.9)
47
+ rspec (~> 2.10)
48
+ simplecov (~> 0.6)
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Vladimir
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,14 @@
1
+ # Medo #
2
+
3
+ Simple console app for managing todos on the fly :)
4
+
5
+ [![Build Status](https://secure.travis-ci.org/v-yarotsky/medo.png)](http://travis-ci.org/v-yarotsky/medo)
6
+
7
+ ## The Changelog ##
8
+
9
+ ### v0.1.0 (15.06.2012) ###
10
+ * Store tasks in JSON
11
+ * Add the 'show' command
12
+ * Add colorful output
13
+ * Make output fit terminal
14
+
@@ -0,0 +1,29 @@
1
+ # encoding: utf-8
2
+ require 'rubygems'
3
+ require 'bundler'
4
+
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+
13
+ require 'rake'
14
+ require 'rake/testtask'
15
+ require 'cucumber'
16
+ require 'cucumber/rake/task'
17
+
18
+ Rake::TestTask.new do |t|
19
+ t.test_files = Dir.glob('spec/**/*_spec.rb')
20
+ t.verbose = true
21
+ end
22
+
23
+ Cucumber::Rake::Task.new(:features) do |t|
24
+ t.cucumber_opts = "features --tags ~@wip --format pretty -x"
25
+ t.fork = false
26
+ end
27
+
28
+ task :default => [:test, :features]
29
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'fileutils'
5
+
6
+ begin
7
+ require 'gli'
8
+ require 'medo'
9
+ require 'medo/support'
10
+ require 'medo/file_task_storage'
11
+ rescue LoadError => e #development
12
+ raise if $loaded
13
+ $:.unshift File.expand_path('../../lib', __FILE__) #load what we work on, even if gem installed
14
+ require 'bundler'
15
+ Bundler.setup(:default)
16
+ $loaded = true
17
+ retry
18
+ end
19
+
20
+ include GLI::App
21
+ include Medo
22
+
23
+ program_desc 'Simple CLI To-Do manager'
24
+ version VERSION
25
+
26
+ commands_from 'medo/commands'
27
+
28
+ default_command :list
29
+
30
+ desc "A file with tasks"
31
+ flag [:f, "tasks-file"], :default_value => File.join(ENV['HOME'], '.medo-tasks')
32
+
33
+ desc "Do not use colorful output"
34
+ switch "no-color", :negatable => false
35
+
36
+ Signal.trap("SIGINT") do
37
+ puts "Terminating"
38
+ exit 1
39
+ end
40
+
41
+ around do |global_options, command, options, arguments, cmd|
42
+ FileTaskStorage.using_storage(global_options.fetch(:"tasks-file")) do |the_storage|
43
+ define_singleton_method(:storage) { the_storage }
44
+
45
+ def self.choose_task(argv, tasks)
46
+ input = argv.shift
47
+ number = Integer(input) rescue
48
+ raise(ArgumentError, "Invalid task #: #{input}")
49
+ task = tasks.reject(&:done?).sort[number - 1] or raise RuntimeError,
50
+ "No such task!"
51
+ [task, number]
52
+ end
53
+
54
+ cmd.call
55
+ end
56
+ end
57
+
58
+ run(ARGV)
59
+
@@ -0,0 +1,18 @@
1
+ Feature: Add note to task
2
+ In order to be able to sketch some ideas
3
+ As a user
4
+ I want to add notes to my tasks
5
+
6
+ Background:
7
+ Given there's no file "/tmp/test-medo-tasks"
8
+
9
+ Scenario: Add todo
10
+ When I successfully run `medo --tasks-file=/tmp/test-medo-tasks new Hello World`
11
+ And I successfully run `medo --tasks-file=/tmp/test-medo-tasks new Goodbye Windows`
12
+ And I successfully run `medo --tasks-file=/tmp/test-medo-tasks note 1 "Trash the PC"`
13
+ And I successfully run `medo --tasks-file=/tmp/test-medo-tasks note 2 The Note`
14
+ And I successfully run `medo --tasks-file=/tmp/test-medo-tasks show 1`
15
+ Then the output should contain "Trash the PC"
16
+ And the output should not contain "The Note"
17
+ When I successfully run `medo --tasks-file=/tmp/test-medo-tasks show 2`
18
+ Then the output should contain "The Note"
@@ -0,0 +1,12 @@
1
+ Feature: Add todo
2
+ In order to be able to keep my in focus
3
+ As a user
4
+ I want to add tasks to lists
5
+
6
+ Background:
7
+ Given there's no file "/tmp/test-medo-tasks"
8
+
9
+ Scenario: Add todo
10
+ When I successfully run `medo --tasks-file=/tmp/test-medo-tasks new Hello World`
11
+ And I successfully run `medo --tasks-file=/tmp/test-medo-tasks ls`
12
+ Then the output should contain "Hello World"
@@ -0,0 +1,25 @@
1
+ Feature: List tasks
2
+ In order to keep myseld focused
3
+ As a user
4
+ I want to be able to view my current tasks
5
+
6
+ Scenario: List tasks
7
+ Given I have the following tasks in "/tmp/test-medo-tasks"
8
+ """
9
+ [
10
+ {"done": false, "description": "Buy Milk", "created_at": "2012-01-05 12:36:00 +0300", "completed_at": null, "notes": ["at least 6% fat"]},
11
+ {"done": true, "description": "Buy Butter", "created_at": "2012-01-05 12:40:00 +0300", "completed_at": "2012-01-05 13:00:00 +0300", "notes": []}
12
+ ]
13
+
14
+ """
15
+ When I successfully run `medo --no-color --tasks-file="/tmp/test-medo-tasks"`
16
+ Then the output should contain "Buy Milk"
17
+ And the output should contain "Buy Butter"
18
+
19
+ Scenario: There are no tasks
20
+ Given I have the following tasks in "/tmp/test-medo-tasks"
21
+ """
22
+
23
+ """
24
+ When I successfully run `medo --no-color --tasks-file="/tmp/test-medo-tasks"`
25
+ Then the output should contain "no tasks"
@@ -0,0 +1,3 @@
1
+ Given /^there's no file "(.*?)"$/ do |file|
2
+ FileUtils.rm file if File.exist?(file)
3
+ end
@@ -0,0 +1,6 @@
1
+ Given /^I have the following tasks in "(.*?)"$/ do |filename, tasks|
2
+ @tasks_file_path = filename
3
+ File.open(filename, "w") do |f|
4
+ f.write(tasks)
5
+ end
6
+ end
@@ -0,0 +1,14 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'aruba/cucumber'
4
+ require 'fileutils'
5
+
6
+ ENV['PATH'] = "#{File.expand_path('../../../bin', __FILE__)}#{File::PATH_SEPARATOR}#{ENV['PATH']}"
7
+ ENV['GLI_DEBUG'] = "true"
8
+
9
+ After do
10
+ if defined? @tasks_file_path and File.exist?(@tasks_file_path)
11
+ FileUtils.rm(@tasks_file_path)
12
+ end
13
+ end
14
+
@@ -0,0 +1,6 @@
1
+ require 'medo/task'
2
+
3
+ module Medo
4
+ version_file = File.expand_path('../VERSION', File.dirname(__FILE__))
5
+ VERSION = File.read(version_file).freeze
6
+ end
@@ -0,0 +1,9 @@
1
+ desc "Clear done todos"
2
+ command :clear do |c|
3
+ c.action do |global_options, options, args|
4
+ tasks = storage.read.reject(&:done?)
5
+ storage.write(tasks)
6
+ storage.commit
7
+ puts "Done tasks cleared"
8
+ end
9
+ end
@@ -0,0 +1,13 @@
1
+ desc "Delete a todo"
2
+ command [:delete, :rm] do |c|
3
+ c.action do |global_options, options, args|
4
+ tasks = storage.read
5
+
6
+ task, number = choose_task(args, tasks)
7
+ tasks -= [task]
8
+
9
+ storage.write(tasks)
10
+ storage.commit
11
+ puts "Task #{number} removed"
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ desc "Mark todo as done"
2
+ command :done do |c|
3
+ c.action do |global_options, options, args|
4
+ tasks = storage.read
5
+
6
+ task, number = choose_task(args, tasks)
7
+ task.done
8
+
9
+ storage.write(tasks)
10
+ storage.commit
11
+ puts "Task #{number} done"
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ require 'medo/text_task_writer'
2
+
3
+ desc "List all todos"
4
+ command [:list, :ls] do |c|
5
+ c.action do |global_options, options, args|
6
+ tasks = storage.read
7
+
8
+ include TextTaskWriter::Decorators
9
+ writer = NumbersDecorator.decorate(TextTaskWriter.new)
10
+
11
+ #waiting for fix of https://github.com/davetron5000/gli/pull/90
12
+ ColorsDecorator.decorate(writer) unless global_options[:"no-color"] == false
13
+ writer.add_tasks(tasks)
14
+ writer.write
15
+ end
16
+ end
@@ -0,0 +1,15 @@
1
+ desc "Create a todo"
2
+ command :new do |c|
3
+ c.action do |global_options, options, args|
4
+ tasks = storage.read
5
+
6
+ task_description = args.join(" ")
7
+ task, number = Task.new(task_description)
8
+
9
+ tasks << task
10
+
11
+ storage.write(tasks)
12
+ storage.commit
13
+ puts "Task added"
14
+ end
15
+ end
@@ -0,0 +1,18 @@
1
+ desc "Add note to a todo"
2
+ command :note do |c|
3
+ c.action do |global_options, options, args|
4
+ tasks = storage.read
5
+
6
+ task, number = choose_task(args, tasks)
7
+
8
+ note = args.join(" ").strip
9
+ raise ArgumentError, "No note given" if note.empty?
10
+
11
+ task.notes << note
12
+
13
+ storage.write(tasks)
14
+ storage.commit
15
+ puts "Note for task #{number} added"
16
+ end
17
+ end
18
+
@@ -0,0 +1,19 @@
1
+ require 'medo/text_task_writer'
2
+
3
+ desc "Show particular note"
4
+ command [:show, :cat] do |c|
5
+ c.action do |global_options, options, args|
6
+ tasks = storage.read
7
+
8
+ include TextTaskWriter::Decorators
9
+ writer = NumbersDecorator.decorate(TextTaskWriter.new)
10
+
11
+ #waiting for fix of https://github.com/davetron5000/gli/pull/90
12
+ ColorsDecorator.decorate(writer) unless global_options[:"no-color"] == false
13
+
14
+ task, number = choose_task(args, tasks)
15
+ writer.add_tasks([task])
16
+ writer.write
17
+ end
18
+ end
19
+
@@ -0,0 +1,53 @@
1
+ require 'fileutils'
2
+ require 'tempfile'
3
+ require 'medo/json_task_reader'
4
+ require 'medo/json_task_writer'
5
+
6
+ module Medo
7
+ class FileTaskStorage
8
+ def self.using_storage(filename)
9
+ storage = self.new(filename)
10
+ yield storage
11
+ ensure
12
+ storage.dispose if storage
13
+ end
14
+
15
+ def initialize(filename, reader_class = JsonTaskReader, writer_class = JsonTaskWriter)
16
+ @filename, @reader_class, @writer_class = filename, reader_class, writer_class
17
+ FileUtils.touch(@filename)
18
+ end
19
+
20
+ def read
21
+ begin
22
+ File.open(@filename, "rb") do |f|
23
+ @reader_class.new(f).read
24
+ end
25
+ rescue => e
26
+ []
27
+ end
28
+ end
29
+
30
+ def write(tasks)
31
+ serializer = @writer_class.new(tempfile)
32
+ serializer.add_tasks(tasks)
33
+ serializer.write
34
+ ensure
35
+ tempfile.close
36
+ end
37
+
38
+ def commit
39
+ FileUtils.cp(tempfile.path, @filename)
40
+ end
41
+
42
+ def dispose
43
+ tempfile.unlink
44
+ end
45
+
46
+ private
47
+
48
+ def tempfile
49
+ @tempfile ||= Tempfile.new(File.basename(@filename), :binmode => true)
50
+ end
51
+ end
52
+ end
53
+