keener 0.1.0

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.
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: