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