virility 0.0.6 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -16,18 +16,23 @@ Or install it yourself as:
16
16
 
17
17
  $ gem install virility
18
18
 
19
- ## Usage
19
+ ## Basic Usage
20
20
 
21
- The Excitation object will collect the data from all of the available strategies.
21
+ If all you need is the raw shares numbers for a URL, Virility has some very simple methods you can use right out of the box:
22
22
 
23
- stats = Virility::Excitation.new("http://rubygems.org")
24
- stats.poll # returns a hash with the collected output of all data sources
25
- stats.counts # returns a hash of just the virility counts => {:delicious=>943, :facebook=>72, :pinterest=>0, :plusone=>137, :stumbleupon=>1488, :twitter=>2319}
26
- stats.total # returns the sum of all virility counts
23
+ Virility.poll("http://rubygems.org") # => Returns a hash with the collected results from all of the social network strategies
24
+ Virility.counts("http://rubygems.org") # => {:delicious=>0, :facebook=>72, :pinterest=>0, :plus_one=>138, :stumble_upon=>1488, :twitter=>2322}
25
+ Virility.total("http://rubygems.org") # => 4020
26
+ Virility.url("http://rubygems.org") # => Returns a Virility::Excitation object that you can manipulate
27
27
 
28
- One common implementation would be to collect the total count of all social shares for a given URL
28
+ ## More Granular Usage
29
29
 
30
- Virility::Excitation.new("http://rubygems.org").total # => 4959
30
+ The Virility::Excitation object does the heavy lifting of collecting the data from all of the available strategies.
31
+
32
+ virility = Virility::Excitation.new("http://rubygems.org")
33
+ virility.poll # returns a hash with the collected output of all data sources
34
+ virility.counts # returns a hash of just the virility counts => {:delicious=>943, :facebook=>72, :pinterest=>0, :plusone=>137, :stumbleupon=>1488, :twitter=>2319}
35
+ virility.total # returns the sum of all virility counts
31
36
 
32
37
  ## Individual Strategies
33
38
 
@@ -39,19 +44,35 @@ Currently there is support for the following social resources:
39
44
  * Google Plus One
40
45
  * Stumble Upon
41
46
 
42
- Each social resources is implemented as a strategy and contains at least two methods: poll and count.
47
+ Each social resource is implemented as a Virility::Strategy and contains at least three methods: poll, results and count. __poll__ does the work of querying the API to get the data and returns the same hash as the results method, __results__ returns the hashed values that were provided by the social network and __count__ pulls out the individual number of shares for that social network.
48
+
49
+ #### Strategy Initialization
50
+
51
+ There are several ways you can access the object for an individual strategy.
52
+
53
+ The Virility object has a __factory__ method that will return the strategy object:
54
+
55
+ tweets = Virility.factory(:twitter, "http://rubygems.org") # => returns a Virility::Twitter object with the rubygems url
56
+
57
+ You can also instate the Virility::Twitter object directly:
58
+
59
+ tweets = Virility::Twitter.new("http://rubygems.org")
43
60
 
44
- _poll_ does the work of querying the API to get the data
45
- _count_ pulls out the individual number of shares for that social
61
+ #### Individual Usage Example
46
62
 
47
63
  Let's say you only need to get the number of tweets for a URL, you could use the Virility::Twitter class by itself:
48
64
 
49
65
  tweets = Virility::Twitter.new("http://rubygems.org")
50
- tweets.poll # returns a hash with the collected output from Twitter => {"url"=>"http://rubygems.org/", "count"=>2319}
51
- tweets.count # returns the number of tweets for that URL => 2319
66
+ tweets.poll # returns a hash with the collected output from Twitter => {"url"=>"http://rubygems.org/", "count"=>2319}
67
+ tweets.results # returns a hash with the collected output from Twitter => {"url"=>"http://rubygems.org/", "count"=>2319}
68
+ tweets.count # returns the number of tweets for that URL => 2319
52
69
 
53
70
  ## Facebook Usage
54
71
 
72
+ fb = Virility::Facebook.new("http://rubygems.org")
73
+ fb.poll # returns a hash with the collected output from Facebook
74
+ fb.count # returns the total_count for that URL
75
+
55
76
  The Facebook strategy leverages the FQL query against the link_stat table. Because of this, the following data fields are available:
56
77
  * like_count
57
78
  * click_count
@@ -59,16 +80,61 @@ The Facebook strategy leverages the FQL query against the link_stat table. Becau
59
80
  * comment_count
60
81
  * commentsbox_count
61
82
  * total_count (used as the default count for all Facebook activity)
62
- * comments_fbid
63
83
 
