sift 1.0.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|