taco_it 1.3.1 → 1.4.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.
- data/bin/taco +4 -743
- data/lib/taco/change.rb +75 -0
- data/lib/taco/cli.rb +107 -0
- data/lib/taco/defaults/tacorc +10 -0
- data/lib/taco/issue.rb +240 -0
- data/lib/taco/schema.rb +208 -0
- data/lib/taco/taco.rb +158 -0
- data/lib/taco/tacorc.rb +24 -0
- data/lib/taco.rb +4 -922
- metadata +10 -3
data/lib/taco/taco.rb
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'time'
|
4
|
+
|
5
|
+
require 'taco/issue'
|
6
|
+
|
7
|
+
class Taco
|
8
|
+
HOME_DIR = '.taco'
|
9
|
+
|
10
|
+
attr_accessor :home
|
11
|
+
|
12
|
+
class NotFound < Exception; end
|
13
|
+
class Ambiguous < Exception; end
|
14
|
+
|
15
|
+
def initialize(root_path=nil)
|
16
|
+
@home = File.join(root_path || Dir.getwd, HOME_DIR)
|
17
|
+
end
|
18
|
+
|
19
|
+
def init!
|
20
|
+
raise IOError.new("Could not create #{@home}\nDirectory already exists.") if File.exists?(@home)
|
21
|
+
|
22
|
+
FileUtils.mkdir_p(@home)
|
23
|
+
|
24
|
+
"Initialized #{@home}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def write!(issue_or_issues)
|
28
|
+
issues = issue_or_issues.is_a?(Array) ? issue_or_issues : [ issue_or_issues ]
|
29
|
+
|
30
|
+
issues.each do |issue|
|
31
|
+
the_json = issue.to_json # do this first so we don't bother the filesystem if the issue is invalid
|
32
|
+
open(File.join(@home, issue.id), 'w') { |f| f.write(the_json) }
|
33
|
+
end
|
34
|
+
|
35
|
+
issue_or_issues
|
36
|
+
end
|
37
|
+
|
38
|
+
def read(issue_id)
|
39
|
+
issue_path = File.join(@home, issue_id)
|
40
|
+
|
41
|
+
unless File.exist? issue_path
|
42
|
+
entries = Dir[File.join(@home, "*#{issue_id}*")]
|
43
|
+
|
44
|
+
raise NotFound.new("Issue not found.") unless entries.size > 0
|
45
|
+
unless entries.size == 1
|
46
|
+
issue_list = entries.map do |entry|
|
47
|
+
issue = read(File.basename(entry))
|
48
|
+
"#{issue.id} : #{issue.summary}"
|
49
|
+
end
|
50
|
+
raise Ambiguous.new("Found several matching issues:\n%s" % issue_list.join("\n"))
|
51
|
+
end
|
52
|
+
|
53
|
+
issue_path = entries[0]
|
54
|
+
issue_id = File.basename entries[0]
|
55
|
+
end
|
56
|
+
|
57
|
+
the_json = open(issue_path) { |f| f.read }
|
58
|
+
|
59
|
+
issue = Issue.from_json the_json
|
60
|
+
|
61
|
+
raise Issue::Invalid.new("Issue ID does not match filename: #{issue.id} != #{issue_id}") unless issue.id == issue_id
|
62
|
+
|
63
|
+
issue
|
64
|
+
end
|
65
|
+
|
66
|
+
def list(opts={})
|
67
|
+
filter_match = if opts.fetch(:filters, []).size > 0
|
68
|
+
conditions = opts[:filters].map do |filter|
|
69
|
+
attr, val = filter.split(':')
|
70
|
+
%Q|i.send("#{attr}") == "#{val}"|
|
71
|
+
end.join ' && '
|
72
|
+
|
73
|
+
# FIXME: eval-ing user input? madness!
|
74
|
+
eval "Proc.new { |i| #{conditions} }"
|
75
|
+
else
|
76
|
+
nil
|
77
|
+
end
|
78
|
+
|
79
|
+
ids = Dir.glob("#{@home}/*")
|
80
|
+
|
81
|
+
ids.map do |name|
|
82
|
+
id = File.basename name
|
83
|
+
issue = Issue.from_json(open(name) { |f| f.read })
|
84
|
+
|
85
|
+
next unless filter_match.nil? || filter_match.call(issue)
|
86
|
+
|
87
|
+
raise Issue::Invalid.new("Issue ID does not match filename: #{issue.id} != #{id}") unless issue.id == id
|
88
|
+
|
89
|
+
short_id = 8.upto(id.size).each do |n|
|
90
|
+
short_id = id[0...n]
|
91
|
+
break short_id unless ids.count { |i| i.include? short_id } > 1
|
92
|
+
end
|
93
|
+
|
94
|
+
if opts[:short_ids]
|
95
|
+
[ issue, short_id ]
|
96
|
+
else
|
97
|
+
issue
|
98
|
+
end
|
99
|
+
end.reject(&:nil?).sort_by { |thing| opts[:short_ids] ? thing[0] : thing}
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class IssueEditor
|
104
|
+
def initialize(taco, retry_path)
|
105
|
+
@taco, @retry_path = taco, retry_path
|
106
|
+
end
|
107
|
+
|
108
|
+
def new_issue!(opts={})
|
109
|
+
if opts[:from_file]
|
110
|
+
text = open(opts[:from_file]) { |f| f.read }
|
111
|
+
else
|
112
|
+
raise ArgumentError.new("Please define $EDITOR in your environment.") unless ENV['EDITOR']
|
113
|
+
text = invoke_editor(opts[:template])
|
114
|
+
end
|
115
|
+
|
116
|
+
write_issue!(Issue.from_template(text), text) if text
|
117
|
+
end
|
118
|
+
|
119
|
+
def edit_issue!(issue, opts={})
|
120
|
+
if text = invoke_editor(opts[:template] || issue.to_template)
|
121
|
+
write_issue!(issue.update_from_template!(text), text)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
private
|
126
|
+
def write_issue!(issue, text)
|
127
|
+
begin
|
128
|
+
@taco.write! issue
|
129
|
+
rescue Exception => e
|
130
|
+
open(@retry_path, 'w') { |f| f.write(text) } if text
|
131
|
+
raise e
|
132
|
+
end
|
133
|
+
|
134
|
+
File.unlink @retry_path rescue nil
|
135
|
+
issue
|
136
|
+
end
|
137
|
+
|
138
|
+
def invoke_editor(template)
|
139
|
+
text = nil
|
140
|
+
file = Tempfile.new('taco')
|
141
|
+
|
142
|
+
begin
|
143
|
+
file.write(template)
|
144
|
+
file.close
|
145
|
+
|
146
|
+
cmd = "$EDITOR #{file.path}"
|
147
|
+
system(cmd)
|
148
|
+
|
149
|
+
open(file.path) do |f|
|
150
|
+
text = f.read
|
151
|
+
end
|
152
|
+
ensure
|
153
|
+
File.unlink(file.path) rescue nil
|
154
|
+
end
|
155
|
+
|
156
|
+
text == template ? nil : text
|
157
|
+
end
|
158
|
+
end
|
data/lib/taco/tacorc.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
class TacoRc
|
2
|
+
class ParseError < Exception; end
|
3
|
+
|
4
|
+
def initialize(path)
|
5
|
+
raise ArgumentError.new("no such file: #{path}") unless File.exists? path
|
6
|
+
@path = path
|
7
|
+
end
|
8
|
+
|
9
|
+
def update_schema!(schema)
|
10
|
+
open(@path) do |f|
|
11
|
+
f.readlines.each_with_index do |line, index|
|
12
|
+
next if line =~ /^#/ || line =~ /^\s*$/
|
13
|
+
|
14
|
+
raise ParseError.new("Parse error on line #{index+1} of #{@path}: line does not begin with schema_attr_update") unless line =~ /^\s*schema_attr_update /
|
15
|
+
|
16
|
+
begin
|
17
|
+
eval "#{schema}.#{line.strip}"
|
18
|
+
rescue KeyError, TypeError, NameError => e
|
19
|
+
raise ParseError.new("Parse error on line #{index+1} of #{@path}: #{e.to_s}")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|