customerio 0.7.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- ZGI2YjQwMzVhYWJlNGE2ZWRiNDA4ZmY2MjQ3ZGJiMzEwNDlhNGU2ZA==
4
+ MWFhMDk1ZmMzOTBjZTZhMzYxNGYyZTJmZDFlMTdiZWI1NjdhMTY4YQ==
5
5
  data.tar.gz: !binary |-
6
- OWFlNzY0YzcwZDczMGU4NzRiNDQxMDIzY2FhYzEyMTlhNjU4N2ZjMw==
6
+ YmMyODY3OTIxMjhkN2MxYzVlNzdlOWRiOTgxNzc2OWUyYWJjNDc0Ng==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- NjIxNDhlYzU0OGRmMWI3Y2I5YmViYWExZDNlYTEyNjUyYTRmYjgwZDM1NTM2
10
- ZjE4YzdkNzBmYmRkZWY2MDM5YmYxOTI0YjE1YmVlYjdiZjJlZGYyYTQ3ZWRm
11
- Y2IwOGMzYzQ3OWE4YWUxMzM1OWRjODIwZDQzODU2N2ExMjM4NTY=
9
+ NjA5OWUwZDU4ZmJmYjA3YmQ4ZTY0ZDhlNWQzMjU2MDViNzk3NjkxNzI2Zjcw
10
+ YjFjNDg4N2Q5YzQwMWIxZGJkZWFmNzNlYTEzYjVkNWZlY2Q4MjgyNjM3MWNm
11
+ M2ZjMzU5MGUyMzljOTg0NTA0OWM5NDZkMDU4MTVlMzViNDEzYWU=
12
12
  data.tar.gz: !binary |-
13
- YzI0Nzk1NGNkOTFkMWQxZDcyZjkwNzQ3OTlhZGExYjQ5NjhhNWU4ZDUxZTdl
14
- OWZlZWFjNzk5MDkzZDU0OGVkZGU0MzI2ZmU0N2U4NDI2OWRjOTIxODc4YzUy
15
- YTU1NDAyNGJlNzgxMDc1NWU4YjYxNWNiNDdiMjgxNGVmMzU5MDg=
13
+ YTMxNjA2NzJjNzU2ZDExNzBjZDYxODBhMDRiMTg1MWE3ZDIzMmI5YzcxYzE5
14
+ MGNhNTc5YTJmYjRmNjBlYTljODU3YzhiMGZiZGNkM2UyOTZhYzkyMWE0ZTBl
15
+ MGY2Mzg1YzMyMGYxNzg2NGY1YjY4YmFhZjkwYzVjN2IxYjNkYTE=
@@ -12,5 +12,3 @@ rvm:
12
12
  - jruby-head
13
13
  - ree
14
14
  - ruby-head
