ursm-ditz 0.4
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/Changelog +35 -0
- data/README.txt +127 -0
- data/Rakefile +33 -0
- data/ReleaseNotes +50 -0
- data/bin/ditz +213 -0
- data/contrib/completion/_ditz.zsh +29 -0
- data/contrib/completion/ditz.bash +22 -0
- data/lib/component.rhtml +22 -0
- data/lib/ditz.rb +56 -0
- data/lib/hook.rb +67 -0
- data/lib/html.rb +69 -0
- data/lib/index.rhtml +113 -0
- data/lib/issue.rhtml +111 -0
- data/lib/issue_table.rhtml +33 -0
- data/lib/lowline.rb +202 -0
- data/lib/model-objects.rb +314 -0
- data/lib/model.rb +208 -0
- data/lib/operator.rb +549 -0
- data/lib/plugins/git.rb +114 -0
- data/lib/plugins/issue-claiming.rb +92 -0
- data/lib/release.rhtml +69 -0
- data/lib/style.css +127 -0
- data/lib/trollop.rb +518 -0
- data/lib/unassigned.rhtml +31 -0
- data/lib/util.rb +57 -0
- data/lib/vendor/yaml_waml.rb +28 -0
- data/lib/view.rb +16 -0
- data/lib/views.rb +136 -0
- data/man/ditz.1 +38 -0
- metadata +90 -0
data/Changelog
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
== 0.4 / 2008-07-27
|
2
|
+
* bugfix: HOME environment variable now correctly detected on windows
|
3
|
+
* hooks loaded from both home directory and project directory
|
4
|
+
* added bash shell completion
|
5
|
+
* plugin architecture for tighter SCM integration, etc
|
6
|
+
* 'ditz grep' should also grep against comments, log messages, etc
|
7
|
+
* added man page
|
8
|
+
* removed ditz-convert-from-monolith
|
9
|
+
* lots of HTML output tweaking
|
10
|
+
== 0.3 / 2008-06-04
|
11
|
+
* readline support for all text entry
|
12
|
+
* hook system. Use ditz -l to see possible hooks.
|
13
|
+
* new commands: archive, shortlog, set-component
|
14
|
+
* improved commands: log, assign, add-release
|
15
|
+
* new issue type: 'tasks'
|
16
|
+
* 'ditz' by itself shows the todo list
|
17
|
+
* zsh tab completion for subcommands
|
18
|
+
* local config can now specify bugs directory location
|
19
|
+
* issue name interpolation now on all issue fields
|
20
|
+
* bugfix: various HTML generation bugs
|
21
|
+
* bugfix: ditz now works from project subdirectories
|
22
|
+
* bugfix: removed UNIX-specific environment variable assumptions
|
23
|
+
== 0.2 / 2008-04-11
|
24
|
+
* bugfix: store each issue in a separate file to avoid false conflicts
|
25
|
+
* added per-command help
|
26
|
+
* added 'log' command for recent activity
|
27
|
+
* added better exception handling---turn into pretty error messages
|
28
|
+
* added text-area commands like /edit, /reset, etc
|
29
|
+
* all times now stored in UTC
|
30
|
+
== 0.1.2 / 2008-04-04
|
31
|
+
* bugfix: add_reference very broken
|
32
|
+
== 0.1.1 / 2008-04-04
|
33
|
+
* bugfix: bugfix/feature question always returns feature
|
34
|
+
== 0.1 / 2008-04-02
|
35
|
+
* Initial release!
|
data/README.txt
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
== ditz
|
2
|
+
|
3
|
+
by William Morgan <wmorgan-ditz at the masanjin dot nets>
|
4
|
+
|
5
|
+
http://ditz.rubyforge.org
|
6
|
+
|
7
|
+
== DESCRIPTION
|
8
|
+
|
9
|
+
Ditz is a simple, light-weight distributed issue tracker designed to work with
|
10
|
+
distributed version control systems like git, darcs, Mercurial, and Bazaar. It
|
11
|
+
can also be used with centralized systems like SVN.
|
12
|
+
|
13
|
+
Ditz maintains an issue database directory on disk, with files written in a
|
14
|
+
line-based and human-editable format. This directory can be kept under version
|
15
|
+
control, alongside project code.
|
16
|
+
|
17
|
+
There are several different ways to use ditz:
|
18
|
+
|
19
|
+
1. Treat issue change the same as code change: include it as part of commits,
|
20
|
+
and merge it with changes from other developers, resolving conflicts in the
|
21
|
+
usual manner.
|
22
|
+
2. Keep the issue database in the repository but in a separate branch. Issue
|
23
|
+
changes can be managed by your VCS, but is not tied directly to code
|
24
|
+
commits.
|
25
|
+
3. Keep the issue database separate and not under VCS at all.
|
26
|
+
|
27
|
+
Ditz provides a simple, console-based interface for creating and updating the
|
28
|
+
issue database file, and some rudimentary static HTML generation capabilities
|
29
|
+
for producing world-readable status pages (for a demo, see the ditz ditz page).
|
30
|
+
It currently offers no central public method of bug submission.
|
31
|
+
|
32
|
+
== SYNOPSIS
|
33
|
+
|
34
|
+
# set up project. creates the bugs.yaml file.
|
35
|
+
1. ditz init
|
36
|
+
2. ditz add-release
|
37
|
+
|
38
|
+
# add an issue
|
39
|
+
3. ditz add
|
40
|
+
|
41
|
+
# where am i?
|
42
|
+
4. ditz status
|
43
|
+
5. ditz todo (or simply "ditz")
|
44
|
+
|
45
|
+
# do work
|
46
|
+
6. write code
|
47
|
+
7. ditz close <issue-id>
|
48
|
+
8. commit
|
49
|
+
9. goto 3
|
50
|
+
|
51
|
+
# finished!
|
52
|
+
10. ditz release <release-name>
|
53
|
+
|
54
|
+
== THE DITZ DATA MODEL
|
55
|
+
|
56
|
+
Ditz includes the bare minimum set of features necessary for open-source
|
57
|
+
development. Features like time spent, priority, assignment of tasks to
|
58
|
+
developers, due dates, etc. are purposely excluded.
|
59
|
+
|
60
|
+
A ditz project consists of issues, releases and components.
|
61
|
+
|
62
|
+
Issues:
|
63
|
+
Issues are the fundamental currency of issue tracking. A ditz issue is either
|
64
|
+
a feature or a bug, but this distinction doesn't affect anything other than
|
65
|
+
how they're displayed.
|
66
|
+
|
67
|
+
Each issue belongs to exactly one component, and is part of zero or one
|
68
|
+
releases.
|
69
|
+
|
70
|
+
Each issues has an exportable id, in the form of 40 random hex characters.
|
71
|
+
This id is "guaranteed" to be unique across all possible issues and
|
72
|
+
developers, present and future. Issue ids are typically not exposed to the
|
73
|
+
user.
|
74
|
+
|
75
|
+
Issues also have a non-exportable name, which is short and human-readable.
|
76
|
+
All ditz commands use issue names instead of issue ids. Issue ids may change
|
77
|
+
in certain circumstances, specifically after a "ditz drop" command.
|
78
|
+
|
79
|
+
Components:
|
80
|
+
There is always one "general" component, named after the project itself. In
|
81
|
+
the simplest case, this is the only component, and the user is never bothered
|
82
|
+
with the question of which component to assign an issue to.
|
83
|
+
|
84
|
+
Components simply provide a way of organizing issues, and have no real
|
85
|
+
functionality. Issues are assigned names derived form the component they're
|
86
|
+
assigned to.
|
87
|
+
|
88
|
+
Releases:
|
89
|
+
A release is the primary grouping mechanism for issues. Status commands like
|
90
|
+
"ditz status" and "ditz todo" always group issues by release. When a release
|
91
|
+
is 100% complete, it can be marked as released, in which case the associated
|
92
|
+
issues will cease appearing in ditz status and todo messages.
|
93
|
+
|
94
|
+
== FUTURE WORK
|
95
|
+
|
96
|
+
In future releases, Ditz will have a plugin architecture to allow tighter
|
97
|
+
integration with specific SCMs and developer communication channels. (See
|
98
|
+
http://ditz.rubyforge.org/ditz/issue-0704dafe4aef96279364013aba177a0971d425cb.html)
|
99
|
+
|
100
|
+
== LEARNING MORE
|
101
|
+
|
102
|
+
* ditz help
|
103
|
+
* find $DITZ_INSTALL_DIR -type f | xargs cat
|
104
|
+
|
105
|
+
== REQUIREMENTS
|
106
|
+
|
107
|
+
* trollop >= 1.8.2
|
108
|
+
|
109
|
+
== INSTALLATION
|
110
|
+
|
111
|
+
Download tarballs from http://rubyforge.org/projects/ditz/, or command your
|
112
|
+
computer to "gem install ditz".
|
113
|
+
|
114
|
+
== LICENSE
|
115
|
+
|
116
|
+
Copyright (c) 2008 William Morgan.
|
117
|
+
|
118
|
+
This program is free software: you can redistribute it and/or modify
|
119
|
+
it under the terms of the GNU General Public License as published by
|
120
|
+
the Free Software Foundation, either version 3 of the License, or
|
121
|
+
(at your option) any later version.
|
122
|
+
|
123
|
+
This program is distributed in the hope that it will be useful,
|
124
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
125
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
126
|
+
GNU General Public License for more details.
|
127
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'hoe'
|
3
|
+
|
4
|
+
$:.unshift "lib"
|
5
|
+
require 'ditz'
|
6
|
+
|
7
|
+
class Hoe
|
8
|
+
def extra_deps; @extra_deps.reject { |x| Array(x).first == "hoe" } end
|
9
|
+
end # thanks to "Mike H"
|
10
|
+
|
11
|
+
Hoe.new('ditz', Ditz::VERSION) do |p|
|
12
|
+
p.rubyforge_name = 'ditz'
|
13
|
+
p.author = "William Morgan"
|
14
|
+
p.summary = "A simple issue tracker designed to integrate well with distributed version control systems like git and darcs. State is saved to a YAML file kept under version control, allowing issues to be closed/added/modified as part of a commit."
|
15
|
+
|
16
|
+
p.description = p.paragraphs_of('README.txt', 4..11).join("\n\n").gsub(/== SYNOPSIS/, "Synopsis:")
|
17
|
+
p.url = "http://ditz.rubyforge.org"
|
18
|
+
p.changes = p.paragraphs_of('Changelog', 0..0).join("\n\n")
|
19
|
+
p.email = "wmorgan-ditz@masanjin.net"
|
20
|
+
end
|
21
|
+
|
22
|
+
WWW_FILES = FileList["www/*"] + %w(README.txt)
|
23
|
+
|
24
|
+
task :upload_webpage => WWW_FILES do |t|
|
25
|
+
sh "rsync -essh -cavz #{t.prerequisites * ' '} wmorgan@rubyforge.org:/var/www/gforge-projects/ditz/"
|
26
|
+
end
|
27
|
+
|
28
|
+
task :upload_report do |t|
|
29
|
+
sh "ruby -Ilib bin/ditz html ditz"
|
30
|
+
sh "rsync -essh -cavz ditz wmorgan@rubyforge.org:/var/www/gforge-projects/ditz/"
|
31
|
+
end
|
32
|
+
|
33
|
+
# vim: syntax=ruby
|
data/ReleaseNotes
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
0.4
|
2
|
+
---
|
3
|
+
- Command-line completion scripts are now included for bash and zsh. To
|
4
|
+
activate, source the relevant file in $GEMDIR/ditz-0.4/contrib/completion/.
|
5
|
+
|
6
|
+
- Hooks can now be set on a per-project basis. Make a .ditz/hooks directory in
|
7
|
+
your project root and place them there. These will be loaded after any
|
8
|
+
hooks in ~/.ditz/hooks, so they can override or simply supplement.
|
9
|
+
|
10
|
+
- The plugin system is done. There's currently one plugin, for git integration.
|
11
|
+
To enable it, add the line "- git" in a .ditz-plugins file in your project
|
12
|
+
root. The git plugin currently has the following features:
|
13
|
+
|
14
|
+
- Issues can have a git branch assigned to them with "ditz set-branch".
|
15
|
+
- Git commit messages can have a Ditz-issue: header auto-filled if you
|
16
|
+
commit with "ditz commit <issue>" (i.e. instead of git commit).
|
17
|
+
- In both HTML and screen output, commits from the assigned branch, and
|
18
|
+
commits with the corresponding Ditz-issue: header in the log message,
|
19
|
+
will be listed for each issue.
|
20
|
+
|
21
|
+
Note that the plugin system is independent of the hook system. In order
|
22
|
+
to auto-add ditz files to the git index upon modification, you must set
|
23
|
+
up hooks. Example hooks for git are at:
|
24
|
+
http://hackety.org/2008/06/26/gitHooksForDitz.html
|
25
|
+
|
26
|
+
Also note that as soon as a feature branch is merged back into master, ditz
|
27
|
+
loses the ability to distinguish its commits. So the Ditz-issue: approach
|
28
|
+
is probably better if you want a long-term record.
|
29
|
+
|
30
|
+
0.3
|
31
|
+
---
|
32
|
+
Ditz now works from project subdirectories, and you can have a .ditz-config in
|
33
|
+
the project root for project-specific configuration. (This is not merged with
|
34
|
+
the global config, so this file overrides everything in ~/.ditz-config.)
|
35
|
+
|
36
|
+
You can specify an :issue_dir key in this file, which can be a relative path to
|
37
|
+
the directory containing project.yaml. So if you want to rename that directory,
|
38
|
+
or keep it somewhere else, now you can.
|
39
|
+
|
40
|
+
There's also a new hook system for plugging in your own code. Run ditz -l to
|
41
|
+
see a list of available hooks.
|
42
|
+
|
43
|
+
0.2
|
44
|
+
---
|
45
|
+
|
46
|
+
In ditz 0.2, we store issues per file. This avoids many unnecessary conflicts
|
47
|
+
that occur in the single-file case.
|
48
|
+
|
49
|
+
To upgrade your bugs.yaml to a bugs/ directory, you must run
|
50
|
+
ditz-convert-from-monolith.
|
data/bin/ditz
ADDED
@@ -0,0 +1,213 @@
|
|
1
|
+
#!/usr/bin/ruby1.8
|
2
|
+
|
3
|
+
## requires are split in two for efficiency reasons: ditz should be really
|
4
|
+
## fast when using it for completion.
|
5
|
+
$KCODE = "u"
|
6
|
+
|
7
|
+
require 'operator'
|
8
|
+
op = Ditz::Operator.new
|
9
|
+
|
10
|
+
## a secret option for shell completion
|
11
|
+
if ARGV.include? '--commands'
|
12
|
+
puts op.class.operations.map { |name, _| name }
|
13
|
+
exit 0
|
14
|
+
end
|
15
|
+
|
16
|
+
require 'rubygems' rescue LoadError nil
|
17
|
+
require 'fileutils'
|
18
|
+
require 'pathname'
|
19
|
+
require 'trollop'; include Trollop
|
20
|
+
require "ditz"
|
21
|
+
require "vendor/yaml_waml"
|
22
|
+
|
23
|
+
PROJECT_FN = "project.yaml"
|
24
|
+
CONFIG_FN = ".ditz-config"
|
25
|
+
PLUGIN_FN = ".ditz-plugins"
|
26
|
+
|
27
|
+
config_dir = Ditz::find_dir_containing CONFIG_FN
|
28
|
+
plugin_dir = Ditz::find_dir_containing PLUGIN_FN
|
29
|
+
|
30
|
+
$opts = options do
|
31
|
+
version "ditz #{Ditz::VERSION}"
|
32
|
+
opt :issue_dir, "Issue database dir", :default => "bugs"
|
33
|
+
opt :config_file, "Configuration file", :default => File.join(config_dir || ".", CONFIG_FN)
|
34
|
+
opt :plugins_file, "Plugins file", :default => File.join(plugin_dir || ".", PLUGIN_FN)
|
35
|
+
opt :verbose, "Verbose output", :default => false
|
36
|
+
opt :no_comment, "Skip asking for a comment", :default => false
|
37
|
+
opt :list_hooks, "List all hooks and descriptions, and quit.", :short => 'l', :default => false
|
38
|
+
stop_on_unknown
|
39
|
+
end
|
40
|
+
|
41
|
+
Ditz::HookManager.register :startup, <<EOS
|
42
|
+
Executes at startup
|
43
|
+
|
44
|
+
Variables: project, config
|
45
|
+
No return value.
|
46
|
+
EOS
|
47
|
+
|
48
|
+
Ditz::HookManager.register :after_add, <<EOS
|
49
|
+
Executes before terminating if new issue files has been created.
|
50
|
+
Basically you want to instruct your SCM that these files has
|
51
|
+
been added.
|
52
|
+
|
53
|
+
Variables: project, config, issues
|
54
|
+
No return value.
|
55
|
+
EOS
|
56
|
+
|
57
|
+
Ditz::HookManager.register :after_delete, <<EOS
|
58
|
+
Executes before terminating if new issue files has been deleted.
|
59
|
+
Basically you want to instruct your SCM that these files has
|
60
|
+
been deleted.
|
61
|
+
|
62
|
+
Variables: project, config, issues
|
63
|
+
No return value.
|
64
|
+
EOS
|
65
|
+
|
66
|
+
Ditz::HookManager.register :after_update, <<EOS
|
67
|
+
Executes before terminating if new issue files has been updated.
|
68
|
+
You may want to instruct your SCM about these changes.
|
69
|
+
Note that new issues are not considered updated.
|
70
|
+
|
71
|
+
Variables: project, config, issues
|
72
|
+
No return value.
|
73
|
+
EOS
|
74
|
+
|
75
|
+
if $opts[:list_hooks]
|
76
|
+
Ditz::HookManager.print_hooks
|
77
|
+
exit 0
|
78
|
+
end
|
79
|
+
|
80
|
+
plugins = begin
|
81
|
+
Ditz::debug "loading plugins from #{$opts[:plugins_file]}"
|
82
|
+
YAML::load_file$opts[:plugins_file]
|
83
|
+
rescue SystemCallError => e
|
84
|
+
Ditz::debug "can't load plugins file: #{e.message}"
|
85
|
+
[]
|
86
|
+
end
|
87
|
+
|
88
|
+
plugins.each do |p|
|
89
|
+
fn = Ditz::find_ditz_file "plugins/#{p}.rb"
|
90
|
+
Ditz::debug "loading plugin #{p.inspect} from #{fn}"
|
91
|
+
load fn
|
92
|
+
end
|
93
|
+
|
94
|
+
config = begin
|
95
|
+
Ditz::debug "loading config from #{$opts[:config_file]}"
|
96
|
+
Ditz::Config.from $opts[:config_file]
|
97
|
+
rescue SystemCallError => e
|
98
|
+
if ARGV.member? "<options>"
|
99
|
+
## special case here. if we're asking for tab completion, and the config
|
100
|
+
## file doesn't exist, don't do the interactive building. just make a
|
101
|
+
## fake empty one and carry on.
|
102
|
+
Ditz::Config.new
|
103
|
+
else
|
104
|
+
puts <<EOS
|
105
|
+
I wasn't able to find a configuration file #{$opts[:config_file]}.
|
106
|
+
We'll set it up right now.
|
107
|
+
EOS
|
108
|
+
Ditz::Config.create_interactively.save! $opts[:config_file]
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
cmd = ARGV.shift || "todo"
|
113
|
+
issue_dir = Pathname.new(config.issue_dir || $opts[:issue_dir])
|
114
|
+
|
115
|
+
case cmd # some special commands not handled by Ditz::Operator
|
116
|
+
when "init"
|
117
|
+
die "#{issue_dir} directory already exists" if issue_dir.exist?
|
118
|
+
issue_dir.mkdir
|
119
|
+
fn = issue_dir + PROJECT_FN
|
120
|
+
project = op.init
|
121
|
+
project.save! fn
|
122
|
+
puts "Ok, #{issue_dir} directory created successfully."
|
123
|
+
exit
|
124
|
+
when "help"
|
125
|
+
op.do "help", nil, nil, ARGV
|
126
|
+
exit
|
127
|
+
end
|
128
|
+
|
129
|
+
project_root = Ditz::find_dir_containing(issue_dir + PROJECT_FN)
|
130
|
+
die "No #{issue_dir} directory---use 'ditz init' to initialize" unless project_root
|
131
|
+
project_root += issue_dir
|
132
|
+
|
133
|
+
project = begin
|
134
|
+
fn = project_root + PROJECT_FN
|
135
|
+
Ditz::debug "loading project from #{fn}"
|
136
|
+
project = Ditz::Project.from fn
|
137
|
+
|
138
|
+
fn = project_root + "issue-*.yaml"
|
139
|
+
Ditz::debug "loading issues from #{fn}"
|
140
|
+
project.issues = Dir[fn].map { |fn| Ditz::Issue.from fn }
|
141
|
+
Ditz::debug "found #{project.issues.size} issues"
|
142
|
+
project
|
143
|
+
rescue SystemCallError, Ditz::Project::Error => e
|
144
|
+
die "#{e.message} (use 'init' to initialize)"
|
145
|
+
end
|
146
|
+
|
147
|
+
project.validate!
|
148
|
+
project.issues.each { |p| p.project = project}
|
149
|
+
project.assign_issue_names!
|
150
|
+
|
151
|
+
unless op.has_operation? cmd
|
152
|
+
die "no such command: #{cmd}"
|
153
|
+
end
|
154
|
+
|
155
|
+
Ditz::HookManager.run :startup, project, config
|
156
|
+
|
157
|
+
Ditz::debug "executing command #{cmd}"
|
158
|
+
begin
|
159
|
+
op.do cmd, project, config, ARGV
|
160
|
+
rescue Ditz::Operator::Error, Ditz::Release::Error, Ditz::Project::Error, Ditz::Issue::Error => e
|
161
|
+
$stderr.puts "Error: #{e.message}"
|
162
|
+
exit(-1)
|
163
|
+
rescue Errno::EPIPE, Interrupt
|
164
|
+
puts
|
165
|
+
exit 1
|
166
|
+
end
|
167
|
+
|
168
|
+
## save project.yaml
|
169
|
+
dirty = project.each_modelobject { |o| break true if o.changed? } || false
|
170
|
+
if dirty
|
171
|
+
fn = project_root + PROJECT_FN
|
172
|
+
Ditz::debug "project is dirty, saving #{fn}"
|
173
|
+
project.save! fn
|
174
|
+
end
|
175
|
+
|
176
|
+
## project issues are not model fields proper, so they must be
|
177
|
+
## saved independently.
|
178
|
+
changed_issues = project.issues.select { |i| i.changed? }
|
179
|
+
changed_issues.each do |i|
|
180
|
+
i.pathname ||= (project_root + "issue-#{i.id}.yaml")
|
181
|
+
i.project ||= project # hack: not set on new issues
|
182
|
+
Ditz::debug "issue #{i.name} is dirty, saving #{i.pathname}"
|
183
|
+
i.save! i.pathname
|
184
|
+
end
|
185
|
+
|
186
|
+
project.deleted_issues.each do |i|
|
187
|
+
fn = i.pathname
|
188
|
+
Ditz::debug "issue #{i.name} has been deleted, deleting #{fn}"
|
189
|
+
FileUtils.rm fn
|
190
|
+
end
|
191
|
+
|
192
|
+
unless project.added_issues.empty?
|
193
|
+
unless Ditz::HookManager.run :after_add, project, config, project.added_issues
|
194
|
+
puts "You may have to inform your SCM that the following files have been added:"
|
195
|
+
project.added_issues.each { |i| puts " " + i.pathname }
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
unless project.deleted_issues.empty?
|
200
|
+
unless Ditz::HookManager.run :after_delete, project, config, project.deleted_issues
|
201
|
+
puts "You may have to inform your SCM that the following files have been deleted:"
|
202
|
+
project.deleted_issues.each { |i| puts " " + i.pathname }
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
changed_not_added_issues = changed_issues - project.added_issues
|
207
|
+
unless changed_not_added_issues.empty?
|
208
|
+
Ditz::HookManager.run :after_update, project, config, changed_not_added_issues
|
209
|
+
end
|
210
|
+
|
211
|
+
config.save! $opts[:config_file] if config.changed?
|
212
|
+
|
213
|
+
# vim: syntax=ruby
|
@@ -0,0 +1,29 @@
|
|
1
|
+
#compdef ditz
|
2
|
+
|
3
|
+
ME=ditz
|
4
|
+
COMMANDS=--commands
|
5
|
+
OPTIONS='<options>'
|
6
|
+
|
7
|
+
if (($CURRENT == 2)); then
|
8
|
+
# We're completing the first word after the tool: the command.
|
9
|
+
_wanted command expl "$ME command" \
|
10
|
+
compadd -- $( "$ME" "$COMMANDS" )
|
11
|
+
else
|
12
|
+
# Find the options/files/URL/etc. for the current command by using the tool itself.
|
13
|
+
case "${words[$CURRENT]}"; in
|
14
|
+
-*)
|
15
|
+
_wanted args expl "Arguments for $ME ${words[2]}" \
|
16
|
+
compadd -- $( "$ME" "${words[2]}" "$OPTIONS" ; _files )
|
17
|
+
;;
|
18
|
+
ht*|ft*)
|
19
|
+
_arguments '*:URL:_urls'
|
20
|
+
;;
|
21
|
+
/*|./*|\~*|../*)
|
22
|
+
_arguments '*:file:_files'
|
23
|
+
;;
|
24
|
+
*)
|
25
|
+
_wanted args expl "Arguments for $ME ${words[2]}" \
|
26
|
+
compadd -- $( "$ME" "${words[2]}" "$OPTIONS" )
|
27
|
+
;;
|
28
|
+
esac
|
29
|
+
fi
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# ditz bash completion
|
2
|
+
#
|
3
|
+
# author: Christian Garbs
|
4
|
+
#
|
5
|
+
# based on bzr.simple by Martin Pool
|
6
|
+
|
7
|
+
_ditz()
|
8
|
+
{
|
9
|
+
cur=${COMP_WORDS[COMP_CWORD]}
|
10
|
+
if [ $COMP_CWORD -eq 1 ]; then
|
11
|
+
COMPREPLY=( $( compgen -W "$(ditz --commands)" $cur ) )
|
12
|
+
elif [ $COMP_CWORD -eq 2 ]; then
|
13
|
+
cmd=${COMP_WORDS[1]}
|
14
|
+
COMPREPLY=( $( compgen -W "$(ditz "$cmd" '<options>' 2>/dev/null)" $cur ) )
|
15
|
+
elif [ $COMP_CWORD -eq 3 ]; then
|
16
|
+
cmd=${COMP_WORDS[1]}
|
17
|
+
parm1=${COMP_WORDS[2]}
|
18
|
+
COMPREPLY=( $( compgen -W "$(ditz "$cmd" "$parm1" '<options>' 2>/dev/null)" $cur ) )
|
19
|
+
fi
|
20
|
+
}
|
21
|
+
|
22
|
+
complete -F _ditz -o default ditz
|
data/lib/component.rhtml
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
2
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
3
|
+
|
4
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
5
|
+
<head>
|
6
|
+
<title>Component <%= component.name %></title>
|
7
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf8" />
|
8
|
+
<link rel="stylesheet" href="style.css" type="text/css" />
|
9
|
+
</head>
|
10
|
+
|
11
|
+
<body>
|
12
|
+
|
13
|
+
<div><%= link_to "index", "« #{project.name} project page" %></div>
|
14
|
+
|
15
|
+
<h1><%= project.name %> component: <%= component.name %></h1>
|
16
|
+
|
17
|
+
<%= render "issue_table", :show_component => false, :show_release => true %>
|
18
|
+
|
19
|
+
<p class="footer">Generated by <a
|
20
|
+
href="http://ditz.rubyforge.org/">ditz</a>.</p>
|
21
|
+
</body>
|
22
|
+
</html>
|
data/lib/ditz.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
module Ditz
|
2
|
+
|
3
|
+
VERSION = "0.4"
|
4
|
+
|
5
|
+
def debug s
|
6
|
+
puts "# #{s}" if $opts[:verbose]
|
7
|
+
end
|
8
|
+
module_function :debug
|
9
|
+
|
10
|
+
def self.has_readline?
|
11
|
+
@has_readline
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.has_readline= val
|
15
|
+
@has_readline = val
|
16
|
+
end
|
17
|
+
|
18
|
+
begin
|
19
|
+
Ditz::has_readline = false
|
20
|
+
require 'readline'
|
21
|
+
Ditz::has_readline = true
|
22
|
+
rescue LoadError
|
23
|
+
# do nothing
|
24
|
+
end
|
25
|
+
|
26
|
+
def home_dir
|
27
|
+
@home ||=
|
28
|
+
ENV["HOME"] || (ENV["HOMEDRIVE"] && ENV["HOMEPATH"] ? ENV["HOMEDRIVE"] + ENV["HOMEPATH"] : nil) || begin
|
29
|
+
$stderr.puts "warning: can't determine home directory, using '.'"
|
30
|
+
"."
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
## helper for recursive search
|
35
|
+
def find_dir_containing target, start=Pathname.new(".")
|
36
|
+
return start if (start + target).exist?
|
37
|
+
unless start.parent.realpath == start.realpath
|
38
|
+
find_dir_containing target, start.parent
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
## my brilliant solution to the 'gem datadir' problem
|
43
|
+
def find_ditz_file fn
|
44
|
+
dir = $:.find { |p| File.exist? File.expand_path(File.join(p, fn)) }
|
45
|
+
raise "can't find #{fn} in any load path" unless dir
|
46
|
+
File.expand_path File.join(dir, fn)
|
47
|
+
end
|
48
|
+
|
49
|
+
module_function :home_dir, :find_dir_containing, :find_ditz_file
|
50
|
+
end
|
51
|
+
|
52
|
+
require 'model-objects'
|
53
|
+
require 'operator'
|
54
|
+
require 'views'
|
55
|
+
require 'hook'
|
56
|
+
|
data/lib/hook.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
module Ditz
|
2
|
+
class HookManager
|
3
|
+
def initialize
|
4
|
+
@descs = {}
|
5
|
+
@blocks = {}
|
6
|
+
end
|
7
|
+
|
8
|
+
@@instance = nil
|
9
|
+
def self.method_missing m, *a, &b
|
10
|
+
@@instance ||= self.new
|
11
|
+
@@instance.send m, *a, &b
|
12
|
+
end
|
13
|
+
|
14
|
+
def register name, desc
|
15
|
+
@descs[name] = desc
|
16
|
+
@blocks[name] = []
|
17
|
+
end
|
18
|
+
|
19
|
+
def on *names, &block
|
20
|
+
names.each do |name|
|
21
|
+
raise "unregistered hook #{name.inspect}" unless @descs[name]
|
22
|
+
@blocks[name] << block
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def run name, *args
|
27
|
+
raise "unregistered hook #{name.inspect}" unless @descs[name]
|
28
|
+
blocks = hooks_for name
|
29
|
+
return false if blocks.empty?
|
30
|
+
blocks.each { |block| block[*args] }
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
34
|
+
def print_hooks f=$stdout
|
35
|
+
puts <<EOS
|
36
|
+
Ditz has #{@descs.size} registered hooks:
|
37
|
+
|
38
|
+
EOS
|
39
|
+
|
40
|
+
@descs.map{ |k,v| [k.to_s,v] }.sort.each do |name, desc|
|
41
|
+
f.puts <<EOS
|
42
|
+
#{name}
|
43
|
+
#{"-" * name.length}
|
44
|
+
#{desc}
|
45
|
+
EOS
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def enabled? name; !hooks_for(name).empty? end
|
50
|
+
|
51
|
+
def hooks_for name
|
52
|
+
if @blocks[name].nil? || @blocks[name].empty?
|
53
|
+
dirs = [Ditz::home_dir, Ditz::find_dir_containing(".ditz")].compact.map do |d|
|
54
|
+
File.join d, ".ditz", "hooks"
|
55
|
+
end
|
56
|
+
Ditz::debug "looking for hooks in #{dirs.join(" and ")}"
|
57
|
+
files = dirs.map { |d| Dir[File.join(d, "*.rb")] }.flatten
|
58
|
+
files.each do |fn|
|
59
|
+
Ditz::debug "loading hook file #{fn}"
|
60
|
+
load fn
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
@blocks[name] || []
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|