64
- fb = Virility::Facebook.new("http://rubygems.org")
65
- fb.poll # returns a hash with the collected output from Facebook
66
- fb.count # returns the total_count for that URL => 72
84
+ ## Dynamic Finders
85
+
86
+ #### Virility::Excitation
87
+
88
+ If you have a Virility::Excitation object, there are dynamic finders that will return the individual Virility::Strategy object for a social network. Simply call the name of the strategy against the Virility::Excitation object and that strategy will be returned:
89
+
90
+ virility = Virility::Excitation.new("http://rubygems.org")
91
+ facebook = virility.facebook
92
+ twitter = virility.twitter
93
+ delicious = virility.delicious
94
+ pinterest = virility.pinterest
95
+ plus_one = virility.plus_one
96
+ stumble_upon = virility.stumble_upon
97
+
98
+ #### Virility::Strategy
99
+
100
+ If you have a Strategy object, any of the attributes that are commonly returned through the API call will be available as a dynamic finder. This is particularly useful with the Facebook strategy:
101
+
102
+ fb = Virility::Facebook.new("http://rubygems.org/")
103
+ fb.like_count # => 12
104
+ fb.share_count # => 40
105
+ fb.comment_count # => 20
106
+ fb.total_count # => 72
107
+
108
+ #### Combined Finders
109
+
110
+ Leveraging both sets of dynamic finders allows you to build an Excitation object and get all the way through to an attribute for a specific strategy:
111
+
112
+ Virility.url("http://google.com/").facebook.like_count # => 979662
113
+ Virility.url("http://google.com/").stumble_upon.info_link # => "http://www.stumbleupon.com/url/www.google.com/"
67
114
 
68
115
  ## Important Notes
69
116
 
70
117
  URL's are very specific in the context of a social media. For example, http://rubygems.org will return different results than http://rubygems.org/ with a trailing slash. Also, http vs https will give you different numbers. This actually has a lot to do with why we created this gem. When testing be sure to investigate all of the URL variations in order to get the most complete picture of your data.
71
118
 
119
+ #### Case Study
120
+
121
+ Compare the total count results for http://ruby-lang.org/en. One has the trailing slash and one does not.
122
+
123
+ Virility::Excitation.new("http://www.ruby-lang.org/en").total # => 247695
124
+ Virility::Excitation.new("http://www.ruby-lang.org/en/").total # => 253190
125
+
126
+ On this particular day, there was a 5,495 count difference between the two values. Inspecting the actual results shows you which of the social networks takes the varying forms of the urls into account:
127
+
128
+ Virility::Excitation.new("http://www.ruby-lang.org/en").counts
129
+ # => {:delicious=>37, :facebook=>3, :pinterest=>0, :plusone=>20, :stumbleupon=>246937, :twitter=>698}
130
+
131
+ Virility::Excitation.new("http://www.ruby-lang.org/en/").counts
132
+ # => {:delicious=>4314, :facebook=>813, :pinterest=>22, :plusone=>406, :stumbleupon=>246937, :twitter=>698}
133
+
134
+ Stumbleupon and Twitter are consistent while Delicious, Facebook, Pinterest and Google Plus One return different results. Depending on your needs, you could craft an algorithm that takes all of this into account and attempts to deliver an accurate number by combining the data sets that are different and trusting the ones that are the same.
135
+
136
+ Based on this logic, it is possible to consider that the true total share count is closer to _253,250_. Not only is this an opinionated number, it's accuracy is questionable based on assumptions, however if you are just trying to get a ballpark feeling of the virility of your content, this number should suffice.
137
+
72
138
  ## Contributing
73
139
 
74
140
  1. Fork it
@@ -1,11 +1,43 @@
1
1
  require 'cgi'
2
2
  require 'httparty'
3
3
 
4
- require "virility/version"
4
+ require 'virility/version'
5
+ require 'virility/supporter'
5
6
  require 'virility/excitation'
6
- require 'virility/context'
7
+ require 'virility/strategy'
8
+ require 'virility/exceptions'
9
+
7
10
  Dir["#{File.dirname(__FILE__)}/virility/strategies/**/*.rb"].each {|f| require f}
8
11
 
9
12
  module Virility
10
- # Your code goes here...
13
+
14
+ #
15
+ # Public API
16
+ #
17
+
18
+ def self.counts(url)
19
+ Virility::Excitation.new(url).counts
20
+ end
21
+
22
+ def self.total(url)
23
+ Virility::Excitation.new(url).total
24
+ end
25
+
26
+ def self.poll(url)
27
+ Virility::Excitation.new(url).poll
28
+ end
29
+
30
+ def self.url(url)
31
+ virility = Virility::Excitation.new(url)
32
+ virility.poll
33
+ virility
34
+ end
35
+
36
+ #
37
+ # Factory
38
+ #
39
+
40
+ def self.factory(strategy, url)
41
+ Virility::Excitation.new(url).send(strategy)
42
+ end
11
43
  end
