dash-bees 0.21 → 0.22

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 CHANGED
@@ -1,3 +1,16 @@
1
+ 2010-09-23 v0.22 Icons, origins and bug fixes.
2
+
3
+ API change: using special field names (DashFu::Bee::Fields).
4
+
5
+ Added icon URL, and each source gets to report text/URL to the origin (e.g.
6
+ service, search page). These will be shown separate from metadata.
7
+
8
+ Fixed tweet count for Backtweet source.
9
+
10
+ Fixed handling of feeds that lack a title.
11
+
12
+ GitHub/issues now accept repository URL.
13
+
1
14
  2010-09-20 v0.21 Improvements and bug fixes for feed source
2
15
 
3
16
  Truncates long contents.
data/Gemfile CHANGED
@@ -9,7 +9,6 @@ end
9
9
  group :test do
10
10
  gem "awesome_print"
11
11
  gem "builder"
12
- gem "shoulda"
13
12
  gem "timecop"
14
13
  gem "vcr"
15
14
  gem "webmock"
@@ -1,5 +1,5 @@
1
1
  $: << File.dirname(__FILE__) + "/lib"
2
- require "dash-fu/bee"
2
+ require "dash-fu/bee/version"
3
3
 
4
4
  Gem::Specification.new do |spec|
5
5
  spec.name = "dash-bees"
@@ -3,14 +3,18 @@ require "net/http"
3
3
  require "uri"
4
4
  require "open-uri"
5
5
  require "rack"
6
+ require "nokogiri"
7
+ require "sanitize"
8
+ require "dash-fu/bee/version"
9
+ require "dash-fu/bee/http_session"
10
+ require "dash-fu/bee/fields"
6
11
 
7
12
  # See http://dash-fu.com
8
13
  module DashFu
9
14
 
10
15
  # The README covers it all.
11
16
  module Bee
12
-
13
- VERSION = "0.21"
17
+ include DashFu::Bee::Fields
14
18
 
15
19
  class << self
16
20
  attr_accessor :logger
@@ -132,47 +136,7 @@ module DashFu
132
136
  http = Net::HTTP.new(host, port)
133
137
  http.use_ssl = true if port == 443
134
138
  http.start do |http|
135
- yield Session.new(http)
136
- end
137
- end
138
-
139
- # HTTP Session.
140
- class Session
141
- def initialize(http) #:nodoc:
142
- @http = http
143
- end
144
-
145
- # Make a GET request and yield response to the block. Response consists of
146
- # three arguments: status code, response body and response headers. The
147
- # block may be called asynchronoulsy.
148
- def get(path, headers = {}, &block)
149
- response = @http.request(get_request(path, headers || {}))
150
- yield response.code.to_i, response.body, {}
151
- end
152
-
153
- # Make a GET request and yield response to the block. If the response
154
- # status is 200 the second argument is the response JSON object. The block
155
- # may be called asynchronously.
156
- def get_json(path, headers = {}, &block)
157
- response = @http.request(get_request(path, headers || {}))
158
- if Net::HTTPOK === response
159
- json = JSON.parse(response.body) rescue nil
160
- if json
161
- yield response.code.to_i, json, {}
162
- else
163
- yield 500, "Not a JSON document", {}
164
- end
165
- else
166
- yield response.code.to_i, response.message, {}
167
- end
168
- end
169
-
170
- protected
171
-
172
- def get_request(path, headers)
173
- request = Net::HTTP::Get.new(path)
174
- request.basic_auth headers[:username], headers[:password] if headers[:username] && headers[:password]
175
- request
139
+ yield DashFu::HTTPSession.new(http)
176
140
  end
177
141
  end
178
142
 
