attune 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- YmI0ZDQ2MjAxYWIyYzNiNDE0Y2E5YWI1YWFmMTUxMDcyYTNjZTc3Mw==
5
- data.tar.gz: !binary |-
6
- YmRkYWIzMTRhOGNlOTVlYzBhZDFjY2FkMGFkNDY2MGVlNWIzMGY2ZA==
2
+ SHA1:
3
+ metadata.gz: a768188791c5cf1b4dbb0d9bb7639c79be622f49
4
+ data.tar.gz: 0b55667d588255b97df0fc7fbc7d7571f2a3a420
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- M2UyY2E5ZTE5MzAyNGY2NGIzNjQyNDEwYWU0ZTRiN2FkYWEwNDA5MjY3Yjg2
10
- ZTQ3N2Q3ZGU3MzE0OWUzZWNiNmM2ZjUyMTQxMjU4NGJhMTA2MGUzYTI4ODk2
11
- ODhhYmQ1MWQ0ZjM0MDA3ZDU4OWEyNDJjMDExYjY4ODk2MWE1MDk=
12
- data.tar.gz: !binary |-
13
- NWI4YmYxMDMxNWZkMWQxYTZjZTIyODQ1MjdiYjkxMWIwZjYzM2E1MDM3MDk1
14
- M2Y5ZTU5YWQ4ODcwNTk4ZDQ0NWE0ZjA1NzMzOTI1MTQyMGQ0NzQ5MDE3MzFi
15
- YjJmYzVlNTQxNmQ5ZjUyMWQyNzZkZjU5NDMxNjQxZTIzMjA5YTc=
6
+ metadata.gz: a90ccfd4d4aa1893c315e161545a3bd04cdddcdaabdc98f7e4daf809a7a8acbefd3e15d03344fd933ae83e290b7ff3f4bdd2c15b0180565d4b00597c4c7d9e74
7
+ data.tar.gz: 1686b9cfa601e45f89590446c8c69a65f9c6f80b775f4031056fc6f9438c790514f836d7f00622ce31b0562d92e62c643aa19da5a53491c92d08cb2289be1ec8
data/README.md CHANGED
@@ -1,6 +1,10 @@
1
1
  # Attune
2
2
 
