herodot 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 6c56f76d186ec1df50f95f5883ed9dddbc783a2b
4
- data.tar.gz: 5c4a6ee9098fcdbaba1f5c9177e3097323cacbfd
2
+ SHA256:
3
+ metadata.gz: 67bf83f2d851943bf0d40e5e292e09a522bceb8d38f73bc331db415694de94b1
4
+ data.tar.gz: f81ee485d789a866badc55c385a77daffeba0217fcc03fa0a5656c09c5c2827e
5
5
  SHA512:
6
- metadata.gz: 32b26214e6083a539be512c1de6873da6b487c2442b37b0a03d08a99972e89f4b2e6039a28dd983c3b77af25b6dccc4fa76a47dc2fd8442510e15d378054abb3
7
- data.tar.gz: bff391404a04784f6b839d8881cee04f288c600c604290009dd3edf1513b690ad486efcb7fea4ba0c6844279cc42a31e6aea051d1d7b5b43a30cdfc436a35fa9
6
+ metadata.gz: 9412778f3a13fc66db849c373e11504dcc7033ba0cdd3728b7a52ab587183773563f59cd7d8fdb1b048a88d5f32d3c88d1964c213e8912f49bbbd1fb822c1bc1
7
+ data.tar.gz: 67baebca6ca6db82a8a1deae38a69f9321088e8ca9ff565af4dd0cd08b143c8389970abe9faa518af52b17fddc19da55814b1ef6006ca8d8d8d2b75bdc933af6
data/.gitignore CHANGED
@@ -1,6 +1,5 @@
1
1
  /.bundle/
2
2
  /.yardoc
3
- /Gemfile.lock
4
3
  /_yardoc/
5
4
  /coverage/
6
5
  /doc/
data/.rubocop.yml CHANGED
@@ -3,6 +3,10 @@ inherit_gem:
3
3
  - .rubocop.yml
4
4
  - .rubocop-rspec.yml
5
5
 
6
+ AllCops:
7
+ Exclude:
8
+ - 'pkg/**/*'
9
+
6
10
  Rails:
7
11
  Enabled: false
8
12
 
data/.travis.yml CHANGED
@@ -1,6 +1,22 @@
1
1
  sudo: false
2
2
  language: ruby
3
+
3
4
  rvm:
4
- - 2.0.0
5
- - 2.3.3
6
- before_install: gem install bundler -v 1.14.4
5
+ - 2.1.0
6
+ - 2.3.5
7
+ - 2.4.2
8
+
9
+ before_install:
10
+ - gem install bundler -v 1.14.6
11
+
12
+ install:
13
+ - gem update --system
14
+ - bundle install --jobs=3 --retry=3
15
+
16
+ cache:
17
+ bundler: true
18
+ directories:
19
+ - vendor/bundle
20
+
21
+ script:
22
+ - bin/ci
data/Gemfile.lock ADDED
@@ -0,0 +1,66 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ herodot (0.2.1)
5
+ chronic
6
+ commander
7
+ rainbow
8
+ terminal-table
9
+
10
+ GEM
11
+ remote: https://rubygems.org/
12
+ specs:
13
+ ast (2.4.0)
14
+ chronic (0.10.2)
15
+ commander (4.4.3)
16
+ highline (~> 1.7.2)
17
+ diff-lcs (1.3)
18
+ highline (1.7.8)
19
+ parallel (1.12.1)
20
+ parser (2.5.0.0)
21
+ ast (~> 2.4.0)
22
+ powerpack (0.1.1)
23
+ rainbow (3.0.0)
24
+ rake (10.5.0)
25
+ rspec (3.5.0)
26
+ rspec-core (~> 3.5.0)
27
+ rspec-expectations (~> 3.5.0)
28
+ rspec-mocks (~> 3.5.0)
29
+ rspec-core (3.5.4)
30
+ rspec-support (~> 3.5.0)
31
+ rspec-expectations (3.5.0)
32
+ diff-lcs (>= 1.2.0, < 2.0)
33
+ rspec-support (~> 3.5.0)
34
+ rspec-mocks (3.5.0)
35
+ diff-lcs (>= 1.2.0, < 2.0)
36
+ rspec-support (~> 3.5.0)
37
+ rspec-support (3.5.0)
38
+ rubocop (0.52.1)
39
+ parallel (~> 1.10)
40
+ parser (>= 2.4.0.2, < 3.0)
41
+ powerpack (~> 0.1)
42
+ rainbow (>= 2.2.2, < 4.0)
43
+ ruby-progressbar (~> 1.7)
44
+ unicode-display_width (~> 1.0, >= 1.0.1)
45
+ rubocop-bitcrowd (1.2.0)
46
+ rubocop-rspec (1.22.2)
47
+ rubocop (>= 0.52.1)
48
+ ruby-progressbar (1.9.0)
49
+ terminal-table (1.8.0)
50
+ unicode-display_width (~> 1.1, >= 1.1.1)
51
+ unicode-display_width (1.3.0)
52
+
53
+ PLATFORMS
54
+ ruby
55
+
56
+ DEPENDENCIES
57
+ bundler (~> 1.14)
58
+ herodot!
59
+ rake (~> 10.0)
60
+ rspec (~> 3.0)
61
+ rubocop
62
+ rubocop-bitcrowd
63
+ rubocop-rspec
64
+
65
+ BUNDLED WITH
66
+ 1.16.1
data/README.md CHANGED
@@ -12,6 +12,8 @@ Install with:
12
12
 
13
13
  $ gem install herodot
14
14
 
15
+ Make sure you have installed at least ruby 2.1 or any newer ruby version.
16
+
15
17
  ## Usage