@@ -0,0 +1,5 @@
1
+ module Virility
2
+
3
+ class UnknownStrategy < StandardError; end
4
+
5
+ end
@@ -1,5 +1,7 @@
1
1
  module Virility
2
2
  class Excitation
3
+ include Virility::Supporter
4
+
3
5
  attr_accessor :url, :results, :strategies, :counts
4
6
 
5
7
  #
@@ -7,7 +9,7 @@ module Virility
7
9
  #
8
10
 
9
11
  def initialize url
10
- @url = encode url
12
+ @url = url
11
13
  @strategies = {}
12
14
  @results = {}
13
15
  @counts = {}
@@ -19,7 +21,7 @@ module Virility
19
21
  #
20
22
 
21
23
  def poll
22
- if @results.empty?
24
+ if @results.empty?
23
25
  @strategies.each do |name, strategy|
24
26
  begin
25
27
  @results[symbolize_for_key(strategy)] = strategy.poll
@@ -34,14 +36,14 @@ module Virility
34
36
  def get_response(strategy)
35
37
  @strategies[strategy].response if @strategies[strategy]
36
38
  end
37
-
39
+
38
40
  #
39
41
  # Return Collected Counts as a Hash
40
42
  #
41
-
43
+
42
44
  def counts
43
45
  poll
44
- if @counts.empty?
46
+ if @counts.empty?
45
47
  @strategies.each do |name, strategy|
46
48
  begin
47
49
  @counts[symbolize_for_key(strategy)] = strategy.count.to_i
@@ -52,7 +54,7 @@ module Virility
52
54
  end
53
55
  @counts
54
56
  end
55
-
57
+
56
58
  def total_virility
57
59
  counts.values.inject(0) { |result, count| result + count }
58
60
  end
@@ -67,51 +69,35 @@ module Virility
67
69
  end
68
70
 
69
71
  #
70
- # URL Encoding / Decoding Methods
71
- #
72
-
73
- def encode url
74
- CGI.escape url
75
- end
76
-
77
- def url
78
- CGI.unescape @url
79
- end
80
-
81
- def escaped_url
82
- @url
83
- end
84
-
85
- #
86
- # Camelize
72
+ # Reflection
87
73
  #
88
74
 
89
- def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
90
- if first_letter_in_uppercase
91
- lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
92
- else
93
- lower_case_and_underscored_word.first + camelize(lower_case_and_underscored_word)[1..-1]
94
- end
75
+ def attributes
76
+ {:url => @url, :available_strategies => @strategies.keys}
95
77
  end
96
78
 
97
79
  #
98
- # Convert Class Name To Appropriate Key Symbol
80
+ # Dynamic Methods
99
81
  #
100
82
 
101
- def symbolize_for_key(klass)
102
- klass.class.to_s.gsub(/Virility::/, '').downcase.to_sym
83
+ def get_strategy strategy
84
+ if strategy_exists?(strategy)
85
+ @strategies[strategy.to_sym]
86
+ else
87
+ raise UnknownStrategy, "#{strategy} Is Not A Known Strategy"
88
+ end
103
89
  end
104
90
 
105
- def get_class_string(klass)
106
- File.basename(klass).gsub(/\.rb/,'')
91
+ def strategy_exists? strategy
92
+ !@strategies[strategy.to_sym].nil?
107
93
  end
108
94
 
109
- #
110
- # Reflection
111
- #
112
-
113
- def attributes
114
- {:url => @url, :available_strategies => @strategies.keys}
95
+ def method_missing(name, *args, &block)
96
+ if strategy_exists?(name)
97
+ get_strategy(name)
98
+ else
99
+ raise UnknownStrategy, "#{name} Is Not A Known Strategy"
100
+ end
115
101
  end
116
102
 
117
103
  end
@@ -1,14 +1,13 @@
1
1
  module Virility
2
- class Delicious < Context
3
-
4
- def poll
5
- @response = self.class.get("http://feeds.delicious.com/v2/json/urlinfo/data?url=#{@url}")
6
- @results = @response.parsed_response.empty? ? {"total_posts" => 0} : @response.parsed_response.first
2
+ class Delicious < Strategy
3
+
4
+ def census
5
+ self.class.get("http://feeds.delicious.com/v2/json/urlinfo/data?url=#{@url}")
7
6
  end
8
-
7
+
9
8
  def count
10
- @results["total_posts"] || 0
9
+ results["total_posts"] || 0
11
10
  end
12
-
11
+
13
12
  end
14
13
  end
