smyte 0.1.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 +7 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +77 -0
- data/Rakefile +1 -0
- data/lib/smyte.rb +45 -0
- data/lib/smyte/classification.rb +63 -0
- data/lib/smyte/client.rb +80 -0
- data/lib/smyte/config.rb +34 -0
- data/lib/smyte/label_reporter.rb +60 -0
- data/lib/smyte/notification.rb +109 -0
- data/lib/smyte/version.rb +3 -0
- data/smyte.gemspec +30 -0
- metadata +157 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e49d6787f785de76ce55f896fea08e2cfbc98ed2
|
4
|
+
data.tar.gz: 187e626f692fc20205f284fe398be27408d3f25e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ecf2767e5f099f9ffc8e4a9f70f443bd9709c58347457230c6097a2ba5f7f37bbeedd2617d3d44786fd890622b099b5f3c003ba71d98ab5ecd5c55b07fef4102
|
7
|
+
data.tar.gz: 848e86930a5c87cdcaba635d86992524d75e08efc56775c734bfe7606f1e2619258ca6c1c3be5128af4147c44491908fb50eb8065e57ebda756d42e7b36883e2
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Brian Leonard
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
# Smyte
|
2
|
+
|
3
|
+
[Smyte](https://www.smyte.com) is a fraud-checking service. This gem makes it a bit easier to work with their [API](https://docs.smyte.com/).
|
4
|
+
|
5
|
+
## Setup
|
6
|
+
|
7
|
+
`gem install smyte`
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
require 'smyte'
|
11
|
+
|
12
|
+
Smyte.enabled = true
|
13
|
+
Smyte.api_key = '123456789'
|
14
|
+
Smyte.api_secret = 'qwertyuiopasdfghjkl'
|
15
|
+
Smyte.webhook_secret = 'zxcvbnmpoiuytrewq'
|
16
|
+
```
|
17
|
+
|
18
|
+
## Sending data to Smyte
|
19
|
+
|
20
|
+
Uses this [API](https://docs.smyte.com/docs/sending-data-to-smyte).
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
payload = {
|
24
|
+
data: {
|
25
|
+
custom_id: 1
|
26
|
+
},
|
27
|
+
http_request: {
|
28
|
+
headers: {
|
29
|
+
"X-Real-IP" => "5.6.7.8"
|
30
|
+
},
|
31
|
+
path: "/login"
|
32
|
+
},
|
33
|
+
session: {
|
34
|
+
id: 'sessionguid',
|
35
|
+
action: {
|
36
|
+
user_id: 2
|
37
|
+
}
|
38
|
+
},
|
39
|
+
timestamp: Time.now
|
40
|
+
}
|
41
|
+
|
42
|
+
Smyte.action('my_event', payload)
|
43
|
+
|
44
|
+
```
|
45
|
+
|
46
|
+
## Receiving classification results
|
47
|
+
|
48
|
+
Uses this [API](https://docs.smyte.com/docs/receiving-classification-results).
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
# payload same as before (or other, of course)
|
52
|
+
classification = Smyte.classify('my_event', payload)
|
53
|
+
|
54
|
+
puts classification.action # will be :block, :review, or :allow
|
55
|
+
puts classification.label_report # for example on block ["exp:high_fail_rate_payment", "cc_country_mismatch"]
|
56
|
+
|
57
|
+
```
|
58
|
+
|
59
|
+
## Webhooks
|
60
|
+
|
61
|
+
Uses this [API](https://docs.smyte.com/docs/webhooks).
|
62
|
+
Smyte does a POST to your endpoint
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
# for example in a Rails controller action
|
66
|
+
secret = request.headers["X-Smyte-Signature"]
|
67
|
+
notification = ::Smyte.webhook(secret, params)
|
68
|
+
notification.items.each do |item|
|
69
|
+
puts item.action # will be :block, :review, or :allow
|
70
|
+
puts item.type # for example "user"
|
71
|
+
puts item.id # user id
|
72
|
+
puts item.label_report # for example on block ["bad_user"]
|
73
|
+
end
|
74
|
+
|
75
|
+
```
|
76
|
+
|
77
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/lib/smyte.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require "smyte/version"
|
2
|
+
require "forwardable"
|
3
|
+
|
4
|
+
module Smyte
|
5
|
+
|
6
|
+
autoload :Classification, 'smyte/classification'
|
7
|
+
autoload :Client, 'smyte/client'
|
8
|
+
autoload :Config, 'smyte/config'
|
9
|
+
autoload :LabelReporter, 'smyte/label_reporter'
|
10
|
+
autoload :Notification, 'smyte/notification'
|
11
|
+
|
12
|
+
class << self
|
13
|
+
extend Forwardable
|
14
|
+
|
15
|
+
def_delegators :config, :logger=, :logger,
|
16
|
+
:enabled=, :enabled, :enabled?,
|
17
|
+
:api_key=, :api_key,
|
18
|
+
:api_secret=, :api_secret,
|
19
|
+
:webhook_secret=, :webhook_secret
|
20
|
+
|
21
|
+
def_delegators :client, :action, :classify
|
22
|
+
|
23
|
+
|
24
|
+
def webhook(secret, response)
|
25
|
+
::Smyte::Notification.parse(secret, response)
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
|
30
|
+
def reset
|
31
|
+
# used by tests
|
32
|
+
@config = nil
|
33
|
+
end
|
34
|
+
|
35
|
+
def config
|
36
|
+
@config ||= ::Smyte::Config.new
|
37
|
+
end
|
38
|
+
|
39
|
+
def client
|
40
|
+
::Smyte::Client.new
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Smyte
|
2
|
+
class Classification
|
3
|
+
include ::Smyte::LabelReporter
|
4
|
+
|
5
|
+
def self.allowed
|
6
|
+
new('verdict' => "ALLOW", 'labels' => {})
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :response
|
10
|
+
|
11
|
+
def initialize(response)
|
12
|
+
@response = response
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
def parse_labels
|
18
|
+
out = []
|
19
|
+
labels = response["labels"] || {}
|
20
|
+
labels.each do |name, attributes|
|
21
|
+
out << Smyte::Classification::Label.new(name, attributes)
|
22
|
+
end
|
23
|
+
out
|
24
|
+
end
|
25
|
+
|
26
|
+
def parse_allow?
|
27
|
+
response["verdict"] == "ALLOW"
|
28
|
+
end
|
29
|
+
|
30
|
+
class Label
|
31
|
+
attr_reader :name, :response
|
32
|
+
def initialize(name, response)
|
33
|
+
@name = name
|
34
|
+
@response = response
|
35
|
+
end
|
36
|
+
|
37
|
+
# returns :block, :review, :allow, :unknown
|
38
|
+
def action
|
39
|
+
@action ||= calculate_action
|
40
|
+
end
|
41
|
+
|
42
|
+
def experimental?
|
43
|
+
!response["enabled"]
|
44
|
+
end
|
45
|
+
|
46
|
+
protected
|
47
|
+
|
48
|
+
def calculate_action
|
49
|
+
return :allow if response["verdict"] == "ALLOW"
|
50
|
+
|
51
|
+
if response["verdict"] == "BLOCK"
|
52
|
+
if experimental?
|
53
|
+
return :review
|
54
|
+
else
|
55
|
+
return :block
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
return :unknown
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/smyte/client.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'json'
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
module Smyte
|
6
|
+
class Client
|
7
|
+
attr_reader :api_key, :api_secret, :logger, :enabled
|
8
|
+
|
9
|
+
def initialize(options=nil)
|
10
|
+
options ||= {}
|
11
|
+
[:api_key, :api_secret, :logger, :enabled].each do |key|
|
12
|
+
if options.has_key?(key)
|
13
|
+
value = options[key]
|
14
|
+
else
|
15
|
+
value = Smyte.send(key)
|
16
|
+
end
|
17
|
+
instance_variable_set("@#{key}", value)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def action(event_name, payload)
|
22
|
+
return true if !enabled
|
23
|
+
|
24
|
+
process(event_name, payload, "/v2/action")
|
25
|
+
true # any success is just true - nothing else to know
|
26
|
+
end
|
27
|
+
|
28
|
+
def classify(event_name, payload)
|
29
|
+
return Smyte::Classification.allowed if !enabled
|
30
|
+
|
31
|
+
response = process(event_name, payload, "/v2/action/classify")
|
32
|
+
Smyte::Classification.new(response)
|
33
|
+
end
|
34
|
+
|
35
|
+
protected
|
36
|
+
|
37
|
+
def connection
|
38
|
+
return @connection if @connection
|
39
|
+
options = {
|
40
|
+
url: 'https://api.smyte.com',
|
41
|
+
headers: {
|
42
|
+
'Content-Type' => 'application/json'
|
43
|
+
}
|
44
|
+
}
|
45
|
+
@connection = Faraday.new(options) do |faraday|
|
46
|
+
# faraday.response :logger # log requests to STDOUT
|
47
|
+
faraday.adapter Faraday.default_adapter
|
48
|
+
end
|
49
|
+
@connection.basic_auth(api_key, api_secret)
|
50
|
+
@connection
|
51
|
+
end
|
52
|
+
|
53
|
+
def process(event_name, payload, path)
|
54
|
+
payload ||= {}
|
55
|
+
if payload.has_key?('name')
|
56
|
+
payload['name'] = event_name
|
57
|
+
else
|
58
|
+
payload[:name] = event_name
|
59
|
+
end
|
60
|
+
|
61
|
+
response = connection.post do |req|
|
62
|
+
req.url path
|
63
|
+
req.body = payload.to_json
|
64
|
+
end
|
65
|
+
|
66
|
+
if response.success?
|
67
|
+
return JSON.parse(response.body)
|
68
|
+
else
|
69
|
+
hash = JSON.parse(response.body) rescue nil
|
70
|
+
error = "Smyte Error (#{response.status})"
|
71
|
+
if hash && hash["message"]
|
72
|
+
error << ": "
|
73
|
+
error << hash["message"]
|
74
|
+
error << "\n"
|
75
|
+
end
|
76
|
+
raise error
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/lib/smyte/config.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
module Smyte
|
2
|
+
class Config
|
3
|
+
attr_accessor :logger
|
4
|
+
attr_writer :api_key, :api_secret, :webhook_secret
|
5
|
+
|
6
|
+
def api_key
|
7
|
+
return @api_key if @api_key
|
8
|
+
raise "Smyte api_key not set"
|
9
|
+
end
|
10
|
+
|
11
|
+
def api_secret
|
12
|
+
return @api_secret if @api_secret
|
13
|
+
raise "Smyte api_secret not set"
|
14
|
+
end
|
15
|
+
|
16
|
+
def webhook_secret
|
17
|
+
return @webhook_secret if @webhook_secret
|
18
|
+
raise "Smyte webhook_secret not set"
|
19
|
+
end
|
20
|
+
|
21
|
+
def enabled=input
|
22
|
+
@enabled = !!(input.to_s =~ /^(t|1|y|ok)/)
|
23
|
+
end
|
24
|
+
|
25
|
+
def enabled
|
26
|
+
return true if @enabled.nil?
|
27
|
+
@enabled
|
28
|
+
end
|
29
|
+
|
30
|
+
def enabled?
|
31
|
+
enabled
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Smyte
|
2
|
+
module LabelReporter
|
3
|
+
# class needs to define parse_labels and parse_allow?
|
4
|
+
# which returns objects that have an action
|
5
|
+
|
6
|
+
# returns :block, :review, :allow, :unknown
|
7
|
+
def action
|
8
|
+
@action ||= calculate_action
|
9
|
+
end
|
10
|
+
|
11
|
+
def labels
|
12
|
+
@labels ||= parse_labels
|
13
|
+
end
|
14
|
+
|
15
|
+
def label_actions
|
16
|
+
return @label_actions if @label_actions
|
17
|
+
found = Hash.new { |hash, key| hash[key] = [] }
|
18
|
+
labels.each do |label|
|
19
|
+
found[label.action] << label
|
20
|
+
end
|
21
|
+
|
22
|
+
@label_actions = found
|
23
|
+
end
|
24
|
+
|
25
|
+
def label_report
|
26
|
+
return @label_report if @label_report
|
27
|
+
out = []
|
28
|
+
|
29
|
+
labels.each do |label|
|
30
|
+
case label.action
|
31
|
+
when :block, :review
|
32
|
+
out << label.name
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
@label_report = out
|
37
|
+
end
|
38
|
+
|
39
|
+
def reset_labels
|
40
|
+
remove_instance_variable(:@label_report) if defined?(@label_report)
|
41
|
+
remove_instance_variable(:@label_actions) if defined?(@label_actions)
|
42
|
+
remove_instance_variable(:@labels) if defined?(@labels)
|
43
|
+
remove_instance_variable(:@action) if defined?(@action)
|
44
|
+
end
|
45
|
+
|
46
|
+
protected
|
47
|
+
|
48
|
+
def calculate_action
|
49
|
+
return :allow if respond_to?(:parse_allow?, true) && parse_allow?
|
50
|
+
|
51
|
+
if label_actions[:block].size > 0
|
52
|
+
return :block
|
53
|
+
elsif label_actions[:review].size > 0
|
54
|
+
return :review
|
55
|
+
else
|
56
|
+
return :unknown
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module Smyte
|
2
|
+
class Notification
|
3
|
+
def self.parse(webhook_secret, response)
|
4
|
+
if webhook_secret != Smyte.webhook_secret
|
5
|
+
raise "invalid webhook_secret: #{webhook_secret}"
|
6
|
+
end
|
7
|
+
Smyte::Notification.new(response)
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader :response
|
11
|
+
|
12
|
+
def initialize(response)
|
13
|
+
@response = response
|
14
|
+
end
|
15
|
+
|
16
|
+
def items
|
17
|
+
@items ||= parse_items
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
|
22
|
+
def parse_items
|
23
|
+
out = {}
|
24
|
+
items = response["items"] || []
|
25
|
+
items.each do |hash|
|
26
|
+
key = hash["item"]
|
27
|
+
next unless key
|
28
|
+
if out[key]
|
29
|
+
out[key].send(:add_response, hash)
|
30
|
+
else
|
31
|
+
out[key] = Smyte::Notification::Item.new(hash)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
out.values
|
36
|
+
end
|
37
|
+
|
38
|
+
class Item
|
39
|
+
include ::Smyte::LabelReporter
|
40
|
+
|
41
|
+
attr_reader :responses
|
42
|
+
|
43
|
+
def initialize(first_response)
|
44
|
+
@responses = [first_response]
|
45
|
+
end
|
46
|
+
|
47
|
+
def key
|
48
|
+
@item ||= responses.first["item"]
|
49
|
+
end
|
50
|
+
|
51
|
+
def type
|
52
|
+
@type ||= key.split("/").first
|
53
|
+
end
|
54
|
+
|
55
|
+
def id
|
56
|
+
@id ||= key.split("/").last
|
57
|
+
end
|
58
|
+
|
59
|
+
protected
|
60
|
+
|
61
|
+
def add_response(response)
|
62
|
+
reset_labels
|
63
|
+
responses << response
|
64
|
+
responses
|
65
|
+
end
|
66
|
+
|
67
|
+
def parse_labels
|
68
|
+
out = []
|
69
|
+
responses.each do |hash|
|
70
|
+
out << Smyte::Notification::Label.new(hash)
|
71
|
+
end
|
72
|
+
out
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
class Label
|
77
|
+
attr_reader :name, :response
|
78
|
+
def initialize(response)
|
79
|
+
@name = response["labelName"]
|
80
|
+
@response = response
|
81
|
+
end
|
82
|
+
|
83
|
+
# returns :block, :review, :allow, :unknown
|
84
|
+
def action
|
85
|
+
@action ||= calculate_action
|
86
|
+
end
|
87
|
+
|
88
|
+
def experimental?
|
89
|
+
response["labelType"] == "PENDING"
|
90
|
+
end
|
91
|
+
|
92
|
+
protected
|
93
|
+
|
94
|
+
def calculate_action
|
95
|
+
case response["labelType"]
|
96
|
+
when "ALLOW"
|
97
|
+
return :allow
|
98
|
+
when "PENDING"
|
99
|
+
return :review
|
100
|
+
when "ADDED", "BLOCK"
|
101
|
+
return :block
|
102
|
+
else
|
103
|
+
return :unknown
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|
data/smyte.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'smyte/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "smyte"
|
8
|
+
spec.version = Smyte::VERSION
|
9
|
+
spec.authors = ["Brian Leonard"]
|
10
|
+
spec.email = ["brian@bleonard.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Talk to Smyte service}
|
13
|
+
spec.description = %q{Smyte does fraud detection}
|
14
|
+
spec.homepage = "https://www.taskrabbit.com"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_dependency "faraday"
|
23
|
+
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
25
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
26
|
+
spec.add_development_dependency "rspec", "~> 3.4"
|
27
|
+
spec.add_development_dependency "byebug", "~> 3.5.1"
|
28
|
+
spec.add_development_dependency "vcr", "~> 3.0.1"
|
29
|
+
spec.add_development_dependency "webmock", "~> 1.24.3"
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,157 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: smyte
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Brian Leonard
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-04-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: faraday
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.10'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.10'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.4'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.4'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: byebug
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 3.5.1
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 3.5.1
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: vcr
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 3.0.1
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 3.0.1
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: webmock
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 1.24.3
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 1.24.3
|
111
|
+
description: Smyte does fraud detection
|
112
|
+
email:
|
113
|
+
- brian@bleonard.com
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- ".gitignore"
|
119
|
+
- ".rspec"
|
120
|
+
- Gemfile
|
121
|
+
- LICENSE.txt
|
122
|
+
- README.md
|
123
|
+
- Rakefile
|
124
|
+
- lib/smyte.rb
|
125
|
+
- lib/smyte/classification.rb
|
126
|
+
- lib/smyte/client.rb
|
127
|
+
- lib/smyte/config.rb
|
128
|
+
- lib/smyte/label_reporter.rb
|
129
|
+
- lib/smyte/notification.rb
|
130
|
+
- lib/smyte/version.rb
|
131
|
+
- smyte.gemspec
|
132
|
+
homepage: https://www.taskrabbit.com
|
133
|
+
licenses:
|
134
|
+
- MIT
|
135
|
+
metadata: {}
|
136
|
+
post_install_message:
|
137
|
+
rdoc_options: []
|
138
|
+
require_paths:
|
139
|
+
- lib
|
140
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '0'
|
145
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
146
|
+
requirements:
|
147
|
+
- - ">="
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
requirements: []
|
151
|
+
rubyforge_project:
|
152
|
+
rubygems_version: 2.2.3
|
153
|
+
signing_key:
|
154
|
+
specification_version: 4
|
155
|
+
summary: Talk to Smyte service
|
156
|
+
test_files: []
|
157
|
+
has_rdoc:
|