16
18
 
17
19
  Track a git repository:
@@ -50,6 +52,7 @@ Show Help:
50
52
  $ herodot help show
51
53
 
52
54
  ## Linking to issue trackers
55
+
53
56
  If you use https://github.com/bitcrowd/tickety-tick or otherwise have branch names, that contain
54
57
  the issue number, you can link a tracked herodot repository with your issue tracker, so it
55
58
  will print urls of issues it recognizes under the branch name.
data/bin/ci ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'English'
4
+
5
+ module TravisRunner
6
+ def self.execute(title, command)
7
+ puts "== Running #{title} =="
8
+ system command
9
+ yield if block_given?
10
+ raise "#{title} failed" unless $CHILD_STATUS.success?
11
+ end
12
+ end
13
+
14
+ system 'mkdir -p tmp'
15
+
16
+ TravisRunner.execute 'Rubocop', 'bundle exec rubocop'
17
+ TravisRunner.execute 'Rspec', 'bundle exec rspec --exclude-pattern "spec/features/**/*"'
data/herodot.gemspec CHANGED
@@ -1,8 +1,9 @@
1
- # coding: utf-8
1
+
2
2
  lib = File.expand_path('../lib', __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'herodot/version'
5
5
 
6
+ # rubocop:disable Metrics/BlockLength
6
7
  Gem::Specification.new do |spec|
7
8
  spec.name = 'herodot'
8
9
  spec.version = Herodot::VERSION
@@ -19,17 +20,18 @@ Gem::Specification.new do |spec|
19
20
  f.match(%r{^(test|spec|features)/})
20
21
  end
21
22
  spec.bindir = 'exe'
22
- spec.executables = %w(herodot)
23
- spec.require_paths = %w(lib)
23
+ spec.executables = %w[herodot]
24
+ spec.require_paths = %w[lib]
24
25
 
25
26
  spec.add_development_dependency 'bundler', '~> 1.14'
26
27
  spec.add_development_dependency 'rake', '~> 10.0'
27
28
  spec.add_development_dependency 'rspec', '~> 3.0'
28
29
  spec.add_development_dependency 'rubocop'
29
- spec.add_development_dependency 'rubocop-rspec'
30
30
  spec.add_development_dependency 'rubocop-bitcrowd'
31
- spec.add_dependency 'rainbow'
32
- spec.add_dependency 'terminal-table'
31
+ spec.add_development_dependency 'rubocop-rspec'
33
32
  spec.add_dependency 'chronic'
34
33
  spec.add_dependency 'commander'
34
+ spec.add_dependency 'rainbow'
35
+ spec.add_dependency 'terminal-table'
35
36
  end
37
+ # rubocop:enable Metrics/BlockLength
@@ -2,74 +2,76 @@ require 'date'
2
2
  require 'chronic'
3
3
  require 'fileutils'
4
4
 
5
- class Herodot::Commands
6
- SCRIPT = "#!/bin/sh\nherodot track $(pwd)".freeze
5
+ module Herodot
6
+ class Commands
7
+ SCRIPT = "#!/bin/sh\nherodot track $(pwd)".freeze
7
8
 
8
- DEFAULT_RANGE = 'this week'.freeze
9
+ DEFAULT_RANGE = 'this week'.freeze
9
10
 
10
- def self.show(args, config, opts = {})
11
- subject = args.empty? ? DEFAULT_RANGE : args.join(' ')
12
- range = Chronic.parse(subject, guess: false, context: :past)
13
- abort "Date not parsable: #{args.join(' ')}" unless range
14
- worklog = Herodot::Parser.parse(range, config)
15
- decorated_worklog = Herodot::ProjectLink.new(worklog)
16
- output = Herodot::Output.print(decorated_worklog.totals, opts)
17
- puts output
18
- end
11
+ def self.show(args, config, opts = {})
12
+ subject = args.empty? ? DEFAULT_RANGE : args.join(' ')
13
+ range = Chronic.parse(subject, guess: false, context: :past)
14
+ abort "Date not parsable: #{args.join(' ')}" unless range
15
+ worklog = Parser.parse(range, config)
16
+ decorated_worklog = ProjectLink.new(worklog)
17
+ output = Output.print(decorated_worklog.totals, opts)
18
+ puts output
19
+ end
19
20
 
20
- def self.init(path, config)
21
- path = '.' if path.nil?
22
- puts "Start tracking of `#{File.expand_path(path)}` into `#{config.worklog_file}`."
23
- hooks = "#{path}/.git/hooks"
24
- abort('Path is not a git repository.') unless File.exist?(hooks)
25
- %w(post-checkout post-commit).each do |name|
26
- File.open("#{hooks}/#{name}", 'w') { |file| file.write(SCRIPT) }
27
- File.chmod(0o755, "#{hooks}/#{name}")
28
- FileUtils.touch(config.worklog_file)
21
+ def self.init(path, config)
22
+ path = '.' if path.nil?
23
+ puts "Start tracking of `#{File.expand_path(path)}` into `#{config.worklog_file}`."
24
+ hooks = "#{path}/.git/hooks"
25
+ abort('Path is not a git repository.') unless File.exist?(hooks)
26
+ %w[post-checkout post-commit].each do |name|
27
+ File.open("#{hooks}/#{name}", 'w') { |file| file.write(SCRIPT) }
28
+ File.chmod(0o755, "#{hooks}/#{name}")
29
+ FileUtils.touch(config.worklog_file)
30
+ end
29
31
  end
30
- end
31
32
 
32
- def self.track(path, config)
33
- puts 'Logging into worklog'
34
- File.open(config.worklog_file, 'a') do |worklog|
35
- datestr = DateTime.now.strftime("%a %b %e %H:%M:%S %z %Y")
36
- branch = `(cd #{path} && git rev-parse --abbrev-ref HEAD)`.strip
37
- line = [datestr, path, branch].join(";")
38
- worklog.puts(line)
33
+ def self.track(path, config)
34
+ puts 'Logging into worklog'
35
+ File.open(config.worklog_file, 'a') do |worklog|
36
+ datestr = Time.now.strftime('%a %b %e %H:%M:%S %z %Y')
37
+ branch = `(cd #{path} && git rev-parse --abbrev-ref HEAD)`.strip
38
+ line = [datestr, path, branch].join(';')
39
+ worklog.puts(line)
40
+ end
39
41
  end
40
- end
41
42
 
42
- def self.link(path)
43
- path = '.' if path.nil?
44
- choose do |menu|
45
- menu.prompt = 'What tracker do you want to link to?'
46
- menu.choice(:jira) { link_jira(path) }
47
- menu.choice(:github) { link_github(path) }
48
- menu.choice(:gitlab) { link_gitlab(path) }
49
- menu.choices(:other) { link_other(path) }
50
- menu.default = :other
43
+ def self.link(path)
44
+ path = '.' if path.nil?
45
+ choose do |menu|
46
+ menu.prompt = 'What tracker do you want to link to?'
47
+ menu.choice(:jira) { link_jira(path) }
48
+ menu.choice(:github) { link_github(path) }
49
+ menu.choice(:gitlab) { link_gitlab(path) }
50
+ menu.choices(:other) { link_other(path) }
51
+ menu.default = :other
52
+ end
51
53
  end
52
- end
53
54
 
54
- def self.link_jira(path)
55
- prefix = ask('Jira URL prefix (something for https://something.atlassian.net)?')
56
- pattern = ask('Ticket prefix (ABCD for tickets like ABCD-123)')
57
- Herodot::ProjectLink.link(path, "http://#{prefix}.atlassian.net/browse/", "#{pattern}-\\d+")
58
- end
55
+ def self.link_jira(path)
56
+ prefix = ask('Jira URL prefix (something for https://something.atlassian.net)?')
57
+ pattern = ask('Ticket prefix (ABCD for tickets like ABCD-123)')
58
+ ProjectLink.link(path, "http://#{prefix}.atlassian.net/browse/", "#{pattern}-\\d+")
59
+ end
59
60
 
60
- def self.link_github(path)
61
- handle = ask('Github handle (something/something for https://github.com/something/something)?')
62
- Herodot::ProjectLink.link(path, "https://github.com/#{handle}/issues/", '\\d+')
63
- end
61
+ def self.link_github(path)
62
+ handle = ask('Github handle (something/something for https://github.com/something/something)?')
63
+ ProjectLink.link(path, "https://github.com/#{handle}/issues/", '\\d+')
64
+ end
64
65
 
65
- def self.link_gitlab(path)
66
- handle = ask('GitLab handle (something/something for https://gitlab.com/something/something)?')
67
- Herodot::ProjectLink.link(path, "https://gitlab.com/#{handle}/issues/", '\\d+')
68
- end
66
+ def self.link_gitlab(path)
67
+ handle = ask('GitLab handle (something/something for https://gitlab.com/something/something)?')
68
+ ProjectLink.link(path, "https://gitlab.com/#{handle}/issues/", '\\d+')
69
+ end
69
70
 
70
- def self.link_other(path)
71
- url = ask('URL to issue tracker:')
72
- pattern = ask('Ticket regex pattern (ruby):')
73
- Herodot::ProjectLink.link(path, url, pattern)
71
+ def self.link_other(path)
72
+ url = ask('URL to issue tracker:')
73
+ pattern = ask('Ticket regex pattern (ruby):')
74
+ ProjectLink.link(path, url, pattern)
75
+ end
74
76
  end
75
77
  end
@@ -1,44 +1,47 @@
1
1
  require 'yaml'
2
2
 
3
- class Herodot::Configuration
4
- CONFIG_FILE = File.expand_path('~/.herodot.yml').freeze
5
- DEFAULT_CONFIGURATION = {
6
- 'projects_directory' => '~',
7
- 'work_times' => {
8
- 'work_start' => '9:30',
9
- 'lunch_break_start' => '13:00',
10
- 'lunch_break_end' => '13:30',
11
- 'work_end' => '18:00'
12
- }
13
- }.freeze
3
+ module Herodot
4
+ class Configuration
5
+ CONFIG_FILE = File.expand_path('~/.herodot.yml').freeze
6
+ DEFAULT_CONFIGURATION = {
7
+ 'projects_directory' => '~',
8
+ 'work_times' => {
9
+ 'work_start' => '9:30',
10
+ 'lunch_break_start' => '13:00',
11
+ 'lunch_break_end' => '13:30',
12
+ 'work_end' => '18:00'
13
+ }
14
+ }.freeze
14
15
 
15
- def initialize(worklog_file = '~/.worklog')
16
- @worklog_file = worklog_file
17
- if File.exist?(CONFIG_FILE)
18
- @config = load_configuration
19
- else
20
- @config = DEFAULT_CONFIGURATION
21
- save_configuration
16
+ def initialize(worklog_file = '~/.worklog')
17
+ @worklog_file = worklog_file
18
+ if File.exist?(CONFIG_FILE)
19
+ @config = load_configuration
20
+ else
21
+ @config = DEFAULT_CONFIGURATION
22
+ save_configuration
23
+ end
22
24
  end
23
- end
24
25
 
25
- def worklog_file
26
- File.expand_path(@worklog_file)
27
- end
26
+ def worklog_file
27
+ File.expand_path(@worklog_file)
28
+ end
28
29
 
29
- def projects_directory
30
- File.expand_path(@config['projects_directory'])
31
- end
30
+ def projects_directory
31
+ File.expand_path(@config['projects_directory'] || DEFAULT_CONFIGURATION['projects_directory'])
32
+ end
32
33
 
33
- def work_times
34
- @config['work_times'].map { |k, v| [k.to_sym, v.split(':').map(&:to_i)] }
35
- end
34
+ def work_times
35
+ (@config['work_times'] || DEFAULT_CONFIGURATION['work_times'])
36
+ .map { |k, v| [k.to_sym, v.split(':').map(&:to_i)] }
37
+ end
36
38
 
37
- def save_configuration
38
- File.open(CONFIG_FILE, 'w') { |f| YAML.dump(@config, f) }
39
- end
39
+ def save_configuration
40
+ File.open(CONFIG_FILE, 'w') { |f| YAML.dump(@config, f) }
41
+ end
40
42
 
41
- def load_configuration
42
- File.open(CONFIG_FILE) { |f| YAML.load(f) }
43
+ def load_configuration
44
+ File.open(CONFIG_FILE) { |f| YAML.load(f) }
45
+ end
43
46
  end
44
47
  end
@@ -1,58 +1,64 @@
1
1
  require 'terminal-table'
2
2
  require 'json'
3
3
 
4
- class Herodot::Output
5
- HEADERS = %w(Project Branch Time).freeze
6
- EMPTY_WORKLOG_MESSAGE = Rainbow('Not enough entries in the worklog.').red +
7
- ' On a tracked repository `git checkout`'\
8
- ' and `git commit` will add entries.'.freeze
9
- COLORS = %i(green yellow blue magenta cyan aqua silver aliceblue indianred).freeze
10
-
11
- class << self
12
- def format_time(time_is_seconds)
13
- total_seconds = time_is_seconds.to_i
14
- seconds = total_seconds % 60
15
- minutes = (total_seconds / 60) % 60
16
- hours = total_seconds / (60 * 60)
17
- "#{hours}:#{minutes.to_s.rjust(2, '0')}:#{seconds.to_s.rjust(2, '0')}"
18
- end
4
+ module Herodot
5
+ class Output
6
+ HEADERS = %w[Project Branch Time].freeze
7
+ EMPTY_WORKLOG_MESSAGE = Rainbow('Not enough entries in the worklog.').red +
8
+ ' On a tracked repository `git checkout`'\
9
+ ' and `git commit` will add entries.'.freeze
10
+ COLORS = %i[green yellow blue magenta cyan aqua silver aliceblue indianred].freeze
19
11
 
20
- def print(worklogs_totals_per_day, opts)
21
- return convert_format(worklogs_totals_per_day, opts.format) if opts.format
22
- print_table(worklogs_totals_per_day)
23
- end
12
+ class << self
13
+ def format_time(time_is_seconds)
14
+ total_seconds = time_is_seconds.to_i
15
+ seconds = total_seconds % 60
16
+ minutes = (total_seconds / 60) % 60
17
+ hours = total_seconds / (60 * 60)
18
+ "#{hours}:#{minutes.to_s.rjust(2, '0')}:#{seconds.to_s.rjust(2, '0')}"
19
+ end
24
20
 
25
- def convert_format(worklogs_totals_per_day, format)
26
- case format
27
- when 'json'
28
- worklogs_totals_per_day.to_json
21
+ def print(worklogs_totals_per_day, opts)
22
+ return convert_format(worklogs_totals_per_day, opts.format) if opts.format
23
+ print_table(worklogs_totals_per_day)
29
24
  end
30
- end
31
25
 
32
- def print_table(worklogs_totals_per_day)
33
- abort EMPTY_WORKLOG_MESSAGE if worklogs_totals_per_day.empty?
34
- Terminal::Table.new(headings: HEADERS) do |table|
35
- worklogs_totals_per_day.each do |date, times|
36
- table.add_separator
37
- table << [date]
38
- table.add_separator
39
- print_day(times).each { |row| table << row }
40
- table.add_separator
26
+ def convert_format(worklogs_totals_per_day, format)
27
+ case format
28
+ when 'json'
29
+ worklogs_totals_per_day.to_json
41
30
  end
42
31
  end
43
- end
44
32
 
45
- private
33
+ def print_table(worklogs_totals_per_day)
34
+ abort EMPTY_WORKLOG_MESSAGE if worklogs_totals_per_day.empty?
35
+ Terminal::Table.new(headings: HEADERS) do |table|
36
+ worklogs_totals_per_day.each do |date, times|
37
+ table.add_separator
38
+ table << [date]
39
+ table.add_separator
40
+ print_day(times).each { |row| table << row }
41
+ table.add_separator
42
+ end
43
+ end
44
+ end
46
45
 
47
- def colorize(project)
48
- Rainbow(project).color(COLORS[project.chars.map(&:ord).reduce(:+) % COLORS.size])
49
- end
46
+ private
47
+
48
+ def colorize(project)
49
+ Rainbow(project).color(COLORS[project.chars.map(&:ord).reduce(:+) % COLORS.size])
50
+ end
50
51
 
51
- def print_day(times)
52
- times.sort_by { |log| log[:project] }.flat_map do |log|
53
- lines = [[colorize(log[:project]), log[:branch], format_time(log[:time])]]
54
- lines << ['', Rainbow(log[:link]).color(80, 80, 80), ''] if log[:link]
55
- lines
52
+ def times_by_project_and_branch(times)
53
+ times.sort_by { |log| [log[:project], log[:branch]] }
54
+ end
55
+
56
+ def print_day(times)
57
+ times_by_project_and_branch(times).flat_map do |log|
58
+ lines = [[colorize(log[:project]), log[:branch], format_time(log[:time])]]
59
+ lines << ['', Rainbow(log[:link]).color(80, 80, 80), ''] if log[:link]
60
+ lines
61
+ end
56
62
  end
57
63
  end
58
64
  end
@@ -1,31 +1,33 @@
1
1
  require 'csv'
2
2
 
3
- class Herodot::Parser
4
- NO_SUCH_FILE = Rainbow('Worklog missing.').red +
5
- ' Use `herodot init` to start tracking a git repository'\
6
- ' or `herodot help` to open the man page.'.freeze
7
- class << self
8
- def parse(range, config)
9
- worklog = Herodot::Worklog.new(config)
10
- from, to = from_to_from_range(range)
11
- parse_into_worklog(worklog, config.worklog_file, from, to)
12
- worklog
13
- rescue Errno::ENOENT
14
- abort NO_SUCH_FILE
15
- end
3
+ module Herodot
4
+ class Parser
5
+ NO_SUCH_FILE = Rainbow('Worklog missing.').red +
6
+ ' Use `herodot init` to start tracking a git repository'\
7
+ ' or `herodot help` to open the man page.'.freeze
8
+ class << self
9
+ def parse(range, config)
10
+ worklog = Worklog.new(config)
11
+ from, to = from_to_from_range(range)
12
+ parse_into_worklog(worklog, config.worklog_file, from, to)
13
+ worklog
14
+ rescue Errno::ENOENT
15
+ abort NO_SUCH_FILE
16
+ end
16
17
 
17
- def from_to_from_range(range)
18
- return [range, Time.now] unless range.respond_to?(:begin) && range.respond_to?(:end)
19
- [range.begin, range.end + 3600]
20
- end
18
+ def from_to_from_range(range)
19
+ return [range, Time.now] unless range.respond_to?(:begin) && range.respond_to?(:end)
20
+ [range.begin, range.end + 3600]
21
+ end
21
22
 
22
- private
23
+ private
23
24
 
24
- def parse_into_worklog(worklog, file, from, to)
25
- CSV.foreach(file, col_sep: ';') do |row|
26
- next if row[2] == 'HEAD'
27
- time = Time.parse(row[0])
28
- worklog.add_entry(time, row[1], row[2]) if time >= from && time <= to
25
+ def parse_into_worklog(worklog, file, from, to)
26
+ CSV.foreach(file, col_sep: ';') do |row|
27
+ next if row[2] == 'HEAD'
28
+ time = Time.parse(row[0])
29
+ worklog.add_entry(time, row[1], row[2]) if time >= from && time <= to
30
+ end
29
31
  end
30
32
  end
31
33
  end
@@ -1,52 +1,54 @@
1
- class Herodot::ProjectLink
2
- PROJECT_CONFIG = '.herodot.yml'.freeze
1
+ module Herodot
2
+ class ProjectLink
3
+ PROJECT_CONFIG = '.herodot.yml'.freeze
3
4
 
4
- def self.project_config_file(path)
5
- File.join(File.expand_path(path), PROJECT_CONFIG)
6
- end
5
+ def self.project_config_file(path)
6
+ File.join(File.expand_path(path), PROJECT_CONFIG)
7
+ end
7
8
 
8
- def self.link(path, link, pattern)
9
- puts "Write link into #{project_config_file(path)}"
10
- File.open(project_config_file(path), 'w') do |f|
11
- YAML.dump({ link: link, pattern: pattern }, f)
9
+ def self.link(path, link, pattern)
10
+ puts "Write link into #{project_config_file(path)}"
11
+ File.open(project_config_file(path), 'w') do |f|
12
+ YAML.dump({ link: link, pattern: pattern }, f)
13
+ end
12
14
  end
13
- end
14
15
 
15
- def initialize(worklog)
16
- @worklog = worklog
17
- @project_configurations = {}
18
- end
16
+ def initialize(worklog)
17
+ @worklog = worklog
18
+ @project_configurations = {}
19
+ end
19
20
 
20
- def totals
21
- @worklog.totals.map do |date, logs|
22
- [date, decorated_logs(logs)]
21
+ def totals
22
+ @worklog.totals.map do |date, logs|
23
+ [date, decorated_logs(logs)]
24
+ end
23
25
  end
24
- end
25
26
 
26
- private
27
+ private
27
28
 
28
- def decorated_logs(logs)
29
- logs.map do |log|
30
- decorated_log(log)
29
+ def decorated_logs(logs)
30
+ logs.map do |log|
31
+ decorated_log(log)
32
+ end
31
33
  end
32
- end
33
34
 
34
- def decorated_log(log)
35
- link = issue_management_link(log)
36
- return log if link.nil?
37
- log.merge(link: link)
38
- end
35
+ def decorated_log(log)
36
+ link = issue_management_link(log)
37
+ return log if link.nil?
38
+ log.merge(link: link)
39
+ end
39
40
 
40
- def issue_management_link(log)
41
- config = @project_configurations.fetch(log[:path], load_project_configuration(log[:path]))
42
- return nil unless config.fetch(:link, false)
43
- ticket = log[:branch].scan(Regexp.new(config.fetch(:pattern, /$^/)))
44
- [config[:link], ticket.first].join if ticket.any?
45
- end
41
+ def issue_management_link(log)
42
+ config = @project_configurations.fetch(log[:path], load_project_configuration(log[:path]))
43
+ return nil unless config.fetch(:link, false)
44
+ ticket = log[:branch].scan(Regexp.new(config.fetch(:pattern, /$^/)))
45
+ [config[:link], ticket.first].join if ticket.any?
46
+ end
46
47
 
47
- def load_project_configuration(path)
48
- file = self.class.project_config_file(path)
49
- return { link: false } unless File.exist?(file)
50
- File.open(file) { |f| YAML.load(f) }
48
+ def load_project_configuration(path)
49
+ file = self.class.project_config_file(path)
50
+ return { link: false } unless File.exist?(file)
51
+ File.open(file) { |f| YAML.load(f) }
52
+ end
51
53
  end
52
54
  end
@@ -1,3 +1,3 @@
1
1
  module Herodot
2
- VERSION = '0.2.0'.freeze
2
+ VERSION = '0.2.1'.freeze
3
3
  end
@@ -1,85 +1,88 @@
1
- class Herodot::Worklog
2
- attr_reader :branches
3
- END_TRACK_EVENTS = [:work_end, :lunch_break_start, :after_last_dates_end].freeze
4
- START_TRACK_EVNETS = [:work_start, :lunch_break_end, :before_first_dates_start].freeze
5
- EVENTS = (END_TRACK_EVENTS + START_TRACK_EVNETS).freeze
1
+ module Herodot
2
+ class Worklog
3
+ attr_reader :branches
4
+ END_TRACK_EVENTS = %i[work_end lunch_break_start after_last_dates_end].freeze
5
+ START_TRACK_EVNETS = %i[work_start lunch_break_end before_first_dates_start].freeze
6
+ EVENTS = (END_TRACK_EVENTS + START_TRACK_EVNETS).freeze
6
7
 
7
- def initialize(config)
8
- @raw_logs = []
9
- @branches = {}
10
- @dates = []
11
- @config = config
12
- end
8
+ def initialize(config)
9
+ @raw_logs = []
10
+ @branches = {}
11
+ @dates = []
12
+ @config = config
13
+ end
13
14
 
14
- def add_entry(time, project_path, branch)
15
- project = project_path.gsub(@config.projects_directory.to_s, '')
16
- id = "#{project}:#{branch}"
17
- @raw_logs << { time: time, id: id }
18
- @branches[id] = { branch: branch, project: project, path: project_path }
19
- end
15
+ def add_entry(time, project_path, branch)
16
+ return if project_path.nil?
17
+ project = project_path.gsub(@config.projects_directory.to_s, '')
18
+ id = "#{project}:#{branch}"
19
+ @raw_logs << { time: time, id: id }
20
+ @branches[id] = { branch: branch, project: project, path: project_path }
21
+ end
20
22
 
21
- def logs_with_events
22
- filtered_logs = @raw_logs.chunk { |x| x[:id] }.map(&:last).map(&:first)
23
- filtered_logs += work_time_events
24
- filtered_logs << { time: Time.new(0), id: :before_first_dates_start }
25
- filtered_logs << { time: Time.now, id: :after_last_dates_end }
26
- filtered_logs.sort_by { |log| log[:time] }
27
- end
23
+ def logs_with_events
24
+ filtered_logs = @raw_logs.chunk { |x| x[:id] }.map(&:last).map(&:first)
25
+ filtered_logs += work_time_events
26
+ filtered_logs << { time: Time.new(0), id: :before_first_dates_start }
27
+ filtered_logs << { time: Time.now, id: :after_last_dates_end }
28
+ filtered_logs.sort_by { |log| log[:time] }
29
+ end
28
30
 
29
- def logs_with_times
30
- current_id = nil
31
- logs_with_events.each_cons(2).map do |log, following_log|
32
- current_id = log[:id] unless EVENTS.include?(log[:id])
33
- log.merge id: actual_id(current_id, log[:id]),
34
- time: time_between(log, following_log),
35
- date: log[:time].to_date
31
+ def logs_with_times
32
+ current_id = nil
33
+ logs_with_events.each_cons(2).map do |log, following_log|
34
+ current_id = log[:id] unless EVENTS.include?(log[:id])
35
+ log.merge id: actual_id(current_id, log[:id]),
36
+ time: time_between(log, following_log),
37
+ date: log[:time].to_date
38
+ end
36
39
  end
37
- end
38
40
 
39
- def logs_with_times_cleaned
40
- logs_with_times.reject { |log| EVENTS.include?(log[:id]) }
41
- end
41
+ def logs_with_times_cleaned
42
+ logs_with_times.reject { |log| EVENTS.include?(log[:id]) }
43
+ end
42
44
 
43
- def totals
44
- grouped = logs_with_times_cleaned.group_by { |time| time[:date] }
45
- dates.map do |date|
46
- time_sums = grouped[date].each_with_object({}) do |time, sums|
47
- id = time[:id]
48
- sums[id] ||= { time: 0, **branch(id) }
49
- sums[id][:time] += time[:time]
45
+ def totals
46
+ grouped = logs_with_times_cleaned.group_by { |time| time[:date] }
47
+ dates.map do |date|
48
+ time_sums = grouped[date].each_with_object({}) do |time, sums|
49
+ id = time[:id]
50
+ sums[id] ||= { time: 0, **branch(id) }
51
+ sums[id][:time] += time[:time]
52
+ end
53
+ [date, time_sums.values]
50
54
  end
51
- [date, time_sums.values]
52
55
  end
53
- end
54
56
 
55
- def branch(id)
56
- @branches.fetch(id, {})
57
- end
57
+ def branch(id)
58
+ @branches.fetch(id, {})
59
+ end
58
60
 
59
- def dates
60
- @raw_logs.map { |log| log[:time].to_date }.uniq.sort
61
- end
61
+ def dates
62
+ @raw_logs.map { |log| log[:time].to_date }.uniq.sort
63
+ end
62
64
 
63
- def work_time_events
64
- dates.flat_map do |date|
65
- @config.work_times.map { |event, (hour, minute)|
66
- time = Time.new(date.year, date.month, date.day, hour, minute)
67
- next if time > Time.now
68
- { id: event, time: time }
69
- }.compact
65
+ def work_time_events
66
+ dates.flat_map do |date|
67
+ @config.work_times.map { |event, (hour, minute)|
68
+ time = Time.new(date.year, date.month, date.day, hour, minute)
69
+ next if time > Time.now
70
+ { id: event, time: time }
71
+ }.compact
72
+ end
70
73
  end
71
- end
72
74
 
73
- def same_date?(log_entry, other_log_entry)
74
- log_entry[:time].to_date == other_log_entry[:time].to_date
75
- end
75
+ def same_date?(log_entry, other_log_entry)
76
+ log_entry[:time].to_date == other_log_entry[:time].to_date
77
+ end
76
78
 
77
- def time_between(log_entry, following_entry)
78
- return 0 unless same_date?(log_entry, following_entry)
79
- following_entry[:time] - log_entry[:time]
80
- end
79
+ def time_between(log_entry, following_entry)
80
+ return 0 unless same_date?(log_entry, following_entry)
81
+ following_entry[:time] - log_entry[:time]
82
+ end
81
83
 
82
- def actual_id(current_id, id)
83
- END_TRACK_EVENTS.include?(id) ? id : current_id || id
84
+ def actual_id(current_id, id)
85
+ END_TRACK_EVENTS.include?(id) ? id : current_id || id
86
+ end
84
87
  end
85
88
  end
data/lib/herodot.rb CHANGED
@@ -8,88 +8,90 @@ require_relative 'herodot/commands'
8
8
  require_relative 'herodot/output'
9
9
  require_relative 'herodot/project_link'
10
10
 
11
- class Herodot::Application
12
- include Commander::Methods
13
- USER_HOME = File.expand_path('~').to_s
11
+ module Herodot
12
+ class Application
13
+ include Commander::Methods
14
+ USER_HOME = File.expand_path('~').to_s
14
15
 
15
- def run
16
- program :name, 'herodot'
17
- program :version, Herodot::VERSION
18
- program :description, 'Tracks your work based on git branch checkouts'
16
+ def run
17
+ program :name, 'herodot'
18
+ program :version, VERSION
19
+ program :description, 'Tracks your work based on git branch checkouts'
19
20
 
20
- config = Herodot::Configuration.new
21
- init_command(config)
22
- track_command(config)
23
- show_command(config)
24
- link_command(config)
25
- default_command :show
26
- run!
27
- end
21
+ config = Configuration.new
22
+ init_command(config)
23
+ track_command(config)
24
+ show_command(config)
25
+ link_command(config)
26
+ default_command :show
27
+ run!
28
+ end
28
29
 
29
- INIT_DESCRIPTION = 'This command sets up post commit and post checkout hooks'\
30
- ', that will log the current branch into the worklog file.'.freeze
31
- def init_command(config)
32
- command :init do |c|
33
- c.syntax = 'herodot init [<repository path>]'
34
- c.summary = 'Start tracking a repository'
35
- c.description = INIT_DESCRIPTION
36
- c.example 'Start tracking current repository', 'herodot init'
37
- c.action do |args, _|
38
- Herodot::Commands.init(args[0], config)
30
+ INIT_DESCRIPTION = 'This command sets up post commit and post checkout hooks'\
31
+ ', that will log the current branch into the worklog file.'.freeze
32
+ def init_command(config)
33
+ command :init do |c|
34
+ c.syntax = 'herodot init [<repository path>]'
35
+ c.summary = 'Start tracking a repository'
36
+ c.description = INIT_DESCRIPTION
37
+ c.example 'Start tracking current repository', 'herodot init'
38
+ c.action do |args, _|
39
+ Commands.init(args[0], config)
40
+ end
39
41
  end
40
42
  end
41
- end
42
43
 
43
- TRACK_DESCRIPTION = 'This command tracks the current branch/commit in a repo '\
44
- 'and is called from the git hooks installed via `herodot init`.'.freeze
45
- def track_command(config)
46
- command :track do |c|
47
- c.syntax = 'herodot track <repository path>'
48
- c.summary = 'Record git activity in a repository (used internally)'
49
- c.description = TRACK_DESCRIPTION
50
- c.example 'Record the latest branch name etc. to the worklog', 'herodot track .'
51
- c.action do |args, _|
52
- Herodot::Commands.track(args[0], config)
44
+ TRACK_DESCRIPTION = 'This command tracks the current branch/commit in a repo '\
45
+ 'and is called from the git hooks installed via `herodot init`.'.freeze
46
+ def track_command(config)
47
+ command :track do |c|
48
+ c.syntax = 'herodot track <repository path>'
49
+ c.summary = 'Record git activity in a repository (used internally)'
50
+ c.description = TRACK_DESCRIPTION
51
+ c.example 'Record the latest branch name etc. to the worklog', 'herodot track .'
52
+ c.action do |args, _|
53
+ Commands.track(args[0], config)
54
+ end
53
55
  end
54
56
  end
55
- end
56
57
 
57
- SHOW_DESCRIPTION = 'This command parses the worklog file and returns the'\
58
- 'git branch based worklog according to the'\
59
- 'work times specified in the `~/.herodot.yml`.'.freeze
60
- def show_command(config)
61
- command :show do |c|
62
- c.syntax = 'herodot show [<time range>]'
63
- c.summary = 'Shows worklogs'
64
- c.description = SHOW_DESCRIPTION
65
- c.option '--format FORMAT', String, 'Uses specific output format (Supported: json)'
66
- show_command_examples(c)
67
- c.action do |args, options|
68
- Herodot::Commands.show(args, config, options)
58
+ SHOW_DESCRIPTION = 'This command parses the worklog file and returns the'\
59
+ 'git branch based worklog according to the'\
60
+ 'work times specified in the `~/.herodot.yml`.'.freeze
61
+ def show_command(config)
62
+ command :show do |c|
63
+ c.syntax = 'herodot show [<time range>]'
64
+ c.summary = 'Shows worklogs'
65
+ c.description = SHOW_DESCRIPTION
66
+ c.option '--format FORMAT', String, 'Uses specific output format (Supported: json)'
67
+ show_command_examples(c)
68
+ c.action do |args, options|
69
+ Commands.show(args, config, options)
70
+ end
69
71
  end
70
72
  end
71
- end
72
73
 
73
- LINK_DESCRIPTION = 'This command can link a repository to a project issue tracking tool.'\
74
- ' The commmands writes the settings in `project_path/.herodot.yml`.'.freeze
75
- def link_command(_)
76
- command :link do |c|
77
- c.syntax = 'herodot link [<repository path>]'
78
- c.summary = 'Link project with issue tracker'
79
- c.description = SHOW_DESCRIPTION
80
- c.example 'Link current repository', 'herodot link'
81
- c.action do |args, _|
82
- Herodot::Commands.link(args[0])
74
+ LINK_DESCRIPTION = 'This command can link a repository to a project issue tracking tool.'\
75
+ ' The commmands writes the settings in `project_path/.herodot.yml`.'.freeze
76
+ def link_command(_)
77
+ command :link do |c|
78
+ c.syntax = 'herodot link [<repository path>]'
79
+ c.summary = 'Link project with issue tracker'
80
+ c.description = SHOW_DESCRIPTION
81
+ c.example 'Link current repository', 'herodot link'
82
+ c.action do |args, _|
83
+ Commands.link(args[0])
84
+ end
83
85
  end
84
86
  end
85
- end
86
87
 
87
- def show_command_examples(c)
88
- c.example 'Shows this weeks worklogs', 'herodot show'
89
- c.example 'Shows last weeks worklogs', 'herodot show last week'
90
- c.example 'Shows worklogs for last monday', 'herodot show monday'
91
- c.example 'Shows worklogs for 12-12-2016', 'herodot show 12-12-2016'
92
- c.example 'Shows last weeks worklogs as json', 'herodot show --format json last week'
93
- c.example 'Shows last weeks worklogs as json (short)', 'herodot show -f json last week'
88
+ def show_command_examples(c)
89
+ c.example 'Shows this weeks worklogs', 'herodot show'
90
+ c.example 'Shows last weeks worklogs', 'herodot show last week'
91
+ c.example 'Shows worklogs for last monday', 'herodot show monday'
92
+ c.example 'Shows worklogs for 12-12-2016', 'herodot show 12-12-2016'
93
+ c.example 'Shows last weeks worklogs as json', 'herodot show --format json last week'
94
+ c.example 'Shows last weeks worklogs as json (short)', 'herodot show -f json last week'
95
+ end
94
96
  end
95
97
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: herodot
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - bitcrowd
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-08-15 00:00:00.000000000 Z
11
+ date: 2018-02-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -67,7 +67,7 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: rubocop-rspec
70
+ name: rubocop-bitcrowd
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - ">="
@@ -81,7 +81,7 @@ dependencies:
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
- name: rubocop-bitcrowd
84
+ name: rubocop-rspec
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - ">="
@@ -95,7 +95,7 @@ dependencies:
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
97
  - !ruby/object:Gem::Dependency
98
- name: rainbow
98
+ name: chronic
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - ">="
@@ -109,7 +109,7 @@ dependencies:
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
111
  - !ruby/object:Gem::Dependency
112
- name: terminal-table
112
+ name: commander
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
115
  - - ">="
@@ -123,7 +123,7 @@ dependencies:
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0'
125
125
  - !ruby/object:Gem::Dependency
126
- name: chronic
126
+ name: rainbow
127
127
  requirement: !ruby/object:Gem::Requirement
128
128
  requirements:
129
129
  - - ">="
@@ -137,7 +137,7 @@ dependencies:
137
137
  - !ruby/object:Gem::Version
138
138
  version: '0'
139
139
  - !ruby/object:Gem::Dependency
140
- name: commander
140
+ name: terminal-table
141
141
  requirement: !ruby/object:Gem::Requirement
142
142
  requirements:
143
143
  - - ">="
@@ -165,9 +165,11 @@ files:
165
165
  - ".rubocop.yml"
166
166
  - ".travis.yml"
167
167
  - Gemfile
168
+ - Gemfile.lock
168
169
  - LICENSE.txt
169
170
  - README.md
170
171
  - Rakefile
172
+ - bin/ci
171
173
  - bin/console
172
174
  - bin/setup
173
175
  - exe/herodot
@@ -200,7 +202,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
200
202
  version: '0'
201
203
  requirements: []
202
204
  rubyforge_project:
203
- rubygems_version: 2.5.2
205
+ rubygems_version: 2.7.5
204
206
  signing_key:
205
207
  specification_version: 4
206
208
  summary: Track your work with your git activity.