sift 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +5 -0
- data/Gemfile +4 -0
- data/HISTORY +3 -0
- data/README.rdoc +41 -0
- data/Rakefile +14 -0
- data/examples/simple.rb +50 -0
- data/lib/sift.rb +33 -0
- data/lib/sift/client.rb +108 -0
- data/lib/sift/version.rb +3 -0
- data/sift.gemspec +29 -0
- data/spec/integration/sift_spec.rb +7 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/unit/client_spec.rb +82 -0
- data/spec/unit/sift_spec.rb +6 -0
- metadata +107 -0
data/Gemfile
ADDED
data/HISTORY
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
= Sift Science Ruby bindings
|
2
|
+
|
3
|
+
== Installation
|
4
|
+
|
5
|
+
If you want to build the gem from source:
|
6
|
+
|
7
|
+
$ gem build sift.gemspec
|
8
|
+
|
9
|
+
Alternatively, you can install the gem from Rubyforge:
|
10
|
+
|
11
|
+
$ gem install sift
|
12
|
+
|
13
|
+
== Requirements
|
14
|
+
|
15
|
+
* Ruby 1.8.7 or above. (Ruby 1.8.6 might work if you load ActiveSupport.)
|
16
|
+
* HTTParty, 0.8.3 or greater
|
17
|
+
|
18
|
+
For development only:
|
19
|
+
* bundler
|
20
|
+
* rspec, 2.9 or greater
|
21
|
+
* fakeweb, 1.3 or greater
|
22
|
+
|
23
|
+
== Building
|
24
|
+
|
25
|
+
Building and publishing the gem is captured by the following steps:
|
26
|
+
|
27
|
+
$ gem build sift.gemspec
|
28
|
+
$ gem push sift-<current version>.gem
|
29
|
+
|
30
|
+
$ bundle
|
31
|
+
$ rake -T
|
32
|
+
$ rake build
|
33
|
+
$ rake install
|
34
|
+
$ rake release
|
35
|
+
|
36
|
+
== Testing
|
37
|
+
|
38
|
+
To run the various tests use the rake command as follows:
|
39
|
+
|
40
|
+
$ rake spec:unit
|
41
|
+
$ rake spec:integration
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
begin
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
Bundler::GemHelper.install_tasks
|
4
|
+
rescue LoadError => e
|
5
|
+
warn "It is recommended that you use bundler during development: gem install bundler"
|
6
|
+
end
|
7
|
+
|
8
|
+
require "rspec/core/rake_task"
|
9
|
+
|
10
|
+
desc "Run tests"
|
11
|
+
RSpec::Core::RakeTask.new(:spec)
|
12
|
+
|
13
|
+
task :default => :spec
|
14
|
+
task :test => :spec
|
data/examples/simple.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# A simple example of how to use the API.
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'sift'
|
7
|
+
require 'multi_json'
|
8
|
+
|
9
|
+
api_key = "" # TODO: Set your API key here
|
10
|
+
|
11
|
+
class MyLogger
|
12
|
+
def warn(e)
|
13
|
+
puts "[WARN] " + e.to_s
|
14
|
+
end
|
15
|
+
|
16
|
+
def error(e)
|
17
|
+
puts "[ERROR] " + e.to_s
|
18
|
+
end
|
19
|
+
|
20
|
+
def fatal(e)
|
21
|
+
puts "[FATAL] " + e.to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
def info(e)
|
25
|
+
puts "[INFO] " + e.to_s
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
Sift.logger = MyLogger.new
|
30
|
+
|
31
|
+
client = Sift::Client.new(api_key)
|
32
|
+
event = "my_custom_event"
|
33
|
+
properties = {
|
34
|
+
"my_custom_field1" => "my custom value 1",
|
35
|
+
"my_custom_field2" => "my custom value 2",
|
36
|
+
"$user_id" => "3",
|
37
|
+
"$time" => Time.now.to_i,
|
38
|
+
}
|
39
|
+
|
40
|
+
response = client.track(event, properties)
|
41
|
+
if response.nil?
|
42
|
+
puts 'Error: there was an HTTP error calling through the API'
|
43
|
+
else
|
44
|
+
puts 'Successfully sent request; was ok? : ' + response.ok?.to_s
|
45
|
+
puts 'API error message : ' + response.api_error_message.to_s
|
46
|
+
puts 'API status code : ' + response.api_status.to_s
|
47
|
+
puts 'HTTP status code : ' + response.http_status_code.to_s
|
48
|
+
puts 'original request : ' + response.original_request.to_s
|
49
|
+
end
|
50
|
+
|
data/lib/sift.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require "sift/version"
|
2
|
+
require "sift/client"
|
3
|
+
|
4
|
+
module Sift
|
5
|
+
|
6
|
+
# Returns the path for the current API version
|
7
|
+
def self.current_rest_api_path
|
8
|
+
"/v202/events"
|
9
|
+
end
|
10
|
+
|
11
|
+
# Sets the Output logger to use within the client. This can be left uninitializaed
|
12
|
+
# but is useful for debugging.
|
13
|
+
def self.logger=(logger)
|
14
|
+
@logger = logger
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.info(msg)
|
18
|
+
@logger.info(msg) if @logger
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.warn(msg)
|
22
|
+
@logger.warn(msg) if @logger
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.error(msg)
|
26
|
+
@logger.error(msg) if @logger
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.fatal(msg)
|
30
|
+
@logger.fatal(msg) if @logger
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
data/lib/sift/client.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require 'multi_json'
|
3
|
+
|
4
|
+
module Sift
|
5
|
+
|
6
|
+
# Represents the payload returned from a call through the track API
|
7
|
+
#
|
8
|
+
class Response
|
9
|
+
attr_reader :json
|
10
|
+
attr_reader :http_status_code
|
11
|
+
attr_reader :api_status
|
12
|
+
attr_reader :api_error_message
|
13
|
+
attr_reader :original_request
|
14
|
+
|
15
|
+
# Constructor
|
16
|
+
#
|
17
|
+
# == Parameters:
|
18
|
+
# http_response
|
19
|
+
# The HTTP body text returned from the API call. The body is expected to be
|
20
|
+
# a JSON object that can be decoded into status, message and request
|
21
|
+
# sections.
|
22
|
+
#
|
23
|
+
def initialize(http_response, http_response_code)
|
24
|
+
@json = MultiJson.load(http_response)
|
25
|
+
@original_request = MultiJson.load(@json["request"].to_s) if @json["request"]
|
26
|
+
@http_status_code = http_response_code
|
27
|
+
@api_status = @json["status"].to_i
|
28
|
+
@api_error_message = @json["error_message"].to_s
|
29
|
+
end
|
30
|
+
|
31
|
+
# Helper method returns true if and only if the response from the API call was
|
32
|
+
# successful
|
33
|
+
#
|
34
|
+
# == Returns:
|
35
|
+
# true on success; false otherwise
|
36
|
+
def ok?
|
37
|
+
0 == @api_status.to_i
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# This class wraps accesses through the API
|
42
|
+
#
|
43
|
+
class Client
|
44
|
+
API_ENDPOINT = "https://api.siftscience.com"
|
45
|
+
API_TIMEOUT = 2
|
46
|
+
|
47
|
+
include HTTParty
|
48
|
+
base_uri API_ENDPOINT
|
49
|
+
default_timeout API_TIMEOUT
|
50
|
+
|
51
|
+
# Constructor
|
52
|
+
#
|
53
|
+
# == Parameters:
|
54
|
+
# api_key
|
55
|
+
# The Sift Science API key associated with your customer account. This parameter
|
56
|
+
# cannot be nil or blank.
|
57
|
+
# path
|
58
|
+
# The path to the event API, e.g., "/v201/events"
|
59
|
+
#
|
60
|
+
def initialize(api_key, path = Sift.current_rest_api_path)
|
61
|
+
@api_key = api_key.to_s
|
62
|
+
@path = path
|
63
|
+
raise(RuntimeError, "api_key is required") if @api_key.nil? || @api_key.empty?
|
64
|
+
end
|
65
|
+
|
66
|
+
# Tracks an event and associated properties through the Sift Science API. This call
|
67
|
+
# is blocking.
|
68
|
+
#
|
69
|
+
# == Parameters:
|
70
|
+
# event
|
71
|
+
# The name of the event to send. This can be either a reserved event name, like
|
72
|
+
# $transaction or $label or a custom event name (that does not start with a $).
|
73
|
+
# This parameter must be specified.
|
74
|
+
#
|
75
|
+
# properties
|
76
|
+
# A hash of name-value pairs that specify the event-specific attributes to track.
|
77
|
+
# This parameter must be specified.
|
78
|
+
#
|
79
|
+
# timeout
|
80
|
+
# The number of seconds to wait before failing the request. By default this is
|
81
|
+
# configured to 2 seconds (see above). This parameter is optional.
|
82
|
+
#
|
83
|
+
# == Returns:
|
84
|
+
# In the case of an HTTP error (timeout, broken connection, etc.), this
|
85
|
+
# method returns nil; otherwise, a Response object is returned and captures
|
86
|
+
# the status message and status code. In general, you can ignore the returned
|
87
|
+
# result, though.
|
88
|
+
#
|
89
|
+
def track(event, properties = {}, timeout = nil)
|
90
|
+
|
91
|
+
raise(RuntimeError, "event must be a string") if event.nil? || event.to_s.empty?
|
92
|
+
raise(RuntimeError, "properties cannot be empty") if properties.empty?
|
93
|
+
|
94
|
+
options = {
|
95
|
+
:body => MultiJson.dump(properties.merge({"$type" => event,
|
96
|
+
"$api_key" => @api_key})),
|
97
|
+
}
|
98
|
+
options.merge!(:timeout => timeout) unless timeout.nil?
|
99
|
+
begin
|
100
|
+
response = self.class.post(@path, options)
|
101
|
+
Response.new(response.body, response.code)
|
102
|
+
rescue StandardError => e
|
103
|
+
Sift.warn("Failed to track event: " + e.to_s)
|
104
|
+
Sift.warn(e.backtrace)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
data/lib/sift/version.rb
ADDED
data/sift.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "sift/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "sift"
|
7
|
+
s.version = Sift::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Fred Sadaghiani"]
|
10
|
+
s.email = ["freds@siftscience.com"]
|
11
|
+
s.homepage = "http://siftscience.com"
|
12
|
+
s.summary = %q{Sift Science Ruby API Gem}
|
13
|
+
s.description = %q{Sift Science Ruby API. Please see http://siftscience.com for more details.}
|
14
|
+
|
15
|
+
s.rubyforge_project = "sift"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
# Gems that must be intalled for sift to compile and build
|
23
|
+
s.add_development_dependency "rspec", "~> 2.9.0"
|
24
|
+
s.add_development_dependency "fakeweb", "~> 1.3.0"
|
25
|
+
|
26
|
+
# Gems that must be intalled for sift to work
|
27
|
+
s.add_dependency "httparty", ">= 0.8.3"
|
28
|
+
s.add_dependency "multi_json", ">= 1.3.4"
|
29
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
|
2
|
+
|
3
|
+
describe Sift::Client do
|
4
|
+
|
5
|
+
def valid_transaction_properties
|
6
|
+
{
|
7
|
+
:$buyer_user_id => "123456",
|
8
|
+
:$seller_user_id => "654321",
|
9
|
+
:$amount => 1253200,
|
10
|
+
:$currency_code => "USD",
|
11
|
+
:$time => Time.now.to_i,
|
12
|
+
:$transaction_id => "my_transaction_id",
|
13
|
+
:$billing_name => "Mike Snow",
|
14
|
+
:$billing_bin => "411111",
|
15
|
+
:$billing_last4 => "1111",
|
16
|
+
:$billing_address1 => "123 Main St.",
|
17
|
+
:$billing_city => "San Francisco",
|
18
|
+
:$billing_region => "CA",
|
19
|
+
:$billing_country => "US",
|
20
|
+
:$billing_zip => "94131",
|
21
|
+
:$buyer_email => "mike@example.com"
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
def fully_qualified_api_endpoint
|
26
|
+
Sift::Client::API_ENDPOINT + Sift.current_rest_api_path
|
27
|
+
end
|
28
|
+
|
29
|
+
it "Cannot instantiate client with nil or blank api key" do
|
30
|
+
lambda { Sift::Client.new(nil) }.should raise_error
|
31
|
+
lambda { Sift::Client.new("") }.should raise_error
|
32
|
+
end
|
33
|
+
|
34
|
+
it "Track call must specify an event name" do
|
35
|
+
lambda { Sift::Client.new("foo").track(nil) }.should raise_error
|
36
|
+
lambda { Sift::Client.new("foo").track("") }.should raise_error
|
37
|
+
end
|
38
|
+
|
39
|
+
it "Must specify an event name" do
|
40
|
+
lambda { Sift::Client.new("foo").track(nil) }.should raise_error
|
41
|
+
lambda { Sift::Client.new("foo").track("") }.should raise_error
|
42
|
+
end
|
43
|
+
|
44
|
+
it "Must specify properties" do
|
45
|
+
event = "custom_event_name"
|
46
|
+
lambda { Sift::Client.new("foo").track(event) }.should raise_error
|
47
|
+
end
|
48
|
+
|
49
|
+
it "Doesn't raise an exception on Net/HTTP errors" do
|
50
|
+
|
51
|
+
FakeWeb.register_uri(:post, fully_qualified_api_endpoint,
|
52
|
+
:body => nil, :exception => Net::HTTPError)
|
53
|
+
|
54
|
+
api_key = "foobar"
|
55
|
+
event = "$transaction"
|
56
|
+
properties = valid_transaction_properties
|
57
|
+
|
58
|
+
# This method should just return false -- the track call failed because
|
59
|
+
# of an HTTP error
|
60
|
+
Sift::Client.new(api_key).track(event, properties).should eq(nil)
|
61
|
+
end
|
62
|
+
|
63
|
+
it "Successfuly handles an event and returns OK" do
|
64
|
+
|
65
|
+
response_json = { :status => 0, :error_message => "OK" }
|
66
|
+
|
67
|
+
FakeWeb.register_uri(:post, fully_qualified_api_endpoint,
|
68
|
+
:body => MultiJson.dump(response_json),
|
69
|
+
:status => [Net::HTTPOK, "OK"],
|
70
|
+
:content_type => "text/json")
|
71
|
+
|
72
|
+
api_key = "foobar"
|
73
|
+
event = "$transaction"
|
74
|
+
properties = valid_transaction_properties
|
75
|
+
|
76
|
+
response = Sift::Client.new(api_key).track(event, properties)
|
77
|
+
response.ok?.should eq(true)
|
78
|
+
response.api_status.should eq(0)
|
79
|
+
response.api_error_message.should eq("OK")
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
metadata
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sift
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.5
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Fred Sadaghiani
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-08-02 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: &70093902351140 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 2.9.0
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70093902351140
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: fakeweb
|
27
|
+
requirement: &70093902438840 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.3.0
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70093902438840
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: httparty
|
38
|
+
requirement: &70093902565360 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 0.8.3
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70093902565360
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: multi_json
|
49
|
+
requirement: &70093902656720 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.3.4
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *70093902656720
|
58
|
+
description: Sift Science Ruby API. Please see http://siftscience.com for more details.
|
59
|
+
email:
|
60
|
+
- freds@siftscience.com
|
61
|
+
executables: []
|
62
|
+
extensions: []
|
63
|
+
extra_rdoc_files: []
|
64
|
+
files:
|
65
|
+
- .gitignore
|
66
|
+
- Gemfile
|
67
|
+
- HISTORY
|
68
|
+
- README.rdoc
|
69
|
+
- Rakefile
|
70
|
+
- examples/simple.rb
|
71
|
+
- lib/sift.rb
|
72
|
+
- lib/sift/client.rb
|
73
|
+
- lib/sift/version.rb
|
74
|
+
- sift.gemspec
|
75
|
+
- spec/integration/sift_spec.rb
|
76
|
+
- spec/spec_helper.rb
|
77
|
+
- spec/unit/client_spec.rb
|
78
|
+
- spec/unit/sift_spec.rb
|
79
|
+
homepage: http://siftscience.com
|
80
|
+
licenses: []
|
81
|
+
post_install_message:
|
82
|
+
rdoc_options: []
|
83
|
+
require_paths:
|
84
|
+
- lib
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ! '>='
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
|
+
none: false
|
93
|
+
requirements:
|
94
|
+
- - ! '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
requirements: []
|
98
|
+
rubyforge_project: sift
|
99
|
+
rubygems_version: 1.8.10
|
100
|
+
signing_key:
|
101
|
+
specification_version: 3
|
102
|
+
summary: Sift Science Ruby API Gem
|
103
|
+
test_files:
|
104
|
+
- spec/integration/sift_spec.rb
|
105
|
+
- spec/spec_helper.rb
|
106
|
+
- spec/unit/client_spec.rb
|
107
|
+
- spec/unit/sift_spec.rb
|