tbg_social 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/CHANGELOG.md +6 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +41 -0
- data/Rakefile +36 -0
- data/app/assets/images/social/.keep +0 -0
- data/app/assets/javascripts/social/application.js +13 -0
- data/app/assets/stylesheets/social/application.css +13 -0
- data/app/controllers/social/application_controller.rb +4 -0
- data/app/helpers/social/application_helper.rb +4 -0
- data/app/views/layouts/social/application.html.erb +14 -0
- data/bin/rails +12 -0
- data/config/routes.rb +2 -0
- data/fixtures/vcr_cassettes/facebook_engagement_bad_response.yml +48 -0
- data/fixtures/vcr_cassettes/facebook_engagement_count.yml +48 -0
- data/fixtures/vcr_cassettes/single_offer.yml +90 -0
- data/fixtures/vcr_cassettes/twitter_engagement_bad_response.yml +56 -0
- data/fixtures/vcr_cassettes/twitter_engagement_count.yml +56 -0
- data/lib/generators/social/install_generator.rb +18 -0
- data/lib/social.rb +11 -0
- data/lib/social/engageable.rb +5 -0
- data/lib/social/engagement.rb +38 -0
- data/lib/social/engine.rb +5 -0
- data/lib/social/facebook.rb +30 -0
- data/lib/social/google_plus.rb +34 -0
- data/lib/social/invalidation_policy.rb +7 -0
- data/lib/social/model.rb +26 -0
- data/lib/social/twitter.rb +30 -0
- data/lib/social/url.rb +10 -0
- data/lib/social/version.rb +3 -0
- data/lib/tasks/social_tasks.rake +4 -0
- data/social.gemspec +39 -0
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/images/.keep +0 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/controllers/concerns/.keep +0 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.keep +0 -0
- data/spec/dummy/app/models/.keep +0 -0
- data/spec/dummy/app/models/concerns/.keep +0 -0
- data/spec/dummy/app/models/offer.rb +2 -0
- data/spec/dummy/app/models/social_engagement.rb +3 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +23 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +29 -0
- data/spec/dummy/config/environments/production.rb +80 -0
- data/spec/dummy/config/environments/test.rb +36 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +12 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +6 -0
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/migrate/20140602112503_create_social_engagements.rb +14 -0
- data/spec/dummy/db/migrate/20140602131702_create_offers.rb +6 -0
- data/spec/dummy/db/schema.rb +29 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/lib/assets/.keep +0 -0
- data/spec/dummy/log/.keep +0 -0
- data/spec/dummy/public/404.html +58 -0
- data/spec/dummy/public/422.html +58 -0
- data/spec/dummy/public/500.html +57 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/engageable_spec.rb +15 -0
- data/spec/engagement_spec.rb +70 -0
- data/spec/facebook_spec.rb +24 -0
- data/spec/invalidation_policy_spec.rb +11 -0
- data/spec/social_engagement_spec.rb +31 -0
- data/spec/spec_helper.rb +49 -0
- data/spec/twitter_spec.rb +24 -0
- data/spec/url_spec.rb +12 -0
- metadata +368 -0
@@ -0,0 +1,56 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: http://urls.api.twitter.com/1/urls/count.json?url=http://www.studentbeans.com/student101/a/relationships/university-sex-league-6793.html
|
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
|
+
Cache-Control:
|
22
|
+
- must-revalidate, max-age=900
|
23
|
+
Content-Type:
|
24
|
+
- application/json;charset=utf-8
|
25
|
+
Expires:
|
26
|
+
- Wed, 28 May 2014 15:50:03 GMT
|
27
|
+
Last-Modified:
|
28
|
+
- Wed, 28 May 2014 15:35:03 GMT
|
29
|
+
Server:
|
30
|
+
- tfe
|
31
|
+
Content-Length:
|
32
|
+
- '127'
|
33
|
+
Accept-Ranges:
|
34
|
+
- bytes
|
35
|
+
Date:
|
36
|
+
- Wed, 28 May 2014 15:35:03 GMT
|
37
|
+
Via:
|
38
|
+
- 1.1 varnish
|
39
|
+
Age:
|
40
|
+
- '0'
|
41
|
+
Connection:
|
42
|
+
- keep-alive
|
43
|
+
X-Served-By:
|
44
|
+
- cache-tw-lon2-cr1-2-TWLON2
|
45
|
+
X-Cache:
|
46
|
+
- MISS
|
47
|
+
X-Cache-Hits:
|
48
|
+
- '0'
|
49
|
+
Vary:
|
50
|
+
- Accept-Encoding
|
51
|
+
body:
|
52
|
+
encoding: UTF-8
|
53
|
+
string: '{"count":310,"url":"http:\/\/www.studentbeans.com\/student101\/a\/relationships\/university-sex-league-6793.html\/"}'
|
54
|
+
http_version:
|
55
|
+
recorded_at: Wed, 28 May 2014 15:35:03 GMT
|
56
|
+
recorded_with: VCR 2.9.0
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Social
|
2
|
+
class InstallGenerator < Rails::Generators::Base
|
3
|
+
desc 'Creates a SocialEngagement model and db table'
|
4
|
+
|
5
|
+
def create_social_engagement
|
6
|
+
generate "model", "SocialEngagement facebook:integer twitter:integer url:string:uniq created_at:datetime updated_at:datetime --no-test-framework"
|
7
|
+
end
|
8
|
+
|
9
|
+
def migrate
|
10
|
+
rake "db:migrate"
|
11
|
+
rake "db:migrate RAILS_ENV=test"
|
12
|
+
end
|
13
|
+
|
14
|
+
def require_url_in_initializer
|
15
|
+
initializer "social_engagement.rb", "require 'social/url'"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/social.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'social/engagement'
|
2
|
+
require 'social/engageable'
|
3
|
+
require 'social/facebook'
|
4
|
+
require 'social/twitter'
|
5
|
+
require 'social/google_plus'
|
6
|
+
require 'social/invalidation_policy'
|
7
|
+
require "social/engine"
|
8
|
+
require "social/model"
|
9
|
+
|
10
|
+
module Social
|
11
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Social
|
2
|
+
class Engagement
|
3
|
+
|
4
|
+
def self.for(model_instance)
|
5
|
+
url = Url.for(model_instance)
|
6
|
+
for_url(url)
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.for_url(url)
|
10
|
+
begin
|
11
|
+
try_to_get_from_cache(url)
|
12
|
+
rescue ActiveRecord::RecordNotFound
|
13
|
+
social_engagement_from_web = from_web(url)
|
14
|
+
social_engagement_from_web.save
|
15
|
+
social_engagement_from_web
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.from_web(url)
|
20
|
+
facebook = Facebook.total_count(url)
|
21
|
+
twitter = Twitter.total_count(url)
|
22
|
+
SocialEngagement.new(url: url, facebook: facebook, twitter: twitter)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def self.try_to_get_from_cache(url)
|
28
|
+
cached_social_engagement = SocialEngagement.for_url(url)
|
29
|
+
if InvalidationPolicy.valid?(cached_social_engagement)
|
30
|
+
cached_social_engagement
|
31
|
+
else
|
32
|
+
social_engagement_from_web = from_web(url)
|
33
|
+
cached_social_engagement.update_counts(social_engagement_from_web)
|
34
|
+
cached_social_engagement
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Social
|
2
|
+
class Facebook
|
3
|
+
ENDPOINT = 'http://graph.facebook.com/?id=URL'
|
4
|
+
COUNT_KEY = 'shares'
|
5
|
+
|
6
|
+
def self.total_count(url)
|
7
|
+
begin
|
8
|
+
response = Net::HTTP.get(URI(ENDPOINT.gsub('URL', url)))
|
9
|
+
json = JSON.parse(response)
|
10
|
+
if json.has_key?(COUNT_KEY)
|
11
|
+
json[COUNT_KEY]
|
12
|
+
else
|
13
|
+
log_and_return_zero(url, response)
|
14
|
+
end
|
15
|
+
rescue StandardError => e
|
16
|
+
log_and_return_zero(url, nil, e)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def self.log_and_return_zero(url, response = nil, exception = nil)
|
23
|
+
log_message = "Could not get facebook engagement for url: #{url}\n"
|
24
|
+
log_message << "Response: #{response}\n" if response.present?
|
25
|
+
log_message << "Exception: #{exception.message}\n" if exception.present?
|
26
|
+
Rails.logger.warn(log_message)
|
27
|
+
0
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Social
|
2
|
+
class GooglePlus
|
3
|
+
ENDPOINT = 'https://plus.google.com/ripple/details?url=URL'
|
4
|
+
REGEX = /(\d)* public shares/
|
5
|
+
|
6
|
+
def self.total_count(url)
|
7
|
+
begin
|
8
|
+
response = Net::HTTP.get(URI(ENDPOINT.gsub('URL', url)))
|
9
|
+
matches = REGEX.match(response)
|
10
|
+
if matches
|
11
|
+
begin
|
12
|
+
Integer(matches[1])
|
13
|
+
rescue ArgumentError
|
14
|
+
log_and_return_zero(url, response)
|
15
|
+
end
|
16
|
+
else
|
17
|
+
log_and_return_zero(url, response)
|
18
|
+
end
|
19
|
+
rescue StandardError => e
|
20
|
+
log_and_return_zero(url, nil, e)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def self.log_and_return_zero(url, response = nil, exception = nil)
|
27
|
+
log_message = "Could not get Google Plus engagement for url: #{url}\n"
|
28
|
+
log_message << "Response: #{response}\n" if response.present?
|
29
|
+
log_message << "Exception: #{exception.message}\n" if exception.present?
|
30
|
+
Rails.logger.warn(log_message)
|
31
|
+
0
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/social/model.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
module Social
|
2
|
+
module Model
|
3
|
+
def self.included(base)
|
4
|
+
base.validates :url, presence: true
|
5
|
+
|
6
|
+
base.include InstanceMethods
|
7
|
+
base.extend ClassMethods
|
8
|
+
end
|
9
|
+
|
10
|
+
module InstanceMethods
|
11
|
+
def update_counts(social_engagement)
|
12
|
+
unless social_engagement.facebook == facebook && social_engagement.twitter == twitter
|
13
|
+
update(facebook: social_engagement.facebook, twitter: social_engagement.twitter)
|
14
|
+
else
|
15
|
+
touch
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module ClassMethods
|
21
|
+
def for_url(url)
|
22
|
+
find_by_url!(url)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Social
|
2
|
+
class Twitter
|
3
|
+
ENDPOINT = 'http://urls.api.twitter.com/1/urls/count.json?url=URL'
|
4
|
+
COUNT_KEY = 'count'
|
5
|
+
|
6
|
+
def self.total_count(url)
|
7
|
+
begin
|
8
|
+
response = Net::HTTP.get(URI(ENDPOINT.gsub('URL', url)))
|
9
|
+
json = JSON.parse(response)
|
10
|
+
if json.has_key?(COUNT_KEY)
|
11
|
+
json[COUNT_KEY]
|
12
|
+
else
|
13
|
+
log_and_return_zero(url, response)
|
14
|
+
end
|
15
|
+
rescue StandardError => e
|
16
|
+
log_and_return_zero(url, nil, e)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def self.log_and_return_zero(url, response = nil, exception = nil)
|
23
|
+
log_message = "Could not get twitter engagement for url: #{url}\n"
|
24
|
+
log_message << "Response: #{response}\n" if response.present?
|
25
|
+
log_message << "Exception: #{exception.message}\n" if exception.present?
|
26
|
+
Rails.logger.warn(log_message)
|
27
|
+
0
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/social/url.rb
ADDED
data/social.gemspec
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require 'social/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "tbg_social"
|
7
|
+
spec.version = Social::VERSION
|
8
|
+
spec.authors = ["zduci"]
|
9
|
+
spec.email = ["ra.busuioc@gmail.com"]
|
10
|
+
spec.summary = %q{Social engagement module}
|
11
|
+
spec.description = %q{It finds, caches and updates social engagement of urls on social media (facebook, twitter)}
|
12
|
+
spec.homepage = "https://github.com/thebeansgroup/social"
|
13
|
+
spec.license = "MIT"
|
14
|
+
|
15
|
+
spec.files = `git ls-files`.split($/)
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.required_ruby_version = '>= 2.0.0'
|
21
|
+
|
22
|
+
spec.add_dependency "rails", ">= 3.2.13"
|
23
|
+
#spec.add_dependency "activeresource", "~> 4.0"
|
24
|
+
#spec.add_dependency "activeresource"
|
25
|
+
|
26
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
27
|
+
spec.add_development_dependency 'pry-byebug'
|
28
|
+
spec.add_development_dependency 'pry-rails'
|
29
|
+
spec.add_development_dependency 'pry-stack_explorer'
|
30
|
+
spec.add_development_dependency 'awesome_print'
|
31
|
+
spec.add_development_dependency "rake"
|
32
|
+
|
33
|
+
spec.add_development_dependency "rspec"
|
34
|
+
spec.add_development_dependency "rspec-rails"
|
35
|
+
spec.add_development_dependency "shoulda-matchers"
|
36
|
+
spec.add_development_dependency "vcr"
|
37
|
+
spec.add_development_dependency "sqlite3"
|
38
|
+
spec.add_development_dependency "webmock"
|
39
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
== README
|
2
|
+
|
3
|
+
This README would normally document whatever steps are necessary to get the
|
4
|
+
application up and running.
|
5
|
+
|
6
|
+
Things you may want to cover:
|
7
|
+
|
8
|
+
* Ruby version
|
9
|
+
|
10
|
+
* System dependencies
|
11
|
+
|
12
|
+
* Configuration
|
13
|
+
|
14
|
+
* Database creation
|
15
|
+
|
16
|
+
* Database initialization
|
17
|
+
|
18
|
+
* How to run the test suite
|
19
|
+
|
20
|
+
* Services (job queues, cache servers, search engines, etc.)
|
21
|
+
|
22
|
+
* Deployment instructions
|
23
|
+
|
24
|
+
* ...
|
25
|
+
|
26
|
+
|
27
|
+
Please feel free to use a different markup language if you do not plan to run
|
28
|
+
<tt>rake doc:app</tt>.
|
data/spec/dummy/Rakefile
ADDED
File without changes
|
@@ -0,0 +1,13 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
+
// listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
+
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
|
6
|
+
//
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
+
// compiled file.
|
9
|
+
//
|
10
|
+
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
|
11
|
+
// about supported directives.
|
12
|
+
//
|
13
|
+
//= require_tree .
|
@@ -0,0 +1,13 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the top of the
|
9
|
+
* compiled file, but it's generally better to create a new file per style scope.
|
10
|
+
*
|
11
|
+
*= require_self
|
12
|
+
*= require_tree .
|
13
|
+
*/
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|