customerio 0.7.0 → 1.0.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.
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