engagement 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +71 -0
- data/Rakefile +8 -0
- data/engagement.gemspec +31 -0
- data/lib/engagement.rb +5 -0
- data/lib/engagement/comment_counter.rb +20 -0
- data/lib/engagement/comment_counter/disqus.rb +36 -0
- data/lib/engagement/comment_counter/hacker_news.rb +13 -0
- data/lib/engagement/comment_counter/reddit.rb +32 -0
- data/lib/engagement/comment_counter/threaded.rb +28 -0
- data/lib/engagement/version.rb +3 -0
- data/spec/cassettes/Engagement_CommentCounter_Disqus/when_we_don_t_own_the_url/returns_zero_when_we_don_t_own_the_url.yml +48 -0
- data/spec/cassettes/Engagement_CommentCounter_Disqus/when_we_own_the_url/returns_the_number_of_comments_for_the_given_url.yml +89 -0
- data/spec/cassettes/Engagement_CommentCounter_HackerNews/returns_the_number_of_comments_for_the_given_url.yml +56 -0
- data/spec/cassettes/Engagement_CommentCounter_Reddit/with_a_single_listing_returned/.yml +123 -0
- data/spec/cassettes/Engagement_CommentCounter_Reddit/with_multiple_listings_returned/.yml +204 -0
- data/spec/cassettes/Engagement_CommentCounter_Reddit/with_no_results/.yml +124 -0
- data/spec/engagement/comment_counter/disqus_spec.rb +27 -0
- data/spec/engagement/comment_counter/hacker_news_spec.rb +11 -0
- data/spec/engagement/comment_counter/reddit_spec.rb +23 -0
- data/spec/engagement/comment_counter_spec.rb +33 -0
- data/spec/engagement/threaded_comment_counter_spec.rb +30 -0
- data/spec/spec_helper.rb +20 -0
- metadata +207 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4c480010424c0c5e4a459b29f5f1c0ef442033ee
|
4
|
+
data.tar.gz: 4e8048b988f5934eacf5d3f906ec9749845ea743
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e21ae66354f2a68e596782770c9ee077144fa9eb0e49abbb8b44104c19208bd35940f097ca050591f8056895972272ddb3b8a5a5e1db9e2fb68acec965f550b1
|
7
|
+
data.tar.gz: 65842d46f26d7a566a3d12d14dcdb483aa7dcea8f6464f4dc741cff3aa73ef809b4803ca776e55eab5ab8f3953444e05335099dd3aaec56b36e5a719a05d29cb
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Michael Guterl
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
# Engagement
|
2
|
+
|
3
|
+
A ruby gem for measuring the engagement of URLs on various social sites.
|
4
|
+
|
5
|
+
Right now this project is just useful for counting comments for URLs
|
6
|
+
on a variety of social sites. It may grow into something more useful
|
7
|
+
in the future.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
gem 'engagement'
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install engagement
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
hacker_news = Engagement::CommentCounter::HackerNews.new
|
27
|
+
reddit = Engagement::CommentCounter::Reddit.new
|
28
|
+
counter = Engagement::CommentCounter.new([hacker_news, reddit])
|
29
|
+
|
30
|
+
counter.comments_count('http://gaslight.co/blog/why-we-wrote-a-blog') # => 17
|
31
|
+
```
|
32
|
+
|
33
|
+
or to use the threaded version which fetches the counts from each
|
34
|
+
service in a separate thread.
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
counter = Engagement::CommentCounter::Threaded.new([hacker_news, reddit])
|
38
|
+
counter.comments_count('http://gaslight.co/blog/why-we-wrote-a-blog') # => 17
|
39
|
+
```
|
40
|
+
|
41
|
+
### HackerNews
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
hacker_news = Engagement::CommentCounter::HackerNews.new
|
45
|
+
hacker_news.comments_count('http://gaslight.co/blog/why-we-wrote-a-blog') # => 9
|
46
|
+
```
|
47
|
+
|
48
|
+
### Reddit
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
reddit = Engagement::CommentCounter::Reddit.new
|
52
|
+
reddit.comments_count('http://gaslight.co/blog/why-we-wrote-a-blog') # => 8
|
53
|
+
```
|
54
|
+
|
55
|
+
### Disqus
|
56
|
+
|
57
|
+
In order to fetch this data from Disqus you need to own the forum (in
|
58
|
+
Disqus's terminology) where the URL is owned.
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
disqus = Engagement::CommentCounter::Disqus.new(forum_api_key)
|
62
|
+
disqus.comments_count('http://gaslight.co/blog/why-we-wrote-a-blog') # => 2
|
63
|
+
```
|
64
|
+
|
65
|
+
## Contributing
|
66
|
+
|
67
|
+
1. Fork it
|
68
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
69
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
70
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
71
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/engagement.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'engagement/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "engagement"
|
8
|
+
spec.version = Engagement::VERSION
|
9
|
+
spec.authors = ["Michael Guterl"]
|
10
|
+
spec.email = ["michael@diminishing.org"]
|
11
|
+
spec.description = %q{A ruby gem for measuring the engagement of URLs on various social sites.}
|
12
|
+
spec.summary = %q{Get comment counts from Reddit, HackerNews, and Disqus.}
|
13
|
+
spec.homepage = "https://github.com/gaslight/engagement"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency 'bundler', '~> 1.3'
|
22
|
+
spec.add_development_dependency 'rake'
|
23
|
+
spec.add_development_dependency 'rspec'
|
24
|
+
spec.add_development_dependency 'vcr'
|
25
|
+
spec.add_development_dependency 'webmock'
|
26
|
+
spec.add_development_dependency 'debugger'
|
27
|
+
|
28
|
+
spec.add_dependency 'hacker_news_search', '~> 0.0.6'
|
29
|
+
spec.add_dependency 'snoo'
|
30
|
+
spec.add_dependency 'disqus'
|
31
|
+
end
|
data/lib/engagement.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
module Engagement
|
2
|
+
class CommentCounter
|
3
|
+
|
4
|
+
autoload :Threaded, 'engagement/comment_counter/threaded'
|
5
|
+
autoload :HackerNews, 'engagement/comment_counter/hacker_news'
|
6
|
+
autoload :Reddit, 'engagement/comment_counter/reddit'
|
7
|
+
autoload :Disqus, 'engagement/comment_counter/disqus'
|
8
|
+
|
9
|
+
def initialize(places)
|
10
|
+
@places = places
|
11
|
+
end
|
12
|
+
|
13
|
+
def comments_count(url)
|
14
|
+
@places.inject(0) do |comments_count, place|
|
15
|
+
comments_count + place.comments_count(url)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'disqus'
|
2
|
+
|
3
|
+
module Engagement
|
4
|
+
class CommentCounter
|
5
|
+
class Disqus
|
6
|
+
|
7
|
+
def initialize(forum_api_key)
|
8
|
+
@forum_api_key = forum_api_key
|
9
|
+
end
|
10
|
+
|
11
|
+
# When we don't own the thread we currently return 0 to prevent an
|
12
|
+
# exception from being raised. We're not sure that this is the best
|
13
|
+
# long-term solution.
|
14
|
+
def comments_count(url)
|
15
|
+
if (thread_id = thread_id_for(url))
|
16
|
+
comments_count_for_thread_id thread_id
|
17
|
+
else
|
18
|
+
0
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def comments_count_for_thread_id(thread_id)
|
25
|
+
counts = ::Disqus::Api.get_num_posts(thread_ids:[thread_id], forum_api_key: @forum_api_key)["message"]
|
26
|
+
counts[thread_id].last
|
27
|
+
end
|
28
|
+
|
29
|
+
def thread_id_for(url)
|
30
|
+
thread = ::Disqus::Api.get_thread_by_url(forum_api_key: @forum_api_key, url: url)
|
31
|
+
thread['message'] && thread['message']['id']
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'hacker_news_search'
|
2
|
+
|
3
|
+
class Engagement::CommentCounter::HackerNews
|
4
|
+
def initialize
|
5
|
+
@client = HackerNewsSearch.new
|
6
|
+
end
|
7
|
+
|
8
|
+
def comments_count(url)
|
9
|
+
@client.search("items", url)['results'].inject(0) { |comments_count, result|
|
10
|
+
comments_count + result['item']['num_comments']
|
11
|
+
}
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'snoo'
|
2
|
+
|
3
|
+
class Engagement::CommentCounter::Reddit
|
4
|
+
def initialize
|
5
|
+
@client = Snoo::Client.new
|
6
|
+
end
|
7
|
+
|
8
|
+
def comments_count(url)
|
9
|
+
response = @client.search(url).parsed_response
|
10
|
+
|
11
|
+
# Sometimes the Reddit API returns an Array, sometimes it returns a Hash.
|
12
|
+
unless response.is_a?(Array)
|
13
|
+
response = [response]
|
14
|
+
end
|
15
|
+
|
16
|
+
response.inject(0) do |count, listing|
|
17
|
+
count + extract_count_from(listing)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def extract_count_from(response)
|
24
|
+
return 0 unless response && response['data']
|
25
|
+
|
26
|
+
children = response['data']['children']
|
27
|
+
children.inject(0) { |count, entry|
|
28
|
+
data = entry['data']
|
29
|
+
count + (data['num_comments'] || 0)
|
30
|
+
}
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
class Engagement::CommentCounter::Threaded
|
4
|
+
|
5
|
+
def initialize(places)
|
6
|
+
@places = places
|
7
|
+
@mutex = Mutex.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def comments_count(url)
|
11
|
+
comments_count = 0
|
12
|
+
threads = []
|
13
|
+
|
14
|
+
@places.each do |place|
|
15
|
+
threads << Thread.new do
|
16
|
+
count = place.comments_count(url)
|
17
|
+
|
18
|
+
@mutex.synchronize do
|
19
|
+
comments_count += count
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
threads.each(&:join)
|
25
|
+
comments_count
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: http://disqus.com/api/get_thread_by_url/?forum_api_key=<FILTERED>&url=http://google.com
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Accept-Encoding:
|
11
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
12
|
+
Accept:
|
13
|
+
- '*/*'
|
14
|
+
User-Agent:
|
15
|
+
- Ruby
|
16
|
+
response:
|
17
|
+
status:
|
18
|
+
code: 200
|
19
|
+
message: OK
|
20
|
+
headers:
|
21
|
+
Server:
|
22
|
+
- nginx
|
23
|
+
Content-Type:
|
24
|
+
- application/json
|
25
|
+
P3p:
|
26
|
+
- CP="DSP IDC CUR ADM DELi STP NAV COM UNI INT PHY DEM"
|
27
|
+
Vary:
|
28
|
+
- Accept-Encoding, X-Forwarded-Proto
|
29
|
+
Content-Length:
|
30
|
+
- '63'
|
31
|
+
Accept-Ranges:
|
32
|
+
- bytes
|
33
|
+
Date:
|
34
|
+
- Thu, 25 Jul 2013 14:15:07 GMT
|
35
|
+
X-Varnish:
|
36
|
+
- '1970155121'
|
37
|
+
Age:
|
38
|
+
- '0'
|
39
|
+
Via:
|
40
|
+
- 1.1 varnish
|
41
|
+
Connection:
|
42
|
+
- close
|
43
|
+
body:
|
44
|
+
encoding: UTF-8
|
45
|
+
string: '{"message":null,"code":"ok","succeeded":true}'
|
46
|
+
http_version:
|
47
|
+
recorded_at: Thu, 25 Jul 2013 14:15:09 GMT
|
48
|
+
recorded_with: VCR 2.5.0
|
@@ -0,0 +1,89 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: http://disqus.com/api/get_thread_by_url/?forum_api_key=<FILTERED>&url=http://diminishing.org/searching-for-a-hero-ruby
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Accept-Encoding:
|
11
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
12
|
+
Accept:
|
13
|
+
- '*/*'
|
14
|
+
User-Agent:
|
15
|
+
- Ruby
|
16
|
+
response:
|
17
|
+
status:
|
18
|
+
code: 200
|
19
|
+
message: OK
|
20
|
+
headers:
|
21
|
+
Server:
|
22
|
+
- nginx
|
23
|
+
Content-Type:
|
24
|
+
- application/json
|
25
|
+
P3p:
|
26
|
+
- CP="DSP IDC CUR ADM DELi STP NAV COM UNI INT PHY DEM"
|
27
|
+
Vary:
|
28
|
+
- Accept-Encoding, X-Forwarded-Proto
|
29
|
+
Content-Length:
|
30
|
+
- '299'
|
31
|
+
Accept-Ranges:
|
32
|
+
- bytes
|
33
|
+
Date:
|
34
|
+
- Thu, 25 Jul 2013 14:14:47 GMT
|
35
|
+
X-Varnish:
|
36
|
+
- '1970018194'
|
37
|
+
Age:
|
38
|
+
- '0'
|
39
|
+
Via:
|
40
|
+
- 1.1 varnish
|
41
|
+
Connection:
|
42
|
+
- close
|
43
|
+
body:
|
44
|
+
encoding: UTF-8
|
45
|
+
string: '{"message":{"allow_comments":true,"forum":"79677","title":"Searching
|
46
|
+
fora HeroGood Examples","url":"http://diminishing.org/searching-for-a-hero-ruby","num_comments":4,"created_at":"2013-01-27T15:37:02","forum_obj":{"created_at":"2009-01-24
|
47
|
+
21:36:26.853147","shortname":"diminishing","id":"79677","name":"diminishing
|
48
|
+
blog"},"slug":"httpdiminishingorgsearching_for_a_hero_ruby","hidden":false,"identifier":null,"id":"1049620432"},"code":"ok","succeeded":true}'
|
49
|
+
http_version:
|
50
|
+
recorded_at: Thu, 25 Jul 2013 14:14:49 GMT
|
51
|
+
- request:
|
52
|
+
method: get
|
53
|
+
uri: http://disqus.com/api/get_num_posts/?forum_api_key=<FILTERED>&thread_ids=1049620432
|
54
|
+
body:
|
55
|
+
encoding: US-ASCII
|
56
|
+
string: ''
|
57
|
+
headers:
|
58
|
+
Accept-Encoding:
|
59
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
60
|
+
Accept:
|
61
|
+
- '*/*'
|
62
|
+
User-Agent:
|
63
|
+
- Ruby
|
64
|
+
response:
|
65
|
+
status:
|
66
|
+
code: 200
|
67
|
+
message: OK
|
68
|
+
headers:
|
69
|
+
Server:
|
70
|
+
- nginx
|
71
|
+
Date:
|
72
|
+
- Thu, 25 Jul 2013 14:14:47 GMT
|
73
|
+
Content-Type:
|
74
|
+
- application/json
|
75
|
+
Transfer-Encoding:
|
76
|
+
- chunked
|
77
|
+
Connection:
|
78
|
+
- close
|
79
|
+
Vary:
|
80
|
+
- Accept-Encoding
|
81
|
+
- Cookie
|
82
|
+
P3p:
|
83
|
+
- CP="DSP IDC CUR ADM DELi STP NAV COM UNI INT PHY DEM"
|
84
|
+
body:
|
85
|
+
encoding: UTF-8
|
86
|
+
string: '{"message":{"1049620432":[5,4]},"code":"ok","succeeded":true}'
|
87
|
+
http_version:
|
88
|
+
recorded_at: Thu, 25 Jul 2013 14:14:49 GMT
|
89
|
+
recorded_with: VCR 2.5.0
|