blastr 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +4 -0
- data/Manifest.txt +4 -0
- data/README.rdoc +3 -3
- data/bin/blastr +30 -104
- data/lib/blastr.rb +25 -2
- data/lib/blastr/git.rb +77 -0
- data/lib/blastr/scm.rb +13 -0
- data/lib/blastr/svn.rb +83 -0
- data/lib/blastr/tts.rb +52 -0
- metadata +6 -2
data/History.txt
CHANGED
data/Manifest.txt
CHANGED
data/README.rdoc
CHANGED
@@ -31,8 +31,8 @@ All rights reserved.
|
|
31
31
|
|
32
32
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
33
33
|
|
34
|
-
|
35
|
-
|
36
|
-
|
34
|
+
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
35
|
+
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
36
|
+
* Neither the name of this software nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
37
37
|
|
38
38
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/bin/blastr
CHANGED
@@ -1,114 +1,40 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
def
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
end
|
22
|
-
|
23
|
-
class Say < TTSImplementation
|
24
|
-
def initialize; super("say"); end
|
25
|
-
end
|
26
|
-
|
27
|
-
class Espeak < TTSImplementation
|
28
|
-
def initialize; super("espeak"); end
|
29
|
-
end
|
30
|
-
|
31
|
-
class Festival < TTSImplementation
|
32
|
-
def initialize; super("festival"); end
|
33
|
-
def speak(msg)
|
34
|
-
%x[echo "#{msg}" | #{binary} --tts]
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def resolve_tts_system
|
39
|
-
return $tts unless $tts.nil?
|
40
|
-
[ Say.new, Espeak.new, Festival.new ].each do |impl|
|
41
|
-
return impl if impl.available?
|
42
|
-
end
|
43
|
-
raise "No TTS implementation found."
|
44
|
-
end
|
45
|
-
|
46
|
-
def speak(msg)
|
47
|
-
resolve_tts_system.speak(msg)
|
48
|
-
end
|
49
|
-
|
50
|
-
def full_name_of(username)
|
51
|
-
PEOPLE[username] ||= username
|
52
|
-
end
|
53
|
-
|
54
|
-
class LogEntry
|
55
|
-
attr_accessor :revision, :author, :comment
|
56
|
-
def initialize(revision, author, comment)
|
57
|
-
@revision = revision.to_i
|
58
|
-
@author = author
|
59
|
-
@comment = comment
|
60
|
-
end
|
61
|
-
def to_s
|
62
|
-
"revision #{@revision} by #{@author}: #{@comment}"
|
3
|
+
require 'rubygems'
|
4
|
+
require 'yaml'
|
5
|
+
require 'lib/blastr.rb'
|
6
|
+
require 'lib/blastr/tts.rb'
|
7
|
+
|
8
|
+
scm_url = ARGV[0]
|
9
|
+
scm = Blastr.scm(scm_url)
|
10
|
+
since_revision = scm.as_revision(ARGV[1]) if ARGV.size > 1
|
11
|
+
since_revision = scm.latest_revision unless ARGV.size > 1
|
12
|
+
|
13
|
+
class Author
|
14
|
+
PEOPLE_FILE = File.expand_path("~/.blastr/people")
|
15
|
+
def self.people
|
16
|
+
if File.file?(PEOPLE_FILE)
|
17
|
+
yaml = YAML.load_file(PEOPLE_FILE)
|
18
|
+
return yaml unless yaml == false
|
19
|
+
end
|
20
|
+
{ }
|
63
21
|
end
|
64
|
-
end
|
65
|
-
|
66
|
-
def content_of(file)
|
67
|
-
file = open(file)
|
68
|
-
content = file.read
|
69
|
-
file.close
|
70
|
-
content
|
71
|
-
end
|
72
|
-
|
73
|
-
def svn_log(working_copy_path, since_revision = 1)
|
74
|
-
revision = "#{since_revision}:HEAD"
|
75
|
-
revision = "HEAD" if since_revision == "HEAD"
|
76
|
-
cmd = "svn log #{working_copy_path} -r #{revision} > /tmp/svn.log"
|
77
|
-
system(cmd)
|
78
|
-
content_of("/tmp/svn.log")
|
79
|
-
end
|
80
22
|
|
81
|
-
def
|
82
|
-
|
83
|
-
svn_log(working_copy_path, since_revision).scan(/r(\d+)\s\|\s(.*?)\s\|.*?\n\n(.*)\n-+/).each do |entry|
|
84
|
-
entries << LogEntry.new(*entry)
|
23
|
+
def self.full_name_of(username)
|
24
|
+
people[username] ||= username
|
85
25
|
end
|
86
|
-
entries
|
87
26
|
end
|
88
27
|
|
89
|
-
def
|
90
|
-
|
91
|
-
|
92
|
-
1
|
28
|
+
def announce(commit)
|
29
|
+
puts "[#{commit.revision}] Commit by #{commit.author}: #{commit.comment}"
|
30
|
+
Blastr::TTS.speak("Commit by #{Author.full_name_of(commit.author)}: #{commit.comment}")
|
93
31
|
end
|
94
32
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
puts "svn path: #{wc_path}\nrevision: #{revision}\n"
|
101
|
-
speak("Observing Subversion for commits...")
|
102
|
-
|
103
|
-
while true do
|
104
|
-
svn_log_entries(wc_path, revision).each do |entry|
|
105
|
-
if entry.revision.to_i > revision
|
106
|
-
msg = "revision #{entry.revision} by #{full_name_of(entry.author)}: #{entry.comment}"
|
107
|
-
puts msg
|
108
|
-
speak(msg)
|
109
|
-
revision = entry.revision
|
110
|
-
end
|
33
|
+
puts "Polling for #{scm.name} commits since revision #{since_revision}..."
|
34
|
+
while true
|
35
|
+
scm.commits_since(since_revision.to_s).reverse.each do |commit|
|
36
|
+
announce(commit) if since_revision.before?(commit.revision)
|
37
|
+
since_revision = commit.revision
|
111
38
|
end
|
112
|
-
sleep
|
113
|
-
end
|
114
|
-
|
39
|
+
sleep 30
|
40
|
+
end
|
data/lib/blastr.rb
CHANGED
@@ -2,5 +2,28 @@ $:.unshift(File.dirname(__FILE__)) unless
|
|
2
2
|
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
3
|
|
4
4
|
module Blastr
|
5
|
-
VERSION = '0.0.
|
6
|
-
|
5
|
+
VERSION = '0.0.3'
|
6
|
+
|
7
|
+
require 'blastr/git.rb'
|
8
|
+
require 'blastr/svn.rb'
|
9
|
+
|
10
|
+
def Blastr::temp_dir
|
11
|
+
temp_file = Tempfile.new("tmp")
|
12
|
+
temp_dir = temp_file.path
|
13
|
+
temp_file.unlink
|
14
|
+
temp_dir
|
15
|
+
end
|
16
|
+
|
17
|
+
def Blastr::scm_implementations
|
18
|
+
[ Blastr::Subversion, Blastr::Git ]
|
19
|
+
end
|
20
|
+
|
21
|
+
def Blastr::scm(url)
|
22
|
+
scm_implementations.each do |impl|
|
23
|
+
if impl.understands_url?(url)
|
24
|
+
return impl.new(url)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
raise "No SCM implementation found that would understand #{url}"
|
28
|
+
end
|
29
|
+
end
|
data/lib/blastr/git.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'git'
|
5
|
+
require 'blastr/scm.rb'
|
6
|
+
|
7
|
+
module Blastr
|
8
|
+
|
9
|
+
class GitLogEntry < LogEntry
|
10
|
+
def initialize(commit)
|
11
|
+
@revision = GitRevision.new(commit.sha)
|
12
|
+
@author = commit.author.name
|
13
|
+
@comment = commit.message
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class GitRevision
|
18
|
+
attr_accessor :name
|
19
|
+
|
20
|
+
def initialize(name)
|
21
|
+
@name = name
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s
|
25
|
+
@name
|
26
|
+
end
|
27
|
+
|
28
|
+
def before?(revision)
|
29
|
+
return false if @name == "HEAD"
|
30
|
+
return true if revision.name == "HEAD"
|
31
|
+
@name < revision.name
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class Git
|
36
|
+
def name; "Git"; end
|
37
|
+
|
38
|
+
def self.understands_url?(url)
|
39
|
+
url.index("git://") == 0
|
40
|
+
end
|
41
|
+
|
42
|
+
def initialize(git_url)
|
43
|
+
@git_url = git_url
|
44
|
+
end
|
45
|
+
|
46
|
+
def as_revision(arg)
|
47
|
+
raise "Invalid revision: #{arg}" unless arg =~ /^(HEAD(~\d+)?)|([\d\w:-]+)$/
|
48
|
+
GitRevision.new(arg.to_s)
|
49
|
+
end
|
50
|
+
|
51
|
+
def understands_url?(url)
|
52
|
+
url.index("git://") == 0
|
53
|
+
end
|
54
|
+
|
55
|
+
def latest_revision
|
56
|
+
commits = commits_since(as_revision("HEAD~5"))
|
57
|
+
return as_revision("HEAD") unless commits.size > 0
|
58
|
+
GitRevision.new(commits.last.revision.to_s)
|
59
|
+
end
|
60
|
+
|
61
|
+
def commits_since(revision)
|
62
|
+
@clone = ::Git.clone(@git_url, Blastr::temp_dir)
|
63
|
+
begin
|
64
|
+
@clone.chdir do
|
65
|
+
commits = []
|
66
|
+
@clone.log.between(revision.to_s).each do |commit|
|
67
|
+
commits << GitLogEntry.new(commit)
|
68
|
+
end
|
69
|
+
return commits.reverse
|
70
|
+
end
|
71
|
+
ensure
|
72
|
+
FileUtils.remove_dir(@clone.dir, :force => true)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
data/lib/blastr/scm.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
module Blastr
|
2
|
+
class LogEntry
|
3
|
+
attr_accessor :revision, :author, :comment
|
4
|
+
def initialize(revision, author, comment)
|
5
|
+
@revision = revision
|
6
|
+
@author = author
|
7
|
+
@comment = comment
|
8
|
+
end
|
9
|
+
def to_s
|
10
|
+
"revision #{@revision} by #{@author}: #{@comment}"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/lib/blastr/svn.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
require 'blastr/scm.rb'
|
5
|
+
|
6
|
+
module Blastr
|
7
|
+
|
8
|
+
class SubversionRevision
|
9
|
+
attr_accessor :name
|
10
|
+
|
11
|
+
def initialize(name)
|
12
|
+
@name = name
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
@name
|
17
|
+
end
|
18
|
+
|
19
|
+
def before?(revision)
|
20
|
+
return false if @name == "HEAD"
|
21
|
+
return true if revision.name == "HEAD"
|
22
|
+
@name.to_i < revision.name.to_i
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class Subversion
|
27
|
+
def name; "Subversion"; end
|
28
|
+
|
29
|
+
def self.understands_url?(url)
|
30
|
+
not url.match(/(https?|svn):\/\/.+/).nil?
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(svn_url)
|
34
|
+
@svn_url = svn_url
|
35
|
+
end
|
36
|
+
|
37
|
+
def as_revision(arg)
|
38
|
+
raise "Invalid revision: #{arg}" unless arg =~ /^(HEAD)|(\d+)$/
|
39
|
+
SubversionRevision.new(arg.to_s)
|
40
|
+
end
|
41
|
+
|
42
|
+
def latest_revision
|
43
|
+
entries = commits_since(as_revision("HEAD"))
|
44
|
+
return entries.first.revision unless entries.empty?
|
45
|
+
1
|
46
|
+
end
|
47
|
+
|
48
|
+
def commits_since(since_revision = 1)
|
49
|
+
entries = []
|
50
|
+
svn_log(since_revision).scan(/r(\d+)\s\|\s(.*?)\s\|.*?\n\n(.*)\n-+/).each do |entry|
|
51
|
+
revision = as_revision(entry[0])
|
52
|
+
author = entry[1]
|
53
|
+
comment = entry[2]
|
54
|
+
entries << LogEntry.new(revision, author, comment)
|
55
|
+
end
|
56
|
+
entries.reverse
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
def svn_log(since_revision = as_revision("1"))
|
61
|
+
temp_file = Tempfile.new("svn.log").path
|
62
|
+
begin
|
63
|
+
revision = "#{since_revision}:#{as_revision('HEAD')}"
|
64
|
+
revision = as_revision("HEAD") if since_revision.to_s == as_revision("HEAD").to_s
|
65
|
+
cmd = "svn log #{@svn_url} -r #{revision} > #{temp_file}"
|
66
|
+
system(cmd)
|
67
|
+
return content_of(temp_file)
|
68
|
+
ensure
|
69
|
+
FileUtils.remove_file(temp_file)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
def content_of(file)
|
75
|
+
file = open(file)
|
76
|
+
content = file.read
|
77
|
+
file.close
|
78
|
+
content
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
data/lib/blastr/tts.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
module Blastr
|
2
|
+
module TTS
|
3
|
+
|
4
|
+
class TTSImplementation
|
5
|
+
attr_accessor :name
|
6
|
+
|
7
|
+
def initialize(name)
|
8
|
+
@name = name
|
9
|
+
end
|
10
|
+
|
11
|
+
def binary
|
12
|
+
%x[which #{@name}].strip
|
13
|
+
end
|
14
|
+
|
15
|
+
def available?
|
16
|
+
binary.empty? == false
|
17
|
+
end
|
18
|
+
|
19
|
+
def speak(msg)
|
20
|
+
%x[#{binary} "#{msg}"]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Say < TTSImplementation
|
25
|
+
def initialize; super("say"); end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Espeak < TTSImplementation
|
29
|
+
def initialize; super("espeak"); end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Festival < TTSImplementation
|
33
|
+
def initialize; super("festival"); end
|
34
|
+
def speak(msg)
|
35
|
+
%x[echo "#{msg}" | #{binary} --tts]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def TTS::resolve_tts_system
|
40
|
+
return $tts unless $tts.nil?
|
41
|
+
[ Say.new, Espeak.new, Festival.new ].each do |impl|
|
42
|
+
return impl if impl.available?
|
43
|
+
end
|
44
|
+
raise "No TTS implementation found."
|
45
|
+
end
|
46
|
+
|
47
|
+
def TTS::speak(msg)
|
48
|
+
resolve_tts_system.speak(msg)
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: blastr
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lasse Koskela
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-01-
|
12
|
+
date: 2009-01-17 00:00:00 +02:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -52,6 +52,10 @@ files:
|
|
52
52
|
- Rakefile
|
53
53
|
- bin/blastr
|
54
54
|
- lib/blastr.rb
|
55
|
+
- lib/blastr/tts.rb
|
56
|
+
- lib/blastr/scm.rb
|
57
|
+
- lib/blastr/git.rb
|
58
|
+
- lib/blastr/svn.rb
|
55
59
|
- script/console
|
56
60
|
- script/destroy
|
57
61
|
- script/generate
|