dash-bees 0.18
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 +116 -0
- data/Gemfile +16 -0
- data/MIT-LICENSE +21 -0
- data/README.rdoc +113 -0
- data/Rakefile +39 -0
- data/dash-bees.gemspec +25 -0
- data/lib/dash-fu/bee.rb +212 -0
- data/lib/dash-fu/bees/backtweets.rb +49 -0
- data/lib/dash-fu/bees/backtweets.yml +9 -0
- data/lib/dash-fu/bees/github.rb +103 -0
- data/lib/dash-fu/bees/github.yml +15 -0
- data/lib/dash-fu/bees/github_issues.rb +90 -0
- data/lib/dash-fu/bees/github_issues.yml +14 -0
- data/lib/dash-fu/bees/ruby_gems.rb +54 -0
- data/lib/dash-fu/bees/ruby_gems.yml +6 -0
- data/test/api_keys.yml +1 -0
- data/test/backtweets_test.rb +148 -0
- data/test/cassettes/backtweets.yml +75 -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 +252 -0
- data/test/helpers/activity.rb +21 -0
- data/test/helpers/metric.rb +25 -0
- data/test/helpers/person.rb +25 -0
- data/test/helpers/source.rb +107 -0
- data/test/helpers/test.rb +76 -0
- data/test/ruby_gems_test.rb +164 -0
- data/test/setup.rb +32 -0
- data/test/test.log +934 -0
- metadata +165 -0
@@ -0,0 +1,49 @@
|
|
1
|
+
module DashFu::Bee
|
2
|
+
# Track Twitter links using the Backtype API.
|
3
|
+
class Backtweets
|
4
|
+
include DashFu::Bee
|
5
|
+
|
6
|
+
def setup(source, params)
|
7
|
+
url = params["url"].strip.downcase.sub(/^http(s?):\/\//, "")
|
8
|
+
source["source.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, callback)
|
19
|
+
session "backtweets.com" do |http|
|
20
|
+
http.get_json "/search.json?key=#{api_key}&q=#{Rack::Utils.escape source["url"]}" do |status, json|
|
21
|
+
if status == 200 && tweets = json["tweets"]
|
22
|
+
last_tweet_id = source["last_tweet_id"]
|
23
|
+
new_ones = tweets.take_while { |tweet| tweet["tweet_id"] != last_tweet_id }.reverse
|
24
|
+
new_ones.each do |tweet|
|
25
|
+
screen_name, id = tweet["tweet_from_user"], tweet["tweet_id"]
|
26
|
+
url = "http://twitter.com/#{screen_name}/#{id}"
|
27
|
+
html = <<-HTML
|
28
|
+
<a href="#{url}">tweeted</a>:
|
29
|
+
<blockquote>#{tweet["tweet_text"]}</blockquote>
|
30
|
+
HTML
|
31
|
+
person = { fullname: screen_name, identities: %W{twitter.com:#{screen_name}},
|
32
|
+
photo_url: "http://img.tweetimag.es/i/#{screen_name}_n" }
|
33
|
+
callback.activity! uid: id, url: url, html: html, tags: %w{twitter mention},
|
34
|
+
timestamp: Time.parse(tweet["tweet_created_at"]).utc, person: person
|
35
|
+
source["last_tweet_id"] = tweet["tweet_id"]
|
36
|
+
end
|
37
|
+
callback.set! tweets: json["totalresults"].to_i
|
38
|
+
else
|
39
|
+
callback.error! "Last request didn't go as expected, trying again later"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def meta(source)
|
46
|
+
[ { text: "Search yourself", url: "http://backtweets.com/search?q=#{URI.escape source["url"]}" } ]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
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,103 @@
|
|
1
|
+
module DashFu::Bee
|
2
|
+
# Track Github commits, watchers and forks.
|
3
|
+
class Github
|
4
|
+
include DashFu::Bee
|
5
|
+
|
6
|
+
def setup(source, params)
|
7
|
+
repo = params["repo"].strip
|
8
|
+
source["source.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, callback)
|
24
|
+
session "github.com", 443 do |http|
|
25
|
+
auth = { username: "#{source["username"]}/token", password: source["api_token"] }
|
26
|
+
http.get_json "/api/v2/json/repos/show/#{source["repo"]}", auth do |status, json|
|
27
|
+
case status
|
28
|
+
when 200
|
29
|
+
if repository = json["repository"]
|
30
|
+
source.update repository.slice(*%w{description url homepage})
|
31
|
+
callback.set! forks: repository["forks"], watchers: repository["watchers"]
|
32
|
+
end
|
33
|
+
when 404, 400
|
34
|
+
callback.error! "Could not find the repository #{source["repo"]}"
|
35
|
+
when 401
|
36
|
+
callback.error! "You are not authorized to access this repository, or invalid username/password"
|
37
|
+
else
|
38
|
+
callback.error! "Last request didn't go as expected, trying again later"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
http.get_json "/api/v2/json/commits/list/#{source["repo"]}/#{source["branch"]}", auth do |status, json|
|
43
|
+
case status
|
44
|
+
when 200
|
45
|
+
if commits = json["commits"]
|
46
|
+
last_seen_id = source["last_commit"]["id"] if source["last_commit"]
|
47
|
+
new_ones = commits.take_while { |commit| commit["id"] != last_seen_id }
|
48
|
+
merged = new_ones.inject([]) do |all, commit|
|
49
|
+
last = all.last.last unless all.empty?
|
50
|
+
if last && last["committer"]["email"] == commit["committer"]["email"] &&
|
51
|
+
Time.parse(last["committed_date"]) - Time.parse(commit["committed_date"]) < 1.hour
|
52
|
+
all.last << commit
|
53
|
+
else
|
54
|
+
all << [commit]
|
55
|
+
end
|
56
|
+
all
|
57
|
+
end
|
58
|
+
|
59
|
+
merged.reverse.each do |commits|
|
60
|
+
first = commits.first
|
61
|
+
committer = first["committer"]
|
62
|
+
person = { fullname: committer["name"], identities: %W{github.com:#{committer["login"]}}, email: committer["email"] }
|
63
|
+
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>} }
|
64
|
+
html = %{pushed to #{h source["branch"]} at <a href="http://github.com/#{source["repo"]}">#{h source["repo"]}</a>:\n#{messages.join("\n")}}
|
65
|
+
callback.activity! uid: first["id"], html: html, url: first["url"], tags: %w{push},
|
66
|
+
timestamp: Time.parse(first["committed_date"]).utc, person: person
|
67
|
+
end
|
68
|
+
callback.inc! commits: new_ones.count
|
69
|
+
if last_commit = commits.first
|
70
|
+
source.update "last_commit"=>last_commit.slice("id", "message", "url").
|
71
|
+
merge("timestamp"=>Time.parse(last_commit["committed_date"]).utc)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
when 404, 400
|
75
|
+
callback.error! "Could not find the branch #{source["branch"]}"
|
76
|
+
else
|
77
|
+
callback.error! "Github: #{response.code} #{response.message}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def meta(source)
|
84
|
+
meta = [ { title: "Repository", text: source["repo"], url: source["url"] },
|
85
|
+
{ title: "Branch", text: source["branch"] },
|
86
|
+
{ text: source["description"] },
|
87
|
+
{ title: "Home page", url: source["homepage"] } ]
|
88
|
+
if last_commit = source["last_commit"]
|
89
|
+
meta << { title: "Commit", text: last_commit["message"], url: last_commit["url"] }
|
90
|
+
end
|
91
|
+
meta
|
92
|
+
end
|
93
|
+
|
94
|
+
protected
|
95
|
+
|
96
|
+
def http_request(http, source, path)
|
97
|
+
get = Net::HTTP::Get.new(path.gsub(":repo", URI.escape(source["repo"])).gsub(":branch", URI.escape(source["branch"])))
|
98
|
+
get.basic_auth "#{source["username"]}/token", source["api_token"] unless source["username"].blank? && source["api_token"].blank?
|
99
|
+
http.request(get)
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
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,90 @@
|
|
1
|
+
module DashFu::Bee
|
2
|
+
# Track open/closed Github issues.
|
3
|
+
class GithubIssues
|
4
|
+
include DashFu::Bee
|
5
|
+
|
6
|
+
def setup(source, params)
|
7
|
+
repo = params["repo"].to_s.strip
|
8
|
+
source["source.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, callback)
|
22
|
+
session "github.com", 443 do |http|
|
23
|
+
auth = { username: "#{source["username"]}/token", password: source["api_token"] }
|
24
|
+
http.get_json "/api/v2/json/issues/list/#{source["repo"]}/open" do |status, json|
|
25
|
+
case status
|
26
|
+
when 200
|
27
|
+
if open = json["issues"]
|
28
|
+
callback.set! open: open.count
|
29
|
+
open_ids = Set.new(source["open_ids"])
|
30
|
+
open.reject { |issue| open_ids.include?(issue["number"]) }.reverse.each do |issue|
|
31
|
+
sha = Digest::SHA1.hexdigest([source["repo"], "open", issue["number"], issue["updated_at"]].join(":"))
|
32
|
+
url = "http://github.com/#{source["repo"]}/issues#issue/#{issue["number"]}"
|
33
|
+
html = <<-HTML
|
34
|
+
opened <a href="#{url}">issue #{issue["number"]}</a> on #{source["repo"]}:
|
35
|
+
<blockquote>#{h issue["title"]}</blockquote>
|
36
|
+
HTML
|
37
|
+
callback.activity! uid: sha, url: url, html: html, tags: %w{issue opened},
|
38
|
+
timestamp: Time.parse(issue["created_at"]).utc
|
39
|
+
open_ids << issue["number"]
|
40
|
+
end
|
41
|
+
source["open_ids"] = open_ids.to_a
|
42
|
+
end
|
43
|
+
when 404, 400
|
44
|
+
callback.error! "Could not find the repository #{source["repo"]}"
|
45
|
+
when 401
|
46
|
+
callback.error! "You are not authorized to access this repository, or invalid username/password"
|
47
|
+
else
|
48
|
+
callback.error! "Last request didn't go as expected, trying again later"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
http.get_json "/api/v2/json/issues/list/#{source["repo"]}/closed" do |status, json|
|
53
|
+
case status
|
54
|
+
when 200
|
55
|
+
if closed = json["issues"]
|
56
|
+
callback.set! closed: closed.count
|
57
|
+
closed_ids = Set.new(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
|
+
callback.activity! uid: sha, url: url, html: html, tags: %w{issue closed},
|
66
|
+
timestamp: Time.parse(issue["closed_at"]).utc
|
67
|
+
closed_ids << issue["number"]
|
68
|
+
end
|
69
|
+
source["closed_ids"] = closed_ids.to_a
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def meta(source)
|
77
|
+
[ { title: "On Github", url: "http://github.com/#{source["repo"]}/issues" } ]
|
78
|
+
end
|
79
|
+
|
80
|
+
protected
|
81
|
+
|
82
|
+
def http_request(http, source, state)
|
83
|
+
path = "/api/v2/json/issues/list/#{URI.escape source["repo"]}/#{state}"
|
84
|
+
get = Net::HTTP::Get.new(path)
|
85
|
+
get.basic_auth "#{source["username"]}/token", source["api_token"] unless source["username"].blank? && source["api_token"].blank?
|
86
|
+
http.request(get)
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
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,54 @@
|
|
1
|
+
module DashFu::Bee
|
2
|
+
# Track activity (downloads and releases) of a Ruby Gem from rubygems.org.
|
3
|
+
class RubyGems
|
4
|
+
include DashFu::Bee
|
5
|
+
|
6
|
+
def setup(source, params)
|
7
|
+
gem_name = params["gem_name"].to_s.strip
|
8
|
+
source["source.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, callback)
|
19
|
+
gem_name = source["gem_name"]
|
20
|
+
session "rubygems.org" do |http|
|
21
|
+
http.get_json "/api/v1/gems/#{gem_name}.json" do |status, json|
|
22
|
+
case status
|
23
|
+
when 200
|
24
|
+
if source["version"]
|
25
|
+
current, latest = Gem::Version.new(source["version"]), Gem::Version.new(json["version"])
|
26
|
+
if latest > current
|
27
|
+
html = "released <a href=\"http://rubygems.org/gems/#{gem_name}/versions/#{latest}\">#{gem_name} version #{latest}</a>."
|
28
|
+
person = { fullname: "RubyGems", identities: "url:http://rubygems.org/", photo_url: "http://dash-fu.com/images/sources/ruby.png" }
|
29
|
+
callback.activity! uid: "#{gem_name}-#{latest}", url: "http://rubygems.org/gems/#{gem_name}",
|
30
|
+
html: html, tags: %w{release}, person: person
|
31
|
+
end
|
32
|
+
end
|
33
|
+
source["version"] = json["version"]
|
34
|
+
source.update json.slice(*%w{homepage_uri project_uri info authors info})
|
35
|
+
callback.set! downloads: json["downloads"]
|
36
|
+
when 404
|
37
|
+
callback.error! "Could not find the Gem #{gem_name}"
|
38
|
+
else
|
39
|
+
callback.error! "Last request didn't go as expected, trying again later"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def meta(source)
|
46
|
+
[ { title: "Project", text: source["gem_name"], url: source["homepage_uri"] || source["project_uri"] },
|
47
|
+
{ text: source["info"] },
|
48
|
+
{ title: "Version", text: source["version"] },
|
49
|
+
{ title: "Authors", text: source["authors"] },
|
50
|
+
{ title: "Source", text: "RubyGems", url: source["project_uri"] } ]
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
data/test/api_keys.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
backtweets: "4554feeeeeeeeeeeeeee"
|
@@ -0,0 +1,148 @@
|
|
1
|
+
require_relative "setup"
|
2
|
+
|
3
|
+
test DashFu::Bee::Backtweets do
|
4
|
+
context "setup" do
|
5
|
+
setup { source.setup "url"=>"vanity.labnotes.org" }
|
6
|
+
|
7
|
+
should "use normalize URL and remove scheme" do
|
8
|
+
source.setup "url"=>"http://Vanity.Labnotes.Org"
|
9
|
+
assert_equal "Tweets for vanity.labnotes.org", source.name
|
10
|
+
end
|
11
|
+
|
12
|
+
context "metric" do
|
13
|
+
subject { source.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) { source.setup "url"=>" " }
|
33
|
+
end
|
34
|
+
|
35
|
+
should "create valid metric" do
|
36
|
+
source.setup "url"=>"vanity.labnotes.org"
|
37
|
+
assert source.valid?
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
context "update" do
|
43
|
+
setup { source.setup "url"=>"vanity.labnotes.org" }
|
44
|
+
|
45
|
+
should "include API key" do
|
46
|
+
source.update
|
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
|
+
stub_request(:get, /backtweets.com/).to_return body: interactions.first.response.body
|
52
|
+
source.setup "url"=>"need&this=encoded"
|
53
|
+
source.update
|
54
|
+
assert_requested :get, "http://backtweets.com/search.json?key=#{bee.send :api_key}&q=need%26this%3Dencoded"
|
55
|
+
end
|
56
|
+
|
57
|
+
should "handle errors" do
|
58
|
+
stub_request(:get, interactions.first.uri).to_return status: 500
|
59
|
+
source.update
|
60
|
+
assert_equal "Last request didn't go as expected, trying again later", source.last_error
|
61
|
+
end
|
62
|
+
|
63
|
+
should "handle invalid document entity" do
|
64
|
+
stub_request(:get, interactions.first.uri).to_return body: "Not JSON"
|
65
|
+
source.update
|
66
|
+
assert_equal "Last request didn't go as expected, trying again later", source.last_error
|
67
|
+
end
|
68
|
+
|
69
|
+
should "capture number of tweets" do
|
70
|
+
source.update
|
71
|
+
assert_equal({ tweets: 2 }, source.metric.values)
|
72
|
+
end
|
73
|
+
|
74
|
+
context "activity" do
|
75
|
+
setup { source.update }
|
76
|
+
subject { source.activity }
|
77
|
+
|
78
|
+
should "capture tweet identifier" do
|
79
|
+
assert_equal "20959239143", subject.uid
|
80
|
+
end
|
81
|
+
|
82
|
+
should "capture tweet text" do
|
83
|
+
assert_equal <<-HTML, subject.html
|
84
|
+
<a href="http://twitter.com/dude/20959239143">tweeted</a>:
|
85
|
+
<blockquote>Super awesome <a href="http://vanity.labnotes.org/">http://j.mp/aOrUnsz</a></blockquote>
|
86
|
+
HTML
|
87
|
+
end
|
88
|
+
|
89
|
+
should "capture tweet URL" do
|
90
|
+
assert_equal "http://twitter.com/dude/20959239143", subject.url
|
91
|
+
end
|
92
|
+
|
93
|
+
should "capture tweet timestamp" do
|
94
|
+
assert_equal Time.parse("2010-8-12T08:30:04").utc, subject.timestamp
|
95
|
+
end
|
96
|
+
|
97
|
+
should "tag as tweeter and mention" do
|
98
|
+
assert_contains subject.tags, "twitter"
|
99
|
+
assert_contains subject.tags, "mention"
|
100
|
+
end
|
101
|
+
|
102
|
+
should "be valid" do
|
103
|
+
assert subject.valid?
|
104
|
+
end
|
105
|
+
|
106
|
+
context "author" do
|
107
|
+
subject { source.activity.person }
|
108
|
+
|
109
|
+
should "capture full name" do
|
110
|
+
assert_equal "dude", subject.fullname
|
111
|
+
end
|
112
|
+
|
113
|
+
should "capture screen name" do
|
114
|
+
assert_contains subject.identities, "twitter.com:dude"
|
115
|
+
end
|
116
|
+
|
117
|
+
should "capture photo" do
|
118
|
+
assert_equal "http://img.tweetimag.es/i/dude_n", subject.photo_url
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
context "repeating" do
|
124
|
+
setup do
|
125
|
+
source.update
|
126
|
+
second = interactions.last
|
127
|
+
stub_request(:get, second.uri).to_return body: second.response.body
|
128
|
+
source.update
|
129
|
+
end
|
130
|
+
|
131
|
+
should "capture new tweets" do
|
132
|
+
assert_equal 3, source.activities.count
|
133
|
+
assert_match "The last of its kind", source.activity.html
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
context "meta" do
|
141
|
+
setup { source.setup "url"=>"vanity.labnotes.org" }
|
142
|
+
subject { source.meta }
|
143
|
+
|
144
|
+
should "link to search results" do
|
145
|
+
assert subject.include?(text: "Search yourself", url: "http://backtweets.com/search?q=vanity.labnotes.org")
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|