waddup 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
![Waddup](http://office.moonsphere.net/waddup.png?v1)
|
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:
|