powertrack_v2 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 224bad738e8ef15952bc6252b362d516de828de4
4
+ data.tar.gz: a0a7bf95a7223c9f1f18f9ae4134582549ee5f54
5
+ SHA512:
6
+ metadata.gz: 6a668a7da86f731575c161b2a3709e43ed10f4f820d0bd3bcee9ff74ca361c2ee3349eba4a0b6c53a61c7ae3a758d9343efcef734b833ef84b18a88781d89f6b
7
+ data.tar.gz: c377b9e8b4ccc673adbed23ab7dab9e2922f273698692c94d87b0c3b48beb7f5ec1686614f9f1c4d8d45c62d88e2a9b73a71c9f2e8d0fa4a81428b1fe5d124e0
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+ Copyright (c) 2016 NUVI
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
5
+ this software and associated documentation files (the "Software"), to deal in
6
+ the Software without restriction, including without limitation the rights to
7
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
8
+ the Software, and to permit persons to whom the Software is furnished to do so,
9
+ subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
17
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
18
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
+
21
+
data/README.md ADDED
@@ -0,0 +1,100 @@
1
+ PowerTrackV2
2
+ =
3
+
4
+ A Ruby client for managing PowerTrack 2.0 rules, as well as streaming.
5
+
6
+ Authentication
7
+ ==
8
+
9
+ First, authentication is managed through environment variables. If you're
10
+ unfamiliar with this practice, check out [The Rubyist's Guide to Environment
11
+ Variables](http://blog.honeybadger.io/ruby-guide-environment-variables/).
12
+
13
+ In your shell:
14
+
15
+ ```bash
16
+ export POWERTRACK_USERNAME=<your_username>
17
+ export POWERTRACK_PASSWORD=<your_password>
18
+ ```
19
+
20
+ Rule Management
21
+ ==
22
+
23
+ To add rules to your stream:
24
+
25
+ ```ruby
26
+ require 'powertrack_v2'
27
+
28
+ url = PowerTrackV2.rules_url(publisher, account_name, stream_label)
29
+
30
+ rules = [
31
+ PowerTrackV2::Rule.new('rule_value', 'optional_rule_tag'),
32
+ PowerTrackV2::Rule.new('another_value')
33
+ ]
34
+
35
+ response_body = PowerTrackV2.add_rules(url, rules)
36
+ ```
37
+
38
+ To remove rules from your stream:
39
+
40
+ ```ruby
41
+ require 'powertrack_v2'
42
+
43
+ url = PowerTrackV2.rules_url(publisher, account_name, stream_label)
44
+
45
+ rules = [
46
+ Rule.new('rule_value', 'optional_rule_tag'),
47
+ Rule.new('another_value')
48
+ ]
49
+
50
+ response_body = PowerTrackV2.remove_rules(url, rules)
51
+ ```
52
+
53
+ To get all rules for your stream:
54
+
55
+ ```ruby
56
+ require 'powertrack_v2'
57
+
58
+ url = PowerTrackV2.rules_url(publisher, account_name, stream_label)
59
+
60
+ response_body = PowerTrackV2.get_rules(url, rules)
61
+ ```
62
+
63
+ Streaming
64
+ ==
65
+
66
+ To stream from powertrack:
67
+
68
+ ```ruby
69
+ require 'powertrack_v2'
70
+
71
+ url = PowerTrackV2.stream_url(publisher, account, stream_label)
72
+
73
+ PowerTrackV2.stream(url) do |activity|
74
+ puts activity # or whatever you want to do with it.
75
+ end
76
+ ```
77
+
78
+ Some things to note about streaming:
79
+
80
+ 1. The stream function is infinitely blocking, so your streamer should be a
81
+ separate process from your rule manager process.
82
+ 1. Your block is called for every published activity.
83
+ 1. The published activity is a raw, unprocessed response from PowerTrack. This
84
+ library makes no assumptions about what you want to do with the data being
85
+ streamed in, and leaves processing up to you.
86
+ 1. If the connection drops, this library will automatically reconnect using an
87
+ exponential backoff algorithm.
88
+ 1. PowerTrack sends a "\\r\\n" heartbeat every few seconds. By default, the
89
+ streamer discards it so you don't have to worry about it. If you want it to
90
+ pass you the heartbeat, simply add an option to the stream function:
91
+
92
+ ```ruby
93
+ require 'powertrack_v2'
94
+
95
+ url = PowerTrackV2.stream_url(publisher, account, stream_label)
96
+
97
+ PowerTrackV2.stream(url, ignore_heartbeat: false) do |activity|
98
+ puts activity # or whatever you want to do with it.
99
+ end
100
+ ```
@@ -0,0 +1,28 @@
1
+ module PowerTrackV2
2
+ module Errors
3
+ class BadRequestError < StandardError; end
4
+ class UnauthorizedError < StandardError; end
5
+ class NoSuchResourceError < StandardError; end
6
+ class RateLimitedError < StandardError; end
7
+ class ServiceUnavailableError < StandardError; end
8
+ class UnexpectedStatusError < StandardError; end
9
+
10
+ class InvalidRuleError < StandardError; end
11
+ class TooManyRulesError < StandardError; end
12
+
13
+ ERROR_MAP = {
14
+ 400 => BadRequestError,
15
+ 401 => UnauthorizedError,
16
+ 404 => NoSuchResourceError,
17
+ 429 => RateLimitedError,
18
+ 503 => ServiceUnavailableError
19
+ }
20
+
21
+ def self.raise_error_for_code(code)
22
+ message = code.to_s
23
+
24
+ raise ERROR_MAP[code], message if ERROR_MAP[code]
25
+ raise UnexpectedStatusError, message
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,62 @@
1
+ require 'base64'
2
+ require 'httpclient'
3
+ require 'exponential_backoff'
4
+
5
+ module PowerTrackV2
6
+ module HTTP
7
+ def self.auth_digest(username, password)
8
+ ::Base64.strict_encode64("#{username}:#{password}")
9
+ end
10
+
11
+ def self.auth_header(auth_digest)
12
+ {'Authorization' => "Basic #{auth_digest}"}
13
+ end
14
+
15
+ def self.headers(headers = {})
16
+ username = ENV['POWERTRACK_USERNAME']
17
+ password = ENV['POWERTRACK_PASSWORD']
18
+
19
+ digest = auth_digest(username, password)
20
+ headers.merge auth_header(digest)
21
+ end
22
+
23
+ def self.get(url, headers={})
24
+ ::HTTPClient.get(url, nil, headers)
25
+ end
26
+
27
+ def self.post(url, body, headers={})
28
+ ::HTTPClient.post(url, body, headers)
29
+ end
30
+
31
+ def self.stream(url, headers = {}, opts = { ignore_heartbeat: true }, &block)
32
+ http = ::HTTPClient.new
33
+ backoff = ::ExponentialBackoff.new(1, 120)
34
+
35
+ while true
36
+ begin
37
+ connection = connect(url, headers)
38
+ stream_from(connection, opts, backoff, &block)
39
+ rescue StandardError => e
40
+ sleep(backoff.next_interval)
41
+ end
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def self.connect(url, headers)
48
+ http = ::HTTPClient.new
49
+ http.get_async(url, nil, headers)
50
+ end
51
+
52
+ def self.stream_from(connection, opts, backoff, &block)
53
+ ignore_heartbeat = opts[:ignore_heartbeat]
54
+ content = connection.pop.content
55
+ backoff.clear
56
+ until connection.finished?
57
+ next_response = content.gets
58
+ block.call(next_response) unless ignore_heartbeat && next_response == "\r\n"
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,78 @@
1
+ require 'powertrack_v2/http'
2
+ require 'powertrack_v2/errors'
3
+ require 'multi_json'
4
+
5
+ module PowerTrackV2
6
+ class Rule
7
+ MAX_TAG_LENGTH = 255
8
+ MAX_VALUE_LENGTH = 2048
9
+
10
+ attr_accessor :value, :tag
11
+
12
+ def initialize(value, tag=nil)
13
+ @value = value
14
+ @tag = tag
15
+ end
16
+
17
+ def as_json
18
+ hash = {
19
+ value: @value
20
+ }
21
+ hash[:tag] = @tag if @tag && !@tag.empty?
22
+ hash
23
+ end
24
+
25
+ def valid?
26
+ return false if @value.nil? || @value.length > MAX_VALUE_LENGTH
27
+ return false if @tag && @tag.length > MAX_TAG_LENGTH
28
+ true
29
+ end
30
+
31
+ def self.url(publisher, account, stream_label)
32
+ "https://gnip-api.#{publisher}.com/rules/powertrack/accounts/#{account}/publishers/#{publisher}/#{stream_label}.json"
33
+ end
34
+
35
+ def self.get(url, optional_headers={})
36
+ headers = PowerTrackV2::HTTP.headers(optional_headers)
37
+ response = PowerTrackV2::HTTP.get(url, headers)
38
+ handle_response(response)
39
+ end
40
+
41
+ def self.handle_response(response)
42
+ return response.body if response.status == 200 || response.status < 300
43
+ return PowerTrackV2::Errors.raise_error_for_code(response.status)
44
+ end
45
+
46
+ DEFAULT_POST_HEADERS = {
47
+ 'Content-Type' => 'application/json'
48
+ }
49
+
50
+ def self.post(url, rules, optional_headers={})
51
+ validate(rules)
52
+
53
+ base_headers = DEFAULT_POST_HEADERS.merge(optional_headers)
54
+ headers = PowerTrackV2::HTTP.headers(base_headers)
55
+
56
+ body = prepare_rules(rules)
57
+
58
+ response = PowerTrackV2::HTTP.post(url, body, headers)
59
+ handle_response(response)
60
+ end
61
+
62
+ def self.delete(url, rules, optional_headers={})
63
+ post(url + '?_method=delete', rules, optional_headers)
64
+ end
65
+
66
+ def self.validate(rules)
67
+ raise ::PowerTrackV2::Errors::TooManyRulesError if rules.length > 5000
68
+ raise ::PowerTrackV2::Errors::InvalidRuleError unless rules.all? &:valid?
69
+ end
70
+
71
+ private
72
+
73
+ def self.prepare_rules(rules)
74
+ rules = rules.map {|rule| rule.as_json}
75
+ ::MultiJson.dump({rules: rules})
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,12 @@
1
+ module PowerTrackV2
2
+ module Stream
3
+ def self.url(publisher, account, stream_label)
4
+ "https://gnip-stream.#{publisher}.com/stream/powertrack/accounts/#{account}/publishers/#{publisher}/#{stream_label}.json"
5
+ end
6
+
7
+ def self.stream(url, opts = { ignore_heartbeat: true }, &block)
8
+ headers = PowerTrackV2::HTTP.headers
9
+ PowerTrackV2::HTTP.stream(url, headers, opts, &block)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,30 @@
1
+ require 'powertrack_v2/rule'
2
+ require 'powertrack_v2/stream'
3
+ require 'powertrack_v2/http'
4
+ require 'powertrack_v2/errors'
5
+
6
+ module PowerTrackV2
7
+ def self.stream_url(publisher, account, stream_label)
8
+ PowerTrackV2::Stream.url(publisher, account, stream_label)
9
+ end
10
+
11
+ def self.rules_url(publisher, account, stream_label)
12
+ PowerTrackV2::Rule.url(publisher, account, stream_label)
13
+ end
14
+
15
+ def self.add_rules(url, rules, optional_headers = {})
16
+ PowerTrackV2::Rule.post(url, rules, optional_headers)
17
+ end
18
+
19
+ def self.remove_rules(url, rules, optional_headers = {})
20
+ PowerTrackV2::Rule.delete(url, rules, optional_headers)
21
+ end
22
+
23
+ def self.get_rules(url, optional_headers = {})
24
+ PowerTrackV2::Rule.get(url, optional_headers)
25
+ end
26
+
27
+ def self.stream(url, opts = { ignore_heartbeat: true }, &block)
28
+ PowerTrackV2::Stream.stream(url, opts, &block)
29
+ end
30
+ end
metadata ADDED
@@ -0,0 +1,192 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: powertrack_v2
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Cody Poll
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-04-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '2.7'
19
+ - - ">="
20
+ - !ruby/object:Gem::Version
21
+ version: 2.7.0
22
+ name: httpclient
23
+ prerelease: false
24
+ type: :runtime
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '2.7'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 2.7.0
33
+ - !ruby/object:Gem::Dependency
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - "~>"
37
+ - !ruby/object:Gem::Version
38
+ version: '2.16'
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: 2.16.0
42
+ name: march_hare
43
+ prerelease: false
44
+ type: :runtime
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '2.16'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 2.16.0
53
+ - !ruby/object:Gem::Dependency
54
+ requirement: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - "~>"
57
+ - !ruby/object:Gem::Version
58
+ version: 0.0.2
59
+ name: exponential-backoff
60
+ prerelease: false
61
+ type: :runtime
62
+ version_requirements: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - "~>"
65
+ - !ruby/object:Gem::Version
66
+ version: 0.0.2
67
+ - !ruby/object:Gem::Dependency
68
+ requirement: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - "~>"
71
+ - !ruby/object:Gem::Version
72
+ version: '1.11'
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 1.11.2
76
+ name: multi_json
77
+ prerelease: false
78
+ type: :runtime
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '1.11'
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: 1.11.2
87
+ - !ruby/object:Gem::Dependency
88
+ requirement: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - "~>"
91
+ - !ruby/object:Gem::Version
92
+ version: 0.10.3
93
+ name: pry
94
+ prerelease: false
95
+ type: :development
96
+ version_requirements: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - "~>"
99
+ - !ruby/object:Gem::Version
100
+ version: 0.10.3
101
+ - !ruby/object:Gem::Dependency
102
+ requirement: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - "~>"
105
+ - !ruby/object:Gem::Version
106
+ version: 0.2.4
107
+ name: pry-nav
108
+ prerelease: false
109
+ type: :development
110
+ version_requirements: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - "~>"
113
+ - !ruby/object:Gem::Version
114
+ version: 0.2.4
115
+ - !ruby/object:Gem::Dependency
116
+ requirement: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - "~>"
119
+ - !ruby/object:Gem::Version
120
+ version: '1.8'
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: 1.8.3
124
+ name: json_pure
125
+ prerelease: false
126
+ type: :development
127
+ version_requirements: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '1.8'
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: 1.8.3
135
+ - !ruby/object:Gem::Dependency
136
+ requirement: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - "~>"
139
+ - !ruby/object:Gem::Version
140
+ version: '5.8'
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ version: 5.8.4
144
+ name: minitest
145
+ prerelease: false
146
+ type: :development
147
+ version_requirements: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - "~>"
150
+ - !ruby/object:Gem::Version
151
+ version: '5.8'
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ version: 5.8.4
155
+ description: A simple library for streaming from Powertrack 2.0, as well as managing rules for your stream.
156
+ email: developers@nuviapp.com
157
+ executables: []
158
+ extensions: []
159
+ extra_rdoc_files: []
160
+ files:
161
+ - LICENSE
162
+ - README.md
163
+ - lib/powertrack_v2.rb
164
+ - lib/powertrack_v2/errors.rb
165
+ - lib/powertrack_v2/http.rb
166
+ - lib/powertrack_v2/rule.rb
167
+ - lib/powertrack_v2/stream.rb
168
+ homepage: http://github.com/nuvi/powertrack_v2
169
+ licenses:
170
+ - MIT
171
+ metadata: {}
172
+ post_install_message:
173
+ rdoc_options: []
174
+ require_paths:
175
+ - lib
176
+ required_ruby_version: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ required_rubygems_version: !ruby/object:Gem::Requirement
182
+ requirements:
183
+ - - ">="
184
+ - !ruby/object:Gem::Version
185
+ version: '0'
186
+ requirements: []
187
+ rubyforge_project:
188
+ rubygems_version: 2.4.8
189
+ signing_key:
190
+ specification_version: 4
191
+ summary: Powertrack 2.0 client for Ruby
192
+ test_files: []