mastodon 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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