herodot 0.2.0 → 0.2.1

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.
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.