@@ -0,0 +1,17 @@
1
+ module DashFu::Bee
2
+ module Fields
3
+
4
+ # Suggested name for the source.
5
+ SOURCE_NAME = "_name"
6
+ # Information about the origin of the data, using two fields, text and URL.
7
+ ORIGIN = "_origin"
8
+ # URL for an icon for this feed. Icon should have 1:1 aspect ration,
9
+ # 48px/48px is the ideal isize.
10
+ ICON = "_icon"
11
+
12
+ # If this source has a metric, enumerates the columns.
13
+ METRIC_COLUMNS = "_metric_columns"
14
+ # True if there's a metric and it collects totals
15
+ METRIC_TOTALS = "_metric_totals"
16
+ end
17
+ end
@@ -0,0 +1,44 @@
1
+ require "json"
2
+ require "net/http"
3
+
4
+ module DashFu
5
+ # HTTP Session.
6
+ class HTTPSession
7
+ def initialize(http) #:nodoc:
8
+ @http = http
9
+ end
10
+
11
+ # Make a GET request and yield response to the block. Response consists of
12
+ # three arguments: status code, response body and response headers. The
13
+ # block may be called asynchronoulsy.
14
+ def get(path, headers = {}, &block)
15
+ response = @http.request(get_request(path, headers || {}))
16
+ yield response.code.to_i, response.body, {}
17
+ end
18
+
19
+ # Make a GET request and yield response to the block. If the response
20
+ # status is 200 the second argument is the response JSON object. The block
21
+ # may be called asynchronously.
22
+ def get_json(path, headers = {}, &block)
23
+ response = @http.request(get_request(path, headers || {}))
24
+ if Net::HTTPOK === response
25
+ json = JSON.parse(response.body) rescue nil
26
+ if json
27
+ yield response.code.to_i, json, {}
28
+ else
29
+ yield 500, "Not a JSON document", {}
30
+ end
31
+ else
32
+ yield response.code.to_i, response.message, {}
33
+ end
34
+ end
35
+
36
+ protected
37
+
38
+ def get_request(path, headers)
39
+ request = Net::HTTP::Get.new(path)
40
+ request.basic_auth headers[:username], headers[:password] if headers[:username] && headers[:password]
41
+ request
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,5 @@
1
+ module DashFu
2
+ module Bee
3
+ VERSION = "0.22"
4
+ end
5
+ end
@@ -4,15 +4,21 @@ module DashFu::Bee
4
4
  include DashFu::Bee
5
5
 
