frecli 0.3.0 → 0.4.0
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/README.md +40 -6
- data/bin/frecli +20 -86
- data/lib/frecli.rb +2 -0
- data/lib/frecli/cli.rb +69 -0
- data/lib/frecli/queries.rb +18 -14
- data/lib/frecli/version.rb +1 -1
- metadata +5 -19
- data/lib/frecli/table.rb +0 -61
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 16f3a566c8afc7aab09f9214e42ac57d5ab0e247
|
4
|
+
data.tar.gz: 6e49c3f9e9b561da01918ac677e1f5e36dffd7d2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a14af37475312f06d8582ca8997616099ec3f2feb3c340140a3e88ec3ad61f920d46fb6400c0b6bc395bacd032d7583dc9ea955cb0955b7a46bda4f1eb40d91d
|
7
|
+
data.tar.gz: 5bd67c5fc7417e1ea33188eaf04007fa12962eab01529d84e98a850d7a438d880dd4b6d9e026222580c99488fcc0d253352fdbd58a11917d662ce9d302843c6a
|
data/README.md
CHANGED
@@ -7,15 +7,49 @@ Freckle CLI client in Ruby.
|
|
7
7
|
[](https://codeclimate.com/github/shkm/frecli)
|
8
8
|
[](https://codeclimate.com/github/shkm/frecli/coverage)
|
9
9
|
|
10
|
-
## What it
|
10
|
+
## What you can do with it right now
|
11
11
|
|
12
|
-
|
12
|
+
- Basic time tracking (start, pause, log)
|
13
13
|
|
14
|
+
I'd eventually like FreCLI to be a real alternative UI for interacting with Freckle, but have had to scale it down in the short-term due to time constraints. Currently, I'd like to get basic time tracking and logging nailed, as that's the most interesting functionality to me and those around me.
|
14
15
|
|
15
|
-
|
16
|
+
Having said that, I do intend to implement considerably more features. Also note that I'm working on the API client behind this, [freckle-api](https://github.com/shkm/freckle-api), as FreCLI is developed.
|
16
17
|
|
17
|
-
|
18
|
-
|
18
|
+
|
19
|
+
## Commands
|
20
|
+
|
21
|
+
### `frecli time`
|
22
|
+
|
23
|
+
Presents you with a list of projects. Simply enter the number of the project you'd like to time, and away you go.
|
24
|
+
|
25
|
+
### `frecli status`
|
26
|
+
|
27
|
+
Displays the time on the running timer, if there is one.
|
28
|
+
|
29
|
+
### `frecli pause`
|
30
|
+
|
31
|
+
Pauses the running timer.
|
32
|
+
|
33
|
+
### `frecli log [description]`
|
34
|
+
|
35
|
+
Logs the current timer, adding a description if one is given.
|
36
|
+
|
37
|
+
|
38
|
+
## TODO
|
39
|
+
|
40
|
+
- Daily report (time logged, unlogged, total)
|
41
|
+
- More specs
|
42
|
+
- Log timers of other projects
|
43
|
+
- Deal with exceptions / errors
|
44
|
+
- Project selection via settings
|
45
|
+
- Caching
|
46
|
+
- Expanded reports (e.g. weekly, monthly, with natural time parsing)
|
47
|
+
- Various report outputs (e.g. stdout, csv, html)
|
48
|
+
- Administrative features
|
49
|
+
- Project management
|
50
|
+
- Invoices
|
51
|
+
- Tags
|
52
|
+
- Reports for other users
|
19
53
|
|
20
54
|
## Configuration
|
21
55
|
|
@@ -80,5 +114,5 @@ fi
|
|
80
114
|
```
|
81
115
|
|
82
116
|
## Thanks
|
83
|
-
- My work, [Lico Innovations](http://lico.nl/) for using Freckle
|
117
|
+
- My work, [Lico Innovations](http://lico.nl/) for using Freckle and employing the coolest kids in town (I'm the exception).
|
84
118
|
- The awesome Freckle developers — Thomas Cannon in particular — who shared interest in this project and even gave me a free account for testing!
|
data/bin/frecli
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require 'gli'
|
4
|
-
require 'frecli/table'
|
5
4
|
require 'frecli'
|
6
5
|
|
7
6
|
include GLI::App
|
@@ -14,102 +13,37 @@ version Frecli::VERSION
|
|
14
13
|
subcommand_option_handling :normal
|
15
14
|
arguments :strict
|
16
15
|
|
17
|
-
desc '
|
18
|
-
command
|
19
|
-
c.
|
20
|
-
|
21
|
-
all.action do
|
22
|
-
puts Frecli::Table.vertical(
|
23
|
-
Frecli.projects,
|
24
|
-
[ 'ID', 'Name', 'Date created', 'Date updated' ],
|
25
|
-
[ :id, :name, :created_at, :updated_at ]
|
26
|
-
)
|
27
|
-
end
|
16
|
+
desc 'Shows the current timing status'
|
17
|
+
command :status do |c|
|
18
|
+
c.action do
|
19
|
+
Frecli::Cli.status
|
28
20
|
end
|
29
|
-
|
30
|
-
c.desc 'Show a project'
|
31
|
-
c.arg :id, :required
|
32
|
-
c.command [:show, :s] do |show|
|
33
|
-
show.action do |_, _, args|
|
34
|
-
id = args[0]
|
35
|
-
|
36
|
-
puts Frecli::Table.horizontal(
|
37
|
-
Frecli.project(id),
|
38
|
-
{ ID: :id,
|
39
|
-
Name: :name,
|
40
|
-
Minutes: :minutes }
|
41
|
-
)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
c.desc 'Show the currently timed project'
|
46
|
-
c.command [:current, :c] do |current|
|
47
|
-
current.action do
|
48
|
-
|
49
|
-
# TODO: DRY
|
50
|
-
puts Frecli::Table.horizontal(
|
51
|
-
Frecli.project_current,
|
52
|
-
{ ID: :id,
|
53
|
-
Name: :name,
|
54
|
-
Minutes: :minutes }
|
55
|
-
)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
c.default_command :current
|
60
21
|
end
|
61
22
|
|
62
|
-
desc '
|
63
|
-
command
|
64
|
-
c.
|
65
|
-
|
66
|
-
all.action do
|
67
|
-
puts Frecli::Table.vertical(
|
68
|
-
Frecli.timers,
|
69
|
-
[ 'Project ID', 'State', 'Time', 'Date' ],
|
70
|
-
[ -> (timer) { timer.project.id }, :state, :formatted_time, :date ]
|
71
|
-
)
|
72
|
-
end
|
23
|
+
desc 'Times a project'
|
24
|
+
command :time do |c|
|
25
|
+
c.action do
|
26
|
+
Frecli::Cli.time
|
73
27
|
end
|
28
|
+
end
|
74
29
|
|
75
|
-
|
76
|
-
|
77
|
-
c.
|
78
|
-
|
79
|
-
project_id = args[0]
|
80
|
-
|
81
|
-
|
82
|
-
# TODO: DRY
|
83
|
-
puts Frecli::Table.horizontal(
|
84
|
-
Frecli.timer(project_id),
|
85
|
-
{ ID: :id,
|
86
|
-
State: :state,
|
87
|
-
Time: :formatted_time,
|
88
|
-
Description: :description }
|
89
|
-
)
|
90
|
-
end
|
30
|
+
desc 'Pauses the running timer'
|
31
|
+
command :pause do |c|
|
32
|
+
c.action do
|
33
|
+
Frecli::Cli.pause
|
91
34
|
end
|
35
|
+
end
|
92
36
|
|
93
|
-
|
94
|
-
|
95
|
-
|
37
|
+
desc 'Logs the running timer'
|
38
|
+
arg_name 'description', :optional
|
39
|
+
command :log do |c|
|
40
|
+
c.action do |_, _, args|
|
41
|
+
description = args.shift.to_s
|
96
42
|
|
97
|
-
|
98
|
-
puts Frecli::Table.horizontal(
|
99
|
-
Frecli.timer_current,
|
100
|
-
{ ID: :id,
|
101
|
-
State: :state,
|
102
|
-
Time: :formatted_time,
|
103
|
-
Description: :description }
|
104
|
-
)
|
105
|
-
end
|
43
|
+
Frecli::Cli.log(description)
|
106
44
|
end
|
107
|
-
|
108
|
-
c.default_command :current
|
109
45
|
end
|
110
46
|
|
111
|
-
|
112
|
-
|
113
47
|
pre do |global,command,options,args|
|
114
48
|
# Pre logic here
|
115
49
|
# Return true to proceed; false to abort and not call the
|
data/lib/frecli.rb
CHANGED
data/lib/frecli/cli.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
class Frecli
|
2
|
+
module Cli
|
3
|
+
def self.status
|
4
|
+
timer = Frecli.timer_current
|
5
|
+
|
6
|
+
if timer
|
7
|
+
puts "Timer running on #{timer.project.name} (#{timer.formatted_time})."
|
8
|
+
else
|
9
|
+
puts "No timer running."
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.time
|
14
|
+
projects = Frecli.projects.sort { |x, y| x.name <=> y.name }
|
15
|
+
|
16
|
+
puts "Select a project to time:\n\n"
|
17
|
+
|
18
|
+
projects.each_with_index do |project, i|
|
19
|
+
puts "[#{i + 1}] #{project.name}"
|
20
|
+
end
|
21
|
+
|
22
|
+
print "\n\nProject: "
|
23
|
+
|
24
|
+
selection = STDIN.gets.chomp.to_i
|
25
|
+
|
26
|
+
unless (1..projects.count).include? selection
|
27
|
+
puts "Project invalid."
|
28
|
+
|
29
|
+
return
|
30
|
+
end
|
31
|
+
|
32
|
+
project = projects[selection - 1]
|
33
|
+
timer = Frecli.timer_start(project)
|
34
|
+
|
35
|
+
puts "Now timing #{project.name} (#{timer.formatted_time})."
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.pause
|
39
|
+
timer = Frecli.timer_current
|
40
|
+
|
41
|
+
unless timer
|
42
|
+
puts 'No timer running.'
|
43
|
+
return
|
44
|
+
end
|
45
|
+
|
46
|
+
if Frecli.timer_pause(timer)
|
47
|
+
puts "Paused #{timer.project.name} (#{timer.formatted_time})."
|
48
|
+
else
|
49
|
+
puts 'Could not pause timer.'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.log(description = '')
|
54
|
+
timer = Frecli.timer_current
|
55
|
+
|
56
|
+
unless timer
|
57
|
+
puts 'No timer running.'
|
58
|
+
return
|
59
|
+
end
|
60
|
+
|
61
|
+
if Frecli.timer_log(timer, description)
|
62
|
+
puts "Logged #{timer.project.name} (#{timer.formatted_time})."
|
63
|
+
puts %Q("#{description}") unless description.empty?
|
64
|
+
else
|
65
|
+
puts 'Could not log timer.'
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/lib/frecli/queries.rb
CHANGED
@@ -9,15 +9,6 @@ class Frecli
|
|
9
9
|
@api = FreckleApi.new(Settings[:api_key])
|
10
10
|
end
|
11
11
|
|
12
|
-
# The project which is currently being timed.
|
13
|
-
def project_current
|
14
|
-
project_id = timer_current.project.id
|
15
|
-
|
16
|
-
# TODO: use a reload method instead.
|
17
|
-
sleep 0.5
|
18
|
-
project(project_id)
|
19
|
-
end
|
20
|
-
|
21
12
|
def projects
|
22
13
|
api.projects
|
23
14
|
end
|
@@ -26,18 +17,31 @@ class Frecli
|
|
26
17
|
api.project(id)
|
27
18
|
end
|
28
19
|
|
20
|
+
def timers
|
21
|
+
api.timers
|
22
|
+
end
|
23
|
+
|
24
|
+
def timer_log(timer, description = nil)
|
25
|
+
timer.log!(api, description: description)
|
26
|
+
end
|
27
|
+
|
28
|
+
def timer(project_id = nil)
|
29
|
+
api.timer(project_id) || timer_current
|
30
|
+
end
|
31
|
+
|
29
32
|
def timer_current
|
30
33
|
timers.detect { |timer| timer.state == :running }
|
31
34
|
end
|
32
35
|
|
33
|
-
def
|
34
|
-
|
36
|
+
def timer_start(project)
|
37
|
+
FreckleApi::Timer.new(project: project).tap do |timer|
|
38
|
+
timer.start!(api)
|
39
|
+
end
|
35
40
|
end
|
36
41
|
|
37
|
-
def timer
|
38
|
-
|
42
|
+
def timer_pause(timer)
|
43
|
+
timer.pause!(api)
|
39
44
|
end
|
40
|
-
|
41
45
|
end
|
42
46
|
end
|
43
47
|
end
|
data/lib/frecli/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: frecli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jamie Schembri
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-01-
|
11
|
+
date: 2016-01-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -122,34 +122,20 @@ dependencies:
|
|
122
122
|
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '2.13'
|
125
|
-
- !ruby/object:Gem::Dependency
|
126
|
-
name: terminal-table
|
127
|
-
requirement: !ruby/object:Gem::Requirement
|
128
|
-
requirements:
|
129
|
-
- - "~>"
|
130
|
-
- !ruby/object:Gem::Version
|
131
|
-
version: '1.5'
|
132
|
-
type: :runtime
|
133
|
-
prerelease: false
|
134
|
-
version_requirements: !ruby/object:Gem::Requirement
|
135
|
-
requirements:
|
136
|
-
- - "~>"
|
137
|
-
- !ruby/object:Gem::Version
|
138
|
-
version: '1.5'
|
139
125
|
- !ruby/object:Gem::Dependency
|
140
126
|
name: freckle-api
|
141
127
|
requirement: !ruby/object:Gem::Requirement
|
142
128
|
requirements:
|
143
129
|
- - "~>"
|
144
130
|
- !ruby/object:Gem::Version
|
145
|
-
version: 0.1.
|
131
|
+
version: 0.1.6
|
146
132
|
type: :runtime
|
147
133
|
prerelease: false
|
148
134
|
version_requirements: !ruby/object:Gem::Requirement
|
149
135
|
requirements:
|
150
136
|
- - "~>"
|
151
137
|
- !ruby/object:Gem::Version
|
152
|
-
version: 0.1.
|
138
|
+
version: 0.1.6
|
153
139
|
description:
|
154
140
|
email: jamie@schembri.me
|
155
141
|
executables:
|
@@ -162,9 +148,9 @@ files:
|
|
162
148
|
- bin/frecli
|
163
149
|
- frecli.rdoc
|
164
150
|
- lib/frecli.rb
|
151
|
+
- lib/frecli/cli.rb
|
165
152
|
- lib/frecli/queries.rb
|
166
153
|
- lib/frecli/settings.rb
|
167
|
-
- lib/frecli/table.rb
|
168
154
|
- lib/frecli/version.rb
|
169
155
|
- spec/frecli/frecli_spec.rb
|
170
156
|
- spec/frecli/settings_spec.rb
|
data/lib/frecli/table.rb
DELETED
@@ -1,61 +0,0 @@
|
|
1
|
-
require 'terminal-table'
|
2
|
-
|
3
|
-
class Frecli
|
4
|
-
module Table
|
5
|
-
# Table where heading is in the first row,
|
6
|
-
# and values are in the columns
|
7
|
-
#
|
8
|
-
# When records = [ { id: 1, name: 'Hello', body: 'Hello, world!' } ]
|
9
|
-
# headings = 'ID', 'Name', 'Body']
|
10
|
-
# values = [ :id, :name, :body]
|
11
|
-
# +----------------------------+
|
12
|
-
# | ID | Name | Body |
|
13
|
-
# +----------------------------+
|
14
|
-
# | 1 | Hello | Hello, world! |
|
15
|
-
# +----------------------------+
|
16
|
-
def self.vertical(records, headings, values)
|
17
|
-
Terminal::Table.new do |table|
|
18
|
-
|
19
|
-
table.headings = headings
|
20
|
-
table.rows = records.map do |record|
|
21
|
-
values.map { |value| table_item(record, value)}
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
# Table where heading is in the first column,
|
27
|
-
# and value in the second.
|
28
|
-
#
|
29
|
-
# When records = [ { id: 1, name: 'Hello', body: 'Hello, world!' } ]
|
30
|
-
# items = [ { ID: :id, Name: :name, Body: :body } ]
|
31
|
-
# +----------------------+
|
32
|
-
# | ID | 1 |
|
33
|
-
# | Name | Hello |
|
34
|
-
# | Body | Hello, world! |
|
35
|
-
# +----------------------+
|
36
|
-
def self.horizontal(record, items)
|
37
|
-
Terminal::Table.new do |table|
|
38
|
-
table.rows = items.map do |name, value|
|
39
|
-
[name, table_item(record, value)]
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
# When record = { id: 1, name: 'Hello', body: 'Hello, world!' }
|
45
|
-
#
|
46
|
-
# value = :id
|
47
|
-
# => 1
|
48
|
-
#
|
49
|
-
# value = -> (record) { record.name.upcase }
|
50
|
-
# => 'HELLO'
|
51
|
-
#
|
52
|
-
# value = 'foo'
|
53
|
-
# => 'foo'
|
54
|
-
def self.table_item(record, value)
|
55
|
-
return record[value] if value.is_a? Symbol
|
56
|
-
return value[record] if value.respond_to? :call
|
57
|
-
|
58
|
-
value
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|