twingly-analytics 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.travis.yml +17 -0
- data/Gemfile +3 -0
- data/LICENSE +20 -0
- data/README.md +60 -0
- data/Rakefile +17 -0
- data/examples/Gemfile +3 -0
- data/examples/find_all_posts_mentioning_github.rb +29 -0
- data/lib/twingly-analytics/client.rb +19 -0
- data/lib/twingly-analytics/parser.rb +45 -0
- data/lib/twingly-analytics/post.rb +26 -0
- data/lib/twingly-analytics/query.rb +61 -0
- data/lib/twingly-analytics/result.rb +20 -0
- data/lib/twingly-analytics/version.rb +5 -0
- data/lib/twingly-analytics.rb +6 -0
- data/spec/client_spec.rb +27 -0
- data/spec/fixtures/invalid_result.xml +3 -0
- data/spec/fixtures/valid_result.xml +22975 -0
- data/spec/fixtures/vcr_cassettes/search_for_spotify_on_sv_blogs.yml +382 -0
- data/spec/fixtures/vcr_cassettes/search_without_valid_api_key.yml +43 -0
- data/spec/parser_spec.rb +14 -0
- data/spec/post_spec.rb +17 -0
- data/spec/query_spec.rb +110 -0
- data/spec/result_spec.rb +16 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/vcr_setup.rb +7 -0
- data/twingly-analytics-api-ruby.gemspec +29 -0
- metadata +187 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e31d1b593419817798bfa388cdb2b2d3c74d2708
|
4
|
+
data.tar.gz: 3498b2b18671060c8119017bf9bd6ab07c18f8d4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e85e466d13661942b9f6b54b83dac6b9bb7537cc481525783d8863f3750133ed7e0cd68c71569e6c39d45b8c1d00ef588442415a815dbd7f81bb0276d5d76c00
|
7
|
+
data.tar.gz: 580b72a3b84a9398c57401731f644515e07d4b996bd7d59186543a2a2c865d46e74c59e6b56a3504ea8cb2fb6e38b60c66115439b3f5ab400958c392afa56f8f
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
language: ruby
|
2
|
+
cache: bundler
|
3
|
+
rvm:
|
4
|
+
- 1.9.3
|
5
|
+
- 2.0.0
|
6
|
+
- 2.1
|
7
|
+
- 2.2
|
8
|
+
deploy:
|
9
|
+
provider: rubygems
|
10
|
+
api_key:
|
11
|
+
secure: "QxMWXTQ3/T+0IQZl3pk3hmdoX8FcXTXEhH7Jn7Ub1A1D5faa8VNnfYsWAPjP6+VRgVfKEjAsHNMyZQNTvudpPPC/2kAwwzPvStyS+y7KnRkjZ9acC7sYVVel6cZ2lcl+t8iHY+19NRBFBWpq/P0eCAcR/Ew0XXaLZnig5h/1mQo="
|
12
|
+
gem: twingly-analytics
|
13
|
+
gemspec: twingly-analytics-api-ruby.gemspec
|
14
|
+
on:
|
15
|
+
tags: true
|
16
|
+
rvm: 2.2
|
17
|
+
repo: twingly/twingly-analytics-api-ruby
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2013 Twingly AB
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
10
|
+
subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
# Twingly Analytics API Ruby
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/twingly/twingly-analytics-api-ruby.png?branch=master)](https://travis-ci.org/twingly/twingly-analytics-api-ruby)
|
4
|
+
[![Code Climate](https://codeclimate.com/github/twingly/twingly-analytics-api-ruby.png)](https://codeclimate.com/github/twingly/twingly-analytics-api-ruby)
|
5
|
+
|
6
|
+
A Ruby gem for Twingly's Analytics API. Twingly is a blog search service that provides a searchable API called [Twingly Analytics](https://developer.twingly.com/resources/analytics/).
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add to your application's [Gemfile](http://bundler.io/gemfile.html) and then run `bundle`
|
11
|
+
|
12
|
+
```Ruby
|
13
|
+
gem 'twingly-analytics', github: 'twingly/twingly-analytics-api-ruby'
|
14
|
+
```
|
15
|
+
|
16
|
+
## Usage
|
17
|
+
|
18
|
+
```Ruby
|
19
|
+
require 'twingly-analytics'
|
20
|
+
|
21
|
+
client = Twingly::Analytics::Client.new
|
22
|
+
query = client.query
|
23
|
+
query.pattern = 'github page-size:10'
|
24
|
+
query.language = 'sv'
|
25
|
+
result = query.execute
|
26
|
+
=> #<Twingly::Analytics::Result:0x3ff7adcbe3d4 @posts, @number_of_matches_returned=10, @number_of_matches_total=3035221>
|
27
|
+
result.posts # will include all returned posts
|
28
|
+
```
|
29
|
+
|
30
|
+
The `twinlgy-analytics` gem talks to a commercial blog search API and requires an API key. Best practice is to set the `TWINGLY_ANALYTICS_KEY` environment variable to the obtained key. `Twingly::Analytics::Client` can be passed a key at initialization if your setup does not allow environment variables.
|
31
|
+
|
32
|
+
Too learn more about the capabilities of this API you should read the [Twingly Analytics API documentation](https://developer.twingly.com/resources/analytics/).
|
33
|
+
|
34
|
+
## Requirements
|
35
|
+
|
36
|
+
* API key, contact sales@twingly.com to get one
|
37
|
+
* Ruby 1.9, 2.0, 2.1, 2.2
|
38
|
+
|
39
|
+
## License
|
40
|
+
|
41
|
+
The MIT License (MIT)
|
42
|
+
|
43
|
+
Copyright (c) 2013 Twingly AB
|
44
|
+
|
45
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
46
|
+
this software and associated documentation files (the "Software"), to deal in
|
47
|
+
the Software without restriction, including without limitation the rights to
|
48
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
49
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
50
|
+
subject to the following conditions:
|
51
|
+
|
52
|
+
The above copyright notice and this permission notice shall be included in all
|
53
|
+
copies or substantial portions of the Software.
|
54
|
+
|
55
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
56
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
57
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
58
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
59
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
60
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'bundler/gem_tasks'
|
3
|
+
|
4
|
+
desc "Run specs"
|
5
|
+
task :spec do
|
6
|
+
require 'bundler/setup'
|
7
|
+
require 'rspec/core/rake_task'
|
8
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
9
|
+
t.rspec_opts = %w(-fd -c)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
desc "Synonym for spec"
|
14
|
+
task :test => :spec
|
15
|
+
desc "Synonym for spec"
|
16
|
+
task :tests => :spec
|
17
|
+
task :default => :spec
|
data/examples/Gemfile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'bundler'
|
3
|
+
Bundler.require
|
4
|
+
|
5
|
+
# Set environment variable TWINGLY_ANALYTICS_KEY
|
6
|
+
client = Twingly::Analytics::Client.new
|
7
|
+
|
8
|
+
finished = nil
|
9
|
+
start_time = Time.new(2013, 01, 01)
|
10
|
+
blogs = Set.new
|
11
|
+
|
12
|
+
until finished
|
13
|
+
query = client.query
|
14
|
+
query.pattern = "sort-order:asc sort:published github"
|
15
|
+
query.start_time = start_time
|
16
|
+
query.language = 'sv'
|
17
|
+
result = query.execute
|
18
|
+
|
19
|
+
result.posts.each do |post|
|
20
|
+
blogs.add(post.blog_url)
|
21
|
+
end
|
22
|
+
|
23
|
+
finished = start_time == result.posts.last.published
|
24
|
+
start_time = result.posts.last.published
|
25
|
+
end
|
26
|
+
|
27
|
+
blogs.each do |url|
|
28
|
+
puts url
|
29
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Twingly
|
2
|
+
module Analytics
|
3
|
+
class Client
|
4
|
+
attr_accessor :api_key
|
5
|
+
|
6
|
+
def initialize(api_key = nil)
|
7
|
+
@api_key = api_key || env_api_key || fail("Missing API key")
|
8
|
+
end
|
9
|
+
|
10
|
+
def query
|
11
|
+
Query.new(self)
|
12
|
+
end
|
13
|
+
private
|
14
|
+
def env_api_key
|
15
|
+
ENV['TWINGLY_ANALYTICS_KEY']
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
3
|
+
module Twingly
|
4
|
+
module Analytics
|
5
|
+
class Parser
|
6
|
+
def parse(document)
|
7
|
+
result = Result.new
|
8
|
+
nokogiri = Nokogiri::XML(document)
|
9
|
+
|
10
|
+
failure = nokogiri.at_xpath('//name:blogstream/name:operationResult[@resultType="failure"]', :name => 'http://www.twingly.com')
|
11
|
+
fail failure.text if failure
|
12
|
+
|
13
|
+
result.number_of_matches_returned = nokogiri.at_xpath('/twinglydata/@numberOfMatchesReturned').value.to_i
|
14
|
+
result.number_of_matches_total = nokogiri.at_xpath('/twinglydata/@numberOfMatchesTotal').value.to_i
|
15
|
+
result.seconds_elapsed = nokogiri.at_xpath('/twinglydata/@secondsElapsed').value.to_f
|
16
|
+
|
17
|
+
nokogiri.xpath('//post').each do |post|
|
18
|
+
result.posts << parse_post(post)
|
19
|
+
end
|
20
|
+
|
21
|
+
result
|
22
|
+
end
|
23
|
+
private
|
24
|
+
def parse_post(element)
|
25
|
+
post_params = {}
|
26
|
+
element.element_children.each do |child|
|
27
|
+
if child.name == 'tags'
|
28
|
+
post_params[child.name] = parse_tags(child) if child.name == 'tags'
|
29
|
+
else
|
30
|
+
post_params[child.name] = child.text
|
31
|
+
end
|
32
|
+
end
|
33
|
+
post = Post.new
|
34
|
+
post.set_values(post_params)
|
35
|
+
post
|
36
|
+
end
|
37
|
+
|
38
|
+
def parse_tags(element)
|
39
|
+
element.element_children.map do |child|
|
40
|
+
child.text
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'date'
|
4
|
+
|
5
|
+
module Twingly
|
6
|
+
module Analytics
|
7
|
+
class Post
|
8
|
+
attr_reader :url, :title, :summary, :language_code, :indexed,
|
9
|
+
:published, :blog_url, :blog_name, :authority, :blog_rank, :tags
|
10
|
+
|
11
|
+
def set_values(params)
|
12
|
+
@url = params.fetch('url')
|
13
|
+
@title = params.fetch('title')
|
14
|
+
@summary = params.fetch('summary')
|
15
|
+
@language_code = params.fetch('languageCode')
|
16
|
+
@published = DateTime.parse(params.fetch('published'))
|
17
|
+
@indexed = DateTime.parse(params.fetch('indexed'))
|
18
|
+
@blog_url = params.fetch('blogUrl')
|
19
|
+
@blog_name = params.fetch('blogName')
|
20
|
+
@authority = params.fetch('authority').to_i
|
21
|
+
@blog_rank = params.fetch('blogRank').to_i
|
22
|
+
@tags = params.fetch('tags', [])
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
|
3
|
+
module Twingly
|
4
|
+
module Analytics
|
5
|
+
class Query
|
6
|
+
attr_accessor :pattern, :language, :client
|
7
|
+
attr_reader :start_time, :end_time
|
8
|
+
|
9
|
+
BASE_URL = 'https://api.twingly.com'
|
10
|
+
ANALYTICS_PATH = '/analytics/Analytics.ashx'
|
11
|
+
|
12
|
+
def initialize(client)
|
13
|
+
@client = client
|
14
|
+
end
|
15
|
+
|
16
|
+
def url
|
17
|
+
"#{BASE_URL}#{ANALYTICS_PATH}?#{url_parameters}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def execute
|
21
|
+
Parser.new.parse(get_response.body)
|
22
|
+
end
|
23
|
+
|
24
|
+
def start_time=(time)
|
25
|
+
@start_time = time.strftime("%F %T")
|
26
|
+
end
|
27
|
+
|
28
|
+
def end_time=(time)
|
29
|
+
@end_time = time.strftime("%F %T")
|
30
|
+
end
|
31
|
+
|
32
|
+
def url_parameters
|
33
|
+
Faraday::Utils.build_query(request_parameters)
|
34
|
+
end
|
35
|
+
|
36
|
+
def request_parameters
|
37
|
+
fail("Missing pattern") unless pattern
|
38
|
+
|
39
|
+
{
|
40
|
+
:key => client.api_key,
|
41
|
+
:searchpattern => pattern,
|
42
|
+
:documentlang => language,
|
43
|
+
:ts => start_time,
|
44
|
+
:tsTo => end_time,
|
45
|
+
:xmloutputversion => 2
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def get_response
|
52
|
+
connection = Faraday.new(:url => BASE_URL) do |faraday|
|
53
|
+
faraday.request :url_encoded
|
54
|
+
faraday.adapter Faraday.default_adapter
|
55
|
+
end
|
56
|
+
connection.headers[:user_agent] = 'Ruby'
|
57
|
+
connection.get(ANALYTICS_PATH, request_parameters)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Twingly
|
2
|
+
module Analytics
|
3
|
+
class Result
|
4
|
+
attr_accessor :number_of_matches_returned, :number_of_matches_total,
|
5
|
+
:seconds_elapsed
|
6
|
+
|
7
|
+
def posts
|
8
|
+
@posts ||= []
|
9
|
+
end
|
10
|
+
|
11
|
+
def inspect
|
12
|
+
matches = "@posts, "
|
13
|
+
matches << "@number_of_matches_returned=#{self.number_of_matches_returned}, "
|
14
|
+
matches << "@number_of_matches_total=#{self.number_of_matches_total}"
|
15
|
+
|
16
|
+
sprintf("#<%s:0x%x %s>", self.class.name, __id__, matches)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/spec/client_spec.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
include Twingly::Analytics
|
4
|
+
|
5
|
+
describe Client do
|
6
|
+
subject { Client.new('api_key') }
|
7
|
+
context 'with API key as arguments' do
|
8
|
+
it { should be_a Client }
|
9
|
+
end
|
10
|
+
|
11
|
+
context 'with API key from ENV variable' do
|
12
|
+
before { allow_any_instance_of(Client).to receive(:env_api_key).and_return('api_key') }
|
13
|
+
subject { Client.new }
|
14
|
+
it { should be_a Client }
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'without valid API key' do
|
18
|
+
before { allow_any_instance_of(Client).to receive(:env_api_key).and_return(nil) }
|
19
|
+
subject { Client.new }
|
20
|
+
it { expect { subject }.to raise_error(RuntimeError, 'Missing API key') }
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#query' do
|
24
|
+
subject { Client.new('api_key').query }
|
25
|
+
it { should be_a Query }
|
26
|
+
end
|
27
|
+
end
|