keen 0.7.8 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c3cf3ae16e58f3c518d668690727dee634e620ec
4
+ data.tar.gz: bb8e8adde18854b35095b90017dc468900e28201
5
+ SHA512:
6
+ metadata.gz: 097d04970b5ce6c0779b96655cd29aa747d1df5eeb4506dfc4763546fae08c7bd3b6f7ed48f5b4f2dcbd3a4caeb433778fda7bfa67d5871c9f9d3c2ffa4d48e2
7
+ data.tar.gz: 732360e3dd2873579493eb08f407055929cc9d27a5285ef8d592dcfd94f69999d66f4d62787a0557b96b485409bdb2cb35a5233ff270b81b73e057c0e1247cdf
@@ -6,13 +6,19 @@ rvm:
6
6
  - 1.9.3
7
7
  - 2.0.0
8
8
  - jruby-19mode
9
- - rbx-19mode
9
+ - rbx
10
10
 
11
11
  env:
12
12
  - PATTERN=keen
13
- - PATTERN=integration KEEN_MASTER_KEY=f806128f31c349fda124b62d1f4cf4b2 KEEN_WRITE_KEY=f806128f31c349fda124b62d1f4cf4b2 KEEN_READ_KEY=f806128f31c349fda124b62d1f4cf4b2 KEEN_PROJECT_ID=50e5ffa6897a2c319b000000
13
+ #- PATTERN=integration KEEN_MASTER_KEY=f806128f31c349fda124b62d1f4cf4b2 KEEN_WRITE_KEY=f806128f31c349fda124b62d1f4cf4b2 KEEN_READ_KEY=f806128f31c349fda124b62d1f4cf4b2 KEEN_PROJECT_ID=50e5ffa6897a2c319b000000
14
14
  - PATTERN=synchrony
15
15
 
16
+ before_install:
17
+ - gem update bundler
18
+ - bundle --version
19
+ - gem update --system 2.1.11
20
+ - gem --version
21
+
16
22
  matrix:
17
23
  exclude:
18
24
  - rvm: 1.8.7
data/Guardfile CHANGED
@@ -1,5 +1,5 @@
1
1
  group :unit do
2
- guard 'rspec', :spec_paths => "spec/keen" do
2
+ guard 'rspec', :spec_paths => ["spec/keen"] do
3
3
  watch('spec/spec_helper.rb') { "spec" }
4
4
  watch('spec/keen/spec_helper.rb') { "spec" }
5
5
  watch(%r{^spec/keen/.+_spec\.rb$})
@@ -8,7 +8,7 @@ group :unit do
8
8
  end
9
9
 
10
10
  group :integration do
11
- guard 'rspec', :spec_paths => "spec/integration" do
11
+ guard 'rspec', :spec_paths => ["spec/integration"] do
12
12
  watch('spec/spec_helper.rb') { "spec" }
13
13
  watch('spec/integration/spec_helper.rb') { "spec" }
14
14
  watch(%r{^spec/integration/.+_spec\.rb$})
@@ -16,7 +16,7 @@ group :integration do
16
16
  end
17
17
 
18
18
  group :synchrony do
19
- guard 'rspec', :spec_paths => "spec/synchrony" do
19
+ guard 'rspec', :spec_paths => ["spec/synchrony"] do
20
20
  watch('spec/spec_helper.rb') { "spec" }
21
21
  watch('spec/synchrony/spec_helper.rb') { "spec" }
22
22
  watch(%r{^spec/synchrony/.+_spec\.rb$})
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012 Keen Labs
1
+ Copyright (c) 2012-2014 Keen Labs
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -40,7 +40,7 @@ If you're using [foreman](http://ddollar.github.com/foreman/), add this to your
40
40
  KEEN_WRITE_KEY=yyyyyyyyyyyyyyy
41
41
  KEEN_READ_KEY=zzzzzzzzzzzzzzz
42
42
 
43
- If not, make to to export the variable into your shell or put it before the command you use to start your server.
43
+ If not, make a script to export the variables into your shell or put it before the command you use to start your server.
44
44
 
45
45
  When you deploy, make sure your production environment variables are set. For example,
