clitasks 0.0.3

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a8b3bf9526e0ed143b51fafe5ff7f46bf71790cd
4
+ data.tar.gz: f5a371089e0306e5d65eebd711f38b8535b2612e
5
+ SHA512:
6
+ metadata.gz: 37035c652d2994b2416d7026098f9646927d4d085255bf540f5ab0305b894786b80bff04fc3d7a5b0605ae9ccd475b51528b8e374d9d64d4ca6d8ea57292e92f
7
+ data.tar.gz: 2a4d7b4a2f1440b90c12c6cee8285c4684da467b5a7ed866cf16658e195d669bdb42efd67f329a6c06d0d5cd289049500fea80b436ca8ffa9cddd260196e6950
data/bin/task ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pp'
4
+ require 'clitasks'
5
+
6
+ command = ARGV.shift
7
+
8
+ case command
9
+ when 'rebuild'
10
+ CliTasks::Commands.rebuild
11
+ when 'create'
12
+ CliTasks::Commands.create *ARGV
13
+ CliTasks::Commands.rebuild
14
+ when 'edit'
15
+ CliTasks::Commands.edit *ARGV
16
+ CliTasks::Commands.rebuild
17
+ when 'search'
18
+ CliTasks::Commands.search *ARGV
19
+ CliTasks::Commands.rebuild
20
+ when 'start'
21
+ when 'finish'
22
+ when 'tag'
23
+ when 'comment'
24
+ when 'list'
25
+ CliTasks::Commands.list *ARGV
26
+ else
27
+ CliTasks::Commands.list *ARGV.unshift(command)
28
+ end
29
+
@@ -0,0 +1,80 @@
1
+ module CliTasks
2
+ class Commands
3
+ class << self
4
+ def edit(*args)
5
+ files = grep(*args)
6
+ system(ENV['EDITOR'] || 'vim', *files)
7
+ end
8
+
9
+ def search(*args)
10
+ if (args[0] || '').strip =~ /-(s|-simple)/i
11
+ puts grep(*args.tap(&:shift))
12
+ else
13
+ Viewer.print(*grep(*args))
14
+ end
15
+ end
16
+
17
+ def create(*args)
18
+ name = args.join ' '
19
+ timestamp = Time.now.strftime('%Y%m%d%H%M%S')
20
+ filename = "./stories/index/#{timestamp}.rb"
21
+
22
+ FileUtils.mkdir_p("./stories/index")
23
+ checklog("Creating '#{filename}'"){ IO.write(filename, template(name)) }
24
+ checklog("Opening '#{filename}'"){ system(ENV['EDITOR'] || 'vim', filename) }
25
+ end
26
+
27
+ def rebuild
28
+ LinkBuilder.all
29
+ end
30
+
31
+ def stories
32
+ @stories ||= World.instance.stories
33
+ end
34
+
35
+ def list(*args)
36
+ if args.any?
37
+ Viewer.print *args
38
+ else
39
+ Viewer.print 'stories/index/*'
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def grep(*args)
46
+ args.inject(['stories/index']){|files,arg|
47
+ #pp "grep -ril '#{arg}' -- '#{files.join "' '"}'"
48
+ grep = `grep -ril '#{arg}' -- '#{files.join "' '"}'`
49
+ lines = grep.lines.map(&:chomp)
50
+ }
51
+ end
52
+
53
+ def checklog(msg, &block)
54
+ print "#{msg}..."
55
+ block.call
56
+ puts 'done'
57
+ end
58
+
59
+ def template(name)
60
+ data = <<-STORY
61
+ story %q(#{name}) do
62
+ status queued
63
+ points 1
64
+ created_by :unassigned
65
+ assigned_to :unassigned
66
+ tags *%w() # *%w(example example_two)
67
+
68
+ description <<-DESCRIPTION
69
+ DESCRIPTION
70
+
71
+ comment :author, <<-COMMENT
72
+ COMMENT
73
+ end
74
+ STORY
75
+ pattern = data.scan(/\A(\s+)/).uniq.min_by{|s| s.length }.first
76
+ data.gsub(/^#{pattern}/, '')
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,76 @@
1
+ module CliTasks
2
+ class LinkBuilder
3
+ attr_reader :world
4
+ def initialize(path='stories')
5
+ @path = path
6
+ @world = World.instance
7
+ Runner.run [path, 'index'].join(?/)
8
+ end
9
+
10
+ def self.all
11
+ LinkBuilder.new.tap do |links|
12
+ links.remove_all_symlinks
13
+ links.by_tag
14
+ links.by_status
15
+ links.by_creator
16
+ links.by_assignment
17
+ end
18
+ end
19
+
20
+ def remove_all_symlinks
21
+ Dir[ [@path, '**/*'].join(?/) ].each do |file|
22
+ next unless File.symlink?(file)
23
+ FileUtils.rm(file)
24
+ end
25
+ end
26
+
27
+ def create_link(type, dir, story)
28
+ dir = sanitize(dir) || return
29
+ dest = File.join(@path, type, dir)
30
+ link story, dest
31
+ end
32
+
33
+ def by_tag
34
+ world.stories.each do |story|
35
+ story.tags.each do |tag|
36
+ create_link('tags', tag, story)
37
+ end
38
+ end
39
+ end
40
+
41
+ def by_status
42
+ world.stories.each do |story|
43
+ create_link('status', story.status, story)
44
+ end
45
+ end
46
+
47
+ def by_creator
48
+ world.stories.each do |story|
49
+ Array(story.created_by).each do |creator|
50
+ create_link('created_by', creator, story)
51
+ end
52
+ end
53
+ end
54
+
55
+ def by_assignment
56
+ world.stories.each do |story|
57
+ Array(story.assigned_to).each do |assignment|
58
+ create_link('assigned_to', assignment, story)
59
+ end
60
+ end
61
+ end
62
+
63
+ private
64
+
65
+ def sanitize(name)
66
+ return unless name.is_a?(String) || name.is_a?(Symbol)
67
+ String(name).gsub(/(\W|_)+/, '_')
68
+ end
69
+
70
+ def link(story, dest)
71
+ FileUtils.mkdir_p File.expand_path(dest)
72
+ src = Pathname.new(File.expand_path(story.file))
73
+ FileUtils.ln_s src.relative_path_from(Pathname.new(File.expand_path(dest))), File.join(dest, sanitize(story.name))
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,17 @@
1
+ module CliTasks
2
+ class Runner
3
+ def self.run(*files)
4
+ world.reset
5
+ files.flat_map{|file|
6
+ Dir[File.directory?(file) && [file,'/**/*'].join || file]
7
+ }.map{|file|
8
+ load File.expand_path(file)
9
+ world.stories.last.file = file
10
+ }
11
+ end
12
+
13
+ def self.world
14
+ @world ||= World.instance
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,33 @@
1
+ class SimpleDSL
2
+ class << self
3
+ def values(*names)
4
+ names.each do |name|
5
+ define_method(name) do
6
+ "#{name}".to_sym
7
+ end
8
+ end
9
+ end
10
+
11
+ def fields(*names)
12
+ names.each do |name|
13
+ define_method(name) do |val|
14
+ instance_variable_set "@#{name}".to_sym, val
15
+ end
16
+ end
17
+ end
18
+
19
+ def groups(*names)
20
+ names.each do |name|
21
+ define_method(name) do |*vals|
22
+ instance_variable_set "@#{name}".to_sym, vals
23
+ end
24
+ end
25
+ end
26
+
27
+ def custom(*names, &block)
28
+ names.each do |name|
29
+ define_method(name, &block)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,44 @@
1
+ module CliTasks
2
+ class Story
3
+ attr_accessor :file
4
+ attr_reader :id, :status, :points, :name, :description
5
+ def initialize(builder)
6
+ builder.instance_variables.each{|name| instance_variable_set name, builder.instance_variable_get(name) }
7
+ end
8
+
9
+ def id
10
+ @id ||= File.basename(file).sub(/[.]rb$/, '')
11
+ end
12
+
13
+ def tags
14
+ @tags ||= []
15
+ end
16
+
17
+ def comments
18
+ @comments ||= []
19
+ end
20
+
21
+ def created_by
22
+ @created_by ||= []
23
+ @created_by &&= Array(@created_by)
24
+ end
25
+
26
+ def assigned_to
27
+ @assigned_to ||= []
28
+ end
29
+
30
+ def self.build(name, &block)
31
+ Story.new StoryReader.new(name, &block)
32
+ end
33
+ end
34
+
35
+ def story(name, &block)
36
+ stories << Story.build(name, &block)
37
+ end
38
+
39
+ def stories
40
+ World.instance.stories
41
+ end
42
+ end
43
+
44
+ extend CliTasks
@@ -0,0 +1,13 @@
1
+ class StoryReader < SimpleDSL
2
+ def initialize(name, &block)
3
+ @name = name
4
+ instance_eval &block
5
+ end
6
+
7
+ values :queued, :started, :finished,
8
+ :accepted, :rejected, :delivered
9
+
10
+ fields :id, :status, :points, :description, :created_by
11
+ groups :assigned_to, :tags
12
+ custom :comment do |author, body| (@comments ||= []) << {author: author, body: body} end
13
+ end
@@ -0,0 +1,42 @@
1
+ module CliTasks
2
+ class Viewer
3
+ def initialize(*args)
4
+ @files = args
5
+ if args.any?
6
+ Runner.run *args
7
+ else
8
+ Runner.run 'stories/index/*'
9
+ end
10
+ end
11
+
12
+ def self.print(*args)
13
+ new(*args).print
14
+ end
15
+
16
+ def print
17
+ puts header
18
+
19
+ puts stories.sort_by{|s| s.name }.inject({}){|hash,s|
20
+ hash.merge( s.status => hash.fetch(s.status, []) << s )
21
+ }.map{|status,group|
22
+ [separator] + group.map{|s| story(s) }
23
+ }
24
+ end
25
+
26
+ def header
27
+ sprintf(" %-10s | %-20s | %-6s | %-60s | %-s", :status, :id, :points, :name, :tags)
28
+ end
29
+
30
+ def separator
31
+ sprintf(" %-10s | %-20s | %-6s | %-60s | %-s", ?-*10, ?-*20, ?-*6, ?-*60, ?-*20)
32
+ end
33
+
34
+ def story(s)
35
+ sprintf(" %-10s | %-20s | %-6s | %-60s | %-s", s.status, s.id, ?* * s.points.to_i, s.name.slice(0,60), Array(s.tags).join(', '))
36
+ end
37
+
38
+ def stories
39
+ @stories ||= World.instance.stories
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,14 @@
1
+ module CliTasks
2
+ class World
3
+ include Singleton
4
+ attr_writer :stories
5
+
6
+ def reset
7
+ @stories = []
8
+ end
9
+
10
+ def stories
11
+ @stories ||= []
12
+ end
13
+ end
14
+ end
data/lib/clitasks.rb ADDED
@@ -0,0 +1,17 @@
1
+ require 'fileutils'
2
+ require 'pathname'
3
+ require 'pp'
4
+ require 'singleton'
5
+
6
+ require 'clitasks/world'
7
+ require 'clitasks/simple_dsl'
8
+ require 'clitasks/story_reader'
9
+ require 'clitasks/story'
10
+ require 'clitasks/runner'
11
+ require 'clitasks/link_builder'
12
+ require 'clitasks/viewer'
13
+ require 'clitasks/commands'
14
+
15
+ module CliTasks
16
+ end
17
+
metadata ADDED
@@ -0,0 +1,54 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: clitasks
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Joshua "unixsuperhero" Toyota
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-04-26 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: File-based, command-line project manager.
14
+ email: jearsh@gmail.com
15
+ executables:
16
+ - task
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - lib/clitasks.rb
21
+ - lib/clitasks/commands.rb
22
+ - lib/clitasks/link_builder.rb
23
+ - lib/clitasks/runner.rb
24
+ - lib/clitasks/simple_dsl.rb
25
+ - lib/clitasks/story.rb
26
+ - lib/clitasks/story_reader.rb
27
+ - lib/clitasks/viewer.rb
28
+ - lib/clitasks/world.rb
29
+ - bin/task
30
+ homepage: http://github.com/unixsuperhero/clitasks
31
+ licenses:
32
+ - MIT
33
+ metadata: {}
34
+ post_install_message:
35
+ rdoc_options: []
36
+ require_paths:
37
+ - lib
38
+ required_ruby_version: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - '>='
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ required_rubygems_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ requirements: []
49
+ rubyforge_project:
50
+ rubygems_version: 2.0.3
51
+ signing_key:
52
+ specification_version: 4
53
+ summary: CLI Tasks
54
+ test_files: []