6
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
7
+ uri = URI.parse(params["url"]).normalize
8
+ uri = URI.parse("http://#{uri}").normalize if uri.scheme.nil?
9
+ source["url"] = uri.to_s
10
+ short = source["url"].gsub(/^https?:\/\//, "")
11
+ short.gsub!(/\/$/, "") if uri.path == "/"
12
+
13
+ source[SOURCE_NAME] = "Tweets for #{short}"
14
+ source[ORIGIN] = { text: "Backtweets for #{short}", url: "http://backtweets.com/search?q=#{URI.escape source["url"]}" }
15
+ source[METRIC_COLUMNS] = [{ id: "tweets", label: "Tweets" }]
16
+ source[METRIC_TOTALS] = true
12
17
  end
13
18
 
14
19
  def validate(source)
15
- raise "Missing URL" if source["url"].blank?
20
+ uri = URI.parse(source["url"])
21
+ raise "This doesn't look like a valid page name, please check your URL" unless uri.absolute? && uri.scheme == "http" && !uri.host.blank?
16
22
  end
17
23
 
18
24
  def update(source, callback)
@@ -20,6 +26,9 @@ module DashFu::Bee
20
26
  http.get_json "/search.json?key=#{api_key}&q=#{Rack::Utils.escape source["url"]}" do |status, json|
21
27
  if status == 200 && tweets = json["tweets"]
22
28
  last_tweet_id = source["last_tweet_id"]
29
+ # Since we don't get much in terms of history, we only look at this
30
+ # count once; afterwards, we increment for each tweet we find.
31
+ callback.set! tweets: json["totalresults"].to_i unless last_tweet_id
23
32
  new_ones = tweets.take_while { |tweet| tweet["tweet_id"] != last_tweet_id }.reverse
24
33
  new_ones.each do |tweet|
25
34
  screen_name, id = tweet["tweet_from_user"], tweet["tweet_id"]
@@ -30,11 +39,11 @@ module DashFu::Bee
30
39
  HTML
31
40
  person = { fullname: screen_name, identities: %W{twitter.com:#{screen_name}},
32
41
  photo_url: "http://img.tweetimag.es/i/#{screen_name}_n" }
33
- callback.activity! uid: id, url: url, html: html, tags: %w{twitter mention},
42
+ callback.activity! uid: id, url: url, html: html, tags: %w{twitter mention}, public: true,
34
43
  timestamp: Time.parse(tweet["tweet_created_at"]).utc, person: person
35
44
  source["last_tweet_id"] = tweet["tweet_id"]
36
45
  end
37
- callback.set! tweets: json["totalresults"].to_i
46
+ callback.inc! tweets: new_ones.count if last_tweet_id
38
47
  else
39
48
  callback.error! "Last request didn't go as expected, trying again later"
40
49
  end
@@ -42,8 +51,5 @@ module DashFu::Bee
42
51
  end
43
52
  end
44
53
 
45
- def meta(source)
46
- [ { text: "Search yourself", url: "http://backtweets.com/search?q=#{URI.escape source["url"]}" } ]
47
- end
48
54
  end
49
55
  end
@@ -1,37 +1,40 @@
1
- require "sanitize"
2
- require "nokogiri"
3
-
4
1
  module DashFu::Bee
5
2
  # Track Web feed (Atom or RSS).
6
3
  class Feed
7
4
  include DashFu::Bee
8
5
 
9
6
  def setup(source, params)
10
- source["url"] = params["url"].strip
11
- source["source.name"] = "Web feed"
12
- end
7
+ unfeed = params["url"].strip.gsub(/^feed:\/\//i, "http://").gsub(/^feed:/i, "")
8
+ uri = URI.parse(unfeed).normalize
9
+ uri = URI.parse("http://#{uri}").normalize if uri.scheme.nil?
10
+ source["url"] = uri.to_s
11
+ return unless valid_uri?(uri)
13
12
 
14
- def validate(source)
15
- unfeed = source["url"].gsub(/^feed:\/\//, "http://").gsub(/^feed:/, "")
16
- uri = URI.parse(unfeed) rescue nil
17
- raise "Not a valid URL" unless uri && uri.absolute?
18
- raise "Only HTTP/S URLs supported" unless uri.scheme == "http" || uri.scheme == "https"
19
13
  begin
20
14
  uri.open read_timeout: 3, redirect: true do |io|
21
15
  code = io.status.first
22
16
  raise "Cannot read this feed, got status code #{code}" unless code == "200"
23
17
  feed = (Nokogiri::XML(io.read)>"feed").first
24
- source["source.name"] = source["title"] = get_text(feed>"title").strip
18
+ title = get_text(feed>"title").strip
19
+ title = uri.to_s if title.blank?
20
+
21
+ source[SOURCE_NAME] = title
25
22
  alt = (feed>"link[rel=alternate]").first
26
- source["permalink"] = alt["href"] if alt
27
- source["logo"] = (feed>"logo").text
28
- source["url"] = uri.to_s
23
+ permalink = alt ? alt["href"].strip : uri.to_s
24
+ source[ORIGIN] = { text: title, url: permalink }
25
+ source[ICON] = (feed>"icon").text
29
26
  end
30
27
  rescue
28
+ puts $!
31
29
  raise "Cannot read this feed: is it down for you or just for us?"
32
30
  end
33
31
  end
34
32
 
33
+ def validate(source)
34
+ uri = URI.parse(source["url"])
35
+ raise "This doesn't look like a valid feed, please check your URL" unless valid_uri?(uri)
36
+ end
37
+
35
38
  def update(source, callback)
36
39
  uri = URI.parse(source["url"])
37
40
  session uri.host, uri.port do |http|
@@ -77,9 +80,7 @@ module DashFu::Bee
77
80
  end
78
81
 
79
82
  def meta(source)
80
- meta = []
81
- meta << { title: "Source", text: source["title"], url: source["permalink"] }
82
- meta
83
+ []
83
84
  end
84
85
 
85
86
  def get_text(elements)
@@ -102,5 +103,9 @@ module DashFu::Bee
102
103
  Sanitize.clean(truncate_html(html.to_s.strip, length), Sanitize::Config::BASIC.merge(output: :html)).strip
103
104
  end
104
105
 
106
+ def valid_uri?(uri)
107
+ uri.absolute? && (uri.scheme == "http" || uri.scheme == "https") && !uri.host.blank?
108
+ end
109
+
105
110
  end
106
111
  end
@@ -1,7 +1,7 @@
1
1
  en:
2
2
  description: "Gather activities from an Atom feed"
3
3
  inputs: |-
4
- <label>Feed URL <input type="text" name="source[url]" size="50"></label>
4
+ <label>Feed URL <input type="text" name="source[url]" size="70"></label>
5
5
  notes: |-
6
6
  <ul>
7
7
  </ul>
@@ -3,16 +3,22 @@ module DashFu::Bee
3
3
  class Github
4
4
  include DashFu::Bee
5
5
 
6
+ ICON_URL = "http://dash-fu.com/images/sources/github.png"
7
+
6
8
  def setup(source, params)
7
9
  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
10
+ repo = $1 if repo =~ /^http:\/\/github\.com\/([\w-]+\/[\w-]+)/i
11
11
  source["repo"] = repo
12
12
  branch = params["branch"].strip
13
13
  source["branch"] = branch.blank? ? "master" : branch
14
14
  source["username"] = params["username"]
15
15
  source["api_token"] = params["api_token"]
16
+
17
+ source[SOURCE_NAME] = "GitHub: #{repo}"
18
+ source[METRIC_COLUMNS] = [{ id: "commits", label: "Commits" }, { id: "watchers", label: "Watchers" }, { id: "forks", label: "Forks" }]
19
+ source[METRIC_TOTALS] = true
20
+ source[ORIGIN] = { text: repo, url: "http://github.com/#{repo}" }
21
+ source[ICON] = ICON_URL
16
22
  end
17
23
 
18
24
  def validate(source)
@@ -22,7 +28,7 @@ module DashFu::Bee
22
28
 
23
29
  def update(source, callback)
24
30
  session "github.com", 443 do |http|
25
- auth = { username: "#{source["username"]}/token", password: source["api_token"] }
31
+ auth = { username: "#{source["username"]}/token", password: source["api_token"] } unless source["username"].blank?
26
32
  http.get_json "/api/v2/json/repos/show/#{source["repo"]}", auth do |status, json|
27
33
  case status
28
34
  when 200
@@ -63,7 +69,7 @@ module DashFu::Bee
63
69
  person = { fullname: committer["name"], identities: Array.wrap(identity), email: committer["email"] }
64
70
  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>} }
65
71
  html = %{pushed to #{h source["branch"]} at <a href="http://github.com/#{source["repo"]}">#{h source["repo"]}</a>:\n#{messages.join("\n")}}
66
- callback.activity! uid: first["id"], html: html, url: first["url"], tags: %w{push},
72
+ callback.activity! uid: first["id"], html: html, url: first["url"], tags: %w{push}, public: auth.nil?,
67
73
  timestamp: Time.parse(first["committed_date"]).utc, person: person
68
74
  end
69
75
  callback.inc! commits: new_ones.count
@@ -82,8 +88,7 @@ module DashFu::Bee
82
88
  end
83
89
 
84
90
  def meta(source)
85
- meta = [ { title: "Repository", text: source["repo"], url: source["url"] },
86
- { title: "Branch", text: source["branch"] },
91
+ meta = [ { title: "Branch", text: source["branch"] },
87
92
  { text: source["description"] },
88
93
  { title: "Home page", url: source["homepage"] } ]
89
94
  if last_commit = source["last_commit"]
@@ -3,14 +3,20 @@ module DashFu::Bee
3
3
  class GithubIssues
4
4
  include DashFu::Bee
5
5
 
6
+ ICON_URL = "http://dash-fu.com/images/sources/github.png"
7
+
6
8
  def setup(source, params)
7
9
  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
10
+ repo = $1 if repo =~ /^http:\/\/github\.com\/([\w-]+\/[\w-]+)/i
11
11
  source["repo"] = repo
12
12
  source["username"] = params["username"]
13
13
  source["api_token"] = params["api_token"]
14
+
15
+ source[SOURCE_NAME] = "GitHub issues for #{repo}"
16
+ source[METRIC_COLUMNS] = [{ id: "open", label: "Open issues" }, { id: "closed", label: "Closed issues" }]
17
+ source[METRIC_TOTALS] = true
18
+ source[ORIGIN] = { text: repo, url: "http://github.com/#{repo}" }
19
+ source[ICON] = ICON_URL
14
20
  end
15
21
 
16
22
  def validate(source)
@@ -19,9 +25,9 @@ module DashFu::Bee
19
25
  end
20
26
 
21
27
  def update(source, callback)
22
- github = { fullname: "GitHub", identities: %w{site:github.com}, photo_url: "http://github.com/images/error/octocat_happy.gif" }
28
+ github = { fullname: "GitHub", identities: %w{site:github.com}, photo_url: ICON_URL }
23
29
  session "github.com", 443 do |http|
24
- auth = { username: "#{source["username"]}/token", password: source["api_token"] }
30
+ auth = { username: "#{source["username"]}/token", password: source["api_token"] } unless source["username"].blank?
25
31
  http.get_json "/api/v2/json/issues/list/#{source["repo"]}/open" do |status, json|
26
32
  case status
27
33
  when 200
@@ -35,7 +41,7 @@ module DashFu::Bee
35
41
  opened <a href="#{url}">issue #{issue["number"]}</a> on #{source["repo"]}:
36
42
  <blockquote>#{h issue["title"]}</blockquote>
37
43
  HTML
38
- callback.activity! uid: sha, url: url, html: html, tags: %w{issue opened},
44
+ callback.activity! uid: sha, url: url, html: html, tags: %w{issue opened}, public: !auth,
39
45
  person: github, timestamp: Time.parse(issue["created_at"]).utc
40
46
  open_ids << issue["number"]
41
47
  end
@@ -63,7 +69,7 @@ opened <a href="#{url}">issue #{issue["number"]}</a> on #{source["repo"]}:
63
69
  closed <a href="#{url}">issue #{issue["number"]}</a> on #{source["repo"]}:
64
70
  <blockquote>#{h issue["title"]}</blockquote>
65
71
  HTML
66
- callback.activity! uid: sha, url: url, html: html, tags: %w{issue closed},
72
+ callback.activity! uid: sha, url: url, html: html, tags: %w{issue closed}, public: !auth,
67
73
  person: github, timestamp: Time.parse(issue["closed_at"]).utc
68
74
  closed_ids << issue["number"]
69
75
  end
@@ -74,9 +80,5 @@ closed <a href="#{url}">issue #{issue["number"]}</a> on #{source["repo"]}:
74
80
  end
75
81
  end
76
82
 
77
- def meta(source)
78
- [ { title: "On GitHub", url: "http://github.com/#{source["repo"]}/issues" } ]
79
- end
80
-
81
83
  end
82
84
  end
@@ -3,12 +3,17 @@ module DashFu::Bee
3
3
  class RubyGems
4
4
  include DashFu::Bee
5
5
 
6
+ ICON_URL = "http://dash-fu.com/images/sources/ruby.png"
7
+
6
8
  def setup(source, params)
7
9
  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
10
  source["gem_name"] = gem_name
11
+
12
+ source[SOURCE_NAME] = "RubyGems: #{gem_name}"
13
+ source[METRIC_COLUMNS] = [{ id: "downloads", label: "Downloads" }]
14
+ source[METRIC_TOTALS] = true
15
+ source[ORIGIN] = { text: "RubyGems: #{gem_name}", url: "http://rubygems.org/gems/#{gem_name}" }
16
+ source[ICON] = ICON_URL
12
17
  end
13
18
 
14
19
  def validate(source)
@@ -25,8 +30,8 @@ module DashFu::Bee
25
30
  current, latest = Gem::Version.new(source["version"]), Gem::Version.new(json["version"])
26
31
  if latest > current
27
32
  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}",
33
+ person = { fullname: "RubyGems", identities: "url:http://rubygems.org/", photo_url: ICON_URL }
34
+ callback.activity! uid: "#{gem_name}-#{latest}", url: "http://rubygems.org/gems/#{gem_name}", public: true,
30
35
  html: html, tags: %w{release}, person: person
31
36
  end
32
37
  end
@@ -46,8 +51,7 @@ module DashFu::Bee
46
51
  [ { title: "Project", text: source["gem_name"], url: source["homepage_uri"] || source["project_uri"] },
47
52
  { text: source["info"] },
48
53
  { title: "Version", text: source["version"] },
49
- { title: "Authors", text: source["authors"] },
50
- { title: "Source", text: "RubyGems", url: source["project_uri"] } ]
54
+ { title: "Authors", text: source["authors"] } ]
51
55
  end
52
56
 
53
57
  end