@@ -1,14 +1,21 @@
1
1
  module Virility
2
- class Facebook < Context
3
- BASE_URL = "https://api.facebook.com/method/fql.query?query=SELECT+url%2C+normalized_url%2C+share_count%2C+like_count%2C+comment_count%2C+total_count%2C+commentsbox_count%2C+comments_fbid%2C+click_count+FROM+link_stat+WHERE+url%3D"
2
+ class Facebook < Strategy
3
+ BASE_URL = "https://api.facebook.com/method/fql.query?query=SELECT+share_count%2C+like_count%2C+comment_count%2C+total_count%2C+commentsbox_count%2C+click_count+FROM+link_stat+WHERE+url%3D"
4
4
 
5
5
  def poll
6
6
  @response = self.class.get("#{BASE_URL}%22#{@url}%22")
7
- @results = @response.parsed_response["fql_query_response"]["link_stat"]
7
+ @results = valid_response_test ? @response.parsed_response["fql_query_response"]["link_stat"] : {"total_count" => 0}
8
8
  end
9
-
9
+
10
10
  def count
11
- @results["total_count"] || 0
11
+ results["total_count"] || 0
12
12
  end
13
+
14
+ private
15
+
16
+ def valid_response_test
17
+ @response.respond_to?(:parsed_response) and @response.parsed_response.is_a?(Hash) and !@response.parsed_response["fql_query_response"].nil? and !@response.parsed_response["fql_query_response"]["link_stat"].nil?
18
+ end
19
+
13
20
  end
14
21
  end
@@ -1,20 +1,19 @@
1
1
  module Virility
2
- class Pinterest < Context
3
-
2
+ class Pinterest < Strategy
3
+
4
4
  parser(
5
5
  Proc.new do |body, format|
6
6
  MultiJson.decode(body.scan(/(\{.+\})/).flatten.first)
7
7
  end
8
8
  )
9
-
10
- def poll
11
- @response = self.class.get("http://api.pinterest.com/v1/urls/count.json?url=#{@url}")
12
- @results = @response.parsed_response
9
+
10
+ def census
11
+ self.class.get("http://api.pinterest.com/v1/urls/count.json?url=#{@url}")
13
12
  end
14
-
13
+
15
14
  def count
16
- @results["count"] || 0
15
+ results["count"] || 0
17
16
  end
18
-
17
+
19
18
  end
20
19
  end
@@ -1,6 +1,6 @@
1
1
  # http://stackoverflow.com/questions/7403553/how-do-i-get-the-counter-of-a-google-plus-1-button
2
2
  module Virility
3
- class PlusOne < Context
3
+ class PlusOne < Strategy
4
4
 
5
5
  parser(
6
6
  Proc.new do |body, format|
@@ -8,13 +8,12 @@ module Virility
8
8
  end
9
9
  )
10
10
 
11
- def poll
12
- @response = self.class.get("https://plusone.google.com/_/+1/fastbutton?url=#{@url}")
13
- @results = @response.parsed_response
11
+ def census
12
+ self.class.get("https://plusone.google.com/_/+1/fastbutton?url=#{@url}")
14
13
  end
15
14
 
16
15
  def count
17
- @results["shares"] || 0
16
+ results["shares"].to_i || 0
18
17
  end
19
18
 
20
19
  end
@@ -1,5 +1,5 @@
1
1
  module Virility
2
- class StumbleUpon < Context
2
+ class StumbleUpon < Strategy
3
3
 
4
4
  parser(
5
5
  Proc.new do |body, format|
@@ -7,13 +7,12 @@ module Virility
7
7
  end
8
8
  )
9
9
 
10
- def poll
11
- @response = self.class.get("http://www.stumbleupon.com/services/1.01/badge.getinfo?url=#{@url}")
12
- @results = @response.parsed_response
10
+ def census
11
+ self.class.get("http://www.stumbleupon.com/services/1.01/badge.getinfo?url=#{@url}")
13
12
  end
14
13
 
15
14
  def count
16
- @results["views"] || 0
15
+ results["views"] || 0
17
16
  end
18
17
 
19
18
  end
@@ -1,13 +1,12 @@
1
1
  module Virility
2
- class Twitter < Context
2
+ class Twitter < Strategy
3
3
 
4
- def poll
5
- @response = self.class.get("http://urls.api.twitter.com/1/urls/count.json?url=#{@url}")
6
- @results = @response.parsed_response
4
+ def census
5
+ self.class.get("http://urls.api.twitter.com/1/urls/count.json?url=#{@url}")
7
6
  end
8
7
 
9
8
  def count
10
- @results["count"] || 0
9
+ results["count"] || 0
11
10
  end
12
11
 
13
12
  end