Class: GitIssue::Base

Inherits:
Object
  • Object
show all
Includes:
Helper, Term::ANSIColor
Defined in:
lib/git_issue/base.rb

Direct Known Subclasses

Bitbucket, Github, Redmine

Constant Summary collapse

BRANCH_NAME_FORMAT =
"ticket/id/%s"

Constants included from Helper

Helper::CONFIGURE_MESSAGE

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Helper

configure_error, configured_value, get_body_from_editor, get_title_and_body_from_editor, #git_editor, global_configured_value, its_klass_of, #open_editor, #read_body, #split_head_and_body, #work_dir

Constructor Details

#initialize(args, options = {}) ⇒ Base

Returns a new instance of Base.



10
11
12
13
14
15
16
17
18
19
20
# File 'lib/git_issue/base.rb', line 10

def initialize(args, options = {})

  @opt_parse_obj = opt_parser
  args = parse_options(args)

  @sysout = options[:sysout] || $stdout
  @syserr = options[:syserr] || $stderr


  parse_command_and_tickets(args)
end

Instance Attribute Details

#apikeyObject (readonly)

Returns the value of attribute apikey.



7
8
9
# File 'lib/git_issue/base.rb', line 7

def apikey
  @apikey
end

#commandObject (readonly)

Returns the value of attribute command.



7
8
9
# File 'lib/git_issue/base.rb', line 7

def command
  @command
end

#optionsObject (readonly)

Returns the value of attribute options.



7
8
9
# File 'lib/git_issue/base.rb', line 7

def options
  @options
end

#syserrObject

Returns the value of attribute syserr.



8
9
10
# File 'lib/git_issue/base.rb', line 8

def syserr
  @syserr
end

#sysoutObject

Returns the value of attribute sysout.



8
9
10
# File 'lib/git_issue/base.rb', line 8

def sysout
  @sysout
end

#ticketsObject (readonly)

Returns the value of attribute tickets.



7
8
9
# File 'lib/git_issue/base.rb', line 7

def tickets
  @tickets
end

Instance Method Details

#apply_colors(str, *colors) ⇒ Object



324
325
326
# File 'lib/git_issue/base.rb', line 324

def apply_colors(str, *colors)
  @no_color.present? ? str : (colors.map(&method(:send)) + [str, reset]).join
end

#cherry(option = {}) ⇒ Object



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/git_issue/base.rb', line 98

