keener 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in keener.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Mateo Murphy
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,32 @@
1
+ # Keener
2
+
3
+ [![Build Status](https://travis-ci.org/mateomurphy/keener.png?branch=master)](https://travis-ci.org/mateomurphy/keener)
4
+ [![Dependency Status](https://gemnasium.com/mateomurphy/keener.png)](https://gemnasium.com/mateomurphy/keener)
5
+
6
+ Unofficial gem for accessing the keen api, based on [Faraday](https://github.com/lostisland/faraday).
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ gem 'keener'
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install keener
21
+
22
+ ## Usage
23
+
24
+ See http://www.rubydoc.info/github/mateomurphy/keener/master/frames, especially the Keener::Api module
25
+
26
+ ## Contributing
27
+
28
+ 1. Fork it
29
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
30
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
31
+ 4. Push to the branch (`git push origin my-new-feature`)
32
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => [:spec]
data/keener.gemspec ADDED
@@ -0,0 +1,32 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'keener/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "keener"
8
+ gem.version = Keener::VERSION
9
+ gem.authors = ["Mateo Murphy"]
10
+ gem.email = ["mateo.murphy@gmail.com"]
11
+ gem.description = %q{Unofficial gem for accessing keen.io}
12
+ gem.summary = %q{An unofficial gem for accessing keen.io}
13
+ gem.homepage = "https://github.com/mateomurphy/keener"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency 'faraday', "~> 0.8.5"
21
+ gem.add_dependency 'faraday_middleware', '~> 0.9.0'
22
+ gem.add_dependency 'hashie', '~> 1.2.0'
23
+
24
+ gem.add_development_dependency "em-synchrony", "~> 1.0.3"
25
+ gem.add_development_dependency "em-http-request", "~> 1.0.3"
26
+ gem.add_development_dependency "typhoeus"
27
+ gem.add_development_dependency "rspec", "~> 2.12"
28
+ gem.add_development_dependency "vcr", "~> 2.4.0"
29
+ gem.add_development_dependency 'rake'
30
+ gem.add_development_dependency 'yard'
31
+ gem.add_development_dependency 'redcarpet'
32
+ end
data/lib/keener/api.rb ADDED
@@ -0,0 +1,189 @@
1
+ module Keener
2
+
3
+ # Implements direct access to the api
4
+ #
5
+ # See https://keen.io/docs/api/reference/ for detailed info
6
+ module Api
7
+ # GET returns the average across all numeric values for the target property in the event
8
+ # collection matching the given criteria. Non-numeric values are ignored.
9
+ #
10
+ # @return [Resource]
11
+ def average(project_id, event_collection, target_property, options = {})
12
+ options[:event_collection] = event_collection
13
+ options[:target_property] = target_property
14
+
15
+ resource "projects/#{project_id}/queries/average", options
16
+ end
17
+
18
+ # GET returns the number of resources in the event collection matching the given criteria.
19
+ #
20
+ # @return [Resource]
21
+ def count(project_id, event_collection, options = {})
22
+ options[:event_collection] = event_collection
23
+
24
+ resource "projects/#{project_id}/queries/count", options
25
+ end
26
+
27
+ # GET returns the number of UNIQUE resources in the event collection matching the given criteria.
28
+ #
29
+ # @return [Resource]
30
+ def count_unique(project_id, event_collection, target_property, options = {})
31
+ options[:event_collection] = event_collection
32
+ options[:target_property] = target_property
33
+
34
+ resource "projects/#{project_id}/queries/count_unique", options
35
+ end
36
+
37
+ # GET returns the available child resources.
38
+ #
39
+ # @return [Resource]
40
+ def discovery
41
+ resource ''
42
+ end
43
+
44
+ # GET returns available schema information for this event collection,
45
+ # including properties and their type. It also returns links to sub-resources.
46
+ #
47
+ # POST is for inserting one event at a time in a single request. Examples below.
48
+ #
49
+ # DELETE is for deleting the entire event collection. This is irreversible and will only work for collections under 10k events.
50
+ #
51
+ # @return [Resource]
52
+ def event_collection(project_id, event_collection)
53
+ resource "projects/#{project_id}/events/#{event_collection}"
54
+ end
55
+
56
+ # GET returns schema information for all the event collections in this project,
57
+ # including properties and their type.
58
+ #
59
+ # POST is for inserting multiple events in one or more collections, in a single request.
60
+ #
61
+ # @return [Resource]
62
+ def event_collections(project_id)
63
+ resource "projects/#{project_id}/events"
64
+ end
65
+
66
+ # GET creates an extraction request for full-form event data with all property values.
67
+ # If the query string parameter email is specified, then the extraction will be processed
68
+ # asynchronously and an e-mail will be sent to the specified address when it completes.
69
+ # The email will include a link to a downloadable CSV file.
70
+ # If email is omitted, then the extraction will be processed in-line and results will be returned in the GET request.
71
+ #
72
+ # @return [Resource]
73
+ def extraction(project_id, options = {})
74
+ resource "projects/#{project_id}/queries/extraction", options
75
+ end
76
+
77
+ # Funnels count relevant events in succession.
78
+ #
79
+ # @return [Resource]
80
+ def funnel(project_id, options = {})
81
+ resource "projects/#{project_id}/queries/funnel", options
82
+ end
83
+
84
+ # GET returns the maximum numeric value for the target property in the event collection
85
+ # matching the given criteria. Non-numeric values are ignored.
86
+ #
87
+ # @return [Resource]
88
+ def maximum(project_id, event_collection, target_property, options = {})
89
+ options[:event_collection] = event_collection
90
+ options[:target_property] = target_property
91
+
92
+ resource "projects/#{project_id}/queries/maximum", options
93
+ end
94
+
95
+ # GET returns the minimum numeric value for the target property in the event collection
96
+ # matching the given criteria. Non-numeric values are ignored.
97
+ #
98
+ # @return [Resource]
99
+ def minimum(project_id, event_collection, target_property, options = {})
100
+ options[:event_collection] = event_collection
101
+ options[:target_property] = target_property
102
+
103
+ resource "projects/#{project_id}/queries/minimum", options
104
+ end
105
+
106
+ # GET Returns detailed information about the specific project.
107
+ #
108
+ # @return [Resource]
109
+ def project(project_id)
110
+ resource "projects/#{project_id}"
111
+ end
112
+
113
+ # GET Returns the projects accessible to the API user.
114
+ #
115
+ # @return [Resource]
116
+ def projects
117
+ resource 'projects'
118
+ end
119
+
120
+ # GET returns the property name, type, and a link to sub-resources.
121
+ #
122
+ # DELETE is for removing a property and deleting all values stored with that property name.
123
+ #
124
+ # @return [Resource]
125
+ def property(project_id, event_collection, property_name)
126
+ resource "projects/#{project_id}/events/#{event_collection}/properties/#{property_name}"
127
+ end
128
+
129
+ # GET returns the list of available queries.
130
+ #
131
+ # @return [Resource]
132
+ def queries(project_id)
133
+ resource "projects/#{project_id}/queries"
134
+ end
135
+
136
+ # Returns a resource object for the given url and options
137
+ #
138
+ # @return [Resource]
139
+ def resource(url, options = {})
140
+ Resource.new(url, options)
141
+ end
142
+
143
+ # GET returns information about the specified Saved Query and includes links to child-resources.
144
+ #
145
+ # PUT either inserts a new Saved Query if it doesn’t already exist, or updates an existing Saved Query if it does exist.
146
+ #
147
+ # DELETE just plain old deletes the Saved Query.
148
+ #
149
+ # @return [Resource]
150
+ def saved_query(project_id, saved_query_name, options)
151
+ resource "projects/#{project_id}/saved_queries/#{saved_query_name}", options
152
+ end
153
+
154
+ # GET returns the results of the specified Saved Query.
155
+ #
156
+ # @return [Resource]
157
+ def saved_query_result(project_id, saved_query_name)
158
+ resource "projects/#{project_id}/saved_queries/#{saved_query_name}/result"
159
+ end
160
+
161
+ # GET returns all the available Saved Queries for the specified project as well as links to child-resources.
162
+ #
163
+ # @return [Resource]
164
+ def saved_queries(project_id)
165
+ resource "projects/#{project_id}/saved_queries", options
166
+ end
167
+
168
+ # GET returns a list of UNIQUE resources in the event collection matching the given criteria.
169
+ #
170
+ # @return [Resource]
171
+ def select_unique(project_id, event_collection, target_property, options = {})
172
+ options[:event_collection] = event_collection
173
+ options[:target_property] = target_property
174
+
175
+ resource "projects/#{project_id}/queries/select_unique", options
176
+ end
177
+
178
+ # GET returns the sum of all numeric values for the target property in the
179
+ # event collection matching the given criteria. Non-numeric values are ignored.
180
+ #
181
+ # @return [Resource]
182
+ def sum(project_id, event_collection, target_property, options = {})
183
+ options[:event_collection] = event_collection
184
+ options[:target_property] = target_property
185
+
186
+ resource "projects/#{project_id}/queries/sum", options
187
+ end
188
+ end
189
+ end
@@ -0,0 +1,9 @@
1
+ module Keener
2
+ module Config
3
+ attr_accessor :api_key
4
+ attr_accessor :log_responses
5
+ attr_accessor :adapter
6
+ attr_accessor :use_ssl
7
+ attr_accessor :ssl_config
8
+ end
9
+ end
@@ -0,0 +1,35 @@
1
+ module Keener
2
+ module Connection
3
+ def url
4
+ if Keener.use_ssl
5
+ 'https://api.keen.io'
6
+ else
7
+ 'http://api.keen.io'
8
+ end
9
+ end
10
+
11
+ def connection
12
+ @connection ||= ::Faraday.new(:url => url, :ssl => ssl_config) do |config|
13
+ config.headers['Authorization'] = Keener.api_key
14
+ config.headers['Content-Type'] = 'application/json'
15
+
16
+ config.use Response::Middleware
17
+ config.response :json
18
+ config.response :logger if Keener.log_responses
19
+
20
+ #config.use :instrumentation
21
+ config.adapter Keener.adapter || ::Faraday.default_adapter
22
+ end
23
+ end
24
+
25
+ def reset_connection
26
+ @connection = nil
27
+ end
28
+
29
+ def in_parallel(manager = nil, &block)
30
+ connection.in_parallel(manager) do
31
+ block.call(self)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,7 @@
1
+ module Keener
2
+ class Error < StandardError
3
+ class ResourceNotFoundError < Error; end
4
+ end
5
+ end
6
+
7
+
@@ -0,0 +1,70 @@
1
+ module Keener
2
+ class Query
3
+ def initialize(project_id, event_collection, options = {})
4
+ @project_id = project_id
5
+ @event_collection = event_collection
6
+ @options = options
7
+ end
8
+ end
9
+
10
+ class Metric < Query
11
+ def add_filter(property, operator, value)
12
+ @options[:filters] ||= []
13
+ @options[:filters] << {
14
+ :property_name => property,
15
+ :operator => operator,
16
+ :property_value => value
17
+ }
18
+ self
19
+ end
20
+
21
+ def interval(interval)
22
+ @options[:interval] = interval
23
+ self
24
+ end
25
+
26
+ def timeframe(timeframe)
27
+ @options[:timeframe] = timeframe
28
+ self
29
+ end
30
+
31
+ def group_by(group_by)
32
+ @options[:group_by] = group_by
33
+ self
34
+ end
35
+
36
+ def count
37
+ Keened.count(@project_id, @event_collection, @options).get
38
+ end
39
+
40
+ def count_unique(target_property)
41
+ Keened.count_unique(@project_id, @event_collection, target_property, @options).get
42
+ end
43
+
44
+ def minimum(target_property)
45
+ Keened.minimum(@project_id, @event_collection, target_property, @options).get
46
+ end
47
+
48
+ def maximum(target_property)
49
+ Keened.maximum(@project_id, @event_collection, target_property, @options).get
50
+ end
51
+
52
+ def sum(target_property)
53
+ Keened.sum(@project_id, @event_collection, target_property, @options).get
54
+ end
55
+
56
+ def average(target_property)
57
+ Keened.average(@project_id, @event_collection, target_property, @options).get
58
+ end
59
+ end
60
+
61
+ #TODO implement
62
+ class Funnel < Query
63
+
64
+ end
65
+
66
+ #TODO implement
67
+ class Step < Query
68
+
69
+ end
70
+ end
@@ -0,0 +1,51 @@
1
+ module Keener
2
+ # This class represents an API resource and supports methods for making calls on it
3
+ class Resource
4
+ attr_reader :url, :options
5
+
6
+ def initialize(url = '', options = {})
7
+ @url = url
8
+ @options = options
9
+ end
10
+
11
+ # Performs a get request on the given resource. A block passed will be used as an on_complete callback
12
+ #
13
+ # @return [Hashie::Mash] An object representing the result of the request
14
+ def get(&block)
15
+ handle_response Keener.connection.get("#{version}/#{url}", options), &block
16
+ end
17
+
18
+ # Performs a head request on the given resource
19
+ #
20
+ # @return [Faraday::Response] A faraday response object
21
+ def head
22
+ Keener.connection.head("#{version}/#{url}", options)
23
+ end
24
+
25
+ # Checks the given body to see if it's an error object and raises it if it is
26
+ def check_for_errors(body)
27
+ raise body if body.is_a?(Error)
28
+ body
29
+ end
30
+
31
+ # Handles a faraday response, checking if it's an error, and setting up a callback with the passed block
32
+ def handle_response(response, &block)
33
+ if response.finished?
34
+ response = check_for_errors(response.body)
35
+ end
36
+
37
+ if block
38
+ response.on_complete do |env|
39
+ block.call(env[:body])
40
+ end
41
+ end
42
+
43
+ response
44
+ end
45
+
46
+ # Returns the Keen API version to use for requests
47
+ def version
48
+ '3.0'
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,28 @@
1
+ require 'hashie/mash'
2
+
3
+ module Keener
4
+ class Response
5
+ class Middleware < ::Faraday::Response::Middleware
6
+ def check_for_errors(body)
7
+ if body.respond_to?(:error_code)
8
+ Error.const_get(body.error_code).new(body.message)
9
+ else
10
+ body
11
+ end
12
+ end
13
+
14
+ def parse(body)
15
+ case body
16
+ when Hash
17
+ check_for_errors(::Hashie::Mash.new(body))
18
+ when Array
19
+ body.map { |item| parse(item) }
20
+ else
21
+ body
22
+ end
23
+ end
24
+
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,3 @@
1
+ module Keener
2
+ VERSION = "0.1.0"
3
+ end
data/lib/keener.rb ADDED
@@ -0,0 +1,18 @@
1
+ require 'keener/version'
2
+
3
+ require 'faraday'
4
+ require 'faraday_middleware'
5
+
6
+ require 'keener/config'
7
+ require 'keener/response'
8
+ require 'keener/connection'
9
+ require 'keener/resource'
10
+ require 'keener/errors'
11
+ require 'keener/api'
12
+ require 'keener/query'
13
+
14
+ module Keener
15
+ extend Api
16
+ extend Config
17
+ extend Connection
18
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'adapters' do
4
+ describe 'em-http' do
5
+ before :all do
6
+ Keener.reset_connection
7
+ Keener.adapter = :em_http
8
+ end
9
+
10
+ it_behaves_like "a sync adapter"
11
+ it_behaves_like "an async adapter"
12
+ it_behaves_like "a parallel adapter"
13
+ end
14
+
15
+ describe 'em-synchrony' do
16
+ before :all do
17
+ Keener.reset_connection
18
+ Keener.adapter = :em_synchrony
19
+ end
20
+
21
+ it_behaves_like "a sync adapter"
22
+ it_behaves_like "a parallel adapter"
23
+ end
24
+
25
+ describe 'typhoeus' do
26
+ before :all do
27
+ Keener.reset_connection
28
+ Keener.adapter = :typhoeus
29
+ end
30
+
31
+ it_behaves_like "a sync adapter"
32
+ it_behaves_like "a parallel adapter"
33
+ end
34
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe Keener::Config do
4
+ it 'can get and set a config' do
5
+ Keener.api_key.should eq(API_KEY)
6
+ end
7
+ end
@@ -0,0 +1,51 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: http://api.keen.io/3.0/projects/50e8d5f43843316b28000001/queries/count?group_by=character&event_collection=votes
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Authorization:
11
+ - b82b029917fa49af84a5827a43e537a7
12
+ response:
13
+ status:
14
+ code: 200
15
+ message:
16
+ headers:
17
+ date:
18
+ - Thu, 07 Feb 2013 18:39:38 GMT
19
+ content-type:
20
+ - application/json
21
+ connection:
22
+ - close
23
+ content-length:
24
+ - '597'
25
+ expires:
26
+ - Thu, 07 Feb 2013 18:39:38 GMT
27
+ server:
28
+ - TornadoServer/2.4.1
29
+ etag:
30
+ - ! '"4a71bc462e514e53611cd6f04711fe4d1f68693a"'
31
+ cache-control:
32
+ - private, max-age=0, s-maxage=0
33
+ access-control-allow-origin:
34
+ - ! '*'
35
+ access-control-allow-headers:
36
+ - origin, content-type, accept, authorization, user-agent
37
+ body:
38
+ encoding: US-ASCII
39
+ string: ! "{\n \"result\": [\n {\n \"character\": \"Troi\", \n \"result\":
40
+ 101\n }, \n {\n \"character\": \"LaForge\", \n \"result\":
41
+ 571\n }, \n {\n \"character\": \"No answer\", \n \"result\":
42
+ 397\n }, \n {\n \"character\": \"Crusher\", \n \"result\":
43
+ 200\n }, \n {\n \"character\": \"Replicator\", \n \"result\":
44
+ 171\n }, \n {\n \"character\": \"Worf\", \n \"result\": 330\n
45
+ \ }, \n {\n \"character\": \"Riker\", \n \"result\": 280\n
46
+ \ }, \n {\n \"character\": \"Data\", \n \"result\": 342\n },
47
+ \n {\n \"character\": \"Picard\", \n \"result\": 365\n }\n
48
+ \ ]\n}"
49
+ http_version:
50
+ recorded_at: Thu, 07 Feb 2013 18:39:38 GMT
51
+ recorded_with: VCR 2.4.0
@@ -0,0 +1,42 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: http://api.keen.io/3.0/projects/50e8d5f43843316b28000001/queries/count?event_collection=votes
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Authorization:
11
+ - b82b029917fa49af84a5827a43e537a7
12
+ response:
13
+ status:
14
+ code: 200
15
+ message:
16
+ headers:
17
+ date:
18
+ - Thu, 07 Feb 2013 18:39:38 GMT
19
+ content-type:
20
+ - application/json
21
+ connection:
22
+ - close
23
+ content-length:
24
+ - '20'
25
+ expires:
26
+ - Thu, 07 Feb 2013 18:39:38 GMT
27
+ server:
28
+ - TornadoServer/2.4.1
29
+ etag:
30
+ - ! '"09d8b72d478662bc62110091a94b0191b8133575"'
31
+ cache-control:
32
+ - private, max-age=0, s-maxage=0
33
+ access-control-allow-origin:
34
+ - ! '*'
35
+ access-control-allow-headers:
36
+ - origin, content-type, accept, authorization, user-agent
37
+ body:
38
+ encoding: US-ASCII
39
+ string: ! "{\n \"result\": 2757\n}"
40
+ http_version:
41
+ recorded_at: Thu, 07 Feb 2013 18:39:38 GMT
42
+ recorded_with: VCR 2.4.0
@@ -0,0 +1,42 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: http://api.keen.io/3.0/projects/50e8d5f43843316b28000001/queries/count_unique?event_collection=votes&target_property=character
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Authorization:
11
+ - b82b029917fa49af84a5827a43e537a7
12
+ response:
13
+ status:
14
+ code: 200
15
+ message:
16
+ headers:
17
+ date:
18
+ - Thu, 07 Feb 2013 18:24:12 GMT
19
+ content-type:
20
+ - application/json
21
+ connection:
22
+ - close
23
+ content-length:
24
+ - '17'
25
+ expires:
26
+ - Thu, 07 Feb 2013 18:24:12 GMT
27
+ server:
28
+ - TornadoServer/2.4.1
29
+ etag:
30
+ - ! '"07466cb5e5c2fa78072724132267ff0967888ade"'
31
+ cache-control:
32
+ - private, max-age=0, s-maxage=0
33
+ access-control-allow-origin:
34
+ - ! '*'
35
+ access-control-allow-headers:
36
+ - origin, content-type, accept, authorization, user-agent
37
+ body:
38
+ encoding: US-ASCII
39
+ string: ! "{\n \"result\": 9\n}"
40
+ http_version:
41
+ recorded_at: Thu, 07 Feb 2013 18:24:12 GMT
42
+ recorded_with: VCR 2.4.0
@@ -0,0 +1,45 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: http://api.keen.io/3.0/projects/50e8d5f43843316b28000001/events
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Authorization:
11
+ - b82b029917fa49af84a5827a43e537a7
12
+ response:
13
+ status:
14
+ code: 200
15
+ message:
16
+ headers:
17
+ date:
18
+ - Thu, 07 Feb 2013 18:07:17 GMT
19
+ content-type:
20
+ - application/json
21
+ connection:
22
+ - close
23
+ content-length:
24
+ - '229'
25
+ expires:
26
+ - Thu, 07 Feb 2013 18:07:16 GMT
27
+ server:
28
+ - TornadoServer/2.4.1
29
+ etag:
30
+ - ! '"9ea36ba679175b5949aca20923f1880f41c72528"'
31
+ cache-control:
32
+ - private, max-age=0, s-maxage=0
33
+ access-control-allow-origin:
34
+ - ! '*'
35
+ access-control-allow-headers:
36
+ - origin, content-type, accept, authorization, user-agent
37
+ body:
38
+ encoding: US-ASCII
39
+ string: ! "[\n {\n \"name\": \"votes\", \n \"properties\": {\n \"character\":
40
+ \"string\", \n \"keen.created_at\": \"datetime\", \n \"keen.timestamp\":
41
+ \"datetime\"\n }, \n \"url\": \"/3.0/projects/50e8d5f43843316b28000001/events/votes\"\n
42
+ \ }\n]"
43
+ http_version:
44
+ recorded_at: Thu, 07 Feb 2013 18:07:17 GMT
45
+ recorded_with: VCR 2.4.0
@@ -0,0 +1,51 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: http://api.keen.io/3.0/projects/50e8d5f43843316b28000001
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Authorization:
11
+ - b82b029917fa49af84a5827a43e537a7
12
+ response:
13
+ status:
14
+ code: 200
15
+ message:
16
+ headers:
17
+ date:
18
+ - Thu, 07 Feb 2013 18:16:37 GMT
19
+ content-type:
20
+ - application/json
21
+ connection:
22
+ - close
23
+ content-length:
24
+ - '718'
25
+ expires:
26
+ - Thu, 07 Feb 2013 18:16:36 GMT
27
+ server:
28
+ - TornadoServer/2.4.1
29
+ etag:
30
+ - ! '"0b93639278b4fbbc87a2ca117e3f58cf638385eb"'
31
+ cache-control:
32
+ - private, max-age=0, s-maxage=0
33
+ access-control-allow-origin:
34
+ - ! '*'
35
+ access-control-allow-headers:
36
+ - origin, content-type, accept, authorization, user-agent
37
+ body:
38
+ encoding: US-ASCII
39
+ string: ! "{\n \"api_key\": \"b82b029917fa49af84a5827a43e537a7\", \n \"events\":
40
+ [\n {\n \"name\": \"votes\", \n \"url\": \"/3.0/projects/50e8d5f43843316b28000001/votes\"\n
41
+ \ }\n ], \n \"events_url\": \"/3.0/projects/50e8d5f43843316b28000001/events\",
42
+ \n \"id\": \"50e8d5f43843316b28000001\", \n \"name\": \"RubyExampleApp\",
43
+ \n \"organization_id\": \"505e1a90897a2c2fbf000000\", \n \"partners_url\":
44
+ \"/3.0/projects/50e8d5f43843316b28000001/partners\", \n \"queries_url\":
45
+ \"/3.0/projects/50e8d5f43843316b28000001/queries\", \n \"saved_queries\":
46
+ [], \n \"saved_queries_url\": \"/3.0/projects/50e8d5f43843316b28000001/saved_queries\",
47
+ \n \"settings\": {\n \"analysis_limit\": 100, \n \"event_limit\": 10000\n
48
+ \ }, \n \"url\": \"/3.0/projects/50e8d5f43843316b28000001\"\n}"
49
+ http_version:
50
+ recorded_at: Thu, 07 Feb 2013 18:16:37 GMT
51
+ recorded_with: VCR 2.4.0
@@ -0,0 +1,52 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: http://api.keen.io/3.0/projects
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Authorization:
11
+ - b82b029917fa49af84a5827a43e537a7
12
+ response:
13
+ status:
14
+ code: 200
15
+ message:
16
+ headers:
17
+ date:
18
+ - Thu, 07 Feb 2013 18:04:31 GMT
19
+ content-type:
20
+ - application/json
21
+ connection:
22
+ - close
23
+ content-length:
24
+ - '766'
25
+ expires:
26
+ - Thu, 07 Feb 2013 18:04:31 GMT
27
+ server:
28
+ - TornadoServer/2.4.1
29
+ etag:
30
+ - ! '"a8a0a1a620dc6345a3c77036f100da54dd5eb7e3"'
31
+ cache-control:
32
+ - private, max-age=0, s-maxage=0
33
+ access-control-allow-origin:
34
+ - ! '*'
35
+ access-control-allow-headers:
36
+ - origin, content-type, accept, authorization, user-agent
37
+ body:
38
+ encoding: US-ASCII
39
+ string: ! "[\n {\n \"api_key\": \"b82b029917fa49af84a5827a43e537a7\", \n
40
+ \ \"events\": [\n {\n \"name\": \"votes\", \n \"url\":
41
+ \"/3.0/projects/50e8d5f43843316b28000001/votes\"\n }\n ], \n \"events_url\":
42
+ \"/3.0/projects/50e8d5f43843316b28000001/events\", \n \"id\": \"50e8d5f43843316b28000001\",
43
+ \n \"name\": \"RubyExampleApp\", \n \"organization_id\": \"505e1a90897a2c2fbf000000\",
44
+ \n \"partners_url\": \"/3.0/projects/50e8d5f43843316b28000001/partners\",
45
+ \n \"queries_url\": \"/3.0/projects/50e8d5f43843316b28000001/queries\",
46
+ \n \"saved_queries\": [], \n \"saved_queries_url\": \"/3.0/projects/50e8d5f43843316b28000001/saved_queries\",
47
+ \n \"settings\": {\n \"analysis_limit\": 100, \n \"event_limit\":
48
+ 10000\n }, \n \"url\": \"/3.0/projects/50e8d5f43843316b28000001\"\n
49
+ \ }\n]"
50
+ http_version:
51
+ recorded_at: Thu, 07 Feb 2013 18:04:31 GMT
52
+ recorded_with: VCR 2.4.0
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+
3
+ describe Keener::Api do
4
+ before :all do
5
+ Keener.reset_connection
6
+ Keener.adapter = nil
7
+ end
8
+
9
+ describe 'errors' do
10
+ it 'raises a not found error' do
11
+ expect { Keener.project('xxx').get }.to raise_error(Keener::Error::ResourceNotFoundError)
12
+ end
13
+ end
14
+
15
+ describe '.count' do
16
+ context 'without options', :vcr => { :cassette_name => 'count/no_options' } do
17
+ subject :count do
18
+ Keener.count(PROJECT_ID, COLLECTION_NAME).get
19
+ end
20
+
21
+ its (:result) { should eq(2757) }
22
+ end
23
+
24
+ context 'grouped by character', :vcr => { :cassette_name => 'count/grouped' } do
25
+ subject :count do
26
+ Keener.count(PROJECT_ID, COLLECTION_NAME, :group_by => 'character').get.result.first
27
+ end
28
+
29
+ its (:character) { should eq('Troi') }
30
+ its (:result) { should eq(101) }
31
+ end
32
+ end
33
+
34
+ describe '.count_unique', :vcr => { :cassette_name => 'count_unique' } do
35
+ subject :count_unique do
36
+ Keener.count_unique(PROJECT_ID, COLLECTION_NAME, 'character').get
37
+ end
38
+
39
+ its (:result) { should eq(9) }
40
+ end
41
+
42
+ describe '.event_collections', :vcr => { :cassette_name => 'events' } do
43
+ subject :events do
44
+ Keener.event_collections(PROJECT_ID).get
45
+ end
46
+
47
+ its (:count) { should eq(1)}
48
+
49
+ it 'returns votes' do
50
+ events.first.name.should eq('votes')
51
+ end
52
+ end
53
+
54
+ describe '.project', :vcr => { :cassette_name => 'project' } do
55
+ subject :project do
56
+ Keener.project(PROJECT_ID).get
57
+ end
58
+
59
+ it 'returns the right project id' do
60
+ project.id.should eq(PROJECT_ID)
61
+ end
62
+ end
63
+
64
+ describe '.projects', :vcr => { :cassette_name => 'projects' } do
65
+ subject :projects do
66
+ Keener.projects.get
67
+ end
68
+
69
+ its (:count) { should eq(1)}
70
+
71
+ it 'returns the right project id' do
72
+ projects.first.id.should eq(PROJECT_ID)
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,42 @@
1
+ require 'keener'
2
+ require 'vcr'
3
+ require 'eventmachine'
4
+ require 'typhoeus'
5
+ require 'typhoeus/adapters/faraday'
6
+
7
+
8
+ # Integration test data
9
+ PROJECT_ID = "50e8d5f43843316b28000001"
10
+ API_KEY = "b82b029917fa49af84a5827a43e537a7"
11
+ COLLECTION_NAME = "votes"
12
+
13
+ Keener.api_key = API_KEY
14
+
15
+ VCR.configure do |c|
16
+ c.cassette_library_dir = 'spec/fixtures/vcr_cassettes'
17
+ c.hook_into :faraday
18
+ c.configure_rspec_metadata!
19
+
20
+ # Async tests don't work with vcr
21
+ c.allow_http_connections_when_no_cassette = true
22
+ end
23
+
24
+ # This file was generated by the `rspec --init` command. Conventionally, all
25
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
26
+ # Require this file using `require "spec_helper"` to ensure that it is only
27
+ # loaded once.
28
+ #
29
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
30
+ RSpec.configure do |config|
31
+ config.treat_symbols_as_metadata_keys_with_true_values = true
32
+ config.run_all_when_everything_filtered = true
33
+ #config.filter_run :focus
34
+
35
+ # Run specs in random order to surface order dependencies. If you find an
36
+ # order dependency and want to debug it, you can fix the order by providing
37
+ # the seed, which is printed after each run.
38
+ # --seed 1234
39
+ config.order = 'random'
40
+ end
41
+
42
+ Dir["./spec/support/**/*.rb"].sort.each {|f| require f}
@@ -0,0 +1,55 @@
1
+ shared_examples 'a sync adapter' do
2
+ it 'makes a blocking request' do
3
+ Keener.projects.get.first.id.should eq(PROJECT_ID)
4
+ end
5
+ end
6
+
7
+ shared_examples 'an async adapter' do
8
+ it 'calls a passed block' do
9
+ EventMachine.run do
10
+ Keener.projects.get { |response|
11
+ response.first.id.should eq(PROJECT_ID)
12
+ EventMachine.stop
13
+ }
14
+ end
15
+ end
16
+
17
+ it 'returns a response' do
18
+ EventMachine.run do
19
+ Keener.projects.get.on_complete { |env|
20
+ env[:body].first.id.should eq(PROJECT_ID)
21
+ EventMachine.stop
22
+ }
23
+ end
24
+ end
25
+
26
+ it 'returns a body containing a ResourceNotFoundError' do
27
+ EventMachine.run do
28
+ Keener.project('xxx').get.on_complete { |env|
29
+ env[:body].should be_a(Keener::Error::ResourceNotFoundError)
30
+ EventMachine.stop
31
+ }
32
+ end
33
+ end
34
+ end
35
+
36
+ shared_examples 'a parallel adapter' do
37
+ subject :responses do
38
+ responses = []
39
+
40
+ Keener.in_parallel do |k|
41
+ responses << k.count(PROJECT_ID, COLLECTION_NAME).get
42
+ responses << k.count_unique(PROJECT_ID, COLLECTION_NAME, 'character').get
43
+ end
44
+
45
+ responses
46
+ end
47
+
48
+ it 'return the count' do
49
+ responses.first.body.result.should eq(2757)
50
+ end
51
+
52
+ it 'returns the unique count' do
53
+ responses.last.body.result.should eq(9)
54
+ end
55
+ end
metadata ADDED
@@ -0,0 +1,266 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: keener
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Mateo Murphy
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-10 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: faraday
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 0.8.5
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 0.8.5
30
+ - !ruby/object:Gem::Dependency
31
+ name: faraday_middleware
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 0.9.0
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 0.9.0
46
+ - !ruby/object:Gem::Dependency
47
+ name: hashie
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 1.2.0
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 1.2.0
62
+ - !ruby/object:Gem::Dependency
63
+ name: em-synchrony
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 1.0.3
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: 1.0.3
78
+ - !ruby/object:Gem::Dependency
79
+ name: em-http-request
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: 1.0.3
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: 1.0.3
94
+ - !ruby/object:Gem::Dependency
95
+ name: typhoeus
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: rspec
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ~>
116
+ - !ruby/object:Gem::Version
117
+ version: '2.12'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ~>
124
+ - !ruby/object:Gem::Version
125
+ version: '2.12'
126
+ - !ruby/object:Gem::Dependency
127
+ name: vcr
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ~>
132
+ - !ruby/object:Gem::Version
133
+ version: 2.4.0
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ~>
140
+ - !ruby/object:Gem::Version
141
+ version: 2.4.0
142
+ - !ruby/object:Gem::Dependency
143
+ name: rake
144
+ requirement: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ! '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ type: :development
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ - !ruby/object:Gem::Dependency
159
+ name: yard
160
+ requirement: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ! '>='
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ none: false
170
+ requirements:
171
+ - - ! '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ - !ruby/object:Gem::Dependency
175
+ name: redcarpet
176
+ requirement: !ruby/object:Gem::Requirement
177
+ none: false
178
+ requirements:
179
+ - - ! '>='
180
+ - !ruby/object:Gem::Version
181
+ version: '0'
182
+ type: :development
183
+ prerelease: false
184
+ version_requirements: !ruby/object:Gem::Requirement
185
+ none: false
186
+ requirements:
187
+ - - ! '>='
188
+ - !ruby/object:Gem::Version
189
+ version: '0'
190
+ description: Unofficial gem for accessing keen.io
191
+ email:
192
+ - mateo.murphy@gmail.com
193
+ executables: []
194
+ extensions: []
195
+ extra_rdoc_files: []
196
+ files:
197
+ - .gitignore
198
+ - .rspec
199
+ - Gemfile
200
+ - LICENSE.txt
201
+ - README.md
202
+ - Rakefile
203
+ - keener.gemspec
204
+ - lib/keener.rb
205
+ - lib/keener/api.rb
206
+ - lib/keener/config.rb
207
+ - lib/keener/connection.rb
208
+ - lib/keener/errors.rb
209
+ - lib/keener/query.rb
210
+ - lib/keener/resource.rb
211
+ - lib/keener/response.rb
212
+ - lib/keener/version.rb
213
+ - spec/adapters_spec.rb
214
+ - spec/config_spec.rb
215
+ - spec/fixtures/vcr_cassettes/count/grouped.yml
216
+ - spec/fixtures/vcr_cassettes/count/no_options.yml
217
+ - spec/fixtures/vcr_cassettes/count_unique.yml
218
+ - spec/fixtures/vcr_cassettes/events.yml
219
+ - spec/fixtures/vcr_cassettes/project.yml
220
+ - spec/fixtures/vcr_cassettes/projects.yml
221
+ - spec/integration/api_spec.rb
222
+ - spec/spec_helper.rb
223
+ - spec/support/shared_examples.rb
224
+ homepage: https://github.com/mateomurphy/keener
225
+ licenses: []
226
+ post_install_message:
227
+ rdoc_options: []
228
+ require_paths:
229
+ - lib
230
+ required_ruby_version: !ruby/object:Gem::Requirement
231
+ none: false
232
+ requirements:
233
+ - - ! '>='
234
+ - !ruby/object:Gem::Version
235
+ version: '0'
236
+ segments:
237
+ - 0
238
+ hash: 134372974807999632
239
+ required_rubygems_version: !ruby/object:Gem::Requirement
240
+ none: false
241
+ requirements:
242
+ - - ! '>='
243
+ - !ruby/object:Gem::Version
244
+ version: '0'
245
+ segments:
246
+ - 0
247
+ hash: 134372974807999632
248
+ requirements: []
249
+ rubyforge_project:
250
+ rubygems_version: 1.8.24
251
+ signing_key:
252
+ specification_version: 3
253
+ summary: An unofficial gem for accessing keen.io
254
+ test_files:
255
+ - spec/adapters_spec.rb
256
+ - spec/config_spec.rb
257
+ - spec/fixtures/vcr_cassettes/count/grouped.yml
258
+ - spec/fixtures/vcr_cassettes/count/no_options.yml
259
+ - spec/fixtures/vcr_cassettes/count_unique.yml
260
+ - spec/fixtures/vcr_cassettes/events.yml
261
+ - spec/fixtures/vcr_cassettes/project.yml
262
+ - spec/fixtures/vcr_cassettes/projects.yml
263
+ - spec/integration/api_spec.rb
264
+ - spec/spec_helper.rb
265
+ - spec/support/shared_examples.rb
266
+ has_rdoc: