ohac-ditz 0.5.1

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/lib/ditz/views.rb ADDED
@@ -0,0 +1,191 @@
1
+ require "ditz/view"
2
+ require "ditz/html"
3
+ require 'time'
4
+
5
+ module Ditz
6
+
7
+ class ScreenView < View
8
+ def initialize project, config, device=$stdout
9
+ @device = device
10
+ @config = config
11
+ end
12
+
13
+ def format_log_events events
14
+ return "none" if events.empty?
15
+ events.reverse.map do |time, who, what, comment|
16
+ "- #{what} (#{who.shortened_email}, #{time.ago} ago)" +
17
+ (comment =~ /\S/ ? "\n" + comment.gsub(/^/, " > ") : "")
18
+ end.join("\n")
19
+ end
20
+ private :format_log_events
21
+
22
+ def render_issue issue
23
+ status = case issue.status
24
+ when :closed
25
+ "#{issue.status_string}: #{issue.disposition_string}"
26
+ else
27
+ issue.status_string
28
+ end
29
+ desc = if issue.desc.size < 80 - "Description: ".length
30
+ issue.desc
31
+ else
32
+ "\n" + issue.desc.gsub(/^/, " ") + "\n"
33
+ end
34
+ run_pager @config
35
+ @device.puts <<EOS
36
+ #{"Issue #{issue.name}".underline}
37
+ Title: #{issue.title}
38
+ Description: #{desc}
39
+ Type: #{issue.type}
40
+ Status: #{status}
41
+ Creator: #{issue.reporter}
42
+ Age: #{issue.creation_time.ago}
43
+ Release: #{issue.release}
44
+ References: #{issue.references.listify " "}
45
+ Identifier: #{issue.id}
46
+ EOS
47
+
48
+ self.class.view_additions_for(:issue_summary).each { |b| @device.print(b[issue, @config] || next) }
49
+ puts
50
+ self.class.view_additions_for(:issue_details).each { |b| @device.print(b[issue, @config] || next) }
51
+
52
+ @device.puts <<EOS
53
+ Event log:
54
+ #{format_log_events issue.log_events}
55
+ EOS
56
+ end
57
+ end
58
+
59
+ class HtmlView < View
60
+ SUPPORT_FILES = %w(style.css blue-check.png red-check.png green-check.png green-bar.png yellow-bar.png)
61
+
62
+ def initialize project, config, dir
63
+ @project = project
64
+ @config = config
65
+ @dir = dir
66
+ @template_dir = File.dirname Ditz::find_ditz_file("../share/ditz/index.rhtml")
67
+ end
68
+
69
+ def render_all
70
+ Dir.mkdir @dir unless File.exists? @dir
71
+ SUPPORT_FILES.each { |f| FileUtils.cp File.join(@template_dir, f), @dir }
72
+
73
+ ## build up links
74
+ links = {}
75
+ @project.releases.each { |r| links[r] = "release-#{r.name}.html" }
76
+ @project.issues.each { |i| links[i] = "issue-#{i.id}.html" }
77
+ @project.components.each { |c| links[c] = "component-#{c.name}.html" }
78
+ links["unassigned"] = "unassigned.html" # special case: unassigned
79
+ links["index"] = "index.html" # special case: index
80
+
81
+ @project.issues.each do |issue|
82
+ fn = File.join @dir, links[issue]
83
+ #puts "Generating #{fn}..."
84
+
85
+ extra_summary = self.class.view_additions_for(:issue_summary).map { |b| b[issue, @config] }.compact
86
+ extra_details = self.class.view_additions_for(:issue_details).map { |b| b[issue, @config] }.compact
87
+
88
+ erb = ErbHtml.new(@template_dir, links, :issue => issue,
89
+ :release => (issue.release ? @project.release_for(issue.release) : nil),
90
+ :component => @project.component_for(issue.component),
91
+ :project => @project)
92
+
93
+ extra_summary_html = extra_summary.map { |string, extra_binding| erb.render_string string, extra_binding }.join
94
+ extra_details_html = extra_details.map { |string, extra_binding| erb.render_string string, extra_binding }.join
95
+
96
+ File.open(fn, "w") { |f| f.puts erb.render_template("issue", { :extra_summary_html => extra_summary_html, :extra_details_html => extra_details_html }) }
97
+ end
98
+
99
+ @project.releases.each do |r|
100
+ fn = File.join @dir, links[r]
101
+ #puts "Generating #{fn}..."
102
+ File.open(fn, "w") do |f|
103
+ f.puts ErbHtml.new(@template_dir, links, :release => r,
104
+ :issues => @project.issues_for_release(r), :project => @project).
105
+ render_template("release")
106
+ end
107
+ end
108
+
109
+ @project.components.each do |c|
110
+ fn = File.join @dir, links[c]
111
+ #puts "Generating #{fn}..."
112
+ File.open(fn, "w") do |f|
113
+ f.puts ErbHtml.new(@template_dir, links, :component => c,
114
+ :issues => @project.issues_for_component(c), :project => @project).
115
+ render_template("component")
116
+ end
117
+ end
118
+
119
+ fn = File.join @dir, links["unassigned"]
120
+ #puts "Generating #{fn}..."
121
+ File.open(fn, "w") do |f|
122
+ f.puts ErbHtml.new(@template_dir, links,
123
+ :issues => @project.unassigned_issues, :project => @project).
124
+ render_template("unassigned")
125
+ end
126
+
127
+ past_rels, upcoming_rels = @project.releases.partition { |r| r.released? }
128
+ fn = File.join @dir, links["index"]
129
+ #puts "Generating #{fn}..."
130
+ File.open(fn, "w") do |f|
131
+ f.puts ErbHtml.new(@template_dir, links, :project => @project,
132
+ :past_releases => past_rels, :upcoming_releases => upcoming_rels,
133
+ :components => @project.components).
134
+ render_template("index")
135
+ end
136
+ puts "Local generated URL: file://#{File.expand_path(fn)}"
137
+ end
138
+ end
139
+
140
+ class BaetleView < View
141
+ def initialize project, config, dir
142
+ @project = project
143
+ @config = config
144
+ @dir = dir
145
+ end
146
+
147
+ def render_all
148
+ Dir.mkdir @dir unless File.exists? @dir
149
+ fn = File.join @dir, "baetle.rdf"
150
+ File.open(fn, "w") { |f|
151
+ f.puts <<EOS
152
+ @prefix baetle: <http://xmlns.com/baetle/#> .
153
+ @prefix wf: <http://www.w3.org/2005/01/wf/flow#> .
154
+ @prefix sioc: <http://rdfs.org/sioc/ns#> .
155
+ @prefix dc: <http://purl.org/dc/elements/1.1/> .
156
+ @prefix xsd: <http://www.w3.org/2001/XMLSchema#>.
157
+ @prefix : <#> .
158
+
159
+ EOS
160
+ @project.issues.each do |issue|
161
+ # id
162
+ f.print ":#{issue.id} a "
163
+ f.print case issue.type
164
+ when :bugfix, :bug; "baetle:Bug"
165
+ when :feature; "baetle:Enhancement"
166
+ when :task; "wf:Task"
167
+ end
168
+ f.puts " ;"
169
+ # title
170
+ f.puts " baetle:title #{issue.title.dump} ;"
171
+ # summary
172
+ f.puts " baetle:description #{issue.desc.dump} ; "
173
+ # state
174
+ f.print " wf:state baetle:"
175
+ f.print case issue.status
176
+ when :unstarted; "New"
177
+ when :in_progress; "Started"
178
+ when :closed; "Closed"
179
+ when :paused; "Later"
180
+ end
181
+ f.puts " ;"
182
+ # created
183
+ f.puts " baetle:created #{issue.creation_time.xmlschema.dump}^^xsd:dateTime ."
184
+ f.puts
185
+ end
186
+ }
187
+ puts "Local generated URL: file://#{File.expand_path(fn)}"
188
+ end
189
+ end
190
+
191
+ end
data/lib/ditz.rb ADDED
@@ -0,0 +1,110 @@
1
+ require 'pathname'
2
+ require 'trollop'
3
+
4
+ module Ditz
5
+
6
+ VERSION = "0.5"
7
+ attr_accessor :verbose
8
+ module_function :verbose, :verbose=
9
+
10
+ def debug s
11
+ puts "# #{s}" if $verbose || Ditz::verbose
12
+ end
13
+ module_function :debug
14
+
15
+ def self.has_readline?
16
+ @has_readline
17
+ end
18
+
19
+ def self.has_readline= val
20
+ @has_readline = val
21
+ end
22
+
23
+ begin
24
+ Ditz::has_readline = false
25
+ require 'readline'
26
+ Ditz::has_readline = true
27
+ rescue LoadError
28
+ # do nothing
29
+ end
30
+
31
+ def home_dir
32
+ @home ||=
33
+ ENV["HOME"] || (ENV["HOMEDRIVE"] && ENV["HOMEPATH"] ? ENV["HOMEDRIVE"] + ENV["HOMEPATH"] : nil) || begin
34
+ $stderr.puts "warning: can't determine home directory, using '.'"
35
+ "."
36
+ end
37
+ end
38
+
39
+ ## helper for recursive search
40
+ def find_dir_containing target, start=Pathname.new(".")
41
+ return start if (start + target).exist?
42
+ unless start.parent.realpath == start.realpath
43
+ find_dir_containing target, start.parent
44
+ end
45
+ end
46
+
47
+ ## my brilliant solution to the 'gem datadir' problem
48
+ def find_ditz_file fn
49
+ dir = $:.find { |p| File.exist? File.expand_path(File.join(p, fn)) }
50
+ raise LoadError, "can't find #{fn} in any load path" unless dir
51
+ File.expand_path File.join(dir, fn)
52
+ end
53
+
54
+ def load_plugins fn
55
+ Ditz::debug "loading plugins from #{fn}"
56
+ return unless File.exist?(fn)
57
+ plugins = YAML::load_file fn
58
+ plugins.each do |p|
59
+ fn = Ditz::find_ditz_file "ditz/plugins/#{p}.rb"
60
+ Ditz::debug "loading plugin #{p.inspect} from #{fn}"
61
+ require File.expand_path(fn)
62
+ end
63
+ plugins
64
+ end
65
+
66
+ module_function :home_dir, :find_dir_containing, :find_ditz_file, :load_plugins
67
+ end
68
+
69
+ # Git-style automatic pagination of all output.
70
+ # Call run_pager from any opperator needing pagination.
71
+ # Yoinked from http://nex-3.com/posts/73-git-style-automatic-paging-in-ruby#comments
72
+ def run_pager config
73
+ if RUBY_VERSION >= '1.9.0'
74
+ return if RUBY_PLATFORM =~ /win32/
75
+ else
76
+ return if PLATFORM =~ /win32/
77
+ end
78
+ return unless STDOUT.tty?
79
+ return if config.paginate == 'never'
80
+
81
+ read, write = IO.pipe
82
+
83
+ unless Kernel.fork # Child process
84
+ STDOUT.reopen(write)
85
+ STDERR.reopen(write) if STDERR.tty?
86
+ read.close
87
+ write.close
88
+ return
89
+ end
90
+
91
+ # Parent process, become pager
92
+ STDIN.reopen(read)
93
+ read.close
94
+ write.close
95
+
96
+ if config.paginate == 'auto'
97
+ ENV['LESS'] = '' unless ENV['LESS'] # += doesn't work on undefined var
98
+ ENV['LESS'] += 'FRX' # Don't page if the input is short enough
99
+ end
100
+
101
+ Kernel.select [STDIN] # Wait until we have input before we start the pager
102
+ pager = ENV['PAGER'] || 'less'
103
+ exec pager rescue exec "/bin/sh", "-c", pager
104
+ end
105
+
106
+ require 'ditz/model-objects'
107
+ require 'ditz/operator'
108
+ require 'ditz/views'
109
+ require 'ditz/hook'
110
+ require 'ditz/file-storage'
data/man/man1/ditz.1 ADDED
@@ -0,0 +1,38 @@
1
+ .TH "ditz" "1" "0.5" "" ""
2
+ .SH "NAME"
3
+ ditz \- simple, light\-weight distributed issue tracker
4
+ .SH "SYNOPSIS"
5
+ \fBditz\fR [ \fIoptions\fR ] \fIcommand\fR [ \fIarguments\fR ]
6
+
7
+ To list all available commands, use \fBditz help\fR. To get help for a specific command, use \fBditz help
8
+ command\fR.
9
+ .SH "DESCRIPTION"
10
+ Ditz is a simple, light\-weight distributed issue tracker designed to work with
11
+ distributed version control systems like darcs and git. Ditz maintains an issue
12
+ database directory on disk, with files written in a line\-based and human\-
13
+ editable format. This directory is kept under version control alongside
14
+ project code. Changes in issue state is handled by version control like code
15
+ change: included as part of a commit, merged with changes from other
16
+ developers, conflict\-resolved in the standard manner, etc.
17
+
18
+ Ditz provides a simple, console\-based interface for creating and updating the
19
+ issue database files, and some rudimentary HTML generation capabilities for
20
+ producing world\-readable status pages. It offers no central public method of
21
+ bug submission.
22
+ .SH "AUTHOR"
23
+ ditz was written by William Morgan <\fIwmorgan\-ditz@masanjin.net\fR>.
24
+
25
+ This manpage was written for the Debian package of ditz by Christian Garbs
26
+ <\fIdebian@cgarbs.de\fR>.
27
+ .SH "LICENSE"
28
+ Copyright (c) 2008 William Morgan.
29
+
30
+ This program is free software: you can redistribute it and/or modify
31
+ it under the terms of the GNU General Public License as published by
32
+ the Free Software Foundation, either version 3 of the License, or
33
+ (at your option) any later version.
34
+
35
+ This program is distributed in the hope that it will be useful,
36
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
37
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
38
+ GNU General Public License for more details.