def cherry(option = {})
  upstream = options[:upstream]
  head = options[:head]

  commits = %x(git cherry -v #{upstream} #{head}).split(/\n/).map{|s|
    s.scan(/^([+-])\s(\w+)\s(.*)/).first
  }.select{|_, _, msg| msg =~ /#[0-9]+/ }.map{|diff, sha1, msg|
    msg.scan(/#([0-9]+)/).flatten.map{|ticket| [diff, sha1, msg, ticket]}
  }.flatten(1)

  commits.group_by{|d, _, _, n| [d, n]}.each do |k, records|
    diff, ticket = k
    c = case diff
        when "-" then :red
        when "+" then :green
    end

    issue = fetch_issue(ticket, options)

    puts "#{apply_colors(diff, c)} #{oneline_issue(issue, options)}"
    if options[:verbose]
      records.each {|_, sha1, msg| puts "  #{sha1} #{msg}" }
      puts ""
    end
  end
end

#commandsObject



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/git_issue/base.rb', line 125

def commands
  [
  GitIssue::Command.new(:show,   :s, 'show given issue summary. if given no id,  geuss id from current branch name.'),
  GitIssue::Command.new(:view,   :v, 'view issue in browser. if given no id,  geuss id from current branch name.'),
  GitIssue::Command.new(:list,   :l, 'listing issues.'),
  GitIssue::Command.new(:mine,   :m, 'display issues that assigned to you.'),
  GitIssue::Command.new(:commit, :c, 'commit with filling issue subject to messsage.if given no id, geuss id from current branch name.'),
  GitIssue::Command.new(:add,    :a, 'create issue.'),
  GitIssue::Command.new(:update, :u, 'update issue properties. if given no id, geuss id from current branch name.'),
  GitIssue::Command.new(:branch, :b, "checout to branch using specified issue id. if branch dose'nt exisits, create it. (ex ticket/id/<issue_id>)"),
  GitIssue::Command.new(:cherry, :chr, 'find issue not merged upstream.'),

  GitIssue::Command.new(:publish,:pub, "push branch to remote repository and set upstream "),
  GitIssue::Command.new(:rebase, :rb,  "rebase branch onto specific newbase"),

  GitIssue::Command.new(:help,   :h, "show usage.")
  ]
end

#connection(host, port) ⇒ Object



328
329
330
331
332
333
334
335
336
337
# File 'lib/git_issue/base.rb', line 328

def connection(host, port)
  env = ENV['http_proxy'] || ENV['HTTP_PROXY']
  if env
    uri = URI(env)
    proxy_host, proxy_port, proxy_user, proxy_pass = uri.host, uri.port, uri.user, uri.password
    Net::HTTP::Proxy(proxy_host, proxy_port, proxy_user, proxy_pass).new(host, port)
  else
    Net::HTTP.new(host, port)
  end
end

#current_branchObject



181
182
183
184
185
# File 'lib/git_issue/base.rb', line 181

def current_branch
  RUBY_PLATFORM.downcase =~ /mswin(?!ce)|mingw|bccwin|cygwin/ ?
    %x(git branch -l 2> NUL | grep "*" | cut -d " " -f 2).strip :
    %x(git branch -l 2> /dev/null | grep "*" | cut -d " " -f 2).strip
end

#default_cmdObject



57
58
59
# File 'lib/git_issue/base.rb', line 57

def default_cmd
  :list
end

#err(msg) ⇒ Object



320
321
322
# File 'lib/git_issue/base.rb', line 320

def err(msg)
  @syserr.puts msg
end

#executeObject



61
62
63
64
65
66
67
68
69
70
# File 'lib/git_issue/base.rb', line 61

def execute
  if @tickets.nil? ||  @tickets.empty?
    self.send(@command.name, @options)
  else
    @tickets.each do |ticket|
      self.send(@command.name, @options.merge(:ticket_id => ticket))
    end
  end
  true
end

#exit_with_message(msg, status = 1) ⇒ Object



170
171
172
173
# File 'lib/git_issue/base.rb', line 170

def exit_with_message(msg, status=1)
  err msg
  exit(status)
end

#find_command(cmd) ⇒ Object



144
145
146
147
# File 'lib/git_issue/base.rb', line 144

def find_command(cmd)
  cmd = cmd.to_sym
  commands.find{|c| c.name == cmd || c.short_name == cmd }
end

#guess_ticketObject



187
188
189
190
191
192
# File 'lib/git_issue/base.rb', line 187

def guess_ticket
  branch = current_branch
  if branch =~ %r!id/(\d+)! || branch =~ /^(\d+)_/ || branch =~ /_(\d+)$/
    ticket = $1
  end
end

#help(options = {}) ⇒ Object



72
73
74
75
76
77
78
79
# File 'lib/git_issue/base.rb', line 72

def help(options = {})
  puts @opt_parse_obj.banner
  puts "  Commnads:"
  puts usage
  puts ""
  puts "  Options:"
  puts @opt_parse_obj.summarize
end

#mktmpdir(prefix_suffix = nil, tmpdir = nil) ⇒ Object

for 1.8.6…



244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
# File 'lib/git_issue/base.rb', line 244

def mktmpdir(prefix_suffix=nil, tmpdir=nil)
  case prefix_suffix
  when nil
    prefix = "d"
    suffix = ""
  when String
    prefix = prefix_suffix
    suffix = ""
  when Array
    prefix = prefix_suffix[0]
    suffix = prefix_suffix[1]
  else
    raise ArgumentError, "unexpected prefix_suffix: #{prefix_suffix.inspect}"
  end
  tmpdir ||= Dir.tmpdir
  t = Time.now.strftime("%Y%m%d")
  n = nil
  begin
    path = "#{tmpdir}/#{prefix}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}"
    path << "-#{n}" if n
    path << suffix
    Dir.mkdir(path, 0700)
  rescue Errno::EEXIST
    n ||= 0
    n += 1
    retry
  end

  if block_given?
    begin
      yield path
    ensure
      FileUtils.remove_entry_secure path
    end
  else
    path
  end
end

#mlength(s) ⇒ Object

this is unnecessary hacks for multibytes charactors handling…



216
217
218
219
220
221
222
# File 'lib/git_issue/base.rb', line 216

def mlength(s)
  width = 0
  cnt = 0
  bytesize_method = (RUBY_VERSION >= "1.9") ? :bytesize : :length
  s.split(//u).each{|c| cnt += 1 ;width += 1 if c.send(bytesize_method) > 1 }
  cnt + width
end

#mljust(s, n) ⇒ Object

this is unnecessary hacks for multibytes charactors handling…



225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/git_issue/base.rb', line 225

def mljust(s, n)
  return "" unless s
  cnt = 0
  chars = []

  s.split(//u).each do |c|
    next if cnt > n
    chars << c
    cnt += c =~ /^[^ -~。-゚]*$/ ? 2 : 1
  end
  if cnt > n
    chars.pop
    cnt -= chars.last =~ /^[^ -~。-゚]*$/ ? 2 : 1
  end
  chars << " " * (n - cnt) if n > cnt
  chars.join
end

#opt_parserObject



293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
# File 'lib/git_issue/base.rb', line 293

def opt_parser
  OptionParser.new{|opts|
    opts.banner = 'git issue <command> [ticket_id] [<args>]'
    # Register "-" only to be shown in help. Actualy this definition is not used
    opts.on("-",            /^$/,    "read ticket-ids from stdin"){ @options[:from_stdin] = true }
    opts.on("--all",        "-a", "update all paths in the index file "){ @options[:all] = true }
    opts.on("--force",      "-f", "force create branch"){ @options[:force] = true }
    opts.on("--verbose",    "-v", "show issue details"){|v| @options[:verbose] = true}
    opts.on("--max-count=VALUE", "-n=VALUE", "maximum number of issues "){|v| @options[:max_count] = v.to_i}
    opts.on("--oneline",          "display short info"){|v| @options[:oneline] = true}
    opts.on("--raw-id",           "output ticket number only"){|v| @options[:raw_id] = true}
    opts.on("--remote=VALUE",     'on publish, remote repository to push branch ') {|v| @options[:remote] = v}
    opts.on("--onto=VALUE",       'on rebase, start new branch with HEAD equal to "newbase" ') {|v| @options[:onto] = v}

    opts.on("--upstream=VALUE",   'on cherry, upstream branch to compare against. default is tracked remote branch') {|v| @options[:upstream] = v}
    opts.on("--head=VALUE",       'on cherry, working branch. defaults to HEAD') {|v| @options[:head] = v}

    opts.on("--no-color", "turn off colored output"){@no_color = true }
    opts.on("--debug", "debug print"){@debug= true }
  }

end

#parse_command_and_tickets(args) ⇒ Object



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/git_issue/base.rb', line 22

def parse_command_and_tickets(args)
  @tickets = []

  # search "-" options from args
  @options[:from_stdin] = args.find{|v| v == "-"}
  args.delete_if{|v| v == "-"}

  cmd = args.shift

  split_ticket = lambda{|s| s.nil? || s.empty? ? nil : s.split(/,/).map{|v| v.strip.to_i} }

  if options[:from_stdin]
    # read tickets from stdin
    $stdin.each_line do |l|
      @tickets += split_ticket.call(l)
    end
  else
    # parse args
    if cmd =~ /(\d+,?\s?)+/
      @tickets = split_ticket.call(cmd)
      cmd = nil
    end

    @tickets += args.map{|s| split_ticket.call(s)}.flatten.uniq
    @tickets = [guess_ticket].compact if @tickets.empty?
  end

  cmd ||= (@tickets.nil? || @tickets.empty?) ? default_cmd : :show
  cmd = cmd.to_sym

  @command = find_command(cmd)

  exit_with_message("invalid command <#{cmd}>") unless @command
end

#parse_options(args) ⇒ Object



287
288
289
290
291
# File 'lib/git_issue/base.rb', line 287

def parse_options(args)
  @options = {}
  @opt_parse_obj.parse!(args)
  args
end

#prompt(name) ⇒ Object



210
211
212
213
# File 'lib/git_issue/base.rb', line 210

def prompt(name)
 print "#{name}: "
 $stdin.gets.chop
end

#publish(options = {}) ⇒ Object



81
82
83
84
85
# File 'lib/git_issue/base.rb', line 81

def publish(options = {})
  ticket, branch_name = ticket_and_branch(options)
  remote = options[:remote] || "origin"
  system "git push -u #{remote} #{branch_name}"
end

#puts(msg) ⇒ Object



316
317
318
# File 'lib/git_issue/base.rb', line 316

def puts(msg)
  @sysout.puts msg
end

#rebase(options = {}) ⇒ Object



87
88
89
90
91
92
93
94
95
96
# File 'lib/git_issue/base.rb', line 87

def rebase(options = {})
  raise '--onto is required.' unless options[:onto]
  ticket, branch_name = ticket_and_branch(options)
  onto = options[:onto]

  cb = current_branch

  system "git rebase --onto #{onto} #{onto} #{branch_name}"
  system "git checkout #{cb}"
end

#response_success?(response) ⇒ Boolean

Returns:

  • (Boolean)


205
206
207
208
# File 'lib/git_issue/base.rb', line 205

def response_success?(response)
  code = response.code.to_i
  code >= 200 && code < 300
end

#ticket_and_branch(options) ⇒ Object



194
195
196
197
198
199
200
201
202
203
# File 'lib/git_issue/base.rb', line 194

def ticket_and_branch(options)
  if options[:ticket_id]
    ticket = options[:ticket_id]
    branch_name = ticket_branch(ticket)
  else
    branch_name = current_branch
    ticket = guess_ticket
  end
  [ticket, branch_name]
end

#ticket_branch(ticket_id) ⇒ Object



177
178
179
# File 'lib/git_issue/base.rb', line 177

def ticket_branch(ticket_id)
  BRANCH_NAME_FORMAT % ticket_id
end

#time_ago_in_words(time) ⇒ Object



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/git_issue/base.rb', line 153

def time_ago_in_words(time)
  t = Time.parse(time)
  a = (Time.now - t).to_i

  case a
    when 0              then return 'just now'
    when 1..59          then return a.to_s + '秒前'
    when 60..119        then return '1分前'
    when 120..3540      then return (a/60).to_i.to_s + '分前'
    when 3541..7100     then return '1時間前'
    when 7101..82800    then return ((a+99)/3600).to_i.to_s + '時間前'
    when 82801..172000  then return '1日前'
    when 172001..432000 then return ((a+800)/(60*60*24)).to_i.to_s + '日前'
    else return ((a+800)/(60*60*24)).to_i.to_s + '日前'
  end
end

#to_date(d) ⇒ Object



283
284
285
# File 'lib/git_issue/base.rb', line 283

def to_date(d)
  Date.parse(d).strftime('%Y/%m/%d') rescue d
end

#usageObject



149
150
151
# File 'lib/git_issue/base.rb', line 149

def usage
  commands.map{|c| "%-8s %s %s" % [c.name, c.short_name, c.description ] }.join("\n")
end