detom 0.0.1 → 0.0.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.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/README.markdown +35 -1
- data/bin/detom +21 -4
- data/lib/detom.rb +4 -0
- data/lib/detom/commands/client.rb +38 -0
- data/lib/detom/commands/record.rb +41 -21
- data/lib/detom/commands/set.rb +16 -0
- data/lib/detom/local_config.rb +54 -0
- data/lib/detom/version.rb +1 -1
- data/spec/detom/commands/client_spec.rb +69 -0
- data/spec/detom/commands/record_spec.rb +66 -20
- data/spec/detom/local_config_spec.rb +11 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/system/record_spec.rb +32 -0
- data/spec/system/set_spec.rb +91 -0
- metadata +10 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 82f9459a02a471b8cd6fe1168bda3f663cff518e300668c0e1f41f455ac66fa6
|
4
|
+
data.tar.gz: b5edb03876409bb6ba73d0ffd8c9d874a7efacd541487c93ba952b98584ce135
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3bbcfcbde4995ea552c708d3cc161cfcf90189a9afe22162be9f85c6d0acb4f1bab64eec7ad41b1348501fe1e8798da655b2e6ef58ba585bd5260540a4262482
|
7
|
+
data.tar.gz: d32cf43ed5452561b4c93ae743ea6b5f11a3da04c40ff6ee09719981d327d74b45d1774fefcf705231cef8cfd95c8f06f055de7720d198c0213f2cf15eb8c907
|
data/.gitignore
ADDED
data/README.markdown
CHANGED
@@ -1,7 +1,41 @@
|
|
1
|
-
# detom
|
1
|
+
# detom - A personal project
|
2
2
|
|
3
3
|
_Combien de temps?_
|
4
4
|
|
5
5
|
A minimal command line interface for tracking time spent against projects of clients.
|
6
6
|
|
7
7
|
Time is manually logged in minutes or hours, either today or for a specified date.
|
8
|
+
|
9
|
+
This project is a small, personal project that I don't expect anyone else to use seriously.
|
10
|
+
If you do think this is something that you might use, please [let me know on Twitter](https://twitter.com/njpearman).
|
11
|
+
|
12
|
+
Installation is as one would expect:
|
13
|
+
|
14
|
+
```
|
15
|
+
gem install detom
|
16
|
+
```
|
17
|
+
|
18
|
+
## Motivation
|
19
|
+
This is a toy project that allows me to practice a few different things:
|
20
|
+
|
21
|
+
* Think about the design of command line apps.
|
22
|
+
* Practice the topics covered in Build Awesome Command Line Applications in Ruby 2 by David Copeland, published by The Pragmatic Bookshelf but seemingly now out of print (which is a great shame because it's a great book)
|
23
|
+
* Think about what's important when recording time spent on projects and clients.
|
24
|
+
* Practice writing and testing Ruby code.
|
25
|
+
|
26
|
+
## Commands
|
27
|
+
|
28
|
+
For detailed instructions on commands, use `detom --help` and `detom <command> --help`.
|
29
|
+
|
30
|
+
`detom clients`
|
31
|
+
|
32
|
+
Lists all clients that have time recorded against them, and the total amount of time in minutes.
|
33
|
+
|
34
|
+
`detom record`
|
35
|
+
|
36
|
+
Allows an amount of time in minutes to be added to a client / project. Optionally can be set to a different date of the same year.
|
37
|
+
|
38
|
+
## Commands to implement
|
39
|
+
|
40
|
+
* `archive`
|
41
|
+
* `mark`
|
data/bin/detom
CHANGED
@@ -34,15 +34,15 @@ class App
|
|
34
34
|
end
|
35
35
|
|
36
36
|
desc "Record some time spent on a client. <client> and <time> are required. <client> is the name of the client and <time> is an amount of time in minutes, e.g. 39m, or hours, e.g. 3h"
|
37
|
-
arg_name "<
|
37
|
+
arg_name "<time> <client>"
|
38
38
|
command :record do |c|
|
39
39
|
c.flag [:d, :"day-month"]
|
40
40
|
c.action do |global_options, options, args|
|
41
|
-
help_now! "You must provide record with <client> and <time> as arguments" if args.length < 2
|
42
|
-
|
43
41
|
begin
|
44
42
|
store = YamlFileStore.new
|
45
|
-
|
43
|
+
local_config = Detom::LocalConfig.new
|
44
|
+
Commands::Record.new(store, local_config)
|
45
|
+
.call args[0], args[1], options[:d]
|
46
46
|
rescue StandardError => e
|
47
47
|
STDERR.puts e.message
|
48
48
|
STDERR.puts e.backtrace
|
@@ -50,6 +50,23 @@ class App
|
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
+
desc "Sets a default client and project for the current folder, by creating or updating a .detom file. Run in the root of a project directory"
|
54
|
+
arg_name "<client> [project]"
|
55
|
+
command :set do |c|
|
56
|
+
c.action do |global_options, options, args|
|
57
|
+
Commands::Set.new.call args[0], args[1]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
desc "Outputs time tracked against one client in detail."
|
62
|
+
arg_name "<client>"
|
63
|
+
command :client do |c|
|
64
|
+
c.action do |global_options, options, args|
|
65
|
+
store = YamlFileStore.new
|
66
|
+
Commands::Client.new(store).call args[0]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
53
70
|
desc "Archive tracking for a client or a project. Literally copies the client file into ~/.detom/archive/client-<today>.json"
|
54
71
|
arg_name "client"
|
55
72
|
command :archive do |c|
|
data/lib/detom.rb
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
require "detom/version.rb"
|
2
2
|
|
3
3
|
require "detom/yaml_file_store"
|
4
|
+
require "detom/local_config"
|
5
|
+
|
6
|
+
require "detom/commands/client"
|
4
7
|
require "detom/commands/clients"
|
5
8
|
require "detom/commands/record"
|
9
|
+
require "detom/commands/set"
|
6
10
|
|
7
11
|
# Add requires for other files you add to your project here, so
|
8
12
|
# you just need to require this one file in your bin file
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Commands
|
2
|
+
class Client
|
3
|
+
CLIENT_REQUIRED_MESSAGE = "You must provide a client name to detom client."
|
4
|
+
|
5
|
+
def initialize(store)
|
6
|
+
@store = store
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(client_name)
|
10
|
+
raise CLIENT_REQUIRED_MESSAGE if client_name.nil? || client_name.empty?
|
11
|
+
|
12
|
+
client = @store[client_name]
|
13
|
+
|
14
|
+
if client
|
15
|
+
output = client.sort.map do |day, times|
|
16
|
+
"#{day}: #{format times}"
|
17
|
+
end.join "\n"
|
18
|
+
$stdout.puts output
|
19
|
+
else
|
20
|
+
$stdout.puts "No time logged against #{client_name}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
def format(times)
|
26
|
+
total_time = times.inject(0, &:+)
|
27
|
+
|
28
|
+
parts = []
|
29
|
+
hours = (total_time / 60).floor
|
30
|
+
parts << "#{hours}h" if hours > 0
|
31
|
+
|
32
|
+
minutes = total_time % 60
|
33
|
+
parts << "#{minutes}m" if minutes > 0
|
34
|
+
|
35
|
+
parts.join
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -1,35 +1,55 @@
|
|
1
1
|
module Commands
|
2
2
|
class Record
|
3
|
-
|
3
|
+
CLIENT_REQUIRED_MESSAGE = "Cannot log time without a client. Either provide <client> to detom record, or configure a value for client in this directory using detom set."
|
4
|
+
DATE_FORMAT_MESSAGE = "Day/month is an unrecognised format. Use `%d-%m` format"
|
5
|
+
TIME_TO_LOG_FORMAT_MESSAGE = "Time must be provided in minutes or hours, e,g 40m or 3h"
|
6
|
+
|
7
|
+
def initialize(store, local_config)
|
4
8
|
@store = store
|
9
|
+
@local_config = local_config
|
5
10
|
end
|
6
11
|
|
7
|
-
def call(
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
12
|
+
def call(time_to_log, client_name=nil, day_month=nil)
|
13
|
+
client = client_for client_name
|
14
|
+
|
15
|
+
date_stamp = format(day_month)
|
16
|
+
|
17
|
+
client[date_stamp] = [] unless client[date_stamp]
|
18
|
+
|
19
|
+
client[date_stamp] << minutes_from(time_to_log)
|
20
|
+
|
21
|
+
@store.save!
|
22
|
+
$stdout.puts "Logged #{time_to_log} for #{client_name}"
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
def format(day_month)
|
27
|
+
return Time.now.strftime("%Y-%m-%d") if day_month.nil?
|
28
|
+
|
29
|
+
raise DATE_FORMAT_MESSAGE unless day_month =~ /\d\d-\d\d/
|
30
|
+
|
31
|
+
splits = day_month.split "-"
|
32
|
+
[Time.now.year.to_s, splits.last, splits.first].join "-"
|
33
|
+
end
|
34
|
+
|
35
|
+
def client_for(client_name)
|
36
|
+
raise CLIENT_REQUIRED_MESSAGE if client_name.nil? && @local_config.client.nil?
|
37
|
+
|
38
|
+
client_name = client_name || @local_config.client
|
19
39
|
|
20
40
|
@store[client_name] = {} if @store[client_name].nil?
|
21
41
|
|
22
|
-
|
42
|
+
@store[client_name]
|
43
|
+
end
|
23
44
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
45
|
+
def minutes_from(time_to_log)
|
46
|
+
multiplier = case time_to_log
|
47
|
+
when /\d+m/ then 1
|
48
|
+
when /\d+h/ then 60
|
49
|
+
else raise TIME_TO_LOG_FORMAT_MESSAGE
|
28
50
|
end
|
29
51
|
|
30
|
-
|
31
|
-
puts "Logged #{time_to_log} for #{client_name}"
|
52
|
+
multiplier * time_to_log.to_i
|
32
53
|
end
|
33
54
|
end
|
34
55
|
end
|
35
|
-
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Commands
|
2
|
+
class Set
|
3
|
+
def initialize(local_config=Detom::LocalConfig.new)
|
4
|
+
@local_config = local_config
|
5
|
+
end
|
6
|
+
|
7
|
+
def call(client, project=nil)
|
8
|
+
@local_config.load!
|
9
|
+
|
10
|
+
@local_config.client = client
|
11
|
+
@local_config.project = project
|
12
|
+
|
13
|
+
@local_config.save!
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Detom
|
4
|
+
class LocalConfig
|
5
|
+
def load!
|
6
|
+
return if @store
|
7
|
+
|
8
|
+
if Dir.exist? ".detom"
|
9
|
+
raise "Found .detom but it is a directory. Are you running `detom set` in your home directory?\n`detom set` should be run in the root of a project folder"
|
10
|
+
end
|
11
|
+
|
12
|
+
if File.exist? ".detom"
|
13
|
+
@store = YAML.load File.read(".detom")
|
14
|
+
else
|
15
|
+
@store = {}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def load_from!(config)
|
20
|
+
@store ||= {}
|
21
|
+
@store.merge! config
|
22
|
+
end
|
23
|
+
|
24
|
+
def save!
|
25
|
+
@store.keys.each {|key| @store.delete(key) if @store[key].nil? }
|
26
|
+
|
27
|
+
File.open(".detom", "w") {|file| file.write YAML.dump(@store) }
|
28
|
+
puts "New config: #{@store}"
|
29
|
+
end
|
30
|
+
|
31
|
+
def method_missing(name, *args, &block)
|
32
|
+
super unless handle?(name)
|
33
|
+
|
34
|
+
handle(name, *args)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
def handle?(name)
|
39
|
+
%i(client client= project=).include? name
|
40
|
+
end
|
41
|
+
|
42
|
+
def handle(name, *args)
|
43
|
+
self.load!
|
44
|
+
|
45
|
+
setter_match = name.match /(.*)=\B/
|
46
|
+
|
47
|
+
if setter_match && setter_match[1]
|
48
|
+
@store[setter_match[1].to_sym] = args.shift
|
49
|
+
else
|
50
|
+
@store[name]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/lib/detom/version.rb
CHANGED
@@ -0,0 +1,69 @@
|
|
1
|
+
require "detom/commands/client"
|
2
|
+
|
3
|
+
describe Commands::Client do
|
4
|
+
describe "#call" do
|
5
|
+
subject { described_class.new(store).call(client_name) }
|
6
|
+
|
7
|
+
let(:store) { {} }
|
8
|
+
let(:today) { Time.now.strftime("%Y-%m-%d") }
|
9
|
+
|
10
|
+
context "with a nil client" do
|
11
|
+
let(:client_name) { nil }
|
12
|
+
it do
|
13
|
+
expect { subject }.to raise_error Commands::Client::CLIENT_REQUIRED_MESSAGE
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context "with no client given" do
|
18
|
+
let(:client_name) { "" }
|
19
|
+
it do
|
20
|
+
expect { subject }.to raise_error Commands::Client::CLIENT_REQUIRED_MESSAGE
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context "with a client that has no time logged" do
|
25
|
+
let(:client_name) { "foo_client" }
|
26
|
+
it do
|
27
|
+
expect { subject }.to output("No time logged against foo_client\n").to_stdout
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context "with a client that has one entry today" do
|
32
|
+
let(:client_name) { "foo_client" }
|
33
|
+
|
34
|
+
let(:store) { { "foo_client" => { today => [50] } } }
|
35
|
+
it do
|
36
|
+
expected_output = <<OUT
|
37
|
+
#{today}: 50m
|
38
|
+
OUT
|
39
|
+
expect { subject }.to output(expected_output).to_stdout
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context "with a client that has two entries today" do
|
44
|
+
let(:client_name) { "foo_client" }
|
45
|
+
|
46
|
+
let(:store) { { "foo_client" => { today => [50, 5] } } }
|
47
|
+
it do
|
48
|
+
expected_output = <<OUT
|
49
|
+
#{today}: 55m
|
50
|
+
OUT
|
51
|
+
expect { subject }.to output(expected_output).to_stdout
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context "with a client that has two entries today and one in the past" do
|
56
|
+
let(:client_name) { "foo_client" }
|
57
|
+
let(:time_before) { (Time.now - (60*60*24*10)).strftime("%Y-%m-%d") }
|
58
|
+
|
59
|
+
let(:store) { { "foo_client" => { today => [50, 5], time_before => [130] } } }
|
60
|
+
it do
|
61
|
+
expected_output = <<OUT
|
62
|
+
#{time_before}: 2h10m
|
63
|
+
#{today}: 55m
|
64
|
+
OUT
|
65
|
+
expect { subject }.to output(expected_output).to_stdout
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -2,8 +2,16 @@ require "detom/commands/record"
|
|
2
2
|
require "detom/yaml_file_store"
|
3
3
|
|
4
4
|
describe Commands::Record do
|
5
|
-
subject { described_class.new(store) }
|
5
|
+
subject { described_class.new(store, local_config) }
|
6
6
|
let(:store) { YamlFileStore.new(test_filepath) }
|
7
|
+
let(:local_config) { Detom::LocalConfig.new }
|
8
|
+
|
9
|
+
before(:all) do
|
10
|
+
@the_stdout = $stdout
|
11
|
+
$stdout = File.open(File::NULL, 'w')
|
12
|
+
end
|
13
|
+
|
14
|
+
after(:all) { $stdout = @the_stdout }
|
7
15
|
|
8
16
|
describe "#call" do
|
9
17
|
let(:test_filepath) { File.join(File.dirname(__FILE__), "..", "..", "..", "tmp", "record_test") }
|
@@ -13,22 +21,60 @@ describe Commands::Record do
|
|
13
21
|
after { FileUtils.rm_rf test_filepath if Dir.exists? test_filepath }
|
14
22
|
|
15
23
|
context "when recording time today" do
|
24
|
+
context "for project with a configured client" do
|
25
|
+
before { local_config.load_from! client: "faa" }
|
26
|
+
|
27
|
+
it do
|
28
|
+
subject.call("12m")
|
29
|
+
expect(store["faa"]).to eq({ today => [12] })
|
30
|
+
end
|
31
|
+
|
32
|
+
context "and explicitly providing a client" do
|
33
|
+
it do
|
34
|
+
subject.call("50m", "bloo")
|
35
|
+
expect(store["bloo"]).to eq({ today => [50] })
|
36
|
+
expect(store["faa"]).to be_nil
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
16
41
|
context "once for one client" do
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
42
|
+
context "in minutes" do
|
43
|
+
it "stores the time spent on the client" do
|
44
|
+
subject.call("6m", "foo_client")
|
45
|
+
expect(store["foo_client"]).to eq({ today => [6] })
|
46
|
+
expect(File.read(File.join(test_filepath, "foo_client"))).to eq <<-JSON
|
21
47
|
---
|
22
48
|
'#{today}':
|
23
49
|
- 6
|
24
50
|
JSON
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context "in hours" do
|
55
|
+
it "stores the time spent on the client in minutes" do
|
56
|
+
subject.call("6h", "foo_client")
|
57
|
+
expect(store["foo_client"]).to eq({ today => [360] })
|
58
|
+
expect(File.read(File.join(test_filepath, "foo_client"))).to eq <<-JSON
|
59
|
+
---
|
60
|
+
'#{today}':
|
61
|
+
- 360
|
62
|
+
JSON
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context "in an unrecognised format" do
|
67
|
+
it "raises an error" do
|
68
|
+
expect { subject.call("700s", "foo_client") }.to raise_error Commands::Record::TIME_TO_LOG_FORMAT_MESSAGE
|
69
|
+
|
70
|
+
end
|
25
71
|
end
|
26
72
|
end
|
27
73
|
|
28
74
|
context "twice for one client" do
|
29
75
|
it "stores the time spent on the client" do
|
30
|
-
subject.call("
|
31
|
-
subject.call("
|
76
|
+
subject.call("6m", "foo_client")
|
77
|
+
subject.call("39m", "foo_client")
|
32
78
|
expect(store["foo_client"]).to eq({ today => [6, 39] })
|
33
79
|
expect(File.read(File.join(test_filepath, "foo_client"))).to eq <<-JSON
|
34
80
|
---
|
@@ -41,9 +87,9 @@ JSON
|
|
41
87
|
|
42
88
|
context "three times for one client" do
|
43
89
|
it "stores the time spent on the client" do
|
44
|
-
subject.call("
|
45
|
-
subject.call("
|
46
|
-
expect { subject.call("
|
90
|
+
subject.call("6m", "foo_client")
|
91
|
+
subject.call("39m", "foo_client")
|
92
|
+
expect { subject.call("92m", "foo_client") }.to output("Logged 92m for foo_client\n").to_stdout
|
47
93
|
expect(store["foo_client"]).to eq({ today => [6, 39, 92] })
|
48
94
|
expect(File.read(File.join(test_filepath, "foo_client"))).to eq <<-JSON
|
49
95
|
---
|
@@ -57,9 +103,9 @@ JSON
|
|
57
103
|
|
58
104
|
context "once each for two clients" do
|
59
105
|
it "stores the time spent on the clients" do
|
60
|
-
record_command = described_class.new(store)
|
61
|
-
record_command.call("
|
62
|
-
record_command.call("
|
106
|
+
record_command = described_class.new(store, local_config)
|
107
|
+
record_command.call("6m", "foo_client")
|
108
|
+
record_command.call("39m", "raa_client")
|
63
109
|
expect(store["foo_client"]).to eq({ today => [6] })
|
64
110
|
expect(store["raa_client"]).to eq({ today => [39] })
|
65
111
|
expect(File.read(File.join(test_filepath, "foo_client"))).to eq <<-JSON
|
@@ -77,9 +123,9 @@ JSON
|
|
77
123
|
|
78
124
|
context "once each for three clients" do
|
79
125
|
it "stores the time spent on the clients" do
|
80
|
-
subject.call("
|
81
|
-
subject.call("
|
82
|
-
subject.call("
|
126
|
+
subject.call("6m", "foo_client")
|
127
|
+
subject.call("39m", "raa_client")
|
128
|
+
subject.call("72m", "gii_client")
|
83
129
|
expect(store["foo_client"]).to eq({ today => [6] })
|
84
130
|
expect(File.read(File.join(test_filepath, "foo_client"))).to eq <<-JSON
|
85
131
|
---
|
@@ -108,7 +154,7 @@ JSON
|
|
108
154
|
|
109
155
|
context "once for one client" do
|
110
156
|
it "stores the time spent on the client" do
|
111
|
-
subject.call("
|
157
|
+
subject.call("6m", "foo_client", five_days_ago.strftime("%d-%m"))
|
112
158
|
expect(store["foo_client"]).to eq({ expected_formatted_date => [6] })
|
113
159
|
expect(File.read(File.join(test_filepath, "foo_client"))).to eq <<-JSON
|
114
160
|
---
|
@@ -127,8 +173,8 @@ JSON
|
|
127
173
|
|
128
174
|
context "once for one client" do
|
129
175
|
it "stores the time spent on the client" do
|
130
|
-
subject.call("
|
131
|
-
subject.call("
|
176
|
+
subject.call("6m", "foo_client", five_days_ago.strftime("%d-%m"))
|
177
|
+
subject.call("45m", "foo_client", ten_days_ago.strftime("%d-%m"))
|
132
178
|
expect(store["foo_client"]).to eq({ expected_formatted_date_1 => [6], expected_formatted_date_2 => [45] })
|
133
179
|
expect(File.read(File.join(test_filepath, "foo_client"))).to eq <<-JSON
|
134
180
|
---
|
@@ -150,7 +196,7 @@ JSON
|
|
150
196
|
context "once for one client" do
|
151
197
|
it "stores the time spent on the client" do
|
152
198
|
File.open(File.join(test_filepath, "foo_client"), "w") {|f| f.write YAML.dump(expected_formatted_date_1 => [6]) }
|
153
|
-
subject.call("
|
199
|
+
subject.call("45m", "foo_client", ten_days_ago.strftime("%d-%m"))
|
154
200
|
expect(store["foo_client"]).to eq({ expected_formatted_date_1 => [6], expected_formatted_date_2 => [45] })
|
155
201
|
expect(File.read(File.join(test_filepath, "foo_client"))).to eq <<-JSON
|
156
202
|
---
|
data/spec/spec_helper.rb
CHANGED
@@ -44,6 +44,12 @@ RSpec.configure do |config|
|
|
44
44
|
# triggering implicit auto-inclusion in groups with matching metadata.
|
45
45
|
config.shared_context_metadata_behavior = :apply_to_host_groups
|
46
46
|
|
47
|
+
config.order = :random
|
48
|
+
|
49
|
+
# Runs and outputs all assertions in a test regardless of failures.
|
50
|
+
config.define_derived_metadata do |meta|
|
51
|
+
meta[:aggregate_failures] = true
|
52
|
+
end
|
47
53
|
# The settings below are suggested to provide a good initial experience
|
48
54
|
# with RSpec, but feel free to customize to your heart's content.
|
49
55
|
=begin
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require "open3"
|
2
|
+
|
3
|
+
describe "detom record" do
|
4
|
+
subject { Open3.capture3(command) }
|
5
|
+
|
6
|
+
let(:stdout) { subject[0] }
|
7
|
+
let(:stderr) { subject[1] }
|
8
|
+
|
9
|
+
before do
|
10
|
+
@original_dir = Dir.pwd
|
11
|
+
Dir.chdir File.join(File.dirname(__FILE__), "..", "..", "tmp")
|
12
|
+
%x(rm -rf .detom)
|
13
|
+
end
|
14
|
+
|
15
|
+
after do
|
16
|
+
Dir.chdir @original_dir
|
17
|
+
end
|
18
|
+
|
19
|
+
let(:command) { "bundle exec ../bin/detom record 90m" }
|
20
|
+
let(:expected_stdout) do
|
21
|
+
<<OUT
|
22
|
+
Logged 90m for foofoo
|
23
|
+
OUT
|
24
|
+
end
|
25
|
+
|
26
|
+
it do
|
27
|
+
%x(bundle exec ../bin/detom set foofoo)
|
28
|
+
|
29
|
+
expect(stdout).to eq expected_stdout
|
30
|
+
expect(stderr).to eq ""
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require "open3"
|
2
|
+
|
3
|
+
describe "detom set" do
|
4
|
+
subject { Open3.capture3(command) }
|
5
|
+
|
6
|
+
let(:stdout) { subject[0] }
|
7
|
+
let(:stderr) { subject[1] }
|
8
|
+
|
9
|
+
before do
|
10
|
+
@original_dir = Dir.pwd
|
11
|
+
Dir.chdir File.join(File.dirname(__FILE__), "..", "..", "tmp")
|
12
|
+
%x(rm -rf .detom)
|
13
|
+
end
|
14
|
+
|
15
|
+
after do
|
16
|
+
Dir.chdir @original_dir
|
17
|
+
end
|
18
|
+
|
19
|
+
context "when no args are provided" do
|
20
|
+
let(:command) { "bundle exec ../bin/detom set" }
|
21
|
+
let(:expected_stderr) do
|
22
|
+
<<ERR
|
23
|
+
error: Found .detom but it is a directory. Are you running `detom set` in your home directory?
|
24
|
+
`detom set` should be run in the root of a project folder
|
25
|
+
ERR
|
26
|
+
end
|
27
|
+
|
28
|
+
before { %x(mkdir .detom) }
|
29
|
+
|
30
|
+
it do
|
31
|
+
expect(stderr).to eq expected_stderr
|
32
|
+
expect(Dir.exist? ".detom").to be_truthy
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "when a client is provided" do
|
37
|
+
let(:command) { "bundle exec ../bin/detom set foo" }
|
38
|
+
let(:expected_stdout) do
|
39
|
+
<<OUT
|
40
|
+
New config: {:client=>"foo"}
|
41
|
+
OUT
|
42
|
+
end
|
43
|
+
|
44
|
+
it do
|
45
|
+
expect(stdout).to eq expected_stdout
|
46
|
+
expect(File.exist? ".detom").to be_truthy
|
47
|
+
end
|
48
|
+
|
49
|
+
context "and config already exists" do
|
50
|
+
before { %x(bundle exec ../bin/detom set faa) }
|
51
|
+
let(:expected_stdout) do
|
52
|
+
<<OUT
|
53
|
+
New config: {:client=>"foo"}
|
54
|
+
OUT
|
55
|
+
end
|
56
|
+
|
57
|
+
it do
|
58
|
+
expect(stdout).to eq expected_stdout
|
59
|
+
expect(File.exist? ".detom").to be_truthy
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context "when a client and project are provided" do
|
65
|
+
let(:command) { "bundle exec ../bin/detom set foo project_tree" }
|
66
|
+
let(:expected_stdout) do
|
67
|
+
<<OUT
|
68
|
+
New config: {:client=>"foo", :project=>"project_tree"}
|
69
|
+
OUT
|
70
|
+
end
|
71
|
+
|
72
|
+
it do
|
73
|
+
expect(stdout).to eq expected_stdout
|
74
|
+
expect(File.exist? ".detom").to be_truthy
|
75
|
+
end
|
76
|
+
|
77
|
+
context "and config already exists" do
|
78
|
+
before { %x(bundle exec ../bin/detom set faa) }
|
79
|
+
let(:expected_stdout) do
|
80
|
+
<<OUT
|
81
|
+
New config: {:client=>"foo", :project=>"project_tree"}
|
82
|
+
OUT
|
83
|
+
end
|
84
|
+
|
85
|
+
it do
|
86
|
+
expect(stdout).to eq expected_stdout
|
87
|
+
expect(File.exist? ".detom").to be_truthy
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: detom
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- NJ Pearman
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-08-
|
11
|
+
date: 2020-08-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -75,6 +75,7 @@ extra_rdoc_files:
|
|
75
75
|
- README.rdoc
|
76
76
|
- detom.rdoc
|
77
77
|
files:
|
78
|
+
- ".gitignore"
|
78
79
|
- ".rspec"
|
79
80
|
- Gemfile
|
80
81
|
- Gemfile.lock
|
@@ -85,13 +86,20 @@ files:
|
|
85
86
|
- detom.gemspec
|
86
87
|
- detom.rdoc
|
87
88
|
- lib/detom.rb
|
89
|
+
- lib/detom/commands/client.rb
|
88
90
|
- lib/detom/commands/clients.rb
|
89
91
|
- lib/detom/commands/record.rb
|
92
|
+
- lib/detom/commands/set.rb
|
93
|
+
- lib/detom/local_config.rb
|
90
94
|
- lib/detom/version.rb
|
91
95
|
- lib/detom/yaml_file_store.rb
|
96
|
+
- spec/detom/commands/client_spec.rb
|
92
97
|
- spec/detom/commands/clients_spec.rb
|
93
98
|
- spec/detom/commands/record_spec.rb
|
99
|
+
- spec/detom/local_config_spec.rb
|
94
100
|
- spec/spec_helper.rb
|
101
|
+
- spec/system/record_spec.rb
|
102
|
+
- spec/system/set_spec.rb
|
95
103
|
- tmp/record_test.json
|
96
104
|
homepage: https://github.com/njpearman/detom
|
97
105
|
licenses: []
|