mastodon 0.1.0

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.
@@ -0,0 +1,9 @@
1
+ # Mastodon
2
+
3
+ A todo.txt[1] parser.
4
+
5
+ [1]: http://ginatrapani.github.com/todo.txt-cli/
6
+
7
+ ## Installation
8
+
9
+ sudo gem install mastodon
@@ -0,0 +1,55 @@
1
+ begin
2
+ require 'jeweler'
3
+ rescue LoadError
4
+ puts "Jeweler not available, but is required. Install it with: sudo gem install jeweler"
5
+ exit 1
6
+ end
7
+
8
+ @jtasks = Jeweler::Tasks.new do |gemspec|
9
+ gemspec.name = "mastodon"
10
+ gemspec.summary = "A ruby parser for todo.txt files"
11
+ gemspec.description = "Mastodon: A ruby parser for todo.txt files.\n\nAnd the mastodon, like plain text, isn't extinct! (Yet.)"
12
+ gemspec.email = "colin@evaryont.me"
13
+ gemspec.homepage = "http://github.com/evaryont/mastodon/"
14
+ gemspec.authors = ["Colin Shea"]
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ @jeweler = @jtasks.jeweler
18
+
19
+ namespace :version do
20
+ desc "DON'T CALL. Write the version to lib/#{@jeweler.gemspec_helper.spec.name}/version.rb"
21
+ task :ruby do
22
+ # TODO: This is hard coded but it should be dynamic, given
23
+ # the gem spec
24
+ file = File.open("lib/#{@jeweler.gemspec_helper.spec.name}/version.rb", "w")
25
+ # TODO: and so should this class template
26
+ file.write <<-CLASS
27
+ # Automatically generated by `rake version:ruby' - use version:bump to change
28
+ # this, do not edit this directly, as it will be overwritten!
29
+ class #{@jeweler.gemspec_helper.spec.name.capitalize}
30
+ VERSION = [#{@jeweler.major_version}, #{@jeweler.minor_version}, #{@jeweler.patch_version}]
31
+ end
32
+ CLASS
33
+ system("git commit --file='.git/COMMIT_EDITMSG' --amend -q -o 'lib/#{@jeweler.gemspec_helper.spec.name}/version.rb'")
34
+ end
35
+
36
+ # Take advantage of Rake not overwriting tasks.
37
+ #
38
+ # After the 'real' task has been called, write version.rb as well.
39
+ namespace :bump do
40
+ task :major do
41
+ Rake::Task["version:ruby"].execute
42
+ end
43
+ task :minor do
44
+ Rake::Task["version:ruby"].execute
45
+ end
46
+ task :patch do
47
+ Rake::Task["version:ruby"].execute
48
+ end
49
+ end
50
+ end
51
+
52
+ desc "Run the nanotest suite"
53
+ task :test do
54
+ system("ruby -I'lib:test' test/run_tests.rb")
55
+ end
@@ -0,0 +1,5 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 1
4
+ :patch: 0
5
+ :build:
@@ -0,0 +1,82 @@
1
+ require 'set'
2
+ require 'pp'
3
+
4
+ require 'mastodon/todo'
5
+ require 'mastodon/version'
6
+
7
+ class Mastodon
8
+ # A context is: An at-sign (@) followed by one or more non-whitespace characters.
9
+ Context_Regex = /(?<!\+)\B@(\S+)/
10
+ # A project is: An plus sign (+) followed by one or more non-whitespace characters.
11
+ Project_Regex = /(?<!@)\B\+(\S+)/
12
+ # A priority is: A capital letter (A-Z) surrounded by parentheses, at the beginning of the line.
13
+ Priority_Regex = /^\(([A-Z])\)/
14
+
15
+ attr_reader :contexts, :projects, :todos
16
+
17
+ # +todo+: An array of strings (each being an individual todo item)
18
+ def initialize(todos)
19
+ parse! todos.map{|line| line.strip}
20
+ end
21
+
22
+ def parse!(todos)
23
+ @contexts = Set.new
24
+ @projects = Set.new
25
+ @todos = []
26
+
27
+ todos.each do |todo|
28
+ # Looping through the string, find the metadata (context, project,
29
+ # priority). Store it in a temporary variable, then clear it from
30
+ # the original string. Strip all remaining whitespace at the end.
31
+
32
+ current_contexts = []
33
+ current_projects = []
34
+ current_priority, priority = nil, nil
35
+
36
+ until ((context = todo[Context_Regex]).nil?)
37
+ index = todo.index(context)
38
+ todo[index..(index+context.length)] = ""
39
+ current_contexts << context.match(Context_Regex)[1]
40
+ contexts << context.match(Context_Regex)[1]
41
+ end
42
+
43
+ until ((project = todo[Project_Regex]).nil?)
44
+ index = todo.index(project)
45
+ todo[index..(index+project.length)] = ""
46
+ current_projects << project.match(Project_Regex)[1]
47
+ projects << project.match(Project_Regex)[1]
48
+ end
49
+
50
+ priority = todo[Priority_Regex]
51
+ unless priority.nil?
52
+ index = todo.index(priority)
53
+ todo[index..(index+priority.length)] = ""
54
+ current_priority = priority.match(Priority_Regex)[1]
55
+ end
56
+
57
+ todo.strip!
58
+ @todos << Mastodon::Todo.new(todo, current_contexts, current_projects, current_priority)
59
+ end
60
+ end
61
+
62
+ # How many todos there are.
63
+ def size
64
+ @todos.size
65
+ end
66
+
67
+ # Get an individual todo. The id and line number are the same. 0 index, not
68
+ # 1, as files are numbered. So line 3 is id 2. XXX: Change?
69
+ def [](id)
70
+ @todos[id]
71
+ end
72
+
73
+ # Find all todos that have the context @+context+
74
+ def find_context(context)
75
+ @todos.select { |todo| todo.contexts.include? context }
76
+ end
77
+
78
+ # Find all todos that have the project ++project+
79
+ def find_project(project)
80
+ @todos.select { |todo| todo.projects.include? project }
81
+ end
82
+ end
@@ -0,0 +1,27 @@
1
+ class Mastodon
2
+ class Todo < Struct.new(:text, :contexts, :projects, :priority)
3
+ include Comparable
4
+
5
+ def to_s
6
+ pri = priority ? "(#{priority})" : ""
7
+ "#{pri} #{text} @#{contexts.join(' @')} +#{projects.join(' +')}".strip
8
+ end
9
+
10
+ def inspect
11
+ "#<Mastodon::Todo \"#{to_s}\">"
12
+ end
13
+
14
+ # Comparison operator: Sort todos by their priority first, then sort
15
+ # them by the text.
16
+ def <=>(other_todo)
17
+ return -1 if (priority.nil? and !other_todo.priority.nil?)
18
+
19
+ pri = (priority <=> other_todo.priority)
20
+ if pri == 0
21
+ return text <=> other_todo.text
22
+ else
23
+ return pri
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,5 @@
1
+ # Automatically generated by `rake version:ruby' - use version:bump to change
2
+ # this, do not edit this directly, as it will be overwritten!
3
+ class Mastodon
4
+ VERSION = [0, 1, 0]
5
+ end
@@ -0,0 +1,2 @@
1
+ require 'test_helper'
2
+ require 'test_mastodon'
@@ -0,0 +1,14 @@
1
+ require 'nanotest'
2
+
3
+ begin # Optional dependencies
4
+ require 'nanotest/stats' # Gets some nice speed stats.
5
+ require 'redgreen' # Pretty colors!
6
+ rescue LoadError
7
+ end
8
+ include Nanotest
9
+
10
+ # Include the library
11
+ require File.dirname(__FILE__) + "/../lib/mastodon.rb"
12
+
13
+ SAMPLE_TODOS = File.readlines(File.join(File.dirname(__FILE__), "todo.txt")) unless Kernel.const_defined? :SAMPLE_TODOS
14
+ @mast = Mastodon.new(SAMPLE_TODOS)
@@ -0,0 +1,37 @@
1
+ require 'test_helper'
2
+
3
+ # Test searching and caching.
4
+ assert { @mast.projects.to_a == ["proj", "proj2", "proj+phone", "proj/foo", "p_r_o_j", "p-r-o-j", "+proj", "@proj"] }
5
+ assert { @mast.contexts.to_a == ["con", "con2", "con@phone", "con/foo", "c_o_n", "c-o-n", "@con", "+con"] }
6
+
7
+ # Finding existing items
8
+ assert { @mast.find_context("con2").size > 0 }
9
+ assert { @mast.find_context("con2").first.text == "Multiple contexts" }
10
+ assert { @mast.find_project("proj2").size > 0 }
11
+ assert { @mast.find_project("proj2").first.text == "Multiple projects" }
12
+
13
+ # Finding non-existing items
14
+ assert { @mast.find_context("nosuchthing") == [] }
15
+ assert { @mast.find_project("thisprojectdoesntexist") == [] }
16
+
17
+ # General utility methods.
18
+ assert { @mast.size == SAMPLE_TODOS.size }
19
+ # While I can't guarantee a 1:1 match, nothing should be lost. Just possibly rearranged.
20
+ assert { (@mast[0].to_s.chars.to_a == SAMPLE_TODOS[0].strip.chars.to_a) and (@mast[0].to_s.length == SAMPLE_TODOS[0].strip.length) }
21
+
22
+ # Test if correct classes are returned
23
+ assert { @mast.projects.is_a? Set }
24
+ assert { @mast.contexts.is_a? Set }
25
+ assert { @mast[0].is_a? Mastodon::Todo }
26
+
27
+ # Test if matching at the beginning of a line works
28
+ assert { @mast[13].contexts = ["con"] }
29
+ assert { @mast[14].projects = ["proj"] }
30
+
31
+ # Test priority detection
32
+ assert { @mast[3].priority.nil? }
33
+ assert { @mast[4].priority.nil? }
34
+ assert { @mast[5].priority = "A" }
35
+ assert { @mast[6].priority = "Z" }
36
+ assert { @mast[7].priority = "Q" }
37
+ assert { @mast[8].priority.nil? }
@@ -0,0 +1,17 @@
1
+ Todo @con +proj
2
+ left @ right @con
3
+ user@server email
4
+ Multiple projects +proj +proj2
5
+ Multiple contexts @con @con2
6
+ (A) Priority
7
+ (Z) All the way to Z
8
+ (Q) All mixed together @con +proj
9
+ Not a priority: (A)
10
+ Advanced contexts @con@phone @con/foo @c_o_n @c-o-n @@con
11
+ Advanced projects +proj+phone +proj/foo +p_r_o_j +p-r-o-j ++proj
12
+ Mixed symbols: +@proj @+con
13
+ @con Leading with a context
14
+ +proj Leading with a project
15
+ Google+Yahoo+Microsoft == monopoly
16
+ Google + Yahoo + Microsoft == monopoly
17
+ Ready to (ab)use
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mastodon
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Colin Shea
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-12-23 00:00:00 -05:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: |-
17
+ Mastodon: A ruby parser for todo.txt files.
18
+
19
+ And the mastodon, like plain text, isn't extinct! (Yet.)
20
+ email: colin@evaryont.me
21
+ executables: []
22
+
23
+ extensions: []
24
+
25
+ extra_rdoc_files:
26
+ - README.mkd
27
+ files:
28
+ - Rakefile
29
+ - VERSION.yml
30
+ - lib/mastodon.rb
31
+ - lib/mastodon/todo.rb
32
+ - lib/mastodon/version.rb
33
+ - test/run_tests.rb
34
+ - test/test_helper.rb
35
+ - test/test_mastodon.rb
36
+ - test/todo.txt
37
+ - README.mkd
38
+ has_rdoc: true
39
+ homepage: http://github.com/evaryont/mastodon/
40
+ licenses: []
41
+
42
+ post_install_message:
43
+ rdoc_options:
44
+ - --charset=UTF-8
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: "0"
52
+ version:
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ version:
59
+ requirements: []
60
+
61
+ rubyforge_project:
62
+ rubygems_version: 1.3.5
63
+ signing_key:
64
+ specification_version: 3
65
+ summary: A ruby parser for todo.txt files
66
+ test_files:
67
+ - test/run_tests.rb
68
+ - test/test_helper.rb
69
+ - test/test_mastodon.rb