3
- A client for the [Attune ranking API](http://attune.co/). Build using the excellent [faraday](https://github.com/lostisland/faraday) library.
3
+ A client for the [Attune ranking API](http://attune.co/). Built using the excellent [faraday](https://github.com/lostisland/faraday) library.
4
+
5
+ * [Github](https://github.com/DigitalStripe/attune-ruby)
6
+ * [RubyGems](https://rubygems.org/gems/attune)
7
+ * [Documentation](http://rdoc.info/github/DigitalStripe/attune-ruby)
4
8
 
5
9
  ## Installation
6
10
 
@@ -55,22 +59,24 @@ The client can then perform rankings
55
59
  ``` ruby
56
60
  class ProductsController
57
61
  def index
58
- @products = Product.all
62
+ @products = sorted(Product.all)
63
+ end
59
64
 
65
+ private
66
+ def sorted products
60
67
  ranking = attune_client.get_ranking(
61
68
  id: session[:attune_id],
62
69
  view: request.fullpath,
63
70
  collection: 'products',
64
- entities: @products.map(&:id)
71
+ entities: products.map(&:id)
65
72
  )
66
- @products.sort_by do |product|
73
+ products.sort_by do |product|
67
74
  ranking.index(product.id.to_s)
68
75
  end
69
76
  end
70
77
  end
71
78
  ```
72
79
 
73
-
74
80
  ### Configuration
75
81
 
76
82
  Attune can be configured globally
@@ -88,9 +94,26 @@ Settings can also be overridden on a client object
88
94
  client = Attune::Client.new(timeout: 2)
89
95
  ```
90
96
 
97
+ See the documentation for
98
+ [Attune::Configurable](http://rdoc.info/github/DigitalStripe/attune-ruby/master/Attune/Configurable)
99
+ and the
100
+ [default configuration](http://rdoc.info/github/DigitalStripe/attune-ruby/master/Attune/Default)
101
+ for more details.
102
+
103
+ ### Testing
104
+
105
+ For testing and development, the ranking API can be simulated using.
106
+
107
+ ``` ruby
108
+ Attune.test_mode!
109
+ ```
110
+
111
+ In this mode no API calls will be made, and rankings will be returned in their original order.
112
+ Calling `.test_mode!` is equivalent to setting `disabled` to true and `exception_handler` to `:mock`.
113
+
91
114
  ## Contributing
92
115
 
93
- 1. Fork it ( http://github.com/freerunningtech/attune/fork )
116
+ 1. Fork it ( http://github.com/DigitalStripe/attune/fork )
94
117
  2. Create your feature branch (`git checkout -b my-new-feature`)
95
118
  3. Commit your changes (`git commit -am 'Add some feature'`)
96
119
  4. Push to the branch (`git push origin my-new-feature`)
data/attune.gemspec CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
10
10
  spec.email = ["john@freerunningtechnologies.com"]
11
11
  spec.summary = %q{Client for the Attune product ranking API.}
12
12
  spec.description = %q{Client for the Attune product ranking API.}
13
- spec.homepage = "https://github.com/freerunningtech/attune-ruby"
13
+ spec.homepage = "https://github.com/DigitalStripe/attune-ruby"
14
14
  spec.license = "MIT"
15
15
 
16
16
  spec.files = `git ls-files`.split($/)
@@ -25,4 +25,5 @@ Gem::Specification.new do |spec|
25
25
  spec.add_development_dependency "rspec"
26
26
  spec.add_development_dependency "yard"
27
27
  spec.add_development_dependency "redcarpet"
28
+ spec.add_development_dependency "simplecov"
28
29
  end
data/lib/attune.rb CHANGED
@@ -9,4 +9,17 @@ module Attune
9
9
  def self.client
10
10
  Client.new
11
11
  end
12
+
13
+ # Simulate all API calls
14
+ # This is equivalent to setting disabled to true and exception_handler to
15
+ # :mock
16
+ def self.test_mode!
17
+ configure do |c|
18
+ c.exception_handler = :mock
19
+ c.disabled = true
20
+ end
21
+ end
22
+ def self.configure(&block)
23
+ Default.configure(&block)
24
+ end
12
25
  end
@@ -0,0 +1,52 @@
1
+ module Attune
2
+ class CallDropping < Faraday::Middleware
3
+ def initialize app, storage=default_storage
4
+ @app = app
5
+ @storage = storage
6
+ end
7
+
8
+ def call env
9
+ raise_timeout if should_skip?
10
+ begin
11
+ result = @app.call(env)
12
+ increment_skips(-SKIP_STEP)
13
+ result
14
+ rescue Faraday::Error::TimeoutError => e
15
+ increment_skips(SKIP_STEP)
16
+ raise e
17
+ end
18
+ end
19
+
20
+ private
21
+ # requests to skip before retry
22
+ SKIP_STEP = 0.25
23
+
24
+ # The most requests to skip before retrying
25
+ SKIP_MAX = 10.0
26
+
27
+ def raise_timeout
28
+ raise Faraday::Error::TimeoutError, "dropped request"
29
+ end
30
+
31
+ def should_skip?
32
+ rand > (1.0 / (1.0 + skip_rate))
33
+ end
34
+
35
+ def increment_skips amount
36
+ self.skip_rate = skip_rate + amount
37
+
38
+ if skip_rate <= 0.0
39
+ self.skip_rate = 0.0
40
+ elsif skip_rate >= SKIP_MAX
41
+ self.skip_rate = SKIP_MAX
42
+ end
43
+ end
44
+
45
+ def skip_rate; @storage[:skip_rate] || 0.0; end
46
+ def skip_rate= v; @storage[:skip_rate]= v; end
47
+
48
+ def default_storage
49
+ Thread.current
50
+ end
51
+ end
52
+ end
data/lib/attune/client.rb CHANGED
@@ -1,8 +1,12 @@
1
1
  require 'json'
2
2
 
3
3
  module Attune
4
+ class DisabledException < Faraday::Error::ClientError
5
+ def initialize(message="Attune library disabled though config")
6
+ super(message)
7
+ end
8
+ end
4
9
 
5
- # Client for the attune
6
10
  class Client
7
11
  include Attune::Configurable
8
12
 
@@ -38,14 +42,19 @@ module Attune
38
42
  # @option options [String] :user_agent The user agent for the application used by the anonymous users
39
43
  # @return id [String]
40
44
  # @raise [ArgumentError] if user_agent is not provided
45
+ # @raise [Faraday::Error] if the request fails or exceeds the timeout
41
46
  def create_anonymous(options)
42
47
  raise ArgumentError, "user_agent required" unless options[:user_agent]
43
48
  if id = options[:id]
44
- response = put("anonymous/#{id}", {user_agent: options[:user_agent]})
49
+ put("anonymous/#{id}", {user_agent: options[:user_agent]})
45
50
  id
46
51
  else
47
- response = post("anonymous", {user_agent: options[:user_agent]})
48
- response[:location][/\Aurn:id:([a-z0-9\-]+)\Z/, 1]
52
+ if response = post("anonymous", {user_agent: options[:user_agent]})
53
+ response[:location][/\Aurn:id:([a-z0-9\-]+)\Z/, 1]
54
+ else
55
+ # Return a new UUID if there was an exception and we're in mock mode
56
+ SecureRandom.uuid
57
+ end
49
58
  end
50
59
  end
51
60
 
@@ -67,10 +76,15 @@ module Attune
67
76
  # @option options [String] :customer id of customer (optional)
68
77
  # @return ranking [Array<String>] The entities in their ranked order
69
78
  # @raise [ArgumentError] if required parameters are missing
79
+ # @raise [Faraday::Error] if the request fails or exceeds the timeout
70
80
  def get_rankings(options)
71
81
  qs = encoded_ranking_params(options)
72
- response = get("rankings/#{qs}", customer: options.fetch(:customer, 'none'))
73
- JSON.parse(response.body)['ranking']
82
+ if response = get("rankings/#{qs}", customer: options.fetch(:customer, 'none'))
83
+ JSON.parse(response.body)['ranking']
84
+ else
85
+ # In mock mode: return the entities in the order passed in
86
+ options[:entities]
87
+ end
74
88
  end
75
89
 
76
90
  # Get multiple rankings in one call
@@ -92,14 +106,21 @@ module Attune
92
106
  # ])
93
107
  # @param [Array<Hash>] multi_options An array of options (see #get_rankings)
94
108
  # @return [Array<Array<String>>] rankings
109
+ # @raise [Faraday::Error] if the request fails or exceeds the timeout
95
110
  def multi_get_rankings(multi_options)
96
111
  requests = multi_options.map do |options|
97
112
  encoded_ranking_params(options)
98
113
  end
99
- response = get("rankings", ids: requests)
100
- results = JSON.parse(response.body)['results']
101
- results.values.map do |result|
102
- result['ranking']
114
+ if response = get("rankings", ids: requests)
115
+ results = JSON.parse(response.body)['results']
116
+ results.values.map do |result|
117
+ result['ranking']
118
+ end
119
+ else
120
+ # In mock mode: return the entities in the order passed in
121
+ multi_options.map do |options|
122
+ options[:entities]
123
+ end
103
124
  end
104
125
  end
105
126
 
@@ -112,6 +133,7 @@ module Attune
112
133
  # '25892e17-80f6-415f-9c65-7395632f022',
113
134
  # 'cd171f7c-560d-4a62-8d65-16b87419a58'
114
135
  # )
136
+ # @raise [Faraday::Error] if the request fails or exceeds the timeout
115
137
  def bind(id, customer_id)
116
138
  put("bindings/anonymous=#{id}&customer=#{customer_id}")
117
139
  true
@@ -131,27 +153,32 @@ module Attune
131
153
 
132
154
  def get(path, params={})
133
155
  adapter.get(path, params)
134
- rescue Faraday::ClientError => e
156
+ rescue Faraday::Error::ClientError => e
135
157
  handle_exception(e)
136
158
  end
137
159
 
138
160
  def put(path, params={})
139
161
  adapter.put(path, ::JSON.dump(params))
140
- rescue Faraday::ClientError => e
162
+ rescue Faraday::Error::ClientError => e
141
163
  handle_exception(e)
142
164
  end
143
165
 
144
166
  def post(path, params={})
145
167
  adapter.post(path, ::JSON.dump(params))
146
- rescue Faraday::ClientError => e
168
+ rescue Faraday::Error::ClientError => e
147
169
  handle_exception(e)
148
170
  end
149
171
 
150
172
  def handle_exception e
151
- raise e
173
+ if exception_handler == :mock
174
+ nil
175
+ else
176
+ raise e
177
+ end
152
178
  end
153
179
 
154
180
  def adapter
181
+ raise DisabledException if disabled?
155
182
  Faraday.new(url: endpoint, builder: middleware, request: {timeout: timeout})
156
183
  end
157
184
  end
@@ -4,6 +4,7 @@ module Attune
4
4
  :endpoint,
5
5
  :middleware,
6
6
  :disabled,
7
+ :exception_handler,
7
8
  :timeout
8
9
  ]
9
10
 
@@ -15,10 +16,23 @@ module Attune
15
16
 
16
17
  # FIXME
17
18
  attr_accessor :disabled
19
+ alias_method :disabled?, :disabled
18
20
 
19
21
  # Time (in seconds) to wait for requests to finish
20
22
  attr_accessor :timeout
21
23
 
24
+ # How to deal with HTTP exceptions
25
+ # @param [:mock, :raise] handler Method used for handling exceptions.
26
+ # @raise [ArgumentError] if handler is not :mock or :raise
27
+ attr_reader :exception_handler
28
+ def exception_handler= handler
29
+ if [:mock, :raise].include?(handler)
30
+ @exception_handler = handler
31
+ else
32
+ raise ArgumentError, "exception_handler must be :mock or :raise"
33
+ end
34
+ end
35
+
22
36
  # @example configure
23
37
  # Attune.configure do |c|
24
38
  # c.endpoint = "http://example.com:8080/"
@@ -1,4 +1,5 @@
1
1
  require 'attune/param_flattener'
2
+ require "attune/call_dropping"
2
3
  require "attune/json_logger"
3
4
 
4
5
  module Attune
@@ -12,6 +13,9 @@ module Attune
12
13
  # Needed for encoding of BATCH GET requests
13
14
  builder.use Attune::ParamFlattener
14
15
 
16
+ # Log all requests
17
+ builder.use Attune::CallDropping
18
+
15
19
  # Allow one retry per request
16
20
  builder.request :retry, 1
17
21
 
@@ -27,6 +31,7 @@ module Attune
27
31
  c.endpoint = ENDPOINT
28
32
  c.middleware = MIDDLEWARE
29
33
  c.disabled = false
34
+ c.exception_handler = :raise
30
35
  c.timeout = 1
31
36
  end
32
37
  end
@@ -1,3 +1,3 @@
1
1
  module Attune
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -0,0 +1,84 @@
1
+ require 'spec_helper'
2
+
3
+ describe Attune::CallDropping do
4
+ let(:stubs){ Faraday::Adapter::Test::Stubs.new }
5
+ let(:connection) do
6
+ Faraday.new(url: 'http://example.com/') do |builder|
7
+ builder.use described_class, storage
8
+ builder.adapter :test, stubs
9
+ end
10
+ end
11
+ let(:response){ connection.get("/") }
12
+ let(:timeout){ Faraday::Error::TimeoutError.new("simulated timeout") }
13
+
14
+ context "using default storage" do
15
+ let(:connection) do
16
+ Faraday.new(url: 'http://example.com/') do |builder|
17
+ builder.use described_class
18
+ builder.adapter :test, stubs
19
+ end
20
+ end
21
+ it "can be initialized" do
22
+ stubs.get("/"){ [200, {}, "foobar"] }
23
+ expect(response.body).to eq "foobar"
24
+ stubs.verify_stubbed_calls
25
+ end
26
+ end
27
+
28
+ context "without previous timeouts" do
29
+ let(:storage){ {} }
30
+ context "no timeout" do
31
+ it "runs request" do
32
+ stubs.get("/"){ [200, {}, "foobar"] }
33
+ expect(response.body).to eq "foobar"
34
+ expect(storage).to eq(skip_rate: 0.0)
35
+ stubs.verify_stubbed_calls
36
+ end
37
+ end
38
+
39
+ context "with a timeout" do
40
+ it "raises the timeout" do
41
+ stubs.get("/"){ raise timeout }
42
+ expect {
43
+ response
44
+ }.to raise_exception(Faraday::Error::TimeoutError)
45
+ expect(storage).to eq(skip_rate: 0.25)
46
+ stubs.verify_stubbed_calls
47
+ end
48
+ end
49
+ end
50
+
51
+ context "with an existing skip rate" do
52
+ let(:storage){ {skip_rate: 1.0} }
53
+ before{ described_class.any_instance.stub(rand: random) }
54
+ context "request skipped" do
55
+ let(:random){ 1 }
56
+ it "raises timeout" do
57
+ expect {
58
+ response
59
+ }.to raise_exception(Faraday::Error::TimeoutError)
60
+ expect(storage).to eq(skip_rate: 1.00)
61
+ end
62
+ end
63
+
64
+ context "request attempted" do
65
+ context "at max drop rate" do
66
+ let(:storage){ {skip_rate: 10.0} }
67
+ it "leaves drop rate unchanged" do
68
+ stubs.get("/"){ raise timeout }
69
+ expect { response }.to raise_exception(Faraday::Error::TimeoutError)
70
+ expect(storage).to eq(skip_rate: 10.0)
71
+ stubs.verify_stubbed_calls
72
+ end
73
+ end
74
+
75
+ let(:random){ 0 }
76
+ it "succeeds an reduces skip rate" do
77
+ stubs.get("/"){ [200, {}, "foobar"] }
78
+ expect(response.body).to eq "foobar"
79
+ expect(storage).to eq(skip_rate: 0.75)
80
+ stubs.verify_stubbed_calls
81
+ end
82
+ end
83
+ end
84
+ end
@@ -24,6 +24,63 @@ describe Attune::Client do
24
24
  end
25
25
  end
26
26
 
27
+ describe "API errors" do
28
+ it "will raise timeout" do
29
+ stubs.post("anonymous", %[{"user_agent":"Mozilla/5.0"}]){ raise Faraday::Error::TimeoutError.new("test") }
30
+ expect {
31
+ client.create_anonymous(user_agent: 'Mozilla/5.0')
32
+ }.to raise_exception(Faraday::Error::TimeoutError)
33
+ stubs.verify_stubbed_calls
34
+ end
35
+ it "will raise ConnectionFailed" do
36
+ stubs.post("anonymous", %[{"user_agent":"Mozilla/5.0"}]){ raise Faraday::Error::ConnectionFailed.new("test") }
37
+ expect {
38
+ client.create_anonymous(user_agent: 'Mozilla/5.0')
39
+ }.to raise_exception(Faraday::Error::ConnectionFailed)
40
+ stubs.verify_stubbed_calls
41
+ end
42
+ end
43
+
44
+ describe "disabled" do
45
+ context "with raise" do
46
+ let(:options){ {disabled: true, exception_handler: :raise} }
47
+ it "will raise DisalbedException" do
48
+ expect {
49
+ client.create_anonymous(user_agent: 'Mozilla/5.0')
50
+ }.to raise_exception(Attune::DisabledException)
51
+ end
52
+ end
53
+ context "with mock" do
54
+ let(:options){ {disabled: true, exception_handler: :mock} }
55
+ it "mocks create_anonymous with an id" do
56
+ result = client.create_anonymous(id: '12345', user_agent: 'Mozilla/5.0')
57
+ expect(result).to eq('12345')
58
+ end
59
+ it "mocks create_anonymous with no id" do
60
+ result = client.create_anonymous(user_agent: 'Mozilla/5.0')
61
+ expect(result).to match(/^[a-z0-9\-]+$/)
62
+ end
63
+ it "mocks get_rankings" do
64
+ result = client.get_rankings(
65
+ id: 'abcd123',
66
+ view: 'b/mens-pants',
67
+ collection: 'products',
68
+ entities: %w[1001, 1002, 1003, 1004]
69
+ )
70
+ expect(result).to eq %w[1001, 1002, 1003, 1004]
71
+ end
72
+ it "mocks multi_get_rankings" do
73
+ result = client.multi_get_rankings([
74
+ id: 'abcd123',
75
+ view: 'b/mens-pants',
76
+ collection: 'products',
77
+ entities: %w[1001, 1002, 1003, 1004]
78
+ ])
79
+ expect(result).to eq [%w[1001, 1002, 1003, 1004]]
80
+ end
81
+ end
82
+ end
83
+
27
84
  it "can create_anonymous generating an id" do
28
85
  stubs.post("anonymous", %[{"user_agent":"Mozilla/5.0"}]){ [200, {location: 'urn:id:abcd123'}, nil] }
29
86
  id = client.create_anonymous(user_agent: 'Mozilla/5.0')
data/spec/attune_spec.rb CHANGED
@@ -9,4 +9,18 @@ describe Attune do
9
9
  specify { expect(subject.endpoint).to eq 'http://localhost/' }
10
10
  specify { expect(subject.disabled).to eq false }
11
11
  end
12
+ describe 'configure' do
13
+ it "yields with Attune::Default" do
14
+ Attune.configure do |c|
15
+ expect(c).to be Attune::Default
16
+ end
17
+ end
18
+ end
19
+ describe 'test_mode' do
20
+ it "sets correct test options" do
21
+ expect(Attune::Default).to receive(:exception_handler=).with(:mock)
22
+ expect(Attune::Default).to receive(:disabled=).with(true)
23
+ Attune.test_mode!
24
+ end
25
+ end
12
26
  end
data/spec/spec_helper.rb CHANGED
@@ -1,3 +1,9 @@
1
+ require 'simplecov'
2
+ SimpleCov.start do
3
+ add_filter '/vendor'
4
+ add_filter '/spec'
5
+ end
6
+
1
7
  require 'attune'
2
8
 
3
9
  # This file was generated by the `rspec --init` command. Conventionally, all
metadata CHANGED
@@ -1,27 +1,27 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: attune
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Hawthorn
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-12-17 00:00:00.000000000 Z
11
+ date: 2013-12-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ! '>='
17
+ - - '>='
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ! '>='
24
+ - - '>='
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
@@ -42,56 +42,70 @@ dependencies:
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ! '>='
45
+ - - '>='
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ! '>='
52
+ - - '>='
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rspec
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ! '>='
59
+ - - '>='
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ! '>='
66
+ - - '>='
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: yard
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - ! '>='
73
+ - - '>='
74
74
  - !ruby/object:Gem::Version
75
75
  version: '0'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - ! '>='
80
+ - - '>='
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: redcarpet
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - ! '>='
87
+ - - '>='
88
88
  - !ruby/object:Gem::Version
89
89
  version: '0'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - ! '>='
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: simplecov
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
95
109
  - !ruby/object:Gem::Version
96
110
  version: '0'
97
111
  description: Client for the Attune product ranking API.
@@ -111,18 +125,20 @@ files:
111
125
  - Rakefile
112
126
  - attune.gemspec
113
127
  - lib/attune.rb
128
+ - lib/attune/call_dropping.rb
114
129
  - lib/attune/client.rb
115
130
  - lib/attune/configurable.rb
116
131
  - lib/attune/default.rb
117
132
  - lib/attune/json_logger.rb
118
133
  - lib/attune/param_flattener.rb
119
134
  - lib/attune/version.rb
135
+ - spec/attune/call_dropping_spec.rb
120
136
  - spec/attune/client_spec.rb
121
137
  - spec/attune/json_logger_spec.rb
122
138
  - spec/attune_spec.rb
123
139
  - spec/remote_spec.rb
124
140
  - spec/spec_helper.rb
125
- homepage: https://github.com/freerunningtech/attune-ruby
141
+ homepage: https://github.com/DigitalStripe/attune-ruby
126
142
  licenses:
127
143
  - MIT
128
144
  metadata: {}
@@ -132,21 +148,22 @@ require_paths:
132
148
  - lib
133
149
  required_ruby_version: !ruby/object:Gem::Requirement
134
150
  requirements:
135
- - - ! '>='
151
+ - - '>='
136
152
  - !ruby/object:Gem::Version
137
153
  version: '0'
138
154
  required_rubygems_version: !ruby/object:Gem::Requirement
139
155
  requirements:
140
- - - ! '>='
156
+ - - '>='
141
157
  - !ruby/object:Gem::Version
142
158
  version: '0'
143
159
  requirements: []
144
160
  rubyforge_project:
145
- rubygems_version: 2.1.9
161
+ rubygems_version: 2.1.11
146
162
  signing_key:
147
163
  specification_version: 4
148
164
  summary: Client for the Attune product ranking API.
149
165
  test_files:
166
+ - spec/attune/call_dropping_spec.rb
150
167
  - spec/attune/client_spec.rb
151
168
  - spec/attune/json_logger_spec.rb
152
169
  - spec/attune_spec.rb