twig 1.1 → 1.2
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/HISTORY.md +11 -0
- data/README.md +7 -3
- data/bin/twig +5 -1
- data/bin/twig-gh-open +3 -30
- data/bin/twig-gh-open-issue +1 -31
- data/bin/twig-gh-update +1 -31
- data/lib/twig.rb +24 -9
- data/lib/twig/cli.rb +97 -36
- data/lib/twig/display.rb +2 -2
- data/lib/twig/github.rb +36 -0
- data/lib/twig/options.rb +24 -7
- data/lib/twig/version.rb +1 -1
- data/spec/twig/cli_spec.rb +248 -140
- data/spec/twig/display_spec.rb +19 -15
- data/spec/twig/options_spec.rb +33 -25
- data/spec/twig_spec.rb +55 -8
- data/twig.gemspec +1 -1
- metadata +8 -7
data/HISTORY.md
CHANGED
@@ -1,6 +1,17 @@
|
|
1
1
|
Twig
|
2
2
|
====
|
3
3
|
|
4
|
+
1.2 (2013-03-21)
|
5
|
+
----------------
|
6
|
+
* ENHANCEMENT: Add `--only-<property>` and `--except-<property>` options for
|
7
|
+
filtering custom properties by value.
|
8
|
+
* ENHANCEMENT: Simplify setup for writing GitHub-related Twig subcommands.
|
9
|
+
* FIX: Fix showing `twig --help` outside of a Git repository. (GH-16. Thanks
|
10
|
+
[ryangreenberg](https://github.com/ryangreenberg)!)
|
11
|
+
* FIX: Fix showing `twig --version` outside of a Git repository.
|
12
|
+
* FIX: Fix the project's homepage URL in `twig --help`. (GH-17. Thanks
|
13
|
+
[ryangreenberg](https://github.com/ryangreenberg)!)
|
14
|
+
|
4
15
|
1.1 (2013-03-06)
|
5
16
|
----------------
|
6
17
|
* ENHANCEMENT: Add branch name tab completion for `-b` and `--branch` options.
|
data/README.md
CHANGED
@@ -52,14 +52,18 @@ Filtering branches
|
|
52
52
|
------------------
|
53
53
|
|
54
54
|
Twig lists all of your branches by default (newest first), but you can filter
|
55
|
-
them by name and
|
55
|
+
them by age, name, and custom properties:
|
56
56
|
|
57
|
+
* `twig --max-days-old <age>`:
|
58
|
+
Only list branches below a given age
|
57
59
|
* `twig --only-branch <pattern>`:
|
58
60
|
Only list branches whose name matches a given pattern
|
59
61
|
* `twig --except-branch <pattern>`:
|
60
62
|
Don't list branches whose name matches a given pattern
|
61
|
-
* `twig --
|
62
|
-
Only list branches
|
63
|
+
* `twig --only-<property> <pattern>`:
|
64
|
+
Only list branches with a given property that matches a given pattern
|
65
|
+
* `twig --except-<property> <pattern>`:
|
66
|
+
Don't list branches with a given property that matches a given pattern
|
63
67
|
* `twig --all`:
|
64
68
|
List all branches regardless of other filtering options
|
65
69
|
|
data/bin/twig
CHANGED
@@ -3,7 +3,11 @@
|
|
3
3
|
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'twig'))
|
4
4
|
|
5
5
|
twig = Twig.new
|
6
|
-
|
6
|
+
repo_required = ARGV != ['--help'] && ARGV != ['help'] && ARGV != ['--version']
|
7
|
+
|
8
|
+
if repo_required && !twig.repo?
|
9
|
+
abort "Current directory is not a git repository."
|
10
|
+
end
|
7
11
|
|
8
12
|
# Gettin' twiggy wit' it.
|
9
13
|
twig.read_config_file!
|
data/bin/twig-gh-open
CHANGED
@@ -4,37 +4,10 @@
|
|
4
4
|
#
|
5
5
|
# Author: Ron DeVera <http://rondevera.com>
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
if origin_url.empty? || username.empty? || repository.empty?
|
10
|
-
abort_for_non_github_repo
|
11
|
-
end
|
7
|
+
require 'rubygems'
|
8
|
+
require 'twig/github'
|
12
9
|
|
13
|
-
|
14
|
-
end
|
15
|
-
|
16
|
-
def origin_url
|
17
|
-
@origin_url ||= `git config remote.origin.url`.strip
|
18
|
-
end
|
19
|
-
|
20
|
-
def origin_url_parts
|
21
|
-
@origin_url_parts ||= origin_url.split(/[\/:]/)
|
22
|
-
end
|
23
|
-
|
24
|
-
def username
|
25
|
-
@username ||= origin_url_parts[-2] || ''
|
26
|
-
end
|
27
|
-
|
28
|
-
def repository
|
29
|
-
@repo ||= origin_url_parts[-1].sub(/\.git$/, '') || ''
|
30
|
-
end
|
31
|
-
|
32
|
-
def abort_for_non_github_repo
|
33
|
-
abort 'This does not appear to be a GitHub repository.'
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
TwigGithubRepo.new do |gh_repo|
|
10
|
+
Twig::GithubRepo.new do |gh_repo|
|
38
11
|
url = "https://github.com/#{gh_repo.username}/#{gh_repo.repository}"
|
39
12
|
puts "GitHub URL: #{url}"
|
40
13
|
`which open && open #{url}`
|
data/bin/twig-gh-open-issue
CHANGED
@@ -11,37 +11,7 @@
|
|
11
11
|
require 'rubygems'
|
12
12
|
require 'twig'
|
13
13
|
|
14
|
-
|
15
|
-
def initialize
|
16
|
-
if origin_url.empty? || username.empty? || repository.empty?
|
17
|
-
abort_for_non_github_repo
|
18
|
-
end
|
19
|
-
|
20
|
-
yield(self)
|
21
|
-
end
|
22
|
-
|
23
|
-
def origin_url
|
24
|
-
@origin_url ||= `git config remote.origin.url`.strip
|
25
|
-
end
|
26
|
-
|
27
|
-
def origin_url_parts
|
28
|
-
@origin_url_parts ||= origin_url.split(/[\/:]/)
|
29
|
-
end
|
30
|
-
|
31
|
-
def username
|
32
|
-
@username ||= origin_url_parts[-2] || ''
|
33
|
-
end
|
34
|
-
|
35
|
-
def repository
|
36
|
-
@repo ||= origin_url_parts[-1].sub(/\.git$/, '') || ''
|
37
|
-
end
|
38
|
-
|
39
|
-
def abort_for_non_github_repo
|
40
|
-
abort 'This does not appear to be a GitHub repository.'
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
TwigGithubRepo.new do |gh_repo|
|
14
|
+
Twig::GithubRepo.new do |gh_repo|
|
45
15
|
twig = Twig.new
|
46
16
|
twig.read_cli_options!(ARGV)
|
47
17
|
branch_name = twig.options[:branch] || twig.current_branch_name
|
data/bin/twig-gh-update
CHANGED
@@ -10,41 +10,11 @@ require 'json'
|
|
10
10
|
require 'net/https'
|
11
11
|
require 'uri'
|
12
12
|
|
13
|
-
class TwigGithubRepo
|
14
|
-
def initialize
|
15
|
-
if origin_url.empty? || username.empty? || repository.empty?
|
16
|
-
abort_for_non_github_repo
|
17
|
-
end
|
18
|
-
|
19
|
-
yield(self)
|
20
|
-
end
|
21
|
-
|
22
|
-
def origin_url
|
23
|
-
@origin_url ||= `git config remote.origin.url`.strip
|
24
|
-
end
|
25
|
-
|
26
|
-
def origin_url_parts
|
27
|
-
@origin_url_parts ||= origin_url.split(/[\/:]/)
|
28
|
-
end
|
29
|
-
|
30
|
-
def username
|
31
|
-
@username ||= origin_url_parts[-2] || ''
|
32
|
-
end
|
33
|
-
|
34
|
-
def repository
|
35
|
-
@repo ||= origin_url_parts[-1].sub(/\.git$/, '') || ''
|
36
|
-
end
|
37
|
-
|
38
|
-
def abort_for_non_github_repo
|
39
|
-
abort 'This does not appear to be a GitHub repository.'
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
13
|
twig = Twig.new
|
44
14
|
twig.read_config_file!
|
45
15
|
twig.read_cli_options!(ARGV)
|
46
16
|
|
47
|
-
|
17
|
+
Twig::GithubRepo.new do |gh_repo|
|
48
18
|
$stdout.sync = true
|
49
19
|
print 'Getting latest states for GitHub issues...'
|
50
20
|
|
data/lib/twig.rb
CHANGED
@@ -26,7 +26,7 @@ class Twig
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def repo?
|
29
|
-
Twig.run('git rev-parse')
|
29
|
+
Twig.run('git rev-parse 2>&1')
|
30
30
|
$?.success?
|
31
31
|
end
|
32
32
|
|
@@ -57,15 +57,30 @@ class Twig
|
|
57
57
|
max_seconds_old = options[:max_days_old] * 86400 if options[:max_days_old]
|
58
58
|
|
59
59
|
branches.select do |branch|
|
60
|
-
|
61
|
-
|
62
|
-
|
60
|
+
catch :skip_branch do
|
61
|
+
if max_seconds_old
|
62
|
+
seconds_old = now.to_i - branch.last_commit_time.to_i
|
63
|
+
next if seconds_old > max_seconds_old
|
64
|
+
end
|
65
|
+
|
66
|
+
(options[:property_except] || {}).each do |property_name, property_value|
|
67
|
+
if property_name == :branch
|
68
|
+
throw :skip_branch if branch.name =~ property_value
|
69
|
+
elsif branch.get_property(property_name.to_s) =~ property_value
|
70
|
+
throw :skip_branch
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
(options[:property_only] || {}).each do |property_name, property_value|
|
75
|
+
if property_name == :branch
|
76
|
+
throw :skip_branch if branch.name !~ property_value
|
77
|
+
elsif branch.get_property(property_name.to_s) !~ property_value
|
78
|
+
throw :skip_branch
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
true
|
63
83
|
end
|
64
|
-
|
65
|
-
next if options[:branch_except] && branch.name =~ options[:branch_except]
|
66
|
-
next if options[:branch_only] && branch.name !~ options[:branch_only]
|
67
|
-
|
68
|
-
true
|
69
84
|
end
|
70
85
|
end
|
71
86
|
|
data/lib/twig/cli.rb
CHANGED
@@ -19,7 +19,7 @@ class Twig
|
|
19
19
|
|
20
20
|
#{intro}
|
21
21
|
|
22
|
-
|
22
|
+
http://rondevera.github.com/twig
|
23
23
|
|
24
24
|
BANNER
|
25
25
|
end
|
@@ -37,8 +37,8 @@ class Twig
|
|
37
37
|
# Split words into lines
|
38
38
|
while words.any?
|
39
39
|
current_word = words.shift
|
40
|
-
current_word_size =
|
41
|
-
last_line_size = lines.last &&
|
40
|
+
current_word_size = unformat_string(current_word).size
|
41
|
+
last_line_size = lines.last && unformat_string(lines.last).size
|
42
42
|
|
43
43
|
if last_line_size && (last_line_size + current_word_size + 1 <= width)
|
44
44
|
lines.last << ' ' << current_word
|
@@ -82,7 +82,26 @@ class Twig
|
|
82
82
|
|
83
83
|
desc = 'Show this help content.'
|
84
84
|
opts.on('--help', *help_description(desc)) do
|
85
|
-
|
85
|
+
summary_lines = opts.to_a
|
86
|
+
|
87
|
+
# Filter out `--only-PROPERTY` lines
|
88
|
+
summary_lines.each do |line|
|
89
|
+
is_custom_property_only = (
|
90
|
+
line.include?('--only-') &&
|
91
|
+
!line.include?('--only-branch') &&
|
92
|
+
!line.include?('--only-PROPERTY')
|
93
|
+
)
|
94
|
+
is_custom_property_except = (
|
95
|
+
line.include?('--except-') &&
|
96
|
+
!line.include?('--except-branch') &&
|
97
|
+
!line.include?('--except-PROPERTY')
|
98
|
+
)
|
99
|
+
unless is_custom_property_only || is_custom_property_except
|
100
|
+
puts line
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
exit
|
86
105
|
end
|
87
106
|
|
88
107
|
desc = 'Show Twig version.'
|
@@ -94,37 +113,62 @@ class Twig
|
|
94
113
|
|
95
114
|
help_separator(opts, 'Filtering branches:')
|
96
115
|
|
116
|
+
desc = 'Only list branches below a given age.'
|
117
|
+
opts.on(
|
118
|
+
'--max-days-old AGE', *help_description(desc, :add_separator => true)
|
119
|
+
) do |age|
|
120
|
+
set_option(:max_days_old, age)
|
121
|
+
end
|
122
|
+
|
97
123
|
desc = 'Only list branches whose name matches a given pattern.'
|
98
124
|
opts.on(
|
99
125
|
'--only-branch PATTERN',
|
100
126
|
*help_description(desc, :add_separator => true)
|
101
127
|
) do |pattern|
|
102
|
-
set_option(:
|
128
|
+
set_option(:property_only, :branch => pattern)
|
103
129
|
end
|
104
130
|
|
105
131
|
desc = 'Do not list branches whose name matches a given pattern.'
|
106
132
|
opts.on(
|
107
133
|
'--except-branch PATTERN',
|
108
|
-
*help_description(desc, :add_separator =>
|
134
|
+
*help_description(desc, :add_separator => false)
|
135
|
+
# `:add_separator => false` skips the extra line generated by the
|
136
|
+
# custom property filters below.
|
109
137
|
) do |pattern|
|
110
|
-
set_option(:
|
138
|
+
set_option(:property_except, :branch => pattern)
|
111
139
|
end
|
112
140
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
141
|
+
custom_properties = Twig::Branch.all_properties
|
142
|
+
custom_properties.each do |property_name|
|
143
|
+
opts.on("--only-#{property_name} PATTERN") do |pattern|
|
144
|
+
set_option(:property_only, property_name.to_sym => pattern)
|
145
|
+
end
|
146
|
+
|
147
|
+
opts.on("--except-#{property_name} PATTERN") do |pattern|
|
148
|
+
set_option(:property_except, property_name.to_sym => pattern)
|
149
|
+
end
|
118
150
|
end
|
119
151
|
|
152
|
+
custom_properties_desc_lines = [
|
153
|
+
['--only-PROPERTY PATTERN', 'Only list branches with a given property'],
|
154
|
+
['', 'that matches a given pattern.'],
|
155
|
+
['', ''],
|
156
|
+
['--except-PROPERTY PATTERN', 'Do not list branches with a given property'],
|
157
|
+
['', 'that matches a given pattern.']
|
158
|
+
]
|
159
|
+
custom_properties_desc = custom_properties_desc_lines.inject('') do |desc, line_parts|
|
160
|
+
desc + sprintf(' %-29s', line_parts[0]) + line_parts[1] + "\n"
|
161
|
+
end
|
162
|
+
help_separator(opts, custom_properties_desc, :trailing => "\n")
|
163
|
+
|
120
164
|
desc =
|
121
|
-
'Lists all branches regardless of
|
165
|
+
'Lists all branches regardless of other filtering options. ' +
|
122
166
|
'Useful for overriding options in ' +
|
123
167
|
File.basename(Twig::Options::CONFIG_FILE) + '.'
|
124
168
|
opts.on('--all', *help_description(desc)) do |pattern|
|
125
169
|
unset_option(:max_days_old)
|
126
|
-
unset_option(:
|
127
|
-
unset_option(:
|
170
|
+
unset_option(:property_except)
|
171
|
+
unset_option(:property_only)
|
128
172
|
end
|
129
173
|
|
130
174
|
|
@@ -171,7 +215,11 @@ class Twig
|
|
171
215
|
|
172
216
|
option_parser.parse!(args)
|
173
217
|
rescue OptionParser::InvalidOption, OptionParser::MissingArgument => exception
|
174
|
-
|
218
|
+
abort_for_option_exception(exception)
|
219
|
+
end
|
220
|
+
|
221
|
+
def abort_for_option_exception(exception)
|
222
|
+
puts exception.message
|
175
223
|
puts 'For a list of options, run `twig --help`.'
|
176
224
|
exit
|
177
225
|
end
|
@@ -200,36 +248,49 @@ class Twig
|
|
200
248
|
# Get/set branch property
|
201
249
|
if property_value
|
202
250
|
# `$ twig <key> <value>`
|
203
|
-
|
204
|
-
puts set_branch_property(branch_name, property_name, property_value)
|
205
|
-
rescue ArgumentError, RuntimeError => exception
|
206
|
-
abort exception.message
|
207
|
-
end
|
251
|
+
set_branch_property_for_cli(branch_name, property_name, property_value)
|
208
252
|
else
|
209
253
|
# `$ twig <key>`
|
210
|
-
|
211
|
-
value = get_branch_property(branch_name, property_name)
|
212
|
-
if value
|
213
|
-
puts value
|
214
|
-
else
|
215
|
-
abort %{The branch "#{branch_name}" does not have the property "#{property_name}".}
|
216
|
-
end
|
217
|
-
rescue ArgumentError => exception
|
218
|
-
abort exception.message
|
219
|
-
end
|
254
|
+
get_branch_property_for_cli(branch_name, property_name)
|
220
255
|
end
|
221
256
|
elsif property_to_unset
|
222
257
|
# `$ twig --unset <key>`
|
223
|
-
|
224
|
-
puts unset_branch_property(branch_name, property_to_unset)
|
225
|
-
rescue ArgumentError, Twig::Branch::MissingPropertyError => exception
|
226
|
-
abort exception.message
|
227
|
-
end
|
258
|
+
unset_branch_property_for_cli(branch_name, property_to_unset)
|
228
259
|
else
|
229
260
|
# `$ twig`
|
230
261
|
puts list_branches
|
231
262
|
end
|
232
263
|
end
|
233
264
|
|
265
|
+
def get_branch_property_for_cli(branch_name, property_name)
|
266
|
+
begin
|
267
|
+
value = get_branch_property(branch_name, property_name)
|
268
|
+
if value
|
269
|
+
puts value
|
270
|
+
else
|
271
|
+
raise Twig::Branch::MissingPropertyError,
|
272
|
+
%{The branch "#{branch_name}" does not have the property "#{property_name}".}
|
273
|
+
end
|
274
|
+
rescue ArgumentError, Twig::Branch::MissingPropertyError => exception
|
275
|
+
abort exception.message
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
def set_branch_property_for_cli(branch_name, property_name, property_value)
|
280
|
+
begin
|
281
|
+
puts set_branch_property(branch_name, property_name, property_value)
|
282
|
+
rescue ArgumentError, RuntimeError => exception
|
283
|
+
abort exception.message
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
def unset_branch_property_for_cli(branch_name, property_name)
|
288
|
+
begin
|
289
|
+
puts unset_branch_property(branch_name, property_name)
|
290
|
+
rescue ArgumentError, Twig::Branch::MissingPropertyError => exception
|
291
|
+
abort exception.message
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
234
295
|
end
|
235
296
|
end
|
data/lib/twig/display.rb
CHANGED
@@ -125,8 +125,8 @@ class Twig
|
|
125
125
|
"\e[#{string_options.join(';')}m#{string}\e[0m"
|
126
126
|
end
|
127
127
|
|
128
|
-
def
|
129
|
-
string.gsub(/\e\[[0-9]+(;[0-9]+)?m/, '')
|
128
|
+
def unformat_string(string)
|
129
|
+
string.gsub(/\e\[[0-9]+(;[0-9]+)?m/, '')
|
130
130
|
end
|
131
131
|
end # module Display
|
132
132
|
end
|
data/lib/twig/github.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
class Twig
|
2
|
+
class GithubRepo
|
3
|
+
def self.run(command)
|
4
|
+
# Duplicated from `Twig.run` for making lightweight subcommands.
|
5
|
+
`#{command}`.strip
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
if origin_url.empty? || username.empty? || repository.empty?
|
10
|
+
abort_for_non_github_repo
|
11
|
+
end
|
12
|
+
|
13
|
+
yield(self)
|
14
|
+
end
|
15
|
+
|
16
|
+
def origin_url
|
17
|
+
@origin_url ||= Twig::GithubRepo.run('git config remote.origin.url')
|
18
|
+
end
|
19
|
+
|
20
|
+
def origin_url_parts
|
21
|
+
@origin_url_parts ||= origin_url.split(/[\/:]/)
|
22
|
+
end
|
23
|
+
|
24
|
+
def username
|
25
|
+
@username ||= origin_url_parts[-2] || ''
|
26
|
+
end
|
27
|
+
|
28
|
+
def repository
|
29
|
+
@repo ||= origin_url_parts[-1].sub(/\.git$/, '') || ''
|
30
|
+
end
|
31
|
+
|
32
|
+
def abort_for_non_github_repo
|
33
|
+
abort 'This does not appear to be a GitHub repository.'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|