postrank-api 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,29 @@
1
+ # Ruby PostRank API Wrapper
2
+
3
+ PostRank API wrapper for Ruby 1.9.
4
+
5
+ * EventMachine & Fibers under the hood - async friendly.
6
+ * Can be used outside of an EM loop - wrapper will spin up and shut down the reactor on demand.
7
+
8
+ For complete documentation on all endpoints please see [PostRank API Docs](http://apidocs.postrank.com)
9
+
10
+ ## A few simple examples
11
+
12
+ require "postrank-api"
13
+
14
+ api = PostRank::API.new('my-appkey')
15
+
16
+ # map a site to postrank id's + retrieve feed meta data
17
+ igvita = api.feed_info('igvita.com')
18
+
19
+ # grab the latest stories from igvita.com
20
+ feed = api.feed(igvita['id'])
21
+
22
+ # grab the top recent post from igvita.com
23
+ top = api.top_posts(igvita['id'], :num => 1)
24
+
25
+ # lookup the engagement score for the past two days
26
+ eng = api.engagement(igvita['id'], :start => 'yesterday')
27
+
28
+ # lookup social metrics for a url
29
+ metrics = api.metrics('http://www.igvita.com/')
@@ -0,0 +1,22 @@
1
+ require 'rake'
2
+
3
+ begin
4
+ require 'jeweler'
5
+ Jeweler::Tasks.new do |gemspec|
6
+ gemspec.name = "postrank-api"
7
+ gemspec.summary = "PostRank API Wrapper"
8
+ gemspec.description = gemspec.summary
9
+ gemspec.email = "ilya@igvita.com"
10
+ gemspec.homepage = "http://github.com/postrank/postrank-api"
11
+ gemspec.authors = ["Ilya Grigorik"]
12
+ gemspec.required_ruby_version = ">= 1.9.1"
13
+ gemspec.add_dependency('eventmachine', '>= 0.12.9')
14
+ gemspec.add_dependency('em-http')
15
+ gemspec.add_dependency('em-synchrony')
16
+ gemspec.rubyforge_project = "postrank-api"
17
+ end
18
+
19
+ Jeweler::GemcutterTasks.new
20
+ rescue LoadError
21
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
22
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1 @@
1
+ require 'postrank-api/api'
@@ -0,0 +1,140 @@
1
+ require 'em-synchrony'
2
+ require 'em-synchrony/em-http'
3
+
4
+ require 'digest/md5'
5
+ require 'chronic'
6
+ require 'yajl'
7
+
8
+ module PostRank
9
+ class API
10
+ V2_API_BASE = 'http://api.postrank.com/v2'
11
+
12
+ def initialize(appkey)
13
+ @appkey = appkey
14
+ end
15
+
16
+ def feed_info(feeds, opts = {})
17
+ req = {
18
+ :query => {
19
+ :appkey => @appkey,
20
+ :noindex => opts[:noindex] || false,
21
+ },
22
+ :body => build_body(feeds, 'feed')
23
+ }
24
+
25
+ http = post("#{V2_API_BASE}/feed/info", req)
26
+ resp = parse(http.response)
27
+
28
+ resp.key?('items') ? resp['items'] : resp
29
+ end
30
+
31
+ def feed(feed, opts = {})
32
+ req = {
33
+ :query => {
34
+ :appkey => @appkey,
35
+ :level => opts[:level] || 'all',
36
+ :q => opts[:q] || '',
37
+ :num => opts[:num] || 10,
38
+ :start => opts[:start] || 0,
39
+ :id => feed
40
+ }
41
+ }
42
+
43
+ http = get("#{V2_API_BASE}/feed/", req)
44
+ parse(http.response)
45
+ end
46
+
47
+ def top_posts(feed, opts = {})
48
+ req = {
49
+ :query => {
50
+ :appkey => @appkey,
51
+ :q => opts[:q] || '',
52
+ :num => opts[:num] || 10,
53
+ :id => feed
54
+ }
55
+ }
56
+
57
+ http = get("#{V2_API_BASE}/feed/topposts/", req)
58
+ parse(http.response)
59
+ end
60
+
61
+ def feed_engagement(feeds, opts = {})
62
+ opts[:start_time] ||= '1 month ago'
63
+ opts[:end_time] ||= 'today'
64
+ opts[:summary] = true if not opts.key?(:summary)
65
+
66
+ req = {
67
+ :query => {
68
+ :appkey => @appkey,
69
+ :mode => opts[:mode] || 'daily',
70
+ :start_time => Chronic.parse(opts[:start_time]).to_i,
71
+ :end_time => Chronic.parse(opts[:end_time]).to_i
72
+ },
73
+ :body => build_body(feeds, 'feed')
74
+ }
75
+
76
+ req[:query][:summary] = opts[:summary] if opts[:summary]
77
+
78
+ http = post("#{V2_API_BASE}/feed/engagement", req)
79
+ parse(http.response)
80
+ end
81
+
82
+ def metrics(urls, opts = {})
83
+ reverse = {}
84
+ urls = [urls].flatten.map do |url|
85
+ md5 = (url =~ /\w{32}/) ? url : Digest::MD5.hexdigest(url)
86
+ reverse[md5] = url
87
+ md5
88
+ end
89
+
90
+ req = {
91
+ :query => {
92
+ :appkey => @appkey,
93
+ },
94
+ :body => build_body(urls, 'url')
95
+ }
96
+
97
+ http = post("#{V2_API_BASE}/entry/metrics", req)
98
+ parse(http.response).inject({}) do |hash, v|
99
+ hash[reverse[v[0]]] = v[1]
100
+ hash
101
+ end
102
+ end
103
+
104
+ private
105
+
106
+ def parse(data)
107
+ begin
108
+ Yajl::Parser.parse(data)
109
+ rescue Exception => e
110
+ puts "Failed to parse request:"
111
+ puts e.message
112
+ puts e.backtrace[0, 5].join("\n")
113
+ end
114
+ end
115
+
116
+ def build_body(urls, key)
117
+ [urls].flatten.map { |e| "#{key}[]=#{e}" }.join("&")
118
+ end
119
+
120
+ def post(url, req)
121
+ dispatch(:post, url, req)
122
+ end
123
+
124
+ def get(url, req)
125
+ dispatch(:get, url, req)
126
+ end
127
+
128
+ def dispatch(method, url, req)
129
+ if EM.reactor_running?
130
+ http = EM::HttpRequest.new(url).send(method, req)
131
+ else
132
+ EM.synchrony do
133
+ http = EM::HttpRequest.new(url).send(method, req)
134
+ EM.stop
135
+ end
136
+ end
137
+ http
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,57 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{postrank-api}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Ilya Grigorik"]
12
+ s.date = %q{2010-06-03}
13
+ s.description = %q{PostRank API Wrapper}
14
+ s.email = %q{ilya@igvita.com}
15
+ s.extra_rdoc_files = [
16
+ "README.md"
17
+ ]
18
+ s.files = [
19
+ "README.md",
20
+ "Rakefile",
21
+ "VERSION",
22
+ "lib/postrank-api.rb",
23
+ "lib/postrank-api/api.rb",
24
+ "postrank-api.gemspec",
25
+ "spec/api_spec.rb"
26
+ ]
27
+ s.homepage = %q{http://github.com/postrank/postrank-api}
28
+ s.rdoc_options = ["--charset=UTF-8"]
29
+ s.require_paths = ["lib"]
30
+ s.required_ruby_version = Gem::Requirement.new(">= 1.9.1")
31
+ s.rubyforge_project = %q{postrank-api}
32
+ s.rubygems_version = %q{1.3.6}
33
+ s.summary = %q{PostRank API Wrapper}
34
+ s.test_files = [
35
+ "spec/api_spec.rb"
36
+ ]
37
+
38
+ if s.respond_to? :specification_version then
39
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
40
+ s.specification_version = 3
41
+
42
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
43
+ s.add_runtime_dependency(%q<eventmachine>, [">= 0.12.9"])
44
+ s.add_runtime_dependency(%q<em-http>, [">= 0"])
45
+ s.add_runtime_dependency(%q<em-synchrony>, [">= 0"])
46
+ else
47
+ s.add_dependency(%q<eventmachine>, [">= 0.12.9"])
48
+ s.add_dependency(%q<em-http>, [">= 0"])
49
+ s.add_dependency(%q<em-synchrony>, [">= 0"])
50
+ end
51
+ else
52
+ s.add_dependency(%q<eventmachine>, [">= 0.12.9"])
53
+ s.add_dependency(%q<em-http>, [">= 0"])
54
+ s.add_dependency(%q<em-synchrony>, [">= 0"])
55
+ end
56
+ end
57
+
@@ -0,0 +1,150 @@
1
+ require 'spec'
2
+ require 'lib/postrank-api'
3
+ require 'pp'
4
+
5
+ describe PostRank::API do
6
+ IGVITA = '421df2d86ab95100de7dcc2e247a08ab'
7
+ EVERBURNING = 'cb3e81ac96fb0ada1212dfce4f329474'
8
+
9
+ let(:api) { PostRank::API.new('test') }
10
+
11
+ it "should initialize with appkey" do
12
+ lambda {
13
+ PostRank::API.new('test')
14
+ }.should_not raise_error
15
+ end
16
+
17
+ describe "FeedInfo API" do
18
+ it "should query for feed info" do
19
+ igvita = api.feed_info('igvita.com')
20
+
21
+ igvita.class.should == Hash
22
+ igvita['tags'].class.should == Array
23
+ igvita['xml'].should match(/igvita/)
24
+ end
25
+
26
+ it "should query for feed info for multiple feeds" do
27
+ feeds = api.feed_info(['igvita.com', 'everburning.com'])
28
+ feeds.class.should == Array
29
+ feeds.size.should == 2
30
+ end
31
+
32
+ it "should return feed info data in-order" do
33
+ feeds = api.feed_info(['igvita.com', 'everburning.com'])
34
+ feeds.class.should == Array
35
+ feeds.first['xml'].should match('igvita.com')
36
+ end
37
+ end
38
+
39
+ describe "Feed API" do
40
+ it "should retrieve content of a feed" do
41
+ igvita = api.feed_info('igvita.com')
42
+ feed = api.feed(igvita['id'])
43
+
44
+ feed.class.should == Hash
45
+ feed['meta']['title'].should match(/igvita/)
46
+ feed['items'].size.should == 10
47
+ end
48
+
49
+ it "should retrieve 1 entry from a feed" do
50
+ EM.synchrony do
51
+ feed = api.feed(IGVITA, :num => 1)
52
+
53
+ feed.class.should == Hash
54
+ feed['meta']['title'].should match(/igvita/)
55
+ feed['items'].size.should == 1
56
+
57
+ EM.stop
58
+ end
59
+ end
60
+
61
+ it "should retrieve entries matching a query" do
62
+ EM.synchrony do
63
+ feed = api.feed(IGVITA, :q => 'abrakadabra')
64
+
65
+ feed.class.should == Hash
66
+ feed['meta']['title'].should match(/igvita/)
67
+ feed['items'].size.should == 0
68
+
69
+ EM.stop
70
+ end
71
+ end
72
+ end
73
+
74
+ describe "Top Posts API" do
75
+ it "should fetch top posts for a feed" do
76
+ EM.synchrony do
77
+ feed = api.top_posts(IGVITA, :num => 1)
78
+
79
+ feed.class.should == Hash
80
+ feed['meta']['title'].should match(/igvita/)
81
+ feed['items'].size.should == 1
82
+
83
+ EM.stop
84
+ end
85
+ end
86
+ end
87
+
88
+ describe "Feed Engagement API" do
89
+ it "should fetch engagement for a feed" do
90
+ EM.synchrony do
91
+ eng = api.feed_engagement(IGVITA)
92
+
93
+ eng.class.should == Hash
94
+ eng.keys.size.should == 1
95
+ eng[IGVITA]['sum'].class.should == Float
96
+
97
+ EM.stop
98
+ end
99
+ end
100
+
101
+ it "should fetch daily engagement for multiple feeds" do
102
+ EM.synchrony do
103
+ eng = api.feed_engagement([IGVITA, EVERBURNING], {
104
+ :summary => false,
105
+ :start_time => 'yesterday',
106
+ :end_time => 'today'
107
+ })
108
+
109
+ eng.class.should == Hash
110
+ eng.keys.size.should == 2
111
+ eng[IGVITA].keys.size.should == 2
112
+
113
+ EM.stop
114
+ end
115
+ end
116
+ end
117
+
118
+ describe "Metrics API" do
119
+ it "should fetch metrics for a collection of urls" do
120
+ EM.synchrony do
121
+ metrics = api.metrics(['http://www.igvita.com/', 'http://www.everburning.com/'])
122
+ metrics.keys.size.should == 2
123
+
124
+ metrics['http://www.igvita.com/'].class.should == Hash
125
+ metrics['http://www.everburning.com/'].class.should == Hash
126
+
127
+ EM.stop
128
+ end
129
+ end
130
+
131
+ it "should fetch metrics via url md5s" do
132
+ EM.synchrony do
133
+ metrics = api.metrics('1c1a5357e8bd00128db845b2595d5ebe')
134
+
135
+ metrics.keys.size.should == 1
136
+ metrics['1c1a5357e8bd00128db845b2595d5ebe'].class.should == Hash
137
+
138
+ EM.stop
139
+ end
140
+ end
141
+ end
142
+
143
+ it "should invoke and kill EM reactor transparently" do
144
+ metrics = api.metrics('1c1a5357e8bd00128db845b2595d5ebe')
145
+
146
+ metrics.keys.size.should == 1
147
+ metrics['1c1a5357e8bd00128db845b2595d5ebe'].class.should == Hash
148
+ end
149
+
150
+ end
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: postrank-api
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Ilya Grigorik
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-06-03 00:00:00 -04:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: eventmachine
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ - 12
30
+ - 9
31
+ version: 0.12.9
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: em-http
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ segments:
42
+ - 0
43
+ version: "0"
44
+ type: :runtime
45
+ version_requirements: *id002
46
+ - !ruby/object:Gem::Dependency
47
+ name: em-synchrony
48
+ prerelease: false
49
+ requirement: &id003 !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ segments:
54
+ - 0
55
+ version: "0"
56
+ type: :runtime
57
+ version_requirements: *id003
58
+ description: PostRank API Wrapper
59
+ email: ilya@igvita.com
60
+ executables: []
61
+
62
+ extensions: []
63
+
64
+ extra_rdoc_files:
65
+ - README.md
66
+ files:
67
+ - README.md
68
+ - Rakefile
69
+ - VERSION
70
+ - lib/postrank-api.rb
71
+ - lib/postrank-api/api.rb
72
+ - postrank-api.gemspec
73
+ - spec/api_spec.rb
74
+ has_rdoc: true
75
+ homepage: http://github.com/postrank/postrank-api
76
+ licenses: []
77
+
78
+ post_install_message:
79
+ rdoc_options:
80
+ - --charset=UTF-8
81
+ require_paths:
82
+ - lib
83
+ required_ruby_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ segments:
88
+ - 1
89
+ - 9
90
+ - 1
91
+ version: 1.9.1
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ segments:
97
+ - 0
98
+ version: "0"
99
+ requirements: []
100
+
101
+ rubyforge_project: postrank-api
102
+ rubygems_version: 1.3.6
103
+ signing_key:
104
+ specification_version: 3
105
+ summary: PostRank API Wrapper
106
+ test_files:
107
+ - spec/api_spec.rb