dash-mario 0.15
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/CHANGELOG +93 -0
- data/Gemfile +16 -0
- data/MIT-LICENSE +21 -0
- data/README.rdoc +100 -0
- data/Rakefile +34 -0
- data/dash-mario.gemspec +22 -0
- data/lib/dash-fu/mario.rb +154 -0
- data/lib/dash-fu/marios/backtweets.rb +57 -0
- data/lib/dash-fu/marios/backtweets.yml +9 -0
- data/lib/dash-fu/marios/github.rb +110 -0
- data/lib/dash-fu/marios/github.yml +15 -0
- data/lib/dash-fu/marios/github_issues.rb +91 -0
- data/lib/dash-fu/marios/github_issues.yml +14 -0
- data/lib/dash-fu/marios/ruby_gems.rb +55 -0
- data/lib/dash-fu/marios/ruby_gems.yml +6 -0
- data/test/api_keys.yml +1 -0
- data/test/backtweets_test.rb +151 -0
- data/test/cassettes/backtweets.yml +63 -0
- data/test/cassettes/github.yml +126 -0
- data/test/cassettes/github_issues.yml +123 -0
- data/test/cassettes/ruby_gems.yml +36 -0
- data/test/github_issues_test.rb +194 -0
- data/test/github_test.rb +251 -0
- data/test/helpers/activity.rb +21 -0
- data/test/helpers/metric.rb +22 -0
- data/test/helpers/person.rb +25 -0
- data/test/helpers/test.rb +161 -0
- data/test/ruby_gems_test.rb +164 -0
- data/test/setup.rb +31 -0
- data/test/test.log +852 -0
- metadata +164 -0
@@ -0,0 +1,57 @@
|
|
1
|
+
module DashFu::Mario
|
2
|
+
# Track Twitter links using the Backtype API.
|
3
|
+
class Backtweets
|
4
|
+
include DashFu::Mario
|
5
|
+
|
6
|
+
def setup(source, params)
|
7
|
+
url = params["url"].strip.downcase.sub(/^http(s?):\/\//, "")
|
8
|
+
source["metric.name"] = "Tweets for #{url}"
|
9
|
+
source["metric.columns"] = [{ :id=>"tweets", :label=>"Tweets" }]
|
10
|
+
source["metric.totals"] = true
|
11
|
+
source["url"] = url
|
12
|
+
end
|
13
|
+
|
14
|
+
def validate(source)
|
15
|
+
raise "Missing URL" if source["url"].blank?
|
16
|
+
end
|
17
|
+
|
18
|
+
def update(source, request, &block)
|
19
|
+
Net::HTTP.start "backtweets.com" do |http|
|
20
|
+
get = Net::HTTP::Get.new("/search.json?key=#{api_key}&q=#{Rack::Utils.escape source["url"]}")
|
21
|
+
response = http.request(get)
|
22
|
+
json = JSON.parse(response.body) rescue nil if Net::HTTPOK === response
|
23
|
+
unless json
|
24
|
+
logger.error "Backtweets: #{response.code} #{response.message}"
|
25
|
+
raise "Last request didn't go as expected, trying again later"
|
26
|
+
end
|
27
|
+
|
28
|
+
if tweets = json["tweets"]
|
29
|
+
last_tweet_id = source["last_tweet_id"]
|
30
|
+
if last_tweet_id
|
31
|
+
new_ones = tweets.take_while { |tweet| tweet["tweet_id"] != last_tweet_id }.reverse
|
32
|
+
new_ones.each do |tweet|
|
33
|
+
handle, id = tweet["tweet_from_user"], tweet["tweet_id"]
|
34
|
+
url = "http://twitter.com/#{handle}/#{id}"
|
35
|
+
html = <<-HTML
|
36
|
+
<a href="#{url}">tweeted</a>:
|
37
|
+
<blockquote>#{tweet["tweet_text"]}</blockquote>
|
38
|
+
HTML
|
39
|
+
person = { :fullname=>handle, :identities=>%W{twitter.com:#{handle}}, :photo_url=>tweet["tweet_profile_image_url"] }
|
40
|
+
block.call :activity=>{ :uid=>id, :url=>url, :html=>html, :tags=>%w{twitter mention},
|
41
|
+
:timestamp=>Time.parse(tweet["tweet_created_at"]).utc, :person=>person }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
if recent = tweets.first
|
45
|
+
source["last_tweet_id"] = recent["tweet_id"]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
block.call :set=>{ :tweets=>json["totalresults"].to_i }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def meta(source)
|
54
|
+
[ { :text=>"Search yourself", :url=>"http://backtweets.com/search?q=#{URI.escape source["url"]}" } ]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
en:
|
2
|
+
description: "Finds Tweets linking to any URL"
|
3
|
+
inputs: |-
|
4
|
+
<label>URL <input type="text" name="source[url]" size="30"></label>
|
5
|
+
notes: |-
|
6
|
+
<ul>
|
7
|
+
<li>If this is a new search, it may take some time before we get the first results (including historical data). Check back in 8 hours.</li>
|
8
|
+
<li>Data provided by <a href="http://backtweets.com">Backtweets</a></li>
|
9
|
+
</ul>
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module DashFu::Mario
|
2
|
+
# Track Github commits, watchers and forks.
|
3
|
+
class Github
|
4
|
+
include DashFu::Mario
|
5
|
+
|
6
|
+
def setup(source, params)
|
7
|
+
repo = params["repo"].strip
|
8
|
+
source["metric.name"] = "Github: #{repo}"
|
9
|
+
source["metric.columns"] = [{ :id=>"commits", :label=>"Commits" }, { :id=>"watchers", :label=>"Watchers" }, { :id=>"forks", :label=>"Forks" }]
|
10
|
+
source["metric.totals"] = true
|
11
|
+
source["repo"] = repo
|
12
|
+
branch = params["branch"].strip
|
13
|
+
source["branch"] = branch.blank? ? "master" : branch
|
14
|
+
source["username"] = params["username"]
|
15
|
+
source["api_token"] = params["api_token"]
|
16
|
+
end
|
17
|
+
|
18
|
+
def validate(source)
|
19
|
+
raise "Missing repository name" if source["repo"].blank?
|
20
|
+
raise "Need user name and repository name, e.g. assaf/vanity" unless source["repo"][/^[\w-]+\/[\w-]+$/]
|
21
|
+
end
|
22
|
+
|
23
|
+
def update(source, request, &block)
|
24
|
+
http = Net::HTTP.new("github.com", 443)
|
25
|
+
http.use_ssl = true
|
26
|
+
http.start do
|
27
|
+
case response = http_request(http, source, "/api/v2/json/repos/show/:repo")
|
28
|
+
when Net::HTTPOK
|
29
|
+
json = JSON.parse(response.body)["repository"] rescue nil
|
30
|
+
when Net::HTTPNotFound, Net::HTTPBadRequest
|
31
|
+
raise "Could not find the repository #{source["repo"]}"
|
32
|
+
when Net::HTTPUnauthorized
|
33
|
+
raise "You are not authorized to access this repository, or invalid username/password"
|
34
|
+
end
|
35
|
+
if json
|
36
|
+
source.update json.slice(*%w{description url homepage})
|
37
|
+
block.call :set=>{ :forks=>json["forks"], :watchers=>json["watchers"] }
|
38
|
+
else
|
39
|
+
logger.error "Github: #{response.code} #{response.message}"
|
40
|
+
error = true
|
41
|
+
end
|
42
|
+
|
43
|
+
case response = http_request(http, source, "/api/v2/json/commits/list/:repo/:branch")
|
44
|
+
when Net::HTTPOK
|
45
|
+
commits = JSON.parse(response.body)["commits"] rescue nil
|
46
|
+
when Net::HTTPNotFound, Net::HTTPBadRequest
|
47
|
+
raise "Could not find the branch #{source["branch"]}"
|
48
|
+
end
|
49
|
+
|
50
|
+
if commits
|
51
|
+
last_seen_id = source["last_commit"]["id"] if source["last_commit"]
|
52
|
+
if last_seen_id
|
53
|
+
new_ones = commits.take_while { |commit| commit["id"] != last_seen_id }
|
54
|
+
merged = new_ones.inject([]) do |all, commit|
|
55
|
+
last = all.last.last unless all.empty?
|
56
|
+
if last && last["committer"]["email"] == commit["committer"]["email"]
|
57
|
+
all.last << commit
|
58
|
+
else
|
59
|
+
all << [commit]
|
60
|
+
end
|
61
|
+
all
|
62
|
+
end
|
63
|
+
|
64
|
+
merged.reverse.each do |commits|
|
65
|
+
first = commits.first
|
66
|
+
committer = first["committer"]
|
67
|
+
person = { :fullname=>committer["name"], :identities=>%W{github.com:#{committer["login"]}}, :email=>committer["email"] }
|
68
|
+
messages = commits.map { |commit| %{<blockquote><a href="#{commit["url"]}">#{commit["id"][0,7]}</a> #{h commit["message"].strip.split(/[\n\r]/).first[0,50]}</blockquote>} }
|
69
|
+
html = %{pushed to #{h source["branch"]} at <a href="http://github.com/#{source["repo"]}">#{h source["repo"]}</a>:\n#{messages.join("\n")}}
|
70
|
+
block.call :activity=>{ :uid=>first["id"], :html=>html, :url=>first["url"], :tags=>%w{push},
|
71
|
+
:timestamp=>Time.parse(first["committed_date"]).utc, :person=>person }
|
72
|
+
end
|
73
|
+
block.call :inc=>{ :commits=>new_ones.count }
|
74
|
+
else
|
75
|
+
block.call :set=>{ :commits=>commits.count }
|
76
|
+
end
|
77
|
+
if last_commit = commits.first
|
78
|
+
source.update "last_commit"=>last_commit.slice("id", "message", "url").
|
79
|
+
merge("timestamp"=>Time.parse(last_commit["committed_date"]).utc)
|
80
|
+
end
|
81
|
+
else
|
82
|
+
logger.error "Github: #{response.code} #{response.message}"
|
83
|
+
error = true
|
84
|
+
end
|
85
|
+
|
86
|
+
raise "Last request didn't go as expected, trying again later" if error
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def meta(source)
|
91
|
+
meta = [ { :title=>"Repository", :text=>source["repo"], :url=>source["url"] },
|
92
|
+
{ :title=>"Branch", :text=>source["branch"] },
|
93
|
+
{ :text=>source["description"] },
|
94
|
+
{ :title=>"Home page", :url=>source["homepage"] } ]
|
95
|
+
if last_commit = source["last_commit"]
|
96
|
+
meta << { :title=>"Commit", :text=>last_commit["message"], :url=>last_commit["url"] }
|
97
|
+
end
|
98
|
+
meta
|
99
|
+
end
|
100
|
+
|
101
|
+
protected
|
102
|
+
|
103
|
+
def http_request(http, source, path)
|
104
|
+
get = Net::HTTP::Get.new(path.gsub(":repo", URI.escape(source["repo"])).gsub(":branch", URI.escape(source["branch"])))
|
105
|
+
get.basic_auth "#{source["username"]}/token", source["api_token"] unless source["username"].blank? && source["api_token"].blank?
|
106
|
+
http.request(get)
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
en:
|
2
|
+
description: "Tracks Github commits, watchers and forks"
|
3
|
+
inputs: |-
|
4
|
+
<label>User/repository <input type="text" name="source[repo]" size="30"></label>
|
5
|
+
<label>Branch <input type="text" name="source[branch]" size="30" value="master"></label>
|
6
|
+
<p>For private repositories:</p>
|
7
|
+
<label>Username <input type="text" name="source[username]" size="30"></label>
|
8
|
+
<label><a href="https://github.com/account#admin_bucket" target="github">API Token</a> <input type="password" name="source[api_token]" size="30"></label>
|
9
|
+
notes: |-
|
10
|
+
<ul>
|
11
|
+
<li>Enter user name/repository like this: <code>assaf/vanity</code></li>
|
12
|
+
<li>To access private repositories, authenticate using your username and API token.
|
13
|
+
You can find your API token in <a href="https://github.com/account#admin_bucket" target="github">Account Settings</a>.
|
14
|
+
</li>
|
15
|
+
</ul>
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module DashFu::Mario
|
2
|
+
# Track open/closed Github issues.
|
3
|
+
class GithubIssues
|
4
|
+
include DashFu::Mario
|
5
|
+
|
6
|
+
def setup(source, params)
|
7
|
+
repo = params["repo"].to_s.strip
|
8
|
+
source["metric.name"] = "Github Issues for #{repo}"
|
9
|
+
source["metric.columns"] = [{ :id=>"open", :label=>"Open issues" }, { :id=>"closed", :label=>"Closed issues" }]
|
10
|
+
source["metric.totals"] = true
|
11
|
+
source["repo"] = repo
|
12
|
+
source["username"] = params["username"]
|
13
|
+
source["api_token"] = params["api_token"]
|
14
|
+
end
|
15
|
+
|
16
|
+
def validate(source)
|
17
|
+
raise "Missing user/repository" if source["repo"].blank?
|
18
|
+
raise "Need user name and repository name, e.g. assaf/vanity" unless source["repo"][/^[\w-]+\/[\w-]+$/]
|
19
|
+
end
|
20
|
+
|
21
|
+
def update(source, request, &block)
|
22
|
+
http = Net::HTTP.new("github.com", 443)
|
23
|
+
http.use_ssl = true
|
24
|
+
http.start do
|
25
|
+
update = {}
|
26
|
+
case response = http_request(http, source, "open")
|
27
|
+
when Net::HTTPOK
|
28
|
+
open = JSON.parse(response.body)["issues"] rescue nil
|
29
|
+
when Net::HTTPNotFound, Net::HTTPBadRequest
|
30
|
+
raise "Could not find the repository #{source["repo"]}"
|
31
|
+
when Net::HTTPUnauthorized
|
32
|
+
raise "You are not authorized to access this repository, or invalid username/password"
|
33
|
+
end
|
34
|
+
if open
|
35
|
+
update[:open] = open.count
|
36
|
+
if open_ids = source["open-ids"]
|
37
|
+
open.reject { |issue| open_ids.include?(issue["number"]) }.reverse.each do |issue|
|
38
|
+
sha = Digest::SHA1.hexdigest([source["repo"], "open", issue["number"], issue["updated_at"]].join(":"))
|
39
|
+
url = "http://github.com/#{source["repo"]}/issues#issue/#{issue["number"]}"
|
40
|
+
html = <<-HTML
|
41
|
+
opened <a href="#{url}">issue #{issue["number"]}</a> on #{source["repo"]}:
|
42
|
+
<blockquote>#{h issue["title"]}</blockquote>
|
43
|
+
HTML
|
44
|
+
block.call :activity=>{ :uid=>sha, :url=>url, :html=>html, :tags=>%w{issue opened},
|
45
|
+
:timestamp=>Time.parse(issue["created_at"]).utc }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
source["open-ids"] = open.map { |issue| issue["number"] }
|
49
|
+
end
|
50
|
+
|
51
|
+
case response = http_request(http, source, "closed")
|
52
|
+
when Net::HTTPOK
|
53
|
+
closed = JSON.parse(response.body)["issues"] rescue nil
|
54
|
+
end
|
55
|
+
if closed
|
56
|
+
update[:closed] = closed.count
|
57
|
+
if closed_ids = source["closed-ids"]
|
58
|
+
closed.reject { |issue| closed_ids.include?(issue["number"]) }.reverse.each do |issue|
|
59
|
+
sha = Digest::SHA1.hexdigest([source["repo"], "closed", issue["number"], issue["updated_at"]].join(":"))
|
60
|
+
url = "http://github.com/#{source["repo"]}/issues#issue/#{issue["number"]}"
|
61
|
+
html = <<-HTML
|
62
|
+
closed <a href="#{url}">issue #{issue["number"]}</a> on #{source["repo"]}:
|
63
|
+
<blockquote>#{h issue["title"]}</blockquote>
|
64
|
+
HTML
|
65
|
+
block.call :activity=>{ :uid=>sha, :url=>url, :html=>html, :tags=>%w{issue closed},
|
66
|
+
:timestamp=>Time.parse(issue["closed_at"]).utc }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
source["closed-ids"] = closed.map { |issue| issue["number"] }
|
70
|
+
end
|
71
|
+
|
72
|
+
raise "Last request didn't go as expected, trying again later" if update.empty?
|
73
|
+
block.call :set=>update
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def meta(source)
|
78
|
+
[ { :title=>"On Github", :url=>"http://github.com/#{source["repo"]}/issues" } ]
|
79
|
+
end
|
80
|
+
|
81
|
+
protected
|
82
|
+
|
83
|
+
def http_request(http, source, state)
|
84
|
+
path = "/api/v2/json/issues/list/#{URI.escape source["repo"]}/#{state}"
|
85
|
+
get = Net::HTTP::Get.new(path)
|
86
|
+
get.basic_auth "#{source["username"]}/token", source["api_token"] unless source["username"].blank? && source["api_token"].blank?
|
87
|
+
http.request(get)
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
en:
|
2
|
+
description: "Tracks open/closed Github issues"
|
3
|
+
inputs: |-
|
4
|
+
<label>User/repository <input type="text" name="source[repo]" size="30"></label>
|
5
|
+
<p>For private repositories:</p>
|
6
|
+
<label>Username <input type="text" name="source[username]" size="30"></label>
|
7
|
+
<label><a href="https://github.com/account#admin_bucket" target="github">API Token</a> <input type="password" name="source[api_token]" size="30"></label>
|
8
|
+
notes: |-
|
9
|
+
<ul>
|
10
|
+
<li>Enter user name/repository like this: <code>assaf/vanity</code></li>
|
11
|
+
<li>To access private repositories, authenticate using your username and API token.
|
12
|
+
You can find your API token in <a href="https://github.com/account#admin_bucket" target="github">Account Settings</a>.
|
13
|
+
</li>
|
14
|
+
</ul>
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module DashFu::Mario
|
2
|
+
# Track activity (downloads and releases) of a Ruby Gem from rubygems.org.
|
3
|
+
class RubyGems
|
4
|
+
include DashFu::Mario
|
5
|
+
|
6
|
+
def setup(source, params)
|
7
|
+
gem_name = params["gem_name"].to_s.strip
|
8
|
+
source["metric.name"] = "RubyGems: #{gem_name}"
|
9
|
+
source["metric.columns"] = [{ :id=>"downloads", :label=>"Downloads" }]
|
10
|
+
source["metric.totals"] = true
|
11
|
+
source["gem_name"] = gem_name
|
12
|
+
end
|
13
|
+
|
14
|
+
def validate(source)
|
15
|
+
raise "Missing gem name" if source["gem_name"].blank?
|
16
|
+
end
|
17
|
+
|
18
|
+
def update(source, request)
|
19
|
+
gem_name = source["gem_name"]
|
20
|
+
uri = URI.parse("http://rubygems.org/api/v1/gems/#{Rack::Utils.escape gem_name}.json")
|
21
|
+
case response = Net::HTTP.get_response(uri)
|
22
|
+
when Net::HTTPOK
|
23
|
+
json = JSON.parse(response.body) rescue nil
|
24
|
+
when Net::HTTPNotFound
|
25
|
+
raise "Could not find the Gem #{gem_name}"
|
26
|
+
end
|
27
|
+
unless json
|
28
|
+
logger.error "RubyGems: #{response.code} #{response.message}"
|
29
|
+
raise "Last request didn't go as expected, trying again later"
|
30
|
+
end
|
31
|
+
|
32
|
+
if source["version"]
|
33
|
+
current, latest = Gem::Version.new(source["version"]), Gem::Version.new(json["version"])
|
34
|
+
if latest > current
|
35
|
+
html = "released <a href=\"http://rubygems.org/gems/#{gem_name}/versions/#{latest}\">#{gem_name} version #{latest}</a>."
|
36
|
+
person = { :fullname=>"RubyGems", :identities=>"url:http://rubygems.org/", :photo_url=>"http://dash-fu.com/images/sources/ruby.png" }
|
37
|
+
yield :activity=>{ :uid=>"#{gem_name}-#{latest}", :url=>"http://rubygems.org/gems/#{gem_name}",
|
38
|
+
:html=>html, :tags=>%w{release}, :person=>person }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
source["version"] = json["version"]
|
42
|
+
source.update json.slice(*%w{homepage_uri project_uri info authors info})
|
43
|
+
yield :set=>{ :downloads=>json["downloads"] }
|
44
|
+
end
|
45
|
+
|
46
|
+
def meta(source)
|
47
|
+
[ { :title=>"Project", :text=>source["gem_name"], :url=>source["homepage_uri"] || source["project_uri"] },
|
48
|
+
{ :text=>source["info"] },
|
49
|
+
{ :title=>"Version", :text=>source["version"] },
|
50
|
+
{ :title=>"Authors", :text=>source["authors"] },
|
51
|
+
{ :title=>"Source", :text=>"RubyGems", :url=>source["project_uri"] } ]
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
data/test/api_keys.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
backtweets: "4554feeeeeeeeeeeeeee"
|
@@ -0,0 +1,151 @@
|
|
1
|
+
require_relative "setup"
|
2
|
+
|
3
|
+
test DashFu::Mario::Backtweets do
|
4
|
+
context "setup" do
|
5
|
+
setup { setup_source "url"=>"vanity.labnotes.org" }
|
6
|
+
|
7
|
+
should "use normalize URL and remove scheme" do
|
8
|
+
setup_source "url"=>"http://Vanity.Labnotes.Org"
|
9
|
+
assert_equal "Tweets for vanity.labnotes.org", metric.name
|
10
|
+
end
|
11
|
+
|
12
|
+
context "metric" do
|
13
|
+
subject { metric }
|
14
|
+
|
15
|
+
should "use URL" do
|
16
|
+
assert_equal "Tweets for vanity.labnotes.org", subject.name
|
17
|
+
end
|
18
|
+
|
19
|
+
should "measure totals" do
|
20
|
+
assert subject.totals
|
21
|
+
end
|
22
|
+
|
23
|
+
should "capture tweets" do
|
24
|
+
assert subject.columns.include?(:id=>"tweets", :label=>"Tweets")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
context "validation" do
|
31
|
+
should "raise error if URL missing" do
|
32
|
+
assert_raise(RuntimeError) { setup_source "url"=>" " }
|
33
|
+
end
|
34
|
+
|
35
|
+
should "create valid metric" do
|
36
|
+
setup_source "url"=>"vanity.labnotes.org"
|
37
|
+
assert metric.valid?
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
context "update" do
|
43
|
+
setup { setup_source "url"=>"vanity.labnotes.org" }
|
44
|
+
|
45
|
+
should "include API key" do
|
46
|
+
update_source
|
47
|
+
assert_requested :get, "http://backtweets.com/search.json?key=4554feeeeeeeeeeeeeee&q=vanity.labnotes.org"
|
48
|
+
end
|
49
|
+
|
50
|
+
should "properly encode URL" do
|
51
|
+
setup_source "url"=>"need&this=encoded"
|
52
|
+
update_source rescue nil
|
53
|
+
assert_requested :get, "http://backtweets.com/search.json?key=#{mario.send :api_key}&q=need%26this%3Dencoded"
|
54
|
+
end
|
55
|
+
|
56
|
+
should "handle errors" do
|
57
|
+
stub_request(:get, interactions.first.uri).to_return :status=>500
|
58
|
+
assert_raise(RuntimeError) { update_source }
|
59
|
+
assert_equal "Last request didn't go as expected, trying again later", last_error
|
60
|
+
end
|
61
|
+
|
62
|
+
should "handle invlid document entity" do
|
63
|
+
stub_request(:get, interactions.first.uri).to_return :body=>"Not JSON"
|
64
|
+
assert_raise(RuntimeError) { update_source }
|
65
|
+
assert_equal "Last request didn't go as expected, trying again later", last_error
|
66
|
+
end
|
67
|
+
|
68
|
+
should "capture number of tweets" do
|
69
|
+
update_source
|
70
|
+
assert_equal({ :tweets=>11 }, totals)
|
71
|
+
end
|
72
|
+
|
73
|
+
should "ignore previous tweets" do
|
74
|
+
update_source
|
75
|
+
assert activities.empty?
|
76
|
+
end
|
77
|
+
|
78
|
+
context "repeating" do
|
79
|
+
setup do
|
80
|
+
update_source
|
81
|
+
second = interactions.last
|
82
|
+
stub_request(:get, second.uri).to_return :body=>second.response.body
|
83
|
+
update_source
|
84
|
+
end
|
85
|
+
|
86
|
+
should "capture new tweets" do
|
87
|
+
assert_equal 2, activities.count
|
88
|
+
end
|
89
|
+
|
90
|
+
context "activity" do
|
91
|
+
subject { activity }
|
92
|
+
|
93
|
+
should "capture tweet identifier" do
|
94
|
+
assert_equal "20959239300", subject.uid
|
95
|
+
end
|
96
|
+
|
97
|
+
should "capture tweet text" do
|
98
|
+
assert_equal <<-HTML, subject.html
|
99
|
+
<a href="http://twitter.com/assaf/20959239300">tweeted</a>:
|
100
|
+
<blockquote>Super awesome <a href="http://vanity.labnotes.org/">http://j.mp/aOrUnsz</a></blockquote>
|
101
|
+
HTML
|
102
|
+
end
|
103
|
+
|
104
|
+
should "capture tweet URL" do
|
105
|
+
assert_equal "http://twitter.com/assaf/20959239300", subject.url
|
106
|
+
end
|
107
|
+
|
108
|
+
should "capture tweet timestamp" do
|
109
|
+
assert_equal Time.parse("2010-8-22T05:00:04").utc, subject.timestamp
|
110
|
+
end
|
111
|
+
|
112
|
+
should "tag as tweeter and mention" do
|
113
|
+
assert_contains subject.tags, "twitter"
|
114
|
+
assert_contains subject.tags, "mention"
|
115
|
+
end
|
116
|
+
|
117
|
+
should "be valid" do
|
118
|
+
assert subject.valid?
|
119
|
+
end
|
120
|
+
|
121
|
+
context "author" do
|
122
|
+
subject { activity.person }
|
123
|
+
|
124
|
+
should "capture full name" do
|
125
|
+
assert_equal "assaf", subject.fullname
|
126
|
+
end
|
127
|
+
|
128
|
+
should "capture screen name" do
|
129
|
+
assert_contains subject.identities, "twitter.com:assaf"
|
130
|
+
end
|
131
|
+
|
132
|
+
should "capture photo" do
|
133
|
+
assert_equal "http://twitter.com/account/profile_image/assaf", subject.photo_url
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
|
143
|
+
context "meta" do
|
144
|
+
setup { setup_source "url"=>"vanity.labnotes.org" }
|
145
|
+
subject { meta }
|
146
|
+
|
147
|
+
should "link to search results" do
|
148
|
+
assert subject.include?(:text=>"Search yourself", :url=>"http://backtweets.com/search?q=vanity.labnotes.org")
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|