pendaxes 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/.rvmrc +1 -0
- data/Gemfile +4 -0
- data/README.md +148 -0
- data/Rakefile +5 -0
- data/bin/pendaxes +11 -0
- data/fixtures/repo.tar.gz +0 -0
- data/lib/pendaxes/command_line.rb +56 -0
- data/lib/pendaxes/config.rb +10 -0
- data/lib/pendaxes/defaults.rb +12 -0
- data/lib/pendaxes/detector.rb +23 -0
- data/lib/pendaxes/detectors/rspec.rb +78 -0
- data/lib/pendaxes/finder.rb +34 -0
- data/lib/pendaxes/notificator.rb +32 -0
- data/lib/pendaxes/notificators/mail.rb +72 -0
- data/lib/pendaxes/notificators/terminal.rb +19 -0
- data/lib/pendaxes/pending_manager.rb +30 -0
- data/lib/pendaxes/reporter.rb +27 -0
- data/lib/pendaxes/reporters/haml.rb +49 -0
- data/lib/pendaxes/reporters/template.haml +45 -0
- data/lib/pendaxes/reporters/text.rb +13 -0
- data/lib/pendaxes/version.rb +3 -0
- data/lib/pendaxes/workspace.rb +39 -0
- data/lib/pendaxes.rb +10 -0
- data/pendaxes.gemspec +32 -0
- data/spec/defaults_spec.rb +41 -0
- data/spec/detector_spec.rb +12 -0
- data/spec/detectors/rspec_spec.rb +75 -0
- data/spec/finder_spec.rb +62 -0
- data/spec/notificator_spec.rb +41 -0
- data/spec/notificators/mail_spec.rb +184 -0
- data/spec/notificators/terminal_spec.rb +67 -0
- data/spec/pendaxes_spec.rb +7 -0
- data/spec/pending_manager_spec.rb +97 -0
- data/spec/reporter_spec.rb +23 -0
- data/spec/reporters/text_spec.rb +34 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/workspace_spec.rb +115 -0
- metadata +203 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use 1.9.3
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
# Pendaxes
|
2
|
+
|
3
|
+
Throw axes to pending makers!
|
4
|
+
|
5
|
+
Leaving a pending long time is really bad, shouldn't be happened. They'll make a trouble.
|
6
|
+
So, this gem sends notification to committer that added pending after a while from the commit.
|
7
|
+
|
8
|
+
Avoid the trouble due to pending examples :D
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
(1.9 required)
|
13
|
+
|
14
|
+
$ gem install pendaxes
|
15
|
+
|
16
|
+
## Usage
|
17
|
+
|
18
|
+
$ pendaxes <config_file>
|
19
|
+
|
20
|
+
(writing in cron is recommended)
|
21
|
+
|
22
|
+
### Configuration
|
23
|
+
|
24
|
+
#### Minimal
|
25
|
+
|
26
|
+
Clone `https://github.com/foo/bar.git` to `/path/to/be/cloned/repository`, then detect pendings using rspec detector (default).
|
27
|
+
|
28
|
+
Finally send notification to committers via email (from `no-reply@example.com`).
|
29
|
+
|
30
|
+
workspace:
|
31
|
+
path: /path/to/be/cloned/repository # where to clone?
|
32
|
+
repository: https://github.com/foo/bar.git # where clone from?
|
33
|
+
report:
|
34
|
+
use: haml
|
35
|
+
to: "report.html"
|
36
|
+
notifications:
|
37
|
+
- use: mail
|
38
|
+
from: no-reply@example.com
|
39
|
+
reporter:
|
40
|
+
use: haml
|
41
|
+
|
42
|
+
|
43
|
+
#### Full
|
44
|
+
|
45
|
+
detection:
|
46
|
+
use: rspec # use rspec detector for pending detection. (default)
|
47
|
+
# pattern: '*_spec.rb' # this will be passed after `git grep ... --`. Default is "*_spec.rb".
|
48
|
+
# allowed_for: 604800 # (second) = 1 week. Pendings will be marked "not allowed" if it elapsed more than this value
|
49
|
+
|
50
|
+
workspace:
|
51
|
+
path: /path/to/be/cloned/repository # where to clone?
|
52
|
+
repository: "https://github.com/user/repo.git" # where clone from?
|
53
|
+
|
54
|
+
report: # report configuration to save
|
55
|
+
use: haml # what reporter to use (haml and text is bundled in the gem)
|
56
|
+
to: "report.html" # where to save?
|
57
|
+
include_allowed: true # include "allowed" pendings in report. (default: true)
|
58
|
+
# VVV haml reporter specific configuration VVV
|
59
|
+
commit_url: "https://github.com/user/repo/commit/%commit%" # Used for link to commit. %commit% will be replaced to sha1. If not specified, will not be a link.
|
60
|
+
file_url: "https://github.com/user/repo/blob/HEAD/%file%#L%line%" # Used for link to file. %file% and %line% will be replaced to filepath and line no. If not specified, will not be a link.
|
61
|
+
|
62
|
+
notifications: # notifications. multiple values are accepted.
|
63
|
+
- use: terminal # use terminal notificator.
|
64
|
+
- use: mail # use mail notificator.
|
65
|
+
reporter: # reporter setting for this (mail) notification
|
66
|
+
use: haml
|
67
|
+
commit_url: "https://github.com/user/repo/commit/%commit%"
|
68
|
+
file_url: "https://github.com/user/repo/blob/HEAD/%file%#L%line%"
|
69
|
+
|
70
|
+
# VVV mail notificator specific configuration VVV
|
71
|
+
from: no-reply@example.com
|
72
|
+
to: foo@example.com # (optional) mail will be sent once to this mail address.
|
73
|
+
# without this, mail will be sent separated for each committer.
|
74
|
+
# (mails will include pendings added by its recipient only.)
|
75
|
+
|
76
|
+
delivery_method: sendmail # specify delivery_method. https://github.com/mikel/mail for more detail.
|
77
|
+
delivery_options: # (optional) used as option for delivery_method.
|
78
|
+
:location: /usr/sbin/sendmail
|
79
|
+
|
80
|
+
whitelist: # (optional) if whitelist set, mail won't be sent if not matched.
|
81
|
+
- foo@bar # complete match.
|
82
|
+
- /example\.com$/ # used as regexp.
|
83
|
+
blacklist: # (optional) mail won't be sent if matched. preferred than whitelist.
|
84
|
+
- black@example.com
|
85
|
+
- /^black/
|
86
|
+
|
87
|
+
alias: # (optional) Aliasing emails. if mail will be sent to <value> if git commit author is <key>.
|
88
|
+
"foo@gmail.com": "foo@company.com"
|
89
|
+
|
90
|
+
|
91
|
+
* Reporter: generates text or html by given pendings
|
92
|
+
* Notificator: get text or html by reporter, and notify it (via mail, to terminal, etc...)
|
93
|
+
|
94
|
+
## Axes?
|
95
|
+
|
96
|
+
斧... Axe in Japanese. Recently, Japanese engineer says a review comment as axe (斧).
|
97
|
+
|
98
|
+
Throwing axe means "comment my opnion."
|
99
|
+
|
100
|
+
This script throws axe to committer, about his/her uncontrolled pending tests.
|
101
|
+
|
102
|
+
## Requirements
|
103
|
+
|
104
|
+
* Ruby 1.9+ (1.9.3 supported)
|
105
|
+
* git
|
106
|
+
|
107
|
+
we're using `git grep` and `git blame` to detect pendings, and supported test suite is currently `rspec` only.
|
108
|
+
this gem supports git managed repository, and using rspec.
|
109
|
+
|
110
|
+
Patches for other environment are welcomed. :-)
|
111
|
+
|
112
|
+
## Writing notificator and reporter
|
113
|
+
|
114
|
+
TBD
|
115
|
+
|
116
|
+
## License
|
117
|
+
|
118
|
+
MIT License:
|
119
|
+
|
120
|
+
(c) 2012 Shota Fukumori (sora_h)
|
121
|
+
|
122
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
123
|
+
of this software and associated documentation files (the "Software"), to deal
|
124
|
+
in the Software without restriction, including without limitation the rights
|
125
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
126
|
+
copies of the Software, and to permit persons to whom the Software is
|
127
|
+
furnished to do so, subject to the following conditions:
|
128
|
+
|
129
|
+
The above copyright notice and this permission notice shall be included in
|
130
|
+
all copies or substantial portions of the Software.
|
131
|
+
|
132
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
133
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
134
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
135
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
136
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
137
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
138
|
+
THE SOFTWARE.
|
139
|
+
|
140
|
+
## Contributing
|
141
|
+
|
142
|
+
1. Fork it
|
143
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
144
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
145
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
146
|
+
5. Create new Pull Request
|
147
|
+
|
148
|
+
|
data/Rakefile
ADDED
data/bin/pendaxes
ADDED
Binary file
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require_relative "./config"
|
2
|
+
require_relative "./workspace"
|
3
|
+
require_relative "./detector"
|
4
|
+
require_relative "./reporter"
|
5
|
+
require_relative "./notificator"
|
6
|
+
|
7
|
+
module Pendaxes
|
8
|
+
class CommandLine
|
9
|
+
def initialize(*args)
|
10
|
+
@args = args
|
11
|
+
@config = Config.new(YAML.load_file(args.first))
|
12
|
+
end
|
13
|
+
|
14
|
+
def run
|
15
|
+
puts "=> Update repository"
|
16
|
+
update
|
17
|
+
|
18
|
+
puts "=> Detect pendings"
|
19
|
+
detect
|
20
|
+
|
21
|
+
puts "=> Writing report"
|
22
|
+
report
|
23
|
+
|
24
|
+
puts "=> Send notifications"
|
25
|
+
notify
|
26
|
+
|
27
|
+
0
|
28
|
+
end
|
29
|
+
|
30
|
+
def update
|
31
|
+
@workspace = Workspace.new(@config.workspace)
|
32
|
+
@workspace.update
|
33
|
+
end
|
34
|
+
|
35
|
+
def detect
|
36
|
+
@detector = Detector.find(@config.detection.use.to_sym).new(@workspace, {out: $stdout}.merge(@config.detection))
|
37
|
+
@pendings = @detector.detect
|
38
|
+
end
|
39
|
+
|
40
|
+
def report
|
41
|
+
@reporter = Reporter.find(@config.report.use.to_sym).new(@config.report)
|
42
|
+
@reporter.add @pendings
|
43
|
+
report = @reporter.report
|
44
|
+
open(@config.report.to, 'w') {|io| io.puts report }
|
45
|
+
end
|
46
|
+
|
47
|
+
def notify
|
48
|
+
@config.notifications.map{|x| Hashr.new(x) }.each do |notification|
|
49
|
+
puts " * #{notification.use}"
|
50
|
+
notificator = Notificator.find(notification.use.to_sym).new({out: $stdout}.merge(notification))
|
51
|
+
notificator.add @pendings
|
52
|
+
notificator.notify
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Pendaxes
|
2
|
+
module Defaults
|
3
|
+
def defaults(h=nil)
|
4
|
+
default_defaults = self.superclass.respond_to?(:defaults) ? self.superclass.defaults : Hashr.new
|
5
|
+
@defaults ||= default_defaults
|
6
|
+
if h
|
7
|
+
@defaults = default_defaults.merge(h)
|
8
|
+
end
|
9
|
+
@defaults
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative 'pending_manager'
|
2
|
+
require_relative 'defaults'
|
3
|
+
require_relative 'finder'
|
4
|
+
require 'hashr'
|
5
|
+
|
6
|
+
module Pendaxes
|
7
|
+
class Detector
|
8
|
+
extend Defaults
|
9
|
+
extend Finder
|
10
|
+
find_in 'pendaxes/detectors'
|
11
|
+
|
12
|
+
defaults allowed_for: 604800
|
13
|
+
|
14
|
+
def initialize(workspace, config={})
|
15
|
+
@config = Hashr.new(self.class.defaults.merge(config))
|
16
|
+
@workspace = workspace
|
17
|
+
@pendings = []
|
18
|
+
end
|
19
|
+
|
20
|
+
def detect
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require_relative '../detector'
|
3
|
+
require 'time'
|
4
|
+
require 'ripper'
|
5
|
+
require 'ripper/lexer'
|
6
|
+
|
7
|
+
module Pendaxes
|
8
|
+
class Detector
|
9
|
+
class RSpec < Detector
|
10
|
+
PENDERS = %w(xit xexample xspecify pending).freeze
|
11
|
+
GREP_CMD = ( %w(grep --name-only -e) \
|
12
|
+
+ PENDERS.join(' --or -e ').split(/ /) \
|
13
|
+
+ ['--']
|
14
|
+
).freeze
|
15
|
+
PARENTS = %w(context describe it example specify xexample xspecify xit).freeze
|
16
|
+
|
17
|
+
defaults pattern: '*_spec.rb'
|
18
|
+
|
19
|
+
def detect
|
20
|
+
@workspace.dive do
|
21
|
+
pattern = @config.pattern.is_a?(Array) ? @config.pattern : [@config.pattern]
|
22
|
+
grep = @workspace.git(*GREP_CMD, *pattern).force_encoding("UTF-8")
|
23
|
+
return [] unless grep
|
24
|
+
files = grep.split(/\r?\n/).map(&:chomp)
|
25
|
+
|
26
|
+
files.inject([]) do |pendings, file|
|
27
|
+
@config.out.puts "* #{file}" if @config.out
|
28
|
+
file_content = File.read(file).force_encoding(@config.encoding || "UTF-8")
|
29
|
+
lines = file_content.split(/\r?\n/)
|
30
|
+
tokens = Ripper.lex(file_content, file)
|
31
|
+
_prev = nil
|
32
|
+
|
33
|
+
tokens.each_with_index do |token, i|
|
34
|
+
prev = _prev
|
35
|
+
_prev = token[1]
|
36
|
+
next if prev == :on_symbeg
|
37
|
+
next unless token[1] == :on_ident && PENDERS.include?(token[2])
|
38
|
+
pending = {}
|
39
|
+
|
40
|
+
line = token[0][0]
|
41
|
+
|
42
|
+
parent = (i-1).downto(0).inject {|_, j|
|
43
|
+
break lines[tokens[j][0][0]-1] if tokens[j][1] == :on_ident && (j.zero? || tokens[j-1][1] != :on_symbeg) && PARENTS.include?(tokens[j][2])
|
44
|
+
nil
|
45
|
+
}
|
46
|
+
parent.gsub!(/^[ \t]+/, '') if parent
|
47
|
+
|
48
|
+
pending[:example] = {
|
49
|
+
file: file, line: line,
|
50
|
+
message: lines[line-1].gsub(/^[ \t]+/, ''), parent: parent
|
51
|
+
}
|
52
|
+
|
53
|
+
pending[:commit] = blame(file, line)
|
54
|
+
pending[:allowed] = (Time.now - pending[:commit][:at]) <= @config.allowed_for
|
55
|
+
|
56
|
+
pendings << pending
|
57
|
+
end
|
58
|
+
|
59
|
+
pendings
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def blame(file, line)
|
67
|
+
@config.out.puts " * blaming #{file}:#{line}" if @config.out
|
68
|
+
blame = @workspace.git('blame', '-L', "#{line},#{line}", '-l', '-w', '-p', file).force_encoding("UTF-8").split(/\r?\n/).map{|l| l.split(/ /) }
|
69
|
+
commit = {
|
70
|
+
sha: blame[0].first, name: blame[1][1..-1].join(' '),
|
71
|
+
email: blame[2][1..-1].join(' ').gsub(/^</,'').gsub(/>$/,'')
|
72
|
+
}
|
73
|
+
commit[:at] = Time.parse(@workspace.git(*%w(log --pretty=%aD -n1), commit[:sha]).chomp)
|
74
|
+
commit
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Pendaxes
|
2
|
+
module Finder
|
3
|
+
def find(name)
|
4
|
+
@finder_cache ||= {}
|
5
|
+
path = case name
|
6
|
+
when String
|
7
|
+
name
|
8
|
+
when Symbol
|
9
|
+
"#{@finder_prefix || ""}#{name}"
|
10
|
+
else
|
11
|
+
raise ArgumentError, "name should be a kind of String or Symbol"
|
12
|
+
end
|
13
|
+
return @finder_cache[name] if @finder_cache[name]
|
14
|
+
require path
|
15
|
+
announce name, @finder_latest_inherited
|
16
|
+
end
|
17
|
+
|
18
|
+
def inherited(klass)
|
19
|
+
if klass.name
|
20
|
+
announce klass.name.gsub(/^.*::/,'').gsub(/.[A-Z]/){|s| s[0]+"_"+s[1] }.downcase.to_sym, klass
|
21
|
+
end
|
22
|
+
@finder_latest_inherited = klass
|
23
|
+
end
|
24
|
+
|
25
|
+
def announce(name, klass)
|
26
|
+
(@finder_cache ||= {})[name] = klass
|
27
|
+
end
|
28
|
+
|
29
|
+
def find_in(prefix)
|
30
|
+
@finder_prefix = prefix + "/"
|
31
|
+
end
|
32
|
+
private :find_in
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require_relative 'pending_manager'
|
2
|
+
require_relative 'defaults'
|
3
|
+
require_relative 'finder'
|
4
|
+
require 'hashr'
|
5
|
+
|
6
|
+
module Pendaxes
|
7
|
+
class Notificator
|
8
|
+
include PendingManager
|
9
|
+
extend Defaults
|
10
|
+
extend Finder
|
11
|
+
find_in 'pendaxes/notificators'
|
12
|
+
defaults reporter: {use: :text}
|
13
|
+
|
14
|
+
def initialize(config={})
|
15
|
+
@config = Hashr.new(self.class.defaults.merge(config))
|
16
|
+
@pendings = []
|
17
|
+
end
|
18
|
+
|
19
|
+
def notify
|
20
|
+
end
|
21
|
+
|
22
|
+
def reporter
|
23
|
+
Reporter.find(@config.reporter.use.to_sym)
|
24
|
+
end
|
25
|
+
|
26
|
+
def report_for(pendings)
|
27
|
+
r = reporter.new({include_allowed: true}.merge(@config.reporter))
|
28
|
+
r.add pendings
|
29
|
+
r.report
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require_relative '../notificator'
|
2
|
+
require 'mail'
|
3
|
+
|
4
|
+
module Pendaxes
|
5
|
+
class Notificator
|
6
|
+
class Mail < Notificator
|
7
|
+
defaults reporter: {use: :text}, blacklist: []
|
8
|
+
|
9
|
+
def notify
|
10
|
+
if @config.to
|
11
|
+
deliver(pendings, @config.to)
|
12
|
+
else
|
13
|
+
pendings.group_by {|pending| pending[:commit][:email] }.each do |email, pends|
|
14
|
+
deliver(pends, email)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def whitelist
|
20
|
+
if @config.whitelist
|
21
|
+
@whitelist ||= process_email_filter(@config.whitelist)
|
22
|
+
else
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def blacklist
|
28
|
+
@blacklist ||= process_email_filter(@config.blacklist)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def deliver(pends,email)
|
34
|
+
real_email = (@config.alias || {})[email] || email
|
35
|
+
return nil if blacklist.match?(real_email) || (whitelist && !whitelist.match?(real_email))
|
36
|
+
|
37
|
+
mail = ::Mail.new
|
38
|
+
mail.from = @config.from
|
39
|
+
mail.to = real_email
|
40
|
+
mail.subject = "[Pendaxes] Your #{pends.size} pending tests are waiting to be fixed"
|
41
|
+
mail.body = report_for(pends)
|
42
|
+
mail.content_type = 'text/html; charset=utf-8' if reporter.html?
|
43
|
+
|
44
|
+
if @config.delivery_method
|
45
|
+
mail.delivery_method @config.delivery_method.to_sym, @config.delivery_options || {}
|
46
|
+
end
|
47
|
+
|
48
|
+
@config.out.puts mail.inspect if @config.out
|
49
|
+
|
50
|
+
mail.deliver
|
51
|
+
end
|
52
|
+
|
53
|
+
def process_email_filter(list)
|
54
|
+
filter = list.map do |email|
|
55
|
+
if %r{^/(.*)/$} === email
|
56
|
+
Regexp.new($1)
|
57
|
+
else
|
58
|
+
email
|
59
|
+
end
|
60
|
+
end
|
61
|
+
class << filter
|
62
|
+
def match?(email)
|
63
|
+
self.any? do |condition|
|
64
|
+
condition.is_a?(Regexp) ? condition === email : condition == email
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
filter
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative '../notificator'
|
2
|
+
|
3
|
+
module Pendaxes
|
4
|
+
class Notificator
|
5
|
+
class Terminal < Notificator
|
6
|
+
defaults to: $stdout, reporter: {use: :text}
|
7
|
+
|
8
|
+
def notify
|
9
|
+
io = @config.to
|
10
|
+
pendings.group_by{|x| "#{x[:commit][:name]} <#{x[:commit][:email]}>" }.each do |name, pends|
|
11
|
+
io.puts "#{name}:"
|
12
|
+
io.puts
|
13
|
+
io.puts report_for(pends)
|
14
|
+
io.puts
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'hashr'
|
2
|
+
|
3
|
+
module Pendaxes
|
4
|
+
module PendingManager
|
5
|
+
def add(pending={})
|
6
|
+
case pending
|
7
|
+
when Array
|
8
|
+
@pendings.push *pending.map{|x| Hashr.new(x) }
|
9
|
+
when Hash
|
10
|
+
@pendings << Hashr.new(pending)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def pendings
|
15
|
+
if @config.include_allowed
|
16
|
+
all_pendings
|
17
|
+
else
|
18
|
+
@pendings.reject(&:allowed)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def all_pendings
|
23
|
+
@pendings
|
24
|
+
end
|
25
|
+
|
26
|
+
def reset
|
27
|
+
@pendings = []
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require_relative 'pending_manager'
|
2
|
+
require_relative 'defaults'
|
3
|
+
require_relative 'finder'
|
4
|
+
require 'hashr'
|
5
|
+
|
6
|
+
module Pendaxes
|
7
|
+
class Reporter
|
8
|
+
include PendingManager
|
9
|
+
extend Defaults
|
10
|
+
extend Finder
|
11
|
+
find_in 'pendaxes/reporters'
|
12
|
+
|
13
|
+
defaults include_allowed: true
|
14
|
+
|
15
|
+
def initialize(config={})
|
16
|
+
@config = Hashr.new(self.class.defaults.merge(config))
|
17
|
+
@pendings = []
|
18
|
+
end
|
19
|
+
|
20
|
+
def report
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.html?
|
24
|
+
false
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require_relative '../reporter'
|
2
|
+
require 'haml'
|
3
|
+
require 'digest/md5'
|
4
|
+
require 'cgi'
|
5
|
+
|
6
|
+
module Pendaxes
|
7
|
+
class Reporter
|
8
|
+
class Haml < Reporter
|
9
|
+
defaults commit_url: nil,
|
10
|
+
file_url: nil,
|
11
|
+
report_url: nil,
|
12
|
+
gravatar: true,
|
13
|
+
older_first: true,
|
14
|
+
template: "#{File.dirname(__FILE__)}/template.haml"
|
15
|
+
|
16
|
+
def report
|
17
|
+
haml = ::Haml::Engine.new(File.read(@config.template))
|
18
|
+
haml.render(binding, config: @config)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.html?; true; end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def relative_time(time, from = Time.now)
|
26
|
+
diff = from - time
|
27
|
+
return "in the future" if diff < 0
|
28
|
+
case diff
|
29
|
+
when 0..10
|
30
|
+
"Just now"
|
31
|
+
when 11..60
|
32
|
+
"in a minute"
|
33
|
+
when 61...3600
|
34
|
+
"%.1f minutes ago" % (diff/60)
|
35
|
+
when 3600...86400
|
36
|
+
"%.1f hours ago" % (diff/3600.0)
|
37
|
+
when 86400...604800
|
38
|
+
"%.1f days ago" % (diff/86400.0)
|
39
|
+
when 604800...2419200
|
40
|
+
"%.1f weeks ago" % (diff/604800.0)
|
41
|
+
when 2419200...31556926
|
42
|
+
"%.1f months ago" % (diff/2419200.0)
|
43
|
+
else
|
44
|
+
"%.1f years ago" % (diff/31556926.0)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|