waddup 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/.travis.yml +2 -0
- data/CHANGELOG.md +5 -1
- data/Guardfile +3 -2
- data/README.md +68 -0
- data/Rakefile +1 -1
- data/bin/sup +6 -0
- data/bin/waddup +6 -0
- data/lib/waddup.rb +9 -0
- data/lib/waddup/cli.rb +76 -0
- data/lib/waddup/event.rb +14 -0
- data/lib/waddup/extension.rb +4 -0
- data/lib/waddup/extensions/applescript.rb +31 -0
- data/lib/waddup/extensions/system.rb +31 -0
- data/lib/waddup/registry.rb +18 -0
- data/lib/waddup/source.rb +40 -0
- data/lib/waddup/sources/apple_calendar.rb +59 -0
- data/lib/waddup/sources/apple_mail.rb +65 -0
- data/lib/waddup/sources/git.rb +98 -0
- data/lib/waddup/version.rb +1 -1
- data/spec/fixtures/sources/apple_calendar.results +1 -0
- data/spec/fixtures/sources/apple_mail.results +1 -0
- data/spec/fixtures/sources/git.log +2 -0
- data/spec/spec_helper.rb +11 -7
- data/spec/support/fixture.rb +4 -0
- data/spec/support/shell_mock.rb +47 -0
- data/spec/waddup/event_spec.rb +4 -0
- data/spec/waddup/extensions/system_spec.rb +42 -0
- data/spec/waddup/registry_spec.rb +24 -0
- data/spec/waddup/source_spec.rb +38 -0
- data/spec/waddup/sources/apple_calendar_spec.rb +54 -0
- data/spec/waddup/sources/apple_mail_spec.rb +55 -0
- data/spec/waddup/sources/git_spec.rb +100 -0
- data/waddup.gemspec +10 -4
- metadata +76 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9412cd78378d88a3daf9f219baa2cc1a35fd81d7
|
4
|
+
data.tar.gz: c749fa77c45f8a397b86c90c32d56e02e0f8b637
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a8b44de7bd90cfeedfc321be128d67fe72ba714756dea4cb359cddf80e0e4b6eddf636e11c9880649816cfabcac17b19c26d3e57092402890ba473247e8ef108
|
7
|
+
data.tar.gz: fdc7a2ca6d6f514cad0f679b2ec4b1a3024391164642bfdf2649779e890a053492c0af2aed343de3d7cb9d23ffe5b0011453572da59f6997bbfcd686fd598afa
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
data/Guardfile
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
guard :rspec do
|
2
2
|
watch(%r{^spec/.+_spec\.rb$})
|
3
|
-
watch(%r{^lib/(.+)\.rb$})
|
4
|
-
watch(
|
3
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
4
|
+
watch(%r{^spec/support/(.+)\.rb$}) { 'spec' }
|
5
|
+
watch('spec/spec_helper.rb') { 'spec' }
|
5
6
|
end
|
data/README.md
CHANGED
@@ -13,3 +13,71 @@ Perfect for those who have lost track of what they have worked on.
|
|
13
13
|
**Supported Ruby versions: 1.8.7 or higher**
|
14
14
|
|
15
15
|
Licensed under the **MIT** license, see LICENSE for more information.
|
16
|
+
|
17
|
+

|
18
|
+
|
19
|
+
|
20
|
+
## Installation
|
21
|
+
|
22
|
+
Waddup is available from RubyGems and can be installed through the command-line.
|
23
|
+
|
24
|
+
Fire up your favourite terminal and run:
|
25
|
+
|
26
|
+
gem install waddup
|
27
|
+
|
28
|
+
Installing on **OSX** and using the **default system Ruby**? Run:
|
29
|
+
|
30
|
+
sudo gem install waddup
|
31
|
+
|
32
|
+
|
33
|
+
## Usage
|
34
|
+
|
35
|
+
Once installed, use the command `waddup` or its alias `sup` as follows:
|
36
|
+
|
37
|
+
waddup with git and mail since last week until yesterday 23:00
|
38
|
+
|
39
|
+
Waddup is fairly liberal in what it accepts. The keywords described below may be mixed or ommitted as desired.
|
40
|
+
|
41
|
+
|
42
|
+
### Sources
|
43
|
+
|
44
|
+
At present, Waddup ships with three sources:
|
45
|
+
|
46
|
+
* Git `git`
|
47
|
+
* Apple Mail `mail`
|
48
|
+
* Apple Calendar `ical`
|
49
|
+
|
50
|
+
To specify one or multiple sources, use the `with`-keyword forming a regular sentence with the listed aliases:
|
51
|
+
|
52
|
+
waddup with git
|
53
|
+
waddup with git, mail and ical
|
54
|
+
|
55
|
+
When the `with`-keyword is ommitted it will default to all usable sources.
|
56
|
+
|
57
|
+
|
58
|
+
### Start date
|
59
|
+
|
60
|
+
To specify a start date, use either `from` or `since` as a keyword:
|
61
|
+
|
62
|
+
waddup from october 29, 2013 9:00 AM
|
63
|
+
waddup since last friday
|
64
|
+
|
65
|
+
Defaults to right now if a start date is ommitted. This default is likely to change in the future.
|
66
|
+
|
67
|
+
Dates/times are liberally parsed using [Chronic](https://github.com/mojombo/chronic). A grasp of crazy inputs one can use:
|
68
|
+
|
69
|
+
* yesterday
|
70
|
+
* last night
|
71
|
+
* last winter
|
72
|
+
* 3rd wednesday in november
|
73
|
+
* may seventh '97 at three in the morning
|
74
|
+
|
75
|
+
|
76
|
+
### End date
|
77
|
+
|
78
|
+
To specify an end date, use one of `to`, `until`, `uptil`, `upto` or `through`:
|
79
|
+
|
80
|
+
waddup upto one week ago
|
81
|
+
waddup through yesterday
|
82
|
+
|
83
|
+
Defaults to right now if an end date is ommitted.
|
data/Rakefile
CHANGED
data/bin/sup
ADDED
data/bin/waddup
ADDED
data/lib/waddup.rb
CHANGED
@@ -1 +1,10 @@
|
|
1
|
+
require 'waddup/event'
|
2
|
+
require 'waddup/extension'
|
3
|
+
require 'waddup/extensions/system'
|
4
|
+
require 'waddup/extensions/applescript'
|
5
|
+
require 'waddup/registry'
|
6
|
+
require 'waddup/source'
|
7
|
+
require 'waddup/sources/apple_calendar'
|
8
|
+
require 'waddup/sources/apple_mail'
|
9
|
+
require 'waddup/sources/git'
|
1
10
|
require 'waddup/version'
|
data/lib/waddup/cli.rb
CHANGED
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'chronic'
|
2
|
+
|
3
|
+
module Waddup
|
4
|
+
|
5
|
+
class CLI
|
6
|
+
|
7
|
+
attr_accessor :sources, :from, :to
|
8
|
+
|
9
|
+
KEYWORDS = {
|
10
|
+
:sources => %w[with],
|
11
|
+
:from => %w[from since],
|
12
|
+
:to => %w[to until uptil upto through]
|
13
|
+
}
|
14
|
+
|
15
|
+
KEYWORD_BOUNDARY = "(?:\\s#{KEYWORDS.values.flatten.join('|\\s')}|\\Z)"
|
16
|
+
|
17
|
+
def parse!
|
18
|
+
parse_keyword :sources do |match|
|
19
|
+
sources = match[1]
|
20
|
+
@sources = Waddup::Source.usable.select do |source|
|
21
|
+
sources.include? source::ALIAS
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
parse_keyword :from do |match|
|
26
|
+
@from = Chronic.parse match[1]
|
27
|
+
end
|
28
|
+
|
29
|
+
parse_keyword :to do |match|
|
30
|
+
@to = Chronic.parse match[1]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def parse_keyword(keyword, &block)
|
35
|
+
@arguments.match /(?:#{KEYWORDS[keyword].join('|')})\s(.+?)#{KEYWORD_BOUNDARY}/i, &block
|
36
|
+
end
|
37
|
+
|
38
|
+
# Parses given arguments, aggregates events and renders timesheet
|
39
|
+
def run!(arguments)
|
40
|
+
@arguments = arguments.join ' '
|
41
|
+
|
42
|
+
parse!
|
43
|
+
|
44
|
+
# Sanity checking
|
45
|
+
@sources ||= Waddup::Source.usable
|
46
|
+
@from ||= Time.now
|
47
|
+
@to ||= Time.now
|
48
|
+
|
49
|
+
# Aggregate events from all sources
|
50
|
+
events = sources.map do |source|
|
51
|
+
source.new.events from, to
|
52
|
+
end
|
53
|
+
|
54
|
+
# Sort events
|
55
|
+
events.flatten!
|
56
|
+
events.sort_by! &:at
|
57
|
+
|
58
|
+
# Group daily
|
59
|
+
days = events.group_by { |event| event.at.to_date }
|
60
|
+
|
61
|
+
# Generate timesheet
|
62
|
+
days.each_pair do |day, events|
|
63
|
+
puts
|
64
|
+
puts day.strftime('%A, %-d %B %Y')
|
65
|
+
puts
|
66
|
+
events.each do |event|
|
67
|
+
puts " #{event.at.strftime('%H:%M')} #{event.source.class::ICON} #{event.label}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
puts
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
data/lib/waddup/event.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
module Waddup
|
2
|
+
|
3
|
+
module Extension::AppleScript
|
4
|
+
include Waddup::Extension::System
|
5
|
+
|
6
|
+
# Runs given AppleScript
|
7
|
+
#
|
8
|
+
# Options:
|
9
|
+
#
|
10
|
+
# :args (arguments to provide to the script)
|
11
|
+
# :as_ruby (whether to eval results as Ruby)
|
12
|
+
#
|
13
|
+
def applescript(script, options = {})
|
14
|
+
args = options.delete(:args) || []
|
15
|
+
arguments = args.map { |arg| " '#{arg}'" }.join
|
16
|
+
results = run("osascript -s s -e '#{script}'#{arguments}")
|
17
|
+
|
18
|
+
# TODO: This is very scary, find alternatives!
|
19
|
+
eval "[#{results[1...-1]}]" if options.delete(:as_ruby)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Whether AppleScript is available
|
23
|
+
def applescript?
|
24
|
+
osx? && begin
|
25
|
+
run('osalang', :quietly => true).include? 'AppleScript'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'rbconfig'
|
2
|
+
|
3
|
+
module Waddup
|
4
|
+
|
5
|
+
module Extension::System
|
6
|
+
|
7
|
+
# Runs given system command
|
8
|
+
#
|
9
|
+
# Options:
|
10
|
+
#
|
11
|
+
# :quietly (supresses output)
|
12
|
+
#
|
13
|
+
def run(command, options = {})
|
14
|
+
command << ' 2>&1' if options[:quietly]
|
15
|
+
`#{command}`.chomp
|
16
|
+
end
|
17
|
+
|
18
|
+
# Retrieves operating system
|
19
|
+
# See: https://github.com/celluloid/celluloid/blob/master/lib/celluloid/cpu_counter.rb
|
20
|
+
def os
|
21
|
+
@os ||= RbConfig::CONFIG['host_os'][/^[A-Za-z]+/]
|
22
|
+
end
|
23
|
+
|
24
|
+
# Whether running OSX
|
25
|
+
def osx?
|
26
|
+
os == 'darwin'
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Waddup
|
2
|
+
|
3
|
+
# Automatically registers subclasses in its registry
|
4
|
+
module Registry
|
5
|
+
|
6
|
+
# Retrieves a static registry
|
7
|
+
def registry
|
8
|
+
@registry ||= []
|
9
|
+
end
|
10
|
+
|
11
|
+
# Registers given target in static registry
|
12
|
+
def inherited(target)
|
13
|
+
registry << target
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Waddup
|
2
|
+
|
3
|
+
# Denotes a possible source of events
|
4
|
+
# Note: Any subclasses are automatically registered
|
5
|
+
class Source
|
6
|
+
extend Waddup::Registry
|
7
|
+
|
8
|
+
# Aggregates events from this source
|
9
|
+
#
|
10
|
+
# Arguments:
|
11
|
+
#
|
12
|
+
# :from (datetime)
|
13
|
+
# :to (datetime)
|
14
|
+
#
|
15
|
+
def events(from, to)
|
16
|
+
raise NotImplementedError
|
17
|
+
end
|
18
|
+
|
19
|
+
# Delegate for convenience
|
20
|
+
def usable?
|
21
|
+
self.class.usable?
|
22
|
+
end
|
23
|
+
|
24
|
+
class << self
|
25
|
+
|
26
|
+
# Whether this source is usable
|
27
|
+
def usable?
|
28
|
+
raise NotImplementedError
|
29
|
+
end
|
30
|
+
|
31
|
+
# Only usable sources
|
32
|
+
def usable
|
33
|
+
registry.select &:usable?
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Waddup
|
2
|
+
|
3
|
+
class Source::AppleCalendar < Waddup::Source
|
4
|
+
include Waddup::Extension::AppleScript
|
5
|
+
extend Waddup::Extension::AppleScript
|
6
|
+
|
7
|
+
ALIAS = 'ical'
|
8
|
+
ICON = "\xF0\x9F\x93\x85 "
|
9
|
+
|
10
|
+
EVENT_SCRIPT = %Q{
|
11
|
+
on run argv
|
12
|
+
set window_from to date (item 1 of argv)
|
13
|
+
set window_to to date (item 2 of argv)
|
14
|
+
|
15
|
+
tell application "Calendar"
|
16
|
+
set results to {}
|
17
|
+
|
18
|
+
set cdars to (events whose start date <= window_to and end date > window_from) in every calendar
|
19
|
+
repeat with cdar in cdars
|
20
|
+
repeat with evt in cdar
|
21
|
+
set end of results to {summary:summary of evt, start_date:start date of evt as string, end_date:end date of evt as string}
|
22
|
+
end repeat
|
23
|
+
end repeat
|
24
|
+
|
25
|
+
results
|
26
|
+
end tell
|
27
|
+
end run
|
28
|
+
}
|
29
|
+
|
30
|
+
# Aggregates calendar events
|
31
|
+
#
|
32
|
+
# Arguments:
|
33
|
+
#
|
34
|
+
# :from (datetime)
|
35
|
+
# :to (datetime)
|
36
|
+
#
|
37
|
+
def events(from, to)
|
38
|
+
results = applescript EVENT_SCRIPT,
|
39
|
+
:as_ruby => true,
|
40
|
+
:args => [from.strftime('%d/%m/%Y %H:%M'), to.strftime('%d/%m/%Y %H:%M')]
|
41
|
+
|
42
|
+
results.map do |result|
|
43
|
+
Waddup::Event.new do |e|
|
44
|
+
e.label = result[:summary]
|
45
|
+
e.at = DateTime.parse(result[:start_date])
|
46
|
+
e.until = DateTime.parse(result[:end_date])
|
47
|
+
e.source = self
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Requires AppleScript to be available
|
53
|
+
def self.usable?
|
54
|
+
applescript?
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Waddup
|
2
|
+
|
3
|
+
class Source::AppleMail < Waddup::Source
|
4
|
+
include Waddup::Extension::AppleScript
|
5
|
+
extend Waddup::Extension::AppleScript
|
6
|
+
|
7
|
+
ALIAS = 'mail'
|
8
|
+
ICON = "\xE2\x9C\x89\xEF\xB8\x8F "
|
9
|
+
|
10
|
+
# Until OSX Mavericks handles Gmail's sent-mailbox correctly, resort to
|
11
|
+
# iterating through all mailboxes and identify sent messages by sender
|
12
|
+
#
|
13
|
+
# See: http://tidbits.com/article/14219
|
14
|
+
SENT_MAIL_SCRIPT = %Q{
|
15
|
+
on run argv
|
16
|
+
set window_from to date (item 1 of argv)
|
17
|
+
set window_to to date (item 2 of argv)
|
18
|
+
|
19
|
+
tell application "Mail"
|
20
|
+
set results to {}
|
21
|
+
|
22
|
+
repeat with acct in every account
|
23
|
+
set username to user name of acct
|
24
|
+
set mboxes to (messages whose sender contains username and date sent >= window_from and date sent <= window_to) in every mailbox in acct
|
25
|
+
repeat with mbox in mboxes
|
26
|
+
repeat with msg in mbox
|
27
|
+
set the end of results to {subject:subject of msg, datetime:date sent of msg as string}
|
28
|
+
end
|
29
|
+
end repeat
|
30
|
+
end repeat
|
31
|
+
|
32
|
+
results
|
33
|
+
end tell
|
34
|
+
end run
|
35
|
+
}
|
36
|
+
|
37
|
+
# Aggregates sent mail events
|
38
|
+
#
|
39
|
+
# Arguments:
|
40
|
+
#
|
41
|
+
# :from (datetime)
|
42
|
+
# :to (datetime)
|
43
|
+
#
|
44
|
+
def events(from, to)
|
45
|
+
results = applescript SENT_MAIL_SCRIPT,
|
46
|
+
:as_ruby => true,
|
47
|
+
:args => [from.strftime('%d/%m/%Y %H:%M'), to.strftime('%d/%m/%Y %H:%M')]
|
48
|
+
|
49
|
+
results.map do |result|
|
50
|
+
Waddup::Event.new do |e|
|
51
|
+
e.label = result[:subject]
|
52
|
+
e.at = DateTime.parse(result[:datetime])
|
53
|
+
e.source = self
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Requires AppleScript to be available
|
59
|
+
def self.usable?
|
60
|
+
applescript?
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
module Waddup
|
4
|
+
|
5
|
+
class Source::Git < Waddup::Source
|
6
|
+
include Waddup::Extension::System
|
7
|
+
extend Waddup::Extension::System
|
8
|
+
|
9
|
+
ALIAS = 'git'
|
10
|
+
ICON = "\xE2\x9C\x8F\xEF\xB8\x8F "
|
11
|
+
|
12
|
+
attr_accessor :base_path
|
13
|
+
|
14
|
+
# Retrieves author and repositories on initialization
|
15
|
+
#
|
16
|
+
# Arguments
|
17
|
+
#
|
18
|
+
# :base_path (defaults to current working directory)
|
19
|
+
#
|
20
|
+
def initialize(base_path = Dir.pwd)
|
21
|
+
@base_path = base_path
|
22
|
+
end
|
23
|
+
|
24
|
+
# Obtains author from git-config
|
25
|
+
def author
|
26
|
+
@author ||= run 'git config --get user.name'
|
27
|
+
end
|
28
|
+
|
29
|
+
# Collects repositories under base-path
|
30
|
+
def repos
|
31
|
+
@repos ||= Dir["#{base_path}/**/.git"]
|
32
|
+
end
|
33
|
+
|
34
|
+
# Aggregates events from all repositories
|
35
|
+
#
|
36
|
+
# Arguments:
|
37
|
+
#
|
38
|
+
# :from (datetime)
|
39
|
+
# :to (datetime)
|
40
|
+
#
|
41
|
+
def events(from, to)
|
42
|
+
events = repos.map do |repo|
|
43
|
+
events_for_repo from, to, repo
|
44
|
+
end
|
45
|
+
events.flatten!
|
46
|
+
end
|
47
|
+
|
48
|
+
# See: https://www.kernel.org/pub/software/scm/git/docs/git-log.html#_pretty_formats
|
49
|
+
GIT_FORMAT = '%h %ai %s'
|
50
|
+
|
51
|
+
# Pattern to extract from the above format
|
52
|
+
EXTRACT_PATTERN = /([0-9a-f]{7}) ([-0-9: ]+\+\d{4}) (.+)/i
|
53
|
+
|
54
|
+
# Aggregates events for given repository
|
55
|
+
#
|
56
|
+
# Arguments:
|
57
|
+
#
|
58
|
+
# :from (datetime)
|
59
|
+
# :to (datetime)
|
60
|
+
# :repo (path)
|
61
|
+
#
|
62
|
+
def events_for_repo(from, to, repo)
|
63
|
+
repo_label = self.class.label_for_repo repo
|
64
|
+
|
65
|
+
results = run "git --git-dir='#{repo}' log --author='#{author}' --since='#{from.iso8601}' --until='#{to.iso8601}' --format='format:#{GIT_FORMAT}'"
|
66
|
+
results.scan(EXTRACT_PATTERN).map do |hash, datetime, subject|
|
67
|
+
Waddup::Event.new do |e|
|
68
|
+
e.label = "[#{repo_label}] #{subject}"
|
69
|
+
e.at = DateTime.parse(datetime)
|
70
|
+
e.source = self
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Generates label for given repo path
|
76
|
+
def self.label_for_repo(repo)
|
77
|
+
path = Pathname.new(repo)
|
78
|
+
parent = path.parent
|
79
|
+
|
80
|
+
case parent.basename.to_s
|
81
|
+
when 'code', 'design'
|
82
|
+
parent = parent.parent
|
83
|
+
end
|
84
|
+
|
85
|
+
return nil if parent.root?
|
86
|
+
|
87
|
+
parent.basename.to_s
|
88
|
+
end
|
89
|
+
|
90
|
+
# Requires Git to be installed successfully
|
91
|
+
def self.usable?
|
92
|
+
run 'git --version', :quietly => true
|
93
|
+
$?.success?
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
data/lib/waddup/version.rb
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
{{summary:"Waddup meeting", start_date:"Wednesday 16 October 2013 15:43:17", end_date:"Wednesday 16 October 2013 18:00:00"}}
|
@@ -0,0 +1 @@
|
|
1
|
+
{{subject:"E-mail regarding Waddup", datetime:"Wednesday 16 October 2013 15:43:17"}, {subject:"Another e-mail", datetime:"Wednesday 16 October 2013 17:00:12"}}
|
data/spec/spec_helper.rb
CHANGED
@@ -1,10 +1,14 @@
|
|
1
|
-
|
2
|
-
require '
|
1
|
+
if RUBY_VERSION >= '1.9.3'
|
2
|
+
require 'simplecov'
|
3
|
+
require 'coveralls'
|
3
4
|
|
4
|
-
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
5
|
-
|
6
|
-
|
7
|
-
]
|
8
|
-
SimpleCov.start
|
5
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
6
|
+
SimpleCov::Formatter::HTMLFormatter,
|
7
|
+
Coveralls::SimpleCov::Formatter
|
8
|
+
]
|
9
|
+
SimpleCov.start
|
10
|
+
end
|
9
11
|
|
10
12
|
require 'waddup'
|
13
|
+
|
14
|
+
Dir['./spec/support/**/*.rb'].sort.each { |file| require file }
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# Raised when shell operations are invoked
|
2
|
+
class ShellNotAllowedError < StandardError
|
3
|
+
|
4
|
+
def initialize(command)
|
5
|
+
msg = "Shell operation is not allowed: #{command}\n\n"
|
6
|
+
msg << "You can stub this request with the following snippet:\n\n"
|
7
|
+
msg << "stub_shell(\"#{command}\", :output => '', :exitstatus => 0)\n "
|
8
|
+
super msg
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
# Prevent invoking shell operations
|
14
|
+
class Object
|
15
|
+
|
16
|
+
def `(command)
|
17
|
+
raise ShellNotAllowedError, command
|
18
|
+
end
|
19
|
+
|
20
|
+
def system(command)
|
21
|
+
raise ShellNotAllowedError, command
|
22
|
+
end
|
23
|
+
|
24
|
+
# Stubs shell operations matching given command
|
25
|
+
#
|
26
|
+
# Options:
|
27
|
+
# :output (defaults to '')
|
28
|
+
# :exitstatus (defaults to 0)
|
29
|
+
#
|
30
|
+
def stub_shell(command, options = {})
|
31
|
+
output = options.delete(:output) || ''
|
32
|
+
exitstatus = options.delete(:exitstatus) || 0
|
33
|
+
|
34
|
+
block = lambda {
|
35
|
+
if exitstatus.nonzero?
|
36
|
+
Kernel.send :`, "test"
|
37
|
+
else
|
38
|
+
Kernel.send :`, "test success"
|
39
|
+
end
|
40
|
+
output
|
41
|
+
}
|
42
|
+
|
43
|
+
stub(:`).with(command).and_return &block
|
44
|
+
stub(:system).with(command).and_return &block
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Waddup::Extension::System do
|
4
|
+
|
5
|
+
let(:dummy) {
|
6
|
+
Class.new do
|
7
|
+
extend Waddup::Extension::System
|
8
|
+
end
|
9
|
+
}
|
10
|
+
|
11
|
+
describe '#os' do
|
12
|
+
it 'identifies the operating system' do
|
13
|
+
stub_const('RbConfig::CONFIG', 'host_os' => 'linux')
|
14
|
+
expect(dummy.os).to eq 'linux'
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'is cached' do
|
18
|
+
stub_const('RbConfig::CONFIG', 'host_os' => 'foo')
|
19
|
+
expect(dummy.os).to eq 'foo'
|
20
|
+
|
21
|
+
stub_const('RbConfig::CONFIG', 'host_os' => 'foobar')
|
22
|
+
expect(dummy.os).to eq 'foo'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#osx?' do
|
27
|
+
context 'when on OSX' do
|
28
|
+
it 'returns true' do
|
29
|
+
stub_const('RbConfig::CONFIG', 'host_os' => 'darwin')
|
30
|
+
expect(dummy).to be_osx
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'when on other operating systems' do
|
35
|
+
it 'returns false' do
|
36
|
+
stub_const('RbConfig::CONFIG', 'host_os' => 'linux')
|
37
|
+
expect(dummy).not_to be_osx
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Waddup::Registry do
|
4
|
+
|
5
|
+
let(:dummy) {
|
6
|
+
Class.new do
|
7
|
+
extend Waddup::Registry
|
8
|
+
end
|
9
|
+
}
|
10
|
+
let(:target) { Class.new(dummy) }
|
11
|
+
|
12
|
+
describe '#registry' do
|
13
|
+
it 'retrieves the registry' do
|
14
|
+
expect(dummy.registry).to be_an Array
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '#register' do
|
19
|
+
it 'registers subclasses in registry' do
|
20
|
+
expect(dummy.registry).to include target
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Waddup::Source do
|
4
|
+
|
5
|
+
describe '#events' do
|
6
|
+
it 'has to be implemented by subclass' do
|
7
|
+
expect do
|
8
|
+
subject.events nil, nil
|
9
|
+
end.to raise_error NotImplementedError
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '#usable?' do
|
14
|
+
it 'delegates for convenience' do
|
15
|
+
expect(described_class).to receive :usable?
|
16
|
+
subject.usable?
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '::usable?' do
|
21
|
+
it 'has to be implemented by subclass' do
|
22
|
+
expect do
|
23
|
+
described_class.usable?
|
24
|
+
end.to raise_error NotImplementedError
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '::usable' do
|
29
|
+
it 'retrieves usable sources' do
|
30
|
+
described_class.registry.each do |source|
|
31
|
+
source.stub(:usable?).and_return true
|
32
|
+
end
|
33
|
+
|
34
|
+
expect(described_class.usable).to be_an Array
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Waddup::Source::AppleCalendar do
|
4
|
+
let(:from) { DateTime.new 2013, 10, 16 }
|
5
|
+
let(:to) { DateTime.new 2013, 10, 17 }
|
6
|
+
|
7
|
+
describe '#events' do
|
8
|
+
before do
|
9
|
+
subject.stub_shell "osascript -s s -e '#{described_class::EVENT_SCRIPT}' '16/10/2013 00:00' '17/10/2013 00:00'",
|
10
|
+
:output => fixture('sources/apple_calendar.results')
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'aggregates events' do
|
14
|
+
events = subject.events(from, to)
|
15
|
+
|
16
|
+
expect(events.first.label).to eq 'Waddup meeting'
|
17
|
+
|
18
|
+
expect(events.length).to eq 1
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '::usable?' do
|
23
|
+
context 'when on OSX' do
|
24
|
+
before do
|
25
|
+
described_class.stub(:osx?).and_return true
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'when AppleScript is available' do
|
29
|
+
before do
|
30
|
+
described_class.stub_shell 'osalang 2>&1', :output => 'AppleScript'
|
31
|
+
end
|
32
|
+
|
33
|
+
it { should be_usable }
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'when AppleScript is unavailable' do
|
37
|
+
before do
|
38
|
+
described_class.stub_shell 'osalang 2>&1', :exitstatus => 1
|
39
|
+
end
|
40
|
+
|
41
|
+
it { should_not be_usable }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'when on other platforms' do
|
46
|
+
before do
|
47
|
+
described_class.stub(:osx?).and_return false
|
48
|
+
end
|
49
|
+
|
50
|
+
it { should_not be_usable }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Waddup::Source::AppleMail do
|
4
|
+
let(:from) { DateTime.new 2013, 10, 16 }
|
5
|
+
let(:to) { DateTime.new 2013, 10, 17 }
|
6
|
+
|
7
|
+
describe '#events' do
|
8
|
+
before do
|
9
|
+
subject.stub_shell "osascript -s s -e '#{described_class::SENT_MAIL_SCRIPT}' '16/10/2013 00:00' '17/10/2013 00:00'",
|
10
|
+
:output => fixture('sources/apple_mail.results')
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'aggregates events' do
|
14
|
+
events = subject.events(from, to)
|
15
|
+
|
16
|
+
expect(events.first.label).to eq 'E-mail regarding Waddup'
|
17
|
+
expect(events.last.label).to eq 'Another e-mail'
|
18
|
+
|
19
|
+
expect(events.length).to eq 2
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '::usable?' do
|
24
|
+
context 'when on OSX' do
|
25
|
+
before do
|
26
|
+
described_class.stub(:osx?).and_return true
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'when AppleScript is available' do
|
30
|
+
before do
|
31
|
+
described_class.stub_shell 'osalang 2>&1', :output => 'AppleScript'
|
32
|
+
end
|
33
|
+
|
34
|
+
it { should be_usable }
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'when AppleScript is unavailable' do
|
38
|
+
before do
|
39
|
+
described_class.stub_shell 'osalang 2>&1', :exitstatus => 1
|
40
|
+
end
|
41
|
+
|
42
|
+
it { should_not be_usable }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'when on other platforms' do
|
47
|
+
before do
|
48
|
+
described_class.stub(:osx?).and_return false
|
49
|
+
end
|
50
|
+
|
51
|
+
it { should_not be_usable }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Waddup::Source::Git do
|
6
|
+
let(:from) { DateTime.new 2013, 10, 16 }
|
7
|
+
let(:to) { DateTime.new 2013, 10, 17 }
|
8
|
+
|
9
|
+
describe '#author' do
|
10
|
+
it 'obtains author from git-config' do
|
11
|
+
subject.stub(:run).and_return('John Doe')
|
12
|
+
expect(subject.author).to eq 'John Doe'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#repos' do
|
17
|
+
context 'when initialized as-is' do
|
18
|
+
it 'collects repos under current working directory' do
|
19
|
+
expect(Dir).to receive(:[]).with("#{Dir.pwd}/**/.git").and_call_original
|
20
|
+
expect(subject.repos.length).to eq 1
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'when initialized with base-path' do
|
25
|
+
before do
|
26
|
+
subject.base_path = '/projects'
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'collects repos under given base-path' do
|
30
|
+
expect(Dir).to receive(:[]).with('/projects/**/.git')
|
31
|
+
subject.repos
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#events' do
|
37
|
+
it 'delegates event aggregation to #events_for_repo' do
|
38
|
+
subject.stub(:repos).and_return(['/projects/1/.git', '/projects/2/.git'])
|
39
|
+
expect(subject).to receive(:events_for_repo).with(from, to, '/projects/1/.git')
|
40
|
+
expect(subject).to receive(:events_for_repo).with(from, to, '/projects/2/.git')
|
41
|
+
subject.events from, to
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe '#events_for_repo' do
|
46
|
+
before do
|
47
|
+
subject.stub_shell "git --git-dir='/waddup/.git' log --author='John Doe' --since='2013-10-16T00:00:00+00:00' --until='2013-10-17T00:00:00+00:00' --format='format:%h %ai %s'",
|
48
|
+
:output => fixture('sources/git.log')
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'aggregates events for given repo' do
|
52
|
+
subject.stub(:author).and_return('John Doe')
|
53
|
+
events = subject.events_for_repo(from, to, '/waddup/.git')
|
54
|
+
|
55
|
+
expect(events.first.label).to eq '[waddup] Spec all the things™'
|
56
|
+
expect(events.last.label).to eq '[waddup] Morph Git#author and Git#repos into lazy getters'
|
57
|
+
|
58
|
+
expect(events.length).to eq 2
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '::label_for_repo' do
|
63
|
+
context 'with no parent folder' do
|
64
|
+
it 'returns nil' do
|
65
|
+
expect(described_class.label_for_repo '/.git').to be_nil
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'with parent folder' do
|
70
|
+
context 'when meaningless' do
|
71
|
+
it 'labels with grandparent' do
|
72
|
+
expect(described_class.label_for_repo '/project/code/.git').to eq 'project'
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'labels with parent' do
|
77
|
+
expect(described_class.label_for_repo '/project/.git').to eq 'project'
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe '::usable?' do
|
83
|
+
context 'when git is available' do
|
84
|
+
before do
|
85
|
+
described_class.stub_shell 'git --version 2>&1', :exitstatus => 0
|
86
|
+
end
|
87
|
+
|
88
|
+
it { should be_usable }
|
89
|
+
end
|
90
|
+
|
91
|
+
context 'when git is unavailable' do
|
92
|
+
before do
|
93
|
+
described_class.stub_shell 'git --version 2>&1', :exitstatus => 1
|
94
|
+
end
|
95
|
+
|
96
|
+
it { should_not be_usable }
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
data/waddup.gemspec
CHANGED
@@ -20,11 +20,17 @@ Gem::Specification.new do |s|
|
|
20
20
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
21
21
|
s.require_paths = ['lib']
|
22
22
|
|
23
|
-
s.add_dependency '
|
23
|
+
s.add_dependency 'chronic'
|
24
24
|
|
25
|
-
s.add_development_dependency 'coveralls'
|
26
|
-
s.add_development_dependency 'guard-rspec'
|
27
25
|
s.add_development_dependency 'rake'
|
28
26
|
s.add_development_dependency 'rspec'
|
29
|
-
|
27
|
+
|
28
|
+
if RUBY_VERSION >= '1.9.3'
|
29
|
+
s.add_development_dependency 'guard'
|
30
|
+
s.add_development_dependency 'guard-rspec'
|
31
|
+
s.add_development_dependency 'listen'
|
32
|
+
|
33
|
+
s.add_development_dependency 'coveralls'
|
34
|
+
s.add_development_dependency 'simplecov'
|
35
|
+
end
|
30
36
|
end
|
metadata
CHANGED
@@ -1,31 +1,59 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: waddup
|
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
|
- Tim Kurvers
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-10-
|
11
|
+
date: 2013-10-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: chronic
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - '>='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0
|
19
|
+
version: '0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - '>='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0
|
26
|
+
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: guard
|
29
57
|
requirement: !ruby/object:Gem::Requirement
|
30
58
|
requirements:
|
31
59
|
- - '>='
|
@@ -53,7 +81,7 @@ dependencies:
|
|
53
81
|
- !ruby/object:Gem::Version
|
54
82
|
version: '0'
|
55
83
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
84
|
+
name: listen
|
57
85
|
requirement: !ruby/object:Gem::Requirement
|
58
86
|
requirements:
|
59
87
|
- - '>='
|
@@ -67,7 +95,7 @@ dependencies:
|
|
67
95
|
- !ruby/object:Gem::Version
|
68
96
|
version: '0'
|
69
97
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
98
|
+
name: coveralls
|
71
99
|
requirement: !ruby/object:Gem::Requirement
|
72
100
|
requirements:
|
73
101
|
- - '>='
|
@@ -99,7 +127,9 @@ description: Waddup retraces your activities from arbitrary sources - such as ve
|
|
99
127
|
chronological overview
|
100
128
|
email:
|
101
129
|
- tim@moonsphere.net
|
102
|
-
executables:
|
130
|
+
executables:
|
131
|
+
- sup
|
132
|
+
- waddup
|
103
133
|
extensions: []
|
104
134
|
extra_rdoc_files: []
|
105
135
|
files:
|
@@ -112,10 +142,33 @@ files:
|
|
112
142
|
- LICENSE.md
|
113
143
|
- README.md
|
114
144
|
- Rakefile
|
145
|
+
- bin/sup
|
146
|
+
- bin/waddup
|
115
147
|
- lib/waddup.rb
|
116
148
|
- lib/waddup/cli.rb
|
149
|
+
- lib/waddup/event.rb
|
150
|
+
- lib/waddup/extension.rb
|
151
|
+
- lib/waddup/extensions/applescript.rb
|
152
|
+
- lib/waddup/extensions/system.rb
|
153
|
+
- lib/waddup/registry.rb
|
154
|
+
- lib/waddup/source.rb
|
155
|
+
- lib/waddup/sources/apple_calendar.rb
|
156
|
+
- lib/waddup/sources/apple_mail.rb
|
157
|
+
- lib/waddup/sources/git.rb
|
117
158
|
- lib/waddup/version.rb
|
159
|
+
- spec/fixtures/sources/apple_calendar.results
|
160
|
+
- spec/fixtures/sources/apple_mail.results
|
161
|
+
- spec/fixtures/sources/git.log
|
118
162
|
- spec/spec_helper.rb
|
163
|
+
- spec/support/fixture.rb
|
164
|
+
- spec/support/shell_mock.rb
|
165
|
+
- spec/waddup/event_spec.rb
|
166
|
+
- spec/waddup/extensions/system_spec.rb
|
167
|
+
- spec/waddup/registry_spec.rb
|
168
|
+
- spec/waddup/source_spec.rb
|
169
|
+
- spec/waddup/sources/apple_calendar_spec.rb
|
170
|
+
- spec/waddup/sources/apple_mail_spec.rb
|
171
|
+
- spec/waddup/sources/git_spec.rb
|
119
172
|
- waddup.gemspec
|
120
173
|
homepage: https://github.com/timkurvers/waddup
|
121
174
|
licenses: []
|
@@ -142,5 +195,17 @@ specification_version: 4
|
|
142
195
|
summary: Waddup retraces your activities from arbitrary sources such as version control,
|
143
196
|
issue tracking software and mail clients
|
144
197
|
test_files:
|
198
|
+
- spec/fixtures/sources/apple_calendar.results
|
199
|
+
- spec/fixtures/sources/apple_mail.results
|
200
|
+
- spec/fixtures/sources/git.log
|
145
201
|
- spec/spec_helper.rb
|
202
|
+
- spec/support/fixture.rb
|
203
|
+
- spec/support/shell_mock.rb
|
204
|
+
- spec/waddup/event_spec.rb
|
205
|
+
- spec/waddup/extensions/system_spec.rb
|
206
|
+
- spec/waddup/registry_spec.rb
|
207
|
+
- spec/waddup/source_spec.rb
|
208
|
+
- spec/waddup/sources/apple_calendar_spec.rb
|
209
|
+
- spec/waddup/sources/apple_mail_spec.rb
|
210
|
+
- spec/waddup/sources/git_spec.rb
|
146
211
|
has_rdoc:
|