15
- - rbx-18mode
16
- - rbx-19mode
@@ -1,3 +1,11 @@
1
+ ## Customerio 1.0.0 - Mar 15, 2016 ##
2
+
3
+ There is a slight breaking change in this release. If you are depending on the HTTP response object included in the InvalidResponse exception (introduced in 0.6.1), note that it is now a `Net::HTTPBadRequest`.
4
+
5
+ * Remove HTTParty dependency, use Net::HTTP instead. [#42](https://github.com/customerio/customerio-ruby/pull/42)
6
+
7
+ * Update test suite to use webmock, to increase confidence that we're not changing our HTTP requests [#41](https://github.com/customerio/customerio-ruby/pull/41)
8
+
1
9
  ## Customerio 0.7.0 - Mar 2, 2016 ##
2
10
 
3
11
  * Add new method for tracking anonymous events: `anonymous_track`. See README for more details. Many thanks to @sdawson for this contribution! [#36](https://github.com/customerio/customerio-ruby/pull/36)
@@ -15,10 +15,11 @@ Gem::Specification.new do |gem|
15
15
  gem.require_paths = ["lib"]
16
16
  gem.version = Customerio::VERSION
17
17
 
18
- gem.add_dependency('httparty', ["< 0.12", ">= 0.5"])
19
18
  gem.add_dependency('multi_json', "~> 1.0")
20
19
 
21
20
  gem.add_development_dependency('rake')
22
21
  gem.add_development_dependency('rspec')
23
- gem.add_development_dependency('fakeweb')
22
+ gem.add_development_dependency('webmock')
23
+ gem.add_development_dependency('addressable', '~> 2.3.6')
24
+ gem.add_development_dependency('json')
24
25
  end
@@ -2,4 +2,5 @@ require "customerio/version"
2
2
 
3
3
  module Customerio
4
4
  require "customerio/client"
5
+ require "customerio/param_encoder"
5
6
  end
@@ -1,13 +1,13 @@
1
- require 'httparty'
1
+ require 'net/http'
2
2
  require 'multi_json'
3
3
 
4
4
  module Customerio
5
- class Client
6
- include HTTParty
7
- base_uri 'https://track.customer.io'
8
- default_timeout 10
5
+ DEFAULT_BASE_URI = 'https://track.customer.io'
6
+ DEFAULT_TIMEOUT = 10
9
7
 
8
+ class Client
10
9
  class MissingIdAttributeError < RuntimeError; end
10
+ class InvalidRequest < RuntimeError; end
11
11
  class InvalidResponse < RuntimeError
12
12
  attr_reader :response
13
13
 
@@ -18,8 +18,11 @@ module Customerio
18
18
  end
19
19
 
20
20
  def initialize(site_id, secret_key, options = {})
21
- @auth = { :username => site_id, :password => secret_key }
21
+ @username = site_id
22
+ @password = secret_key
22
23
  @json = options.has_key?(:json) ? options[:json] : true
24
+ @base_uri = options[:base_uri] || DEFAULT_BASE_URI
25
+ @timeout = options[:timeout] || DEFAULT_TIMEOUT
23
26
  end
24
27
 
25
28
  def identify(attributes)
@@ -27,7 +30,7 @@ module Customerio
27
30
  end
28
31
 
29
32
  def delete(customer_id)
30
- verify_response(self.class.delete(customer_path(customer_id), options))
33
+ verify_response(request(:delete, customer_path(customer_id)))
31
34
  end
32
35
 
33
36
  def track(*args)
@@ -59,11 +62,7 @@ module Customerio
59
62
 
60
63
  url = customer_path(attributes[:id])
61
64
 
62
- if @json
63
- verify_response(self.class.put(url, options.merge(:body => MultiJson.dump(attributes), :headers => {'Content-Type' => 'application/json'})))
64
- else
65
- verify_response(self.class.put(url, options.merge(:body => attributes)))
66
- end
65
+ verify_response(request(:put, url, attributes))
67
66
  end
68
67
 
69
68
  def create_customer_event(customer_id, event_name, attributes = {})
@@ -77,11 +76,7 @@ module Customerio
77
76
  def create_event(url, event_name, attributes = {})
78
77
  body = { :name => event_name, :data => attributes }
79
78
  body[:timestamp] = attributes[:timestamp] if valid_timestamp?(attributes[:timestamp])
80
- if @json
81
- verify_response(self.class.post(url, options.merge(:body => MultiJson.dump(body), :headers => {'Content-Type' => 'application/json'})))
82
- else
83
- verify_response(self.class.post(url, options.merge(:body => body)))
84
- end
79
+ verify_response(request(:post, url, body))
85
80
  end
86
81
 
87
82
  def customer_path(id)
@@ -94,7 +89,7 @@ module Customerio
94
89
 
95
90
 
96
91
  def verify_response(response)
97
- if response.code >= 200 && response.code < 300
92
+ if response.code.to_i >= 200 && response.code.to_i < 300
98
93
  response
99
94
  else
100
95
  raise InvalidResponse.new("Customer.io API returned an invalid response: #{response.code}", response)
@@ -106,8 +101,46 @@ module Customerio
106
101
  hash.inject({}){ |hash, (k,v)| hash[k.to_sym] = v; hash }
107
102
  end
108
103
 
109
- def options
110
- { :basic_auth => @auth }
104
+ def request(method, path, body = nil, headers = {})
105
+ uri = URI.join(@base_uri, path)
106
+
107
+ session = Net::HTTP.new(uri.host, uri.port)
108
+ session.use_ssl = (uri.scheme == 'https')
109
+ session.open_timeout = @timeout
110
+ session.read_timeout = @timeout
111
+
112
+ req = request_class(method).new(uri.path)
113
+ req.initialize_http_header(headers)
114
+ req.basic_auth @username, @password
115
+
116
+ add_request_body(req, body) unless body.nil?
117
+
118
+ session.start do |http|
119
+ http.request(req)
120
+ end
121
+ end
122
+
123
+ def request_class(method)
124
+ case method
125
+ when :post
126
+ Net::HTTP::Post
127
+ when :put
128
+ Net::HTTP::Put
129
+ when :delete
130
+ Net::HTTP::Delete
131
+ else
132
+ raise InvalidRequest.new("Invalid request method #{method.inspect}")
133
+ end
134
+ end
135
+
136
+ def add_request_body(req, body)
137
+ if @json
138
+ req.add_field('Content-Type', 'application/json')
139
+ req.body = MultiJson.dump(body)
140
+ else
141
+ req.add_field('Content-Type', 'application/x-www-form-urlencoded')
142
+ req.body = ParamEncoder.to_params(body)
143
+ end
111
144
  end
112
145
  end
113
146
  end
@@ -0,0 +1,68 @@
1
+ # Based on HTTParty's HashConversions:
2
+ #
3
+ # https://github.com/jnunemaker/httparty/blob/master/lib/httparty/hash_conversions.rb
4
+ #
5
+ # License: MIT https://github.com/jnunemaker/httparty/blob/master/MIT-LICENSE
6
+
7
+ require 'erb'
8
+ require 'uri'
9
+
10
+ module Customerio
11
+ class ParamEncoder
12
+ # @return <String> This hash as a query string
13
+ #
14
+ # @example
15
+ # { name: "Bob",
16
+ # address: {
17
+ # street: '111 Ruby Ave.',
18
+ # city: 'Ruby Central',
19
+ # phones: ['111-111-1111', '222-222-2222']
20
+ # }
21
+ # }.to_params
22
+ # #=> "name=Bob&address[city]=Ruby Central&address[phones][]=111-111-1111&address[phones][]=222-222-2222&address[street]=111 Ruby Ave."
23
+ def self.to_params(hash)
24
+ hash.to_hash.map { |k, v| normalize_param(k, v) }.join.chop
25
+ end
26
+
27
+ # @param key<Object> The key for the param.
28
+ # @param value<Object> The value for the param.
29
+ #
30
+ # @return <String> This key value pair as a param
31
+ #
32
+ # @example normalize_param(:name, "Bob Jones") #=> "name=Bob%20Jones&"
33
+ def self.normalize_param(key, value)
34
+ param = ''
35
+ stack = []
36
+
37
+ if value.respond_to?(:to_ary)
38
+ param << value.to_ary.map { |element| normalize_param("#{key}[]", element) }.join
39
+ elsif value.respond_to?(:to_hash)
40
+ stack << [key, value.to_hash]
41
+ else
42
+ param << "#{key}=#{escape_value(value)}&"
43
+ end
44
+
45
+ stack.each do |parent, hash|
46
+ hash.each do |k, v|
47
+ if v.respond_to?(:to_hash)
48
+ stack << ["#{parent}[#{k}]", v.to_hash]
49
+ else
50
+ param << normalize_param("#{parent}[#{k}]", v)
51
+ end
52
+ end
53
+ end
54
+
55
+ param
56
+ end
57
+
58
+ # Prefer ERB::Util.url_encode, fall baack to deprecation URI.encode for
59
+ # old Ruby support
60
+ def self.escape_value(value)
61
+ if ERB::Util.respond_to? :url_encode
62
+ ERB::Util.url_encode(value.to_s)
63
+ else
64
+ URI.encode(value.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
65
+ end
66
+ end
67
+ end
68
+ end
@@ -1,3 +1,3 @@
1
1
  module Customerio
2
- VERSION = "0.7.0"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -1,14 +1,17 @@
1
1
  require 'spec_helper'
2
2
  require 'multi_json'
3
3
 
4
+
4
5
  describe Customerio::Client do
5
6
  let(:client) { Customerio::Client.new("SITE_ID", "API_KEY", :json => false) }
6
7
  let(:response) { double("Response", :code => 200) }
7
8
 
8
- before do
9
- # Dont call out to customer.io
10
- Customerio::Client.stub(:post).and_return(response)
11
- Customerio::Client.stub(:put).and_return(response)
9
+ def api_uri(path)
10
+ "https://SITE_ID:API_KEY@track.customer.io#{path}"
11
+ end
12
+
13
+ def json(data)
14
+ MultiJson.dump(data)
12
15
  end
13
16
 
14
17
  describe "json option" do
@@ -17,90 +20,85 @@ describe Customerio::Client do
17
20
  it "uses json by default" do
18
21
  client = Customerio::Client.new("SITE_ID", "API_KEY")
19
22
 
20
- json = MultiJson.dump(body)
21
- Customerio::Client.should_receive(:put).with(
22
- "/api/v1/customers/5",
23
- {
24
- :basic_auth=>{:username=>"SITE_ID", :password=>"API_KEY"},
25
- :body=>json,
26
- :headers=>{"Content-Type"=>"application/json"}
27
- }).and_return(response)
23
+ stub_request(:put, api_uri('/api/v1/customers/5')).
24
+ with(:body => json(body),
25
+ :headers => {'Content-Type'=>'application/json'}).
26
+ to_return(:status => 200, :body => "", :headers => {})
27
+
28
28
  client.identify(body)
29
29
  end
30
30
 
31
31
  it "allows disabling json" do
32
32
  client = Customerio::Client.new("SITE_ID", "API_KEY", :json => false)
33
33
 
34
- Customerio::Client.should_receive(:put).with(
35
- "/api/v1/customers/5",
36
- {
37
- :basic_auth=>{:username=>"SITE_ID", :password=>"API_KEY"},
38
- :body=>body
39
- }).and_return(response)
34
+ stub_request(:put, api_uri('/api/v1/customers/5')).
35
+ with(:body => { :id => "5", :name => "Bob" }).
36
+ to_return(:status => 200, :body => "", :headers => {})
37
+
40
38
  client.identify(body)
41
39
  end
42
40
  end
43
41
 
44
- describe ".base_uri" do
45
- it "should be set to customer.io's api" do
46
- Customerio::Client.base_uri.should == "https://track.customer.io"
47
- end
48
- end
49
-
50
42
  describe "#identify" do
51
43
  it "sends a PUT request to customer.io's customer API" do
52
- Customerio::Client.should_receive(:put).with("/api/v1/customers/5", anything()).and_return(response)
44
+ stub_request(:put, api_uri('/api/v1/customers/5')).
45
+ with(:body => "id=5").
46
+ to_return(:status => 200, :body => "", :headers => {})
47
+
53
48
  client.identify(:id => 5)
54
49
  end
55
50
 
56
51
  it "sends a PUT request to customer.io's customer API using json headers" do
57
52
  client = Customerio::Client.new("SITE_ID", "API_KEY", :json => true)
58
53
  body = { :id => 5, :name => "Bob" }
59
- json = MultiJson.dump(body)
60
- Customerio::Client.should_receive(:put).with(
61
- "/api/v1/customers/5",
62
- {:basic_auth=>{:username=>"SITE_ID", :password=>"API_KEY"},
63
- :body=>json,
64
- :headers=>{"Content-Type"=>"application/json"}}).and_return(response)
54
+
55
+ stub_request(:put, api_uri('/api/v1/customers/5')).
56
+ with(:body => json(body),
57
+ :headers => {'Content-Type'=>'application/json'}).
58
+ to_return(:status => 200, :body => "", :headers => {})
59
+
65
60
  client.identify(body)
66
61
  end
67
62
 
68
63
  it "raises an error if PUT doesn't return a 2xx response code" do
69
- Customerio::Client.should_receive(:put).and_return(double("Response", :code => 500))
64
+ stub_request(:put, api_uri('/api/v1/customers/5')).
65
+ with(:body => "id=5").
66
+ to_return(:status => 500, :body => "", :headers => {})
67
+
70
68
  lambda { client.identify(:id => 5) }.should raise_error(Customerio::Client::InvalidResponse)
71
69
  end
72
70
 
73
71
  it "includes the HTTP response with raised errors" do
74
- response = double("Response", :code => 500, :body => "whatever")
75
- Customerio::Client.should_receive(:put).and_return(response)
72
+ stub_request(:put, api_uri('/api/v1/customers/5')).
73
+ with(:body => "id=5").
74
+ to_return(:status => 500, :body => "whatever", :headers => {})
75
+
76
76
  lambda { client.identify(:id => 5) }.should raise_error {|error|
77
77
  error.should be_a Customerio::Client::InvalidResponse
78
- error.response.should eq response
78
+ error.response.code.should eq "500"
79
+ error.response.body.should eq "whatever"
79
80
  }
80
81
  end
81
82
 
82
- it "uses the site_id and api key for basic auth" do
83
- Customerio::Client.should_receive(:put).with("/api/v1/customers/5", {
84
- :basic_auth => { :username => "SITE_ID", :password => "API_KEY" },
85
- :body => anything()
86
- }).and_return(response)
87
-
88
- client.identify(:id => 5)
89
- end
90
-
91
83
  it "sends along all attributes" do
92
- Customerio::Client.should_receive(:put).with("/api/v1/customers/5", {
93
- :basic_auth => anything(),
84
+ time = Time.now.to_i
85
+
86
+ stub_request(:put, api_uri('/api/v1/customers/5')).with(
94
87
  :body => {
95
- :id => 5,
88
+ :id => "5",
96
89
  :email => "customer@example.com",
97
- :created_at => Time.now.to_i,
90
+ :created_at => time.to_s,
98
91
  :first_name => "Bob",
99
92
  :plan => "basic"
100
- }
101
- }).and_return(response)
102
-
103
- client.identify(:id => 5, :email => "customer@example.com", :created_at => Time.now.to_i, :first_name => "Bob", :plan => "basic")
93
+ }).to_return(:status => 200, :body => "", :headers => {})
94
+
95
+ client.identify({
96
+ :id => 5,
97
+ :email => "customer@example.com",
98
+ :created_at => time,
99
+ :first_name => "Bob",
100
+ :plan => "basic"
101
+ })
104
102
  end
105
103
 
106
104
  it "requires an id attribute" do
@@ -108,6 +106,10 @@ describe Customerio::Client do
108
106
  end
109
107
 
110
108
  it 'should not raise errors when attribute keys are strings' do
109
+ stub_request(:put, api_uri('/api/v1/customers/5')).
110
+ with(:body => "id=5").
111
+ to_return(:status => 200, :body => "", :headers => {})
112
+
111
113
  attributes = { "id" => 5 }
112
114
 
113
115
  lambda { client.identify(attributes) }.should_not raise_error()
@@ -115,88 +117,111 @@ describe Customerio::Client do
115
117
  end
116
118
 
117
119
  describe "#delete" do
118
- it "sends a DELETE request to the customer.io's event API" do
119
- Customerio::Client.should_receive(:delete).with("/api/v1/customers/5", anything()).and_return(response)
120
+ it "sends a DELETE request to the customer.io's event API" do
121
+ stub_request(:delete, api_uri('/api/v1/customers/5')).
122
+ to_return(:status => 200, :body => "", :headers => {})
123
+
120
124
  client.delete(5)
121
- end
125
+ end
122
126
  end
123
127
 
124
128
  describe "#track" do
125
- it "sends a POST request to the customer.io's event API" do
126
- Customerio::Client.should_receive(:post).with("/api/v1/customers/5/events", anything()).and_return(response)
127
- client.track(5, "purchase")
128
- end
129
-
130
129
  it "raises an error if POST doesn't return a 2xx response code" do
131
- Customerio::Client.should_receive(:post).and_return(double("Response", :code => 500))
130
+ stub_request(:post, api_uri('/api/v1/customers/5/events')).
131
+ with(:body => "name=purchase").
132
+ to_return(:status => 500, :body => "", :headers => {})
133
+
132
134
  lambda { client.track(5, "purchase") }.should raise_error(Customerio::Client::InvalidResponse)
133
135
  end
134
136
 
135
- it "uses the site_id and api key for basic auth" do
136
- Customerio::Client.should_receive(:post).with("/api/v1/customers/5/events", {
137
- :basic_auth => { :username => "SITE_ID", :password => "API_KEY" },
138
- :body => anything()
139
- })
137
+ it "uses the site_id and api key for basic auth and sends the event name" do
138
+ stub_request(:post, api_uri('/api/v1/customers/5/events')).
139
+ with(:body => "name=purchase").
140
+ to_return(:status => 200, :body => "", :headers => {})
140
141
 
141
142
  client.track(5, "purchase")
142
- end
143
+ end
143
144
 
144
- it "sends the event name" do
145
- Customerio::Client.should_receive(:post).with("/api/v1/customers/5/events", {
146
- :basic_auth => anything(),
147
- :body => { :name => "purchase", :data => {} }
148
- }).and_return(response)
145
+ it "sends any optional event attributes" do
146
+ stub_request(:post, api_uri('/api/v1/customers/5/events')).
147
+ with(:body => {
148
+ :name => "purchase",
149
+ :data => {
150
+ :type => "socks",
151
+ :price => "13.99"
152
+ }
153
+ }).
154
+ to_return(:status => 200, :body => "", :headers => {})
149
155
 
150
- client.track(5, "purchase")
151
- end
156
+ client.track(5, "purchase", :type => "socks", :price => "13.99")
157
+ end
152
158
 
153
- it "sends any optional event attributes" do
154
- Customerio::Client.should_receive(:post).with("/api/v1/customers/5/events", {
155
- :basic_auth => anything(),
156
- :body => {
157
- :name => "purchase",
158
- :data => { :type => "socks", :price => "13.99" }
159
- }
160
- }).and_return(response)
159
+ it "copes with arrays" do
160
+ stub_request(:post, api_uri('/api/v1/customers/5/events')).
161
+ with(:body => {
162
+ :name => "event",
163
+ :data => {
164
+ :things => ["a", "b", "c"]
165
+ }
166
+ }).
167
+ to_return(:status => 200, :body => "", :headers => {})
161
168
 
162
- client.track(5, "purchase", :type => "socks", :price => "13.99")
163
- end
169
+ client.track(5, "event", :things => ["a", "b", "c"])
170
+ end
171
+
172
+ it "copes with hashes" do
173
+ stub_request(:post, api_uri('/api/v1/customers/5/events')).
174
+ with(:body => {
175
+ :name => "event",
176
+ :data => {
177
+ :stuff => { :a => "b" }
178
+ }
179
+ }).
180
+ to_return(:status => 200, :body => "", :headers => {})
181
+
182
+ client.track(5, "event", :stuff => { :a => "b" })
183
+ end
164
184
 
165
185
  it "sends a POST request as json using json headers" do
166
186
  client = Customerio::Client.new("SITE_ID", "API_KEY", :json => true)
167
- Customerio::Client.should_receive(:post).with(
168
- "/api/v1/customers/5/events", {
169
- :basic_auth => anything(),
170
- :body => MultiJson.dump({
171
- :name => "purchase",
172
- :data => { :type => "socks", :price => "13.99" }
173
- }),
174
- :headers=>{"Content-Type"=>"application/json"}}).and_return(response)
175
- client.track(5, "purchase", :type => "socks", :price => "13.99")
176
- end
187
+ data = { :type => "socks", :price => "13.99" }
188
+ body = { :name => "purchase", :data => data }
177
189
 
190
+ stub_request(:post, api_uri('/api/v1/customers/5/events')).
191
+ with(:body => json(body),
192
+ :headers => {'Content-Type'=>'application/json'}).
193
+ to_return(:status => 200, :body => "", :headers => {})
178
194
 
179
- it "allows sending of a timestamp" do
180
- Customerio::Client.should_receive(:post).with("/api/v1/customers/5/events", {
181
- :basic_auth => anything(),
182
- :body => {
183
- :name => "purchase",
184
- :data => { :type => "socks", :price => "13.99", :timestamp => 1561231234 },
185
- :timestamp => 1561231234
186
- }
187
- }).and_return(response)
195
+ client.track(5, "purchase", data)
196
+ end
197
+
198
+ it "allows sending of a timestamp" do
199
+ stub_request(:post, api_uri('/api/v1/customers/5/events')).
200
+ with(:body => {
201
+ :name => "purchase",
202
+ :data => {
203
+ :type => "socks",
204
+ :price => "13.99",
205
+ :timestamp => "1561231234"
206
+ },
207
+ :timestamp => "1561231234"
208
+ }).
209
+ to_return(:status => 200, :body => "", :headers => {})
188
210
 
189
211
  client.track(5, "purchase", :type => "socks", :price => "13.99", :timestamp => 1561231234)
190
- end
212
+ end
191
213
 
192
214
  it "doesn't send timestamp if timestamp is in milliseconds" do
193
- Customerio::Client.should_receive(:post).with("/api/v1/customers/5/events", {
194
- :basic_auth => anything(),
195
- :body => {
196
- :name => "purchase",
197
- :data => { :type => "socks", :price => "13.99", :timestamp => 1561231234000 }
198
- }
199
- }).and_return(response)
215
+ stub_request(:post, api_uri('/api/v1/customers/5/events')).
216
+ with(:body => {
217
+ :name => "purchase",
218
+ :data => {
219
+ :type => "socks",
220
+ :price => "13.99",
221
+ :timestamp => "1561231234000"
222
+ }
223
+ }).
224
+ to_return(:status => 200, :body => "", :headers => {})
200
225
 
201
226
  client.track(5, "purchase", :type => "socks", :price => "13.99", :timestamp => 1561231234000)
202
227
  end
@@ -204,74 +229,71 @@ describe Customerio::Client do
204
229
  it "doesn't send timestamp if timestamp is a date" do
205
230
  date = Time.now
206
231
 
207
- Customerio::Client.should_receive(:post).with("/api/v1/customers/5/events", {
208
- :basic_auth => anything(),
209
- :body => {
210
- :name => "purchase",
211
- :data => { :type => "socks", :price => "13.99", :timestamp => date }
212
- }
213
- }).and_return(response)
232
+ stub_request(:post, api_uri('/api/v1/customers/5/events')).
233
+ with(:body => {
234
+ :name => "purchase",
235
+ :data => {
236
+ :type => "socks",
237
+ :price => "13.99",
238
+ :timestamp => Time.now.to_s
239
+ }
240
+ }).
241
+ to_return(:status => 200, :body => "", :headers => {})
214
242
 
215
243
  client.track(5, "purchase", :type => "socks", :price => "13.99", :timestamp => date)
216
244
  end
217
245
 
218
- it "doesn't send timestamp if timestamp isn't a integer" do
219
- Customerio::Client.should_receive(:post).with("/api/v1/customers/5/events", {
220
- :basic_auth => anything(),
221
- :body => {
222
- :name => "purchase",
223
- :data => { :type => "socks", :price => "13.99", :timestamp => "Hello world" }
224
- }
225
- }).and_return(response)
246
+ it "doesn't send timestamp if timestamp isn't an integer" do
247
+ stub_request(:post, api_uri('/api/v1/customers/5/events')).
248
+ with(:body => {
249
+ :name => "purchase",
250
+ :data => {
251
+ :type => "socks",
252
+ :price => "13.99",
253
+ :timestamp => "Hello world"
254
+ }
255
+ }).
256
+
257
+ to_return(:status => 200, :body => "", :headers => {})
226
258
 
227
259
  client.track(5, "purchase", :type => "socks", :price => "13.99", :timestamp => "Hello world")
228
260
  end
229
261
 
230
262
  context "tracking an anonymous event" do
231
263
  it "sends a POST request to the customer.io's anonymous event API" do
232
- Customerio::Client.should_receive(:post).with("/api/v1/events", anything()).and_return(response)
233
- client.track("purchase")
234
- end
235
-
236
- it "uses the site_id and api key for basic auth" do
237
- Customerio::Client.should_receive(:post).with("/api/v1/events", {
238
- :basic_auth => { :username => "SITE_ID", :password => "API_KEY" },
239
- :body => anything()
240
- }).and_return(response)
241
-
242
- client.track("purchase")
243
- end
244
-
245
- it "sends the event name" do
246
- Customerio::Client.should_receive(:post).with("/api/v1/events", {
247
- :basic_auth => anything(),
248
- :body => { :name => "purchase", :data => {} }
249
- }).and_return(response)
264
+ stub_request(:post, api_uri('/api/v1/events')).
265
+ with(:body => "name=purchase").
266
+ to_return(:status => 200, :body => "", :headers => {})
250
267
 
251
268
  client.track("purchase")
252
269
  end
253
270
 
254
271
  it "sends any optional event attributes" do
255
- Customerio::Client.should_receive(:post).with("/api/v1/events", {
256
- :basic_auth => anything(),
257
- :body => {
272
+ stub_request(:post, api_uri('/api/v1/events')).
273
+ with(:body => {
258
274
  :name => "purchase",
259
- :data => { :type => "socks", :price => "13.99" }
260
- }
261
- }).and_return(response)
275
+ :data => {
276
+ :type => "socks",
277
+ :price => "13.99"
278
+ }
279
+ }).
280
+ to_return(:status => 200, :body => "", :headers => {})
262
281
 
263
282
  client.track("purchase", :type => "socks", :price => "13.99")
264
283
  end
265
284
 
266
285
  it "allows sending of a timestamp" do
267
- Customerio::Client.should_receive(:post).with("/api/v1/events", {
268
- :basic_auth => anything(),
269
- :body => {
286
+ stub_request(:post, api_uri('/api/v1/events')).
287
+ with(:body => {
270
288
  :name => "purchase",
271
- :data => { :type => "socks", :price => "13.99", :timestamp => 1561231234 },
272
- :timestamp => 1561231234
273
- }
274
- }).and_return(response)
289
+ :data => {
290
+ :type => "socks",
291
+ :price => "13.99",
292
+ :timestamp => "1561231234"
293
+ },
294
+ :timestamp => "1561231234"
295
+ }).
296
+ to_return(:status => 200, :body => "", :headers => {})
275
297
 
276
298
  client.track("purchase", :type => "socks", :price => "13.99", :timestamp => 1561231234)
277
299
  end
@@ -279,55 +301,50 @@ describe Customerio::Client do
279
301
  end
280
302
 
281
303
  describe "#anonymous_track" do
282
- it "sends a POST request to the customer.io event API" do
283
- Customerio::Client.should_receive(:post).with("/api/v1/events", anything()).and_return(response)
284
- client.anonymous_track("purchase")
285
- end
286
-
287
304
  it "raises an error if POST doesn't return a 2xx response code" do
288
- Customerio::Client.should_receive(:post).and_return(double("Response", :code => 500))
289
- lambda { client.anonymous_track("purchase") }.should raise_error(Customerio::Client::InvalidResponse)
290
- end
291
-
292
- it "uses the site_id and api key for basic auth" do
293
- Customerio::Client.should_receive(:post).with("/api/v1/events", {
294
- :basic_auth => { :username => "SITE_ID", :password => "API_KEY" },
295
- :body => anything()
296
- })
305
+ stub_request(:post, api_uri('/api/v1/events')).
306
+ with(:body => "name=purchase").
307
+ to_return(:status => 500, :body => "", :headers => {})
297
308
 
298
- client.anonymous_track("purchase")
309
+ lambda { client.anonymous_track("purchase") }.should raise_error(Customerio::Client::InvalidResponse)
299
310
  end
300
311
 
301
- it "sends the event name" do
302
- Customerio::Client.should_receive(:post).with("/api/v1/events", {
303
- :basic_auth => anything(),
304
- :body => { :name => "purchase", :data => {} }
305
- }).and_return(response)
312
+ it "uses the site_id and api key for basic auth and sends the event name" do
313
+ stub_request(:post, api_uri('/api/v1/events')).
314
+ with(:body => "name=purchase").
315
+ to_return(:status => 200, :body => "", :headers => {})
306
316
 
307
317
  client.anonymous_track("purchase")
308
318
  end
309
319
 
310
320
  it "sends any optional event attributes" do
311
- Customerio::Client.should_receive(:post).with("/api/v1/events", {
312
- :basic_auth => anything(),
313
- :body => {
314
- :name => "purchase",
315
- :data => { :type => "socks", :price => "27.99" }
316
- }
317
- }).and_return(response)
321
+ stub_request(:post, api_uri('/api/v1/events')).
322
+ with(:body => {
323
+ :name => "purchase",
324
+ :data => {
325
+ :type => "socks",
326
+ :price => "27.99"
327
+ },
328
+ }).
329
+
330
+ to_return(:status => 200, :body => "", :headers => {})
318
331
 
319
332
  client.anonymous_track("purchase", :type => "socks", :price => "27.99")
320
333
  end
321
334
 
322
335
  it "allows sending of a timestamp" do
323
- Customerio::Client.should_receive(:post).with("/api/v1/events", {
324
- :basic_auth => anything(),
325
- :body => {
326
- :name => "purchase",
327
- :data => { :type => "socks", :price => "27.99", :timestamp => 1561235678 },
328
- :timestamp => 1561235678
329
- }
330
- }).and_return(response)
336
+ stub_request(:post, api_uri('/api/v1/events')).
337
+ with(:body => {
338
+ :name => "purchase",
339
+ :data => {
340
+ :type => "socks",
341
+ :price => "27.99",
342
+ :timestamp => "1561235678"
343
+ },
344
+ :timestamp => "1561235678"
345
+ }).
346
+
347
+ to_return(:status => 200, :body => "", :headers => {})
331
348
 
332
349
  client.anonymous_track("purchase", :type => "socks", :price => "27.99", :timestamp => 1561235678)
333
350
  end
@@ -2,11 +2,13 @@ require 'rubygems'
2
2
  require 'bundler/setup'
3
3
 
4
4
  require 'customerio'
5
- require 'fakeweb'
5
+ require 'webmock'
6
6
 
7
- FakeWeb.allow_net_connect = false
7
+ WebMock.disable_net_connect!
8
8
 
9
9
  require 'rspec'
10
+ require 'webmock/rspec'
11
+
10
12
  RSpec.configure do |config|
11
13
  config.expect_with(:rspec) { |c| c.syntax = :should }
12
14
  config.mock_with(:rspec) { |c| c.syntax = :should }
metadata CHANGED
@@ -1,51 +1,45 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: customerio
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Allison
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-03-02 00:00:00.000000000 Z
11
+ date: 2016-03-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: httparty
14
+ name: multi_json
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - <
18
- - !ruby/object:Gem::Version
19
- version: '0.12'
20
- - - ! '>='
17
+ - - ~>
21
18
  - !ruby/object:Gem::Version
22
- version: '0.5'
19
+ version: '1.0'
23
20
  type: :runtime
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
26
23
  requirements:
27
- - - <
28
- - !ruby/object:Gem::Version
29
- version: '0.12'
30
- - - ! '>='
24
+ - - ~>
31
25
  - !ruby/object:Gem::Version
32
- version: '0.5'
26
+ version: '1.0'
33
27
  - !ruby/object:Gem::Dependency
34
- name: multi_json
28
+ name: rake
35
29
  requirement: !ruby/object:Gem::Requirement
36
30
  requirements:
37
- - - ~>
31
+ - - ! '>='
38
32
  - !ruby/object:Gem::Version
39
- version: '1.0'
40
- type: :runtime
33
+ version: '0'
34
+ type: :development
41
35
  prerelease: false
42
36
  version_requirements: !ruby/object:Gem::Requirement
43
37
  requirements:
44
- - - ~>
38
+ - - ! '>='
45
39
  - !ruby/object:Gem::Version
46
- version: '1.0'
40
+ version: '0'
47
41
  - !ruby/object:Gem::Dependency
48
- name: rake
42
+ name: rspec
49
43
  requirement: !ruby/object:Gem::Requirement
50
44
  requirements:
51
45
  - - ! '>='
@@ -59,7 +53,7 @@ dependencies:
59
53
  - !ruby/object:Gem::Version
60
54
  version: '0'
61
55
  - !ruby/object:Gem::Dependency
62
- name: rspec
56
+ name: webmock
63
57
  requirement: !ruby/object:Gem::Requirement
64
58
  requirements:
65
59
  - - ! '>='
@@ -73,7 +67,21 @@ dependencies:
73
67
  - !ruby/object:Gem::Version
74
68
  version: '0'
75
69
  - !ruby/object:Gem::Dependency
76
- name: fakeweb
70
+ name: addressable
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: 2.3.6
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: 2.3.6
83
+ - !ruby/object:Gem::Dependency
84
+ name: json
77
85
  requirement: !ruby/object:Gem::Requirement
78
86
  requirements:
79
87
  - - ! '>='
@@ -103,6 +111,7 @@ files:
103
111
  - customerio.gemspec
104
112
  - lib/customerio.rb
105
113
  - lib/customerio/client.rb
114
+ - lib/customerio/param_encoder.rb
106
115
  - lib/customerio/version.rb
107
116
  - spec/client_spec.rb
108
117
  - spec/spec_helper.rb