46
46
  set [config vars](https://devcenter.heroku.com/articles/config-vars) on Heroku. (We recommend this
@@ -89,7 +89,7 @@ Thread.new { EventMachine.run }
89
89
  The best place for this is in an initializer, or anywhere that runs when your app boots up.
90
90
  Here's a useful blog article that explains more about this approach - [EventMachine and Passenger](http://railstips.org/blog/archives/2011/05/04/eventmachine-and-passenger/).
91
91
 
92
- And here's a gist that shows an example of [Eventmachine with Unicorn](https://gist.github.com/jonkgrimes/5103321). Thanks to [jonkgrimes](https://github.com/jonkgrimes) for sharing this with us!
92
+ And here's an example repository that shows an example of [Eventmachine with Unicorn](https://github.com/dzello/em-unicorn/blob/master/unicorn.rb), specifically the Unicorn config for starting and stopping EventMachine after forking.
93
93
 
94
94
  Now, in your code, replace `publish` with `publish_async`. Bind callbacks if you require them.
95
95
 
@@ -154,7 +154,7 @@ Keen.delete(:signups) # => true
154
154
 
155
155
  # Or just delete an event corresponding to a particular user
156
156
  Keen.delete(:signups, filters: [{
157
- property_name: 'username', operator: 'eq', property_value: "Bob"
157
+ :property_name => 'username', ;operator => 'eq', :property_value => "Bob"
158
158
  }]) # => true
159
159
  ```
160
160
 
@@ -207,7 +207,7 @@ If you call `publish_async` and `EM::Synchrony` is defined the method will retur
207
207
  directly. (It does not return the deferrable on which to register callbacks.) Likewise, it will raise
208
208
  exceptions 'synchronously' should they happen.
209
209
 
210
- #### Beacon URL's
210
+ #### Beacon URLs
211
211
 
212
212
  It's possible to publish events to your Keen IO project using the HTTP GET method.
213
213
  This is useful for situations like tracking email opens using [image beacons](http://en.wikipedia.org/wiki/Web_bug).
@@ -222,10 +222,10 @@ Keen.beacon_url("sign_ups", :recipient => "foo@foo.com")
222
222
  # => "https://api.keen.io/3.0/projects/xxxxxx/events/email_opens?api_key=yyyyyy&data=eyJyZWNpcGllbnQiOiJmb29AZm9vLmNvbSJ9"
223
223
  ```
224
224
 
225
- To track email opens, simply add an image to your email template that points to this URL.
225
+ To track email opens, simply add an image to your email template that points to this URL. For further information on how to do this, see the [image beacon documentation](https://keen.io/docs/data-collection/image-beacon/).
226
226
 
227
- #### Redirect URL's
228
- Redirect URL's are just like image beacon URL's with the addition of a `redirect` query parameter. This parameter is used
227
+ #### Redirect URLs
228
+ Redirect URLs are just like image beacon URLs with the addition of a `redirect` query parameter. This parameter is used
229
229
  to issue a redirect to a certain URL after an event is recorded.
230
230
 
231
231
  ```
@@ -233,10 +233,48 @@ Keen.redirect_url("sign_ups", { :recipient => "foo@foo.com" }, "http://foo.com")
233
233
  # => "https://api.keen.io/3.0/projects/xxxxxx/events/email_opens?api_key=yyyyyy&data=eyJyZWNpcGllbnQiOiJmb29AZm9vLmNvbSJ9&redirect=http://foo.com"
234
234
  ```
235
235
 
236
- This is helpful for tracking email clickthroughs.
236
+ This is helpful for tracking email clickthroughs. See the [redirect documentation](https://keen.io/docs/data-collection/redirect/) for further information.
237
+
238
+ #### Generating scoped keys
239
+
240
+ A [scoped key](https://keen.io/docs/security/#scoped-key) is a string, generated with your API Key, that represents some encrypted authentication and query options.
241
+ Use them to control what data queries have access to.
242
+
243
+ ``` ruby
244
+ # "my-api-key" should be your MASTER API key
245
+ scoped_key = Keen::ScopedKey.new("my-api-key", { "filters" => [{
246
+ "property_name" => "accountId",
247
+ "operator" => "eq",
248
+ "property_value" => "123456"
249
+ }]}).encrypt! # "4d1982fe601b359a5cab7ac7845d3bf27026936cdbf8ce0ab4ebcb6930d6cf7f139e..."
250
+ ```
251
+
252
+ You can use the scoped key created in Ruby for API requests from any client. Scoped keys are commonly used in JavaScript, where credentials are visible and need to be protected.
253
+
254
+ ### Troubleshooting
255
+
256
+ ##### EventMachine
257
+
258
+ If you run into `Keen::Error: Keen IO Exception: An EventMachine loop must be running to use publish_async calls` or
259
+ `Uncaught RuntimeError: eventmachine not initialized: evma_set_pending_connect_timeout`, this means that the EventMachine
260
+ loop has died. This can happen for a variety of reasons, and every app is different. [Issue #22](https://github.com/keenlabs/keen-gem/issues/22) shows how to add some extra protection to avoid this situation.
261
+
262
+ ##### publish_async in a script or worker
263
+
264
+ If you write a script that uses `publish_async`, you need to keep the script alive long enough for the call(s) to complete.
265
+ EventMachine itself won't do this because it runs in a different thread. Here's an [example gist](https://gist.github.com/dzello/7472823) that shows how to exit the process after the event has been recorded.
237
266
 
238
267
  ### Changelog
239
268
 
269
+ ##### 0.8.0
270
+ + **UPGRADE WARNING** Do you use spaces in collection names? Or other special characters? Read [this post](https://groups.google.com/forum/?fromgroups#!topic/keen-io-devs/VtCgPuNKrgY) from the mailing list to make sure your collection names don't change.
271
+ + Add support for generating [scoped keys](https://keen.io/docs/security/#scoped-key).
272
+ + Make collection name encoding more robust. Make sure collection names are encoded identically for publishing events, running queries, and performing deletes.
273
+ + Add support for [grouping by multiple properties](https://keen.io/docs/data-analysis/group-by/#grouping-by-multiple-properties).
274
+
275
+ ##### 0.7.8
276
+ + Add support for redirect URL creation.
277
+
240
278
  ##### 0.7.7
241
279
  + Add support for HTTP and SOCKS proxies. Set `KEEN_PROXY_URL` to the proxy URL and `KEEN_PROXY_TYPE` to 'socks5' if you need to. These
242
280
  properties can also be set on the client instances as `proxy_url` and `proxy_type`.
@@ -299,6 +337,22 @@ at [users.keen.io](http://users.keen.io). We'd love to hear your feedback and id
299
337
  keen-gem is an open source project and we welcome your contributions.
300
338
  Fire away with issues and pull requests!
301
339
 
340
+ #### Running Tests
341
+
342
+ `bundle exec rake spec` - Run unit specs. HTTP is mocked.
343
+
344
+ `bundle exec rake integration` - Run integration specs with the real API. Requires env variables. See [.travis.yml](https://github.com/keenlabs/keen-gem/blob/master/.travis.yml).
345
+
346
+ `bundle exec rake synchrony` - Run async publishing specs with `EM::Synchrony`.
347
+
348
+ Similarly, you can use guard to listen for changes to files and run specs.
349
+
350
+ `bundle exec guard -g unit`
351
+
352
+ `bundle exec guard -g integration`
353
+
354
+ `bundle exec guard -g synchrony`
355
+
302
356
  ### Community Contributors
303
357
  + [alexkwolfe](https://github.com/alexkwolfe)
304
358
  + [peteygao](https://github.com/peteygao)
@@ -10,24 +10,32 @@ Gem::Specification.new do |s|
10
10
  s.homepage = "https://github.com/keenlabs/keen-gem"
11
11
  s.summary = "Keen IO API Client"
12
12
  s.description = "Send events and build analytics features into your Ruby applications."
13
+ s.license = "MIT"
14
+
15
+ s.post_install_message = "**UPGRADE WARNING** Do you use spaces in collection names? Or other special characters? Read https://groups.google.com/forum/?fromgroups#!topic/keen-io-devs/VtCgPuNKrgY from the mailing list to make sure your collection names don't change!"
13
16
 
14
17
  s.add_dependency "multi_json", "~> 1.0"
18
+ s.add_dependency "addressable", "~> 2.3.5"
15
19
  s.add_dependency "jruby-openssl" if defined?(JRUBY_VERSION)
16
20
 
21
+ s.add_dependency 'rubysl', '~> 2.0' if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
22
+
17
23
  # guard
18
- s.add_development_dependency 'guard'
19
- s.add_development_dependency 'guard-rspec'
24
+ unless RUBY_VERSION.start_with? '1.8'
25
+ s.add_development_dependency 'guard'
26
+ s.add_development_dependency 'guard-rspec'
20
27
 
21
- # guard cross-platform listener trick
22
- s.add_development_dependency 'rb-inotify'
23
- s.add_development_dependency 'rb-fsevent'
24
- s.add_development_dependency 'rb-fchange'
28
+ # guard cross-platform listener trick
29
+ s.add_development_dependency 'rb-inotify'
30
+ s.add_development_dependency 'rb-fsevent'
31
+ s.add_development_dependency 'rb-fchange'
25
32
 
26
- # guard notifications
27
- s.add_development_dependency 'ruby_gntp'
33
+ # guard notifications
34
+ s.add_development_dependency 'ruby_gntp'
28
35
 
29
- # fix guard prompt
30
- s.add_development_dependency 'rb-readline' # or compile ruby w/ readline
36
+ # fix guard prompt
37
+ s.add_development_dependency 'rb-readline' # or compile ruby w/ readline
38
+ end
31
39
 
32
40
  # debuggers
33
41
  if /\Aruby/ === RUBY_DESCRIPTION
@@ -2,6 +2,7 @@ require 'logger'
2
2
  require 'forwardable'
3
3
 
4
4
  require 'keen/client'
5
+ require 'keen/scoped_key'
5
6
 
6
7
  module Keen
7
8
  class Error < RuntimeError
@@ -0,0 +1,43 @@
1
+ require 'openssl'
2
+ require 'digest'
3
+ require 'base64'
4
+
5
+ module Keen
6
+ module AESHelper
7
+
8
+ BLOCK_SIZE = 32
9
+
10
+ def aes256_encrypt(key, plaintext)
11
+ aes = OpenSSL::Cipher::AES.new(256, :CBC)
12
+ aes.encrypt
13
+ aes.key = key
14
+ iv = aes.random_iv
15
+ [aes.update(plaintext) + aes.final, iv]
16
+ end
17
+
18
+ def aes256_decrypt(key, iv_plus_encrypted)
19
+ iv = iv_plus_encrypted[0, 16]
20
+ encrypted = iv_plus_encrypted[16, iv_plus_encrypted.length]
21
+ aes = OpenSSL::Cipher::AES.new(256, :CBC)
22
+ aes.decrypt
23
+ aes.key = key
24
+ aes.iv = iv
25
+ aes.update(encrypted) + aes.final
26
+ end
27
+
28
+ def hexlify(msg)
29
+ msg.unpack('H*')[0]
30
+ end
31
+
32
+ def unhexlify(msg)
33
+ [msg].pack('H*')
34
+ end
35
+
36
+ def pad(msg)
37
+ pad_len = BLOCK_SIZE - (msg.length % BLOCK_SIZE)
38
+ padding = pad_len.chr * pad_len
39
+ padded = msg + padding
40
+ padded
41
+ end
42
+ end
43
+ end
@@ -3,11 +3,11 @@ require 'keen/version'
3
3
  require 'keen/client/publishing_methods'
4
4
  require 'keen/client/querying_methods'
5
5
  require 'keen/client/maintenance_methods'
6
-
7
6
  require 'openssl'
8
7
  require 'multi_json'
9
8
  require 'base64'
10
9
  require 'cgi'
10
+ require 'addressable/uri'
11
11
 
12
12
  module Keen
13
13
  class Client
@@ -92,25 +92,15 @@ module Keen
92
92
  end
93
93
 
94
94
  def api_event_collection_resource_path(event_collection)
95
- "/#{api_version}/projects/#{project_id}/events/#{CGI.escape(event_collection.to_s)}"
95
+ encoded_collection_name = Addressable::URI.escape(event_collection.to_s)
96
+ encoded_collection_name.gsub! '/', '%2F'
97
+ "/#{api_version}/projects/#{project_id}/events/#{encoded_collection_name}"
96
98
  end
97
99
 
98
100
  def preprocess_params(params)
99
- if params.key?(:filters)
100
- params[:filters] = MultiJson.encode(params[:filters])
101
- end
102
-
103
- if params.key?(:steps)
104
- params[:steps] = MultiJson.encode(params[:steps])
105
- end
106
-
107
- if params.key?(:analyses)
108
- params[:analyses] = MultiJson.encode(params[:analyses])
109
- end
110
-
111
- if params.key?(:timeframe) && params[:timeframe].is_a?(Hash)
112
- params[:timeframe] = MultiJson.encode(params[:timeframe])
113
- end
101
+ preprocess_encodables(params)
102
+ preprocess_timeframe(params)
103
+ preprocess_group_by(params)
114
104
 
115
105
  query_params = ""
116
106
  params.each do |param, value|
@@ -121,6 +111,28 @@ module Keen
121
111
  query_params
122
112
  end
123
113
 
114
+ def preprocess_encodables(params)
115
+ [:filters, :steps, :analyses].each do |key|
116
+ if params.key?(key)
117
+ params[key] = MultiJson.encode(params[key])
118
+ end
119
+ end
120
+ end
121
+
122
+ def preprocess_timeframe(params)
123
+ timeframe = params[:timeframe]
124
+ if timeframe.is_a?(Hash)
125
+ params[:timeframe] = MultiJson.encode(timeframe)
126
+ end
127
+ end
128
+
129
+ def preprocess_group_by(params)
130
+ group_by = params[:group_by]
131
+ if group_by.is_a?(Array)
132
+ params[:group_by] = MultiJson.encode(group_by)
133
+ end
134
+ end
135
+
124
136
  def method_missing(_method, *args, &block)
125
137
  if config = CONFIG[_method.to_sym]
126
138
  if config.is_a?(Proc)
@@ -61,8 +61,6 @@ module Keen
61
61
  ensure_write_key!
62
62
  check_event_data!(event_collection, properties)
63
63
 
64
- deferrable = EventMachine::DefaultDeferrable.new
65
-
66
64
  http_client = Keen::HTTP::Async.new(
67
65
  self.api_url,
68
66
  {:proxy_url => self.proxy_url, :proxy_type => self.proxy_type})
@@ -73,29 +71,9 @@ module Keen
73
71
  )
74
72
 
75
73
  if defined?(EM::Synchrony)
76
- if http.error
77
- error = HttpError.new("HTTP em-synchrony publish_async error: #{http.error}")
78
- Keen.logger.error(error)
79
- raise error
80
- else
81
- process_response(http.response_header.status, http.response.chomp)
82
- end
74
+ process_with_synchrony(http)
83
75
  else
84
- http.callback {
85
- begin
86
- response = process_response(http.response_header.status, http.response.chomp)
87
- rescue Exception => e
88
- Keen.logger.error(e)
89
- deferrable.fail(e)
90
- end
91
- deferrable.succeed(response) if response
92
- }
93
- http.errback {
94
- error = Error.new("HTTP publish_async failure: #{http.error}")
95
- Keen.logger.error(error)
96
- deferrable.fail(error)
97
- }
98
- deferrable
76
+ process_with_callbacks(http)
99
77
  end
100
78
  end
101
79
 
@@ -131,6 +109,35 @@ module Keen
131
109
 
132
110
  private
133
111
 
112
+ def process_with_synchrony(http)
113
+ if http.error
114
+ error = HttpError.new("HTTP em-synchrony publish_async error: #{http.error}")
115
+ Keen.logger.error(error)
116
+ raise error
117
+ else
118
+ process_response(http.response_header.status, http.response.chomp)
119
+ end
120
+ end
121
+
122
+ def process_with_callbacks(http)
123
+ deferrable = EventMachine::DefaultDeferrable.new
124
+ http.callback {
125
+ begin
126
+ response = process_response(http.response_header.status, http.response.chomp)
127
+ rescue Exception => e
128
+ Keen.logger.error(e)
129
+ deferrable.fail(e)
130
+ end
131
+ deferrable.succeed(response) if response
132
+ }
133
+ http.errback {
134
+ error = Error.new("HTTP publish_async failure: #{http.error}")
135
+ Keen.logger.error(error)
136
+ deferrable.fail(error)
137
+ }
138
+ deferrable
139
+ end
140
+
134
141
  def publish_body(path, body, error_method)
135
142
  begin
136
143
  response = Keen::HTTP::Sync.new(
@@ -0,0 +1,35 @@
1
+ require 'multi_json'
2
+ require 'keen/aes_helper'
3
+
4
+ module Keen
5
+ class ScopedKey
6
+ include AESHelper
7
+ extend AESHelper
8
+
9
+ attr_accessor :api_key
10
+ attr_accessor :data
11
+
12
+ class << self
13
+ def decrypt!(api_key, scoped_key)
14
+ encrypted = unhexlify(scoped_key)
15
+ padded_api_key = pad(api_key)
16
+ decrypted = aes256_decrypt(padded_api_key, encrypted)
17
+ data = MultiJson.load(decrypted)
18
+ self.new(api_key, data)
19
+ end
20
+ end
21
+
22
+ def initialize(api_key, data)
23
+ self.api_key = api_key
24
+ self.data = data
25
+ end
26
+
27
+ def encrypt!
28
+ json_str = MultiJson.dump(self.data)
29
+ padded_api_key = pad(self.api_key)
30
+ padded_data = pad(json_str)
31
+ encrypted, iv = aes256_encrypt(padded_api_key, json_str)
32
+ hexlify(iv) + hexlify(encrypted)
33
+ end
34
+ end
35
+ end
@@ -1,3 +1,3 @@
1
1
  module Keen
2
- VERSION = "0.7.8"
2
+ VERSION = "0.8.0"
3
3
  end
@@ -5,7 +5,7 @@ describe "Keen IO API" do
5
5
  let(:write_key) { ENV['KEEN_WRITE_KEY'] }
6
6
 
7
7
  describe "publishing" do
8
- let(:collection) { "users" }
8
+ let(:collection) { "User posts.new" }
9
9
  let(:event_properties) { { "name" => "Bob" } }
10
10
  let(:api_success) { { "created" => true } }
11
11
 
@@ -116,6 +116,24 @@ describe "Keen IO API" do
116
116
  it "should return a valid count_unique" do
117
117
  Keen.count_unique(event_collection, :target_property => "price").should == 2
118
118
  end
119
+
120
+ it "should return a valid count with group_by" do
121
+ response = Keen.average(event_collection, :group_by => "username", :target_property => "price")
122
+ bobs_response = response.select { |result| result["username"] == "bob" }.first
123
+ bobs_response["result"].should == 10
124
+ teds_response = response.select { |result| result["username"] == "ted" }.first
125
+ teds_response["result"].should == 20
126
+ end
127
+
128
+ it "should return a valid count with multi-group_by" do
129
+ response = Keen.average(event_collection, :group_by => ["username", "price"], :target_property => "price")
130
+ bobs_response = response.select { |result| result["username"] == "bob" }.first
131
+ bobs_response["result"].should == 10
132
+ bobs_response["price"].should == 10
133
+ teds_response = response.select { |result| result["username"] == "ted" }.first
134
+ teds_response["result"].should == 20
135
+ teds_response["price"].should == 20
136
+ end
119
137
 
120
138
  it "should return a valid sum" do
121
139
  Keen.sum(event_collection, :target_property => "price").should == 30
@@ -171,7 +189,7 @@ describe "Keen IO API" do
171
189
  before do
172
190
  Keen.publish(event_collection, :delete => "me")
173
191
  Keen.publish(event_collection, :delete => "you")
174
- sleep(3)
192
+ sleep(10)
175
193
  end
176
194
 
177
195
  it "should delete the event" do
@@ -4,7 +4,7 @@ describe Keen::Client::PublishingMethods do
4
4
  let(:project_id) { "12345" }
5
5
  let(:write_key) { "abcde" }
6
6
  let(:api_url) { "https://unreal.keen.io" }
7
- let(:collection) { "users" }
7
+ let(:collection) { "some :actions_to.record" }
8
8
  let(:event_properties) { { "name" => "Bob" } }
9
9
  let(:api_success) { { "created" => true } }
10
10
  let(:client) { Keen::Client.new(
@@ -37,9 +37,9 @@ describe Keen::Client::PublishingMethods do
37
37
  end
38
38
 
39
39
  it "should url encode the event collection" do
40
- stub_keen_post(api_event_collection_resource_url(api_url, "foo+bar"), 201, "")
41
- client.publish("foo bar", event_properties)
42
- expect_keen_post(api_event_collection_resource_url(api_url, "foo+bar"), event_properties, "sync", write_key)
40
+ stub_keen_post(api_event_collection_resource_url(api_url, "User%20posts.new%20)(*%26%5E%25%40!)%3A%3A%2520%2520"), 201, "")
41
+ client.publish("User posts.new )(*&^%@!)::%20%20", event_properties)
42
+ expect_keen_post(api_event_collection_resource_url(api_url, "User%20posts.new%20)(*%26%5E%25%40!)%3A%3A%2520%2520"), event_properties, "sync", write_key)
43
43
  end
44
44
 
45
45
  it "should wrap exceptions" do
@@ -138,11 +138,11 @@ describe Keen::Client::PublishingMethods do
138
138
  end
139
139
 
140
140
  it "should url encode the event collection" do
141
- stub_keen_post(api_event_collection_resource_url(api_url, "foo+bar"), 201, api_success)
141
+ stub_keen_post(api_event_collection_resource_url(api_url, 'User%20posts.new%20)(*%26%5E%25%40!)%3A%3A%2520%2520'), 201, api_success)
142
142
  EM.run {
143
- client.publish_async("foo bar", event_properties).callback {
143
+ client.publish_async('User posts.new )(*&^%@!)::%20%20', event_properties).callback {
144
144
  begin
145
- expect_keen_post(api_event_collection_resource_url(api_url, "foo+bar"), event_properties, "async", write_key)
145
+ expect_keen_post(api_event_collection_resource_url(api_url, 'User%20posts.new%20)(*%26%5E%25%40!)%3A%3A%2520%2520'), event_properties, "async", write_key)
146
146
  ensure
147
147
  EM.stop
148
148
  end
@@ -75,6 +75,16 @@ describe Keen::Client do
75
75
  filter_str = CGI.escape(MultiJson.encode(filters))
76
76
  test_query("&filters=#{filter_str}", :filters => filters)
77
77
  end
78
+
79
+ it "should encode a single group by property" do
80
+ test_query("&group_by=one%20foo", :group_by => "one foo")
81
+ end
82
+
83
+ it "should encode multi-group by properly" do
84
+ group_by = ["one", "two"]
85
+ group_by_str = CGI.escape(MultiJson.encode(group_by))
86
+ test_query("&group_by=#{group_by_str}", :group_by => group_by)
87
+ end
78
88
 
79
89
  it "should encode absolute timeframes properly" do
80
90
  timeframe = {
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ describe Keen::ScopedKey do
4
+ let(:api_key) { "ab428324dbdbcfe744" }
5
+ let(:bad_api_key) { "badbadbadbad" }
6
+ let(:data) { {
7
+ "filters" => [{
8
+ "property_name" => "accountId",
9
+ "operator" => "eq",
10
+ "property_value" => "123456"
11
+ }]
12
+ }}
13
+ let(:new_scoped_key) { Keen::ScopedKey.new(api_key, data) }
14
+
15
+ describe "constructor" do
16
+ it "should retain the api_key" do
17
+ new_scoped_key.api_key.should == api_key
18
+ end
19
+
20
+ it "should retain the data" do
21
+ new_scoped_key.data.should == data
22
+ end
23
+ end
24
+
25
+ describe "encrypt! and decrypt!" do
26
+ it "should encrypt and hex encode the data using the api key" do
27
+ encrypted_str = new_scoped_key.encrypt!
28
+ other_api_key = Keen::ScopedKey.decrypt!(api_key, encrypted_str)
29
+ other_api_key.data.should == data
30
+ end
31
+
32
+ it "should not decrypt the scoped key with a bad api key" do
33
+ encrypted_str = new_scoped_key.encrypt!
34
+ expect {
35
+ other_api_key = Keen::ScopedKey.decrypt!(bad_api_key, encrypted_str)
36
+ }.to raise_error(OpenSSL::Cipher::CipherError)
37
+ end
38
+ end
39
+ end
metadata CHANGED
@@ -1,8 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: keen
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.8
5
- prerelease:
4
+ version: 0.8.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Kyle Wild
@@ -11,12 +10,11 @@ authors:
11
10
  autorequire:
12
11
  bindir: bin
13
12
  cert_chain: []
14
- date: 2013-11-14 00:00:00.000000000 Z
13
+ date: 2014-01-09 00:00:00.000000000 Z
15
14
  dependencies:
16
15
  - !ruby/object:Gem::Dependency
17
16
  name: multi_json
18
17
  requirement: !ruby/object:Gem::Requirement
19
- none: false
20
18
  requirements:
21
19
  - - ~>
22
20
  - !ruby/object:Gem::Version
@@ -24,137 +22,120 @@ dependencies:
24
22
  type: :runtime
25
23
  prerelease: false
26
24
  version_requirements: !ruby/object:Gem::Requirement
27
- none: false
28
25
  requirements:
29
26
  - - ~>
30
27
  - !ruby/object:Gem::Version
31
28
  version: '1.0'
32
29
  - !ruby/object:Gem::Dependency
33
- name: guard
30
+ name: addressable
34
31
  requirement: !ruby/object:Gem::Requirement
35
- none: false
36
32
  requirements:
37
- - - ! '>='
33
+ - - ~>
38
34
  - !ruby/object:Gem::Version
39
- version: '0'
40
- type: :development
35
+ version: 2.3.5
36
+ type: :runtime
41
37
  prerelease: false
42
38
  version_requirements: !ruby/object:Gem::Requirement
43
- none: false
44
39
  requirements:
45
- - - ! '>='
40
+ - - ~>
46
41
  - !ruby/object:Gem::Version
47
- version: '0'
42
+ version: 2.3.5
48
43
  - !ruby/object:Gem::Dependency
49
- name: guard-rspec
44
+ name: guard
50
45
  requirement: !ruby/object:Gem::Requirement
51
- none: false
52
46
  requirements:
53
- - - ! '>='
47
+ - - '>='
54
48
  - !ruby/object:Gem::Version
55
49
  version: '0'
56
50
  type: :development
57
51
  prerelease: false
58
52
  version_requirements: !ruby/object:Gem::Requirement
59
- none: false
60
53
  requirements:
61
- - - ! '>='
54
+ - - '>='
62
55
  - !ruby/object:Gem::Version
63
56
  version: '0'
64
57
  - !ruby/object:Gem::Dependency
65
- name: rb-inotify
58
+ name: guard-rspec
66
59
  requirement: !ruby/object:Gem::Requirement
67
- none: false
68
60
  requirements:
69
- - - ! '>='
61
+ - - '>='
70
62
  - !ruby/object:Gem::Version
71
63
  version: '0'
72
64
  type: :development
73
65
  prerelease: false
74
66
  version_requirements: !ruby/object:Gem::Requirement
75
- none: false
76
67
  requirements:
77
- - - ! '>='
68
+ - - '>='
78
69
  - !ruby/object:Gem::Version
79
70
  version: '0'
80
71
  - !ruby/object:Gem::Dependency
81
- name: rb-fsevent
72
+ name: rb-inotify
82
73
  requirement: !ruby/object:Gem::Requirement
83
- none: false
84
74
  requirements:
85
- - - ! '>='
75
+ - - '>='
86
76
  - !ruby/object:Gem::Version
87
77
  version: '0'
88
78
  type: :development
89
79
  prerelease: false
90
80
  version_requirements: !ruby/object:Gem::Requirement
91
- none: false
92
81
  requirements:
93
- - - ! '>='
82
+ - - '>='
94
83
  - !ruby/object:Gem::Version
95
84
  version: '0'
96
85
  - !ruby/object:Gem::Dependency
97
- name: rb-fchange
86
+ name: rb-fsevent
98
87
  requirement: !ruby/object:Gem::Requirement
99
- none: false
100
88
  requirements:
101
- - - ! '>='
89
+ - - '>='
102
90
  - !ruby/object:Gem::Version
103
91
  version: '0'
104
92
  type: :development
105
93
  prerelease: false
106
94
  version_requirements: !ruby/object:Gem::Requirement
107
- none: false
108
95
  requirements:
109
- - - ! '>='
96
+ - - '>='
110
97
  - !ruby/object:Gem::Version
111
98
  version: '0'
112
99
  - !ruby/object:Gem::Dependency
113
- name: ruby_gntp
100
+ name: rb-fchange
114
101
  requirement: !ruby/object:Gem::Requirement
115
- none: false
116
102
  requirements:
117
- - - ! '>='
103
+ - - '>='
118
104
  - !ruby/object:Gem::Version
119
105
  version: '0'
120
106
  type: :development
121
107
  prerelease: false
122
108
  version_requirements: !ruby/object:Gem::Requirement
123
- none: false
124
109
  requirements:
125
- - - ! '>='
110
+ - - '>='
126
111
  - !ruby/object:Gem::Version
127
112
  version: '0'
128
113
  - !ruby/object:Gem::Dependency
129
- name: rb-readline
114
+ name: ruby_gntp
130
115
  requirement: !ruby/object:Gem::Requirement
131
- none: false
132
116
  requirements:
133
- - - ! '>='
117
+ - - '>='
134
118
  - !ruby/object:Gem::Version
135
119
  version: '0'
136
120
  type: :development
137
121
  prerelease: false
138
122
  version_requirements: !ruby/object:Gem::Requirement
139
- none: false
140
123
  requirements:
141
- - - ! '>='
124
+ - - '>='
142
125
  - !ruby/object:Gem::Version
143
126
  version: '0'
144
127
  - !ruby/object:Gem::Dependency
145
- name: debugger
128
+ name: rb-readline
146
129
  requirement: !ruby/object:Gem::Requirement
147
- none: false
148
130
  requirements:
149
- - - ! '>='
131
+ - - '>='
150
132
  - !ruby/object:Gem::Version
151
133
  version: '0'
152
134
  type: :development
153
135
  prerelease: false
154
136
  version_requirements: !ruby/object:Gem::Requirement
155
- none: false
156
137
  requirements:
157
- - - ! '>='
138
+ - - '>='
158
139
  - !ruby/object:Gem::Version
159
140
  version: '0'
160
141
  description: Send events and build analytics features into your Ruby applications.
@@ -174,11 +155,13 @@ files:
174
155
  - config/cacert.pem
175
156
  - keen.gemspec
176
157
  - lib/keen.rb
158
+ - lib/keen/aes_helper.rb
177
159
  - lib/keen/client.rb
178
160
  - lib/keen/client/maintenance_methods.rb
179
161
  - lib/keen/client/publishing_methods.rb
180
162
  - lib/keen/client/querying_methods.rb
181
163
  - lib/keen/http.rb
164
+ - lib/keen/scoped_key.rb
182
165
  - lib/keen/version.rb
183
166
  - spec/integration/api_spec.rb
184
167
  - spec/integration/spec_helper.rb
@@ -187,33 +170,36 @@ files:
187
170
  - spec/keen/client/querying_methods_spec.rb
188
171
  - spec/keen/client_spec.rb
189
172
  - spec/keen/keen_spec.rb
173
+ - spec/keen/scoped_key_spec.rb
190
174
  - spec/keen/spec_helper.rb
191
175
  - spec/spec_helper.rb
192
176
  - spec/synchrony/spec_helper.rb
193
177
  - spec/synchrony/synchrony_spec.rb
194
178
  homepage: https://github.com/keenlabs/keen-gem
195
- licenses: []
196
- post_install_message:
179
+ licenses:
180
+ - MIT
181
+ metadata: {}
182
+ post_install_message: '**UPGRADE WARNING** Do you use spaces in collection names?
183
+ Or other special characters? Read https://groups.google.com/forum/?fromgroups#!topic/keen-io-devs/VtCgPuNKrgY
184
+ from the mailing list to make sure your collection names don''t change!'
197
185
  rdoc_options: []
198
186
  require_paths:
199
187
  - lib
200
188
  required_ruby_version: !ruby/object:Gem::Requirement
201
- none: false
202
189
  requirements:
203
- - - ! '>='
190
+ - - '>='
204
191
  - !ruby/object:Gem::Version
205
192
  version: '0'
206
193
  required_rubygems_version: !ruby/object:Gem::Requirement
207
- none: false
208
194
  requirements:
209
- - - ! '>='
195
+ - - '>='
210
196
  - !ruby/object:Gem::Version
211
197
  version: '0'
212
198
  requirements: []
213
199
  rubyforge_project:
214
- rubygems_version: 1.8.23
200
+ rubygems_version: 2.0.3
215
201
  signing_key:
216
- specification_version: 3
202
+ specification_version: 4
217
203
  summary: Keen IO API Client
218
204
  test_files:
219
205
  - spec/integration/api_spec.rb
@@ -223,7 +209,9 @@ test_files:
223
209
  - spec/keen/client/querying_methods_spec.rb
224
210
  - spec/keen/client_spec.rb
225
211
  - spec/keen/keen_spec.rb
212
+ - spec/keen/scoped_key_spec.rb
226
213
  - spec/keen/spec_helper.rb
227
214
  - spec/spec_helper.rb
228
215
  - spec/synchrony/spec_helper.rb
229
216
  - spec/synchrony/synchrony_spec.rb
217
+ has_rdoc: