attune 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YmI0ZDQ2MjAxYWIyYzNiNDE0Y2E5YWI1YWFmMTUxMDcyYTNjZTc3Mw==
5
+ data.tar.gz: !binary |-
6
+ YmRkYWIzMTRhOGNlOTVlYzBhZDFjY2FkMGFkNDY2MGVlNWIzMGY2ZA==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ M2UyY2E5ZTE5MzAyNGY2NGIzNjQyNDEwYWU0ZTRiN2FkYWEwNDA5MjY3Yjg2
10
+ ZTQ3N2Q3ZGU3MzE0OWUzZWNiNmM2ZjUyMTQxMjU4NGJhMTA2MGUzYTI4ODk2
11
+ ODhhYmQ1MWQ0ZjM0MDA3ZDU4OWEyNDJjMDExYjY4ODk2MWE1MDk=
12
+ data.tar.gz: !binary |-
13
+ NWI4YmYxMDMxNWZkMWQxYTZjZTIyODQ1MjdiYjkxMWIwZjYzM2E1MDM3MDk1
14
+ M2Y5ZTU5YWQ4ODcwNTk4ZDQ0NWE0ZjA1NzMzOTI1MTQyMGQ0NzQ5MDE3MzFi
15
+ YjJmYzVlNTQxNmQ5ZjUyMWQyNzZkZjU5NDMxNjQxZTIzMjA5YTc=
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.yardopts ADDED
@@ -0,0 +1,2 @@
1
+ --markup-provider=redcarpet
2
+ --markup=markdown
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in attune.gemspec
4
+ gemspec
5
+ gem 'guard-rspec'
6
+ gem 'pry'
7
+ gem 'pry-rescue'
8
+ gem 'pry-stack_explorer'
data/Guardfile ADDED
@@ -0,0 +1,9 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard :rspec do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+ end
9
+
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 John Hawthorn
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,97 @@
1
+ # Attune
2
+
3
+ A client for the [Attune ranking API](http://attune.co/). Build using the excellent [faraday](https://github.com/lostisland/faraday) library.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'attune'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ ## Usage
16
+
17
+ ### Example rails usage
18
+
19
+ Requests are performed through a client object
20
+
21
+ ``` ruby
22
+ client = Attune::Client.new
23
+ ```
24
+
25
+ Visitors to the application should be tagged with an anonymous user id
26
+
27
+ ``` ruby
28
+ class ApplicationController
29
+ before_filter do
30
+ session[:attune_id] ||= attune_client.create_anonymous(user_agent: request.env["HTTP_USER_AGENT"])
31
+ end
32
+
33
+ private
34
+ def attune_client
35
+ @attune_client ||= Attune.client
36
+ end
37
+ end
38
+ ```
39
+
40
+ The user id can be bound to a customer id at login
41
+
42
+ ``` ruby
43
+ class SessionsController
44
+ # ...
45
+
46
+ def create
47
+ # ...
48
+ attune_client.bind(session[:attune_id], current_user.id)
49
+ end
50
+ end
51
+ ```
52
+
53
+ The client can then perform rankings
54
+
55
+ ``` ruby
56
+ class ProductsController
57
+ def index
58
+ @products = Product.all
59
+
60
+ ranking = attune_client.get_ranking(
61
+ id: session[:attune_id],
62
+ view: request.fullpath,
63
+ collection: 'products',
64
+ entities: @products.map(&:id)
65
+ )
66
+ @products.sort_by do |product|
67
+ ranking.index(product.id.to_s)
68
+ end
69
+ end
70
+ end
71
+ ```
72
+
73
+
74
+ ### Configuration
75
+
76
+ Attune can be configured globally
77
+
78
+ ``` ruby
79
+ Attune.configure do |c|
80
+ c.endpoint = "http://example.com/"
81
+ c.timeout = 5
82
+ end
83
+ ```
84
+
85
+ Settings can also be overridden on a client object
86
+
87
+ ``` ruby
88
+ client = Attune::Client.new(timeout: 2)
89
+ ```
90
+
91
+ ## Contributing
92
+
93
+ 1. Fork it ( http://github.com/freerunningtech/attune/fork )
94
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
95
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
96
+ 4. Push to the branch (`git push origin my-new-feature`)
97
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/attune.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'attune/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "attune"
8
+ spec.version = Attune::VERSION
9
+ spec.authors = ["John Hawthorn"]
10
+ spec.email = ["john@freerunningtechnologies.com"]
11
+ spec.summary = %q{Client for the Attune product ranking API.}
12
+ spec.description = %q{Client for the Attune product ranking API.}
13
+ spec.homepage = "https://github.com/freerunningtech/attune-ruby"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "faraday"
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.2"
24
+ spec.add_development_dependency "rake"
25
+ spec.add_development_dependency "rspec"
26
+ spec.add_development_dependency "yard"
27
+ spec.add_development_dependency "redcarpet"
28
+ end
@@ -0,0 +1,158 @@
1
+ require 'json'
2
+
3
+ module Attune
4
+
5
+ # Client for the attune
6
+ class Client
7
+ include Attune::Configurable
8
+
9
+ # Initializes a new Client
10
+ #
11
+ # @example
12
+ # client = Attune::Client.new(
13
+ # endpoint: "http://example.com:8080/",
14
+ # timeout: 10
15
+ # )
16
+ #
17
+ # @param [Hash] options Options for connection (see Attune::Configurable)
18
+ # @returns A new client object
19
+ def initialize(options={})
20
+ Attune::Configurable::KEYS.each do |key|
21
+ send("#{key}=", options[key] || Attune::Default.send(key))
22
+ end
23
+ end
24
+
25
+ # Create an anonymous tracked user
26
+ #
27
+ # @example Generate a new id (preferred)
28
+ # anonymous_id = client.create_anonymous(
29
+ # user_agent: 'Mozilla/5.0'
30
+ # )
31
+ # @example Create using an existing id
32
+ # client.create_anonymous(
33
+ # id: '0cddbc0-6114-11e3-949a-0800200c9a66',
34
+ # user_agent: 'Mozilla/5.0'
35
+ # )
36
+ # @param [Hash] options
37
+ # @option options [String] :id optional. An id will be generated if this is not provided
38
+ # @option options [String] :user_agent The user agent for the application used by the anonymous users
39
+ # @return id [String]
40
+ # @raise [ArgumentError] if user_agent is not provided
41
+ def create_anonymous(options)
42
+ raise ArgumentError, "user_agent required" unless options[:user_agent]
43
+ if id = options[:id]
44
+ response = put("anonymous/#{id}", {user_agent: options[:user_agent]})
45
+ id
46
+ else
47
+ response = post("anonymous", {user_agent: options[:user_agent]})
48
+ response[:location][/\Aurn:id:([a-z0-9\-]+)\Z/, 1]
49
+ end
50
+ end
51
+
52
+ # Returns all entities from the specified collection in order of the user's preference
53
+ #
54
+ # @example
55
+ # rankings = client.get_rankings(
56
+ # id: '0cddbc0-6114-11e3-949a-0800200c9a66',
57
+ # view: 'b/mens-pants',
58
+ # collection: 'products',
59
+ # entities: %w[1001, 1002, 1003, 1004]
60
+ # )
61
+ # @param [Hash] options
62
+ # @option options [String] :id The anonymous user id for whom to grab rankings
63
+ # @option options [String] :view The page or app URN on which the entities will be displayed
64
+ # @option options [String] :collection name of the collection of entities
65
+ # @option options [Array<String>] :entities entities to be ranked. These should be numeric strings or integers.
66
+ # @option options [String] :ip ip address of remote user. Used for geolocation (optional)
67
+ # @option options [String] :customer id of customer (optional)
68
+ # @return ranking [Array<String>] The entities in their ranked order
69
+ # @raise [ArgumentError] if required parameters are missing
70
+ def get_rankings(options)
71
+ qs = encoded_ranking_params(options)
72
+ response = get("rankings/#{qs}", customer: options.fetch(:customer, 'none'))
73
+ JSON.parse(response.body)['ranking']
74
+ end
75
+
76
+ # Get multiple rankings in one call
77
+ #
78
+ # @example
79
+ # rankings = client.get_rankings([
80
+ # {
81
+ # id: '0cddbc0-6114-11e3-949a-0800200c9a66',
82
+ # view: 'b/mens-pants',
83
+ # collection: 'products',
84
+ # entities: %w[1001, 1002, 1003, 1004]
85
+ # },
86
+ # {
87
+ # id: '0cddbc0-6114-11e3-949a-0800200c9a66',
88
+ # view: 'b/mens-pants',
89
+ # collection: 'products',
90
+ # entities: %w[2001, 2002, 2003, 2004]
91
+ # }
92
+ # ])
93
+ # @param [Array<Hash>] multi_options An array of options (see #get_rankings)
94
+ # @return [Array<Array<String>>] rankings
95
+ def multi_get_rankings(multi_options)
96
+ requests = multi_options.map do |options|
97
+ encoded_ranking_params(options)
98
+ end
99
+ response = get("rankings", ids: requests)
100
+ results = JSON.parse(response.body)['results']
101
+ results.values.map do |result|
102
+ result['ranking']
103
+ end
104
+ end
105
+
106
+ # Binds an anonymous user to a customer id
107
+ #
108
+ # @param [String] id The anonymous visitor to bind
109
+ # @param [String] customer_id The customer id to bind
110
+ # @example
111
+ # rankings = client.bind(
112
+ # '25892e17-80f6-415f-9c65-7395632f022',
113
+ # 'cd171f7c-560d-4a62-8d65-16b87419a58'
114
+ # )
115
+ def bind(id, customer_id)
116
+ put("bindings/anonymous=#{id}&customer=#{customer_id}")
117
+ true
118
+ end
119
+
120
+ private
121
+ def encoded_ranking_params(options)
122
+ params = {
123
+ anonymous: options.fetch(:id),
124
+ view: options.fetch(:view),
125
+ entity_collection: options.fetch(:collection),
126
+ entities: options.fetch(:entities).join(','),
127
+ ip: options.fetch(:ip, 'none')
128
+ }
129
+ Faraday::Utils::ParamsHash[params].to_query
130
+ end
131
+
132
+ def get(path, params={})
133
+ adapter.get(path, params)
134
+ rescue Faraday::ClientError => e
135
+ handle_exception(e)
136
+ end
137
+
138
+ def put(path, params={})
139
+ adapter.put(path, ::JSON.dump(params))
140
+ rescue Faraday::ClientError => e
141
+ handle_exception(e)
142
+ end
143
+
144
+ def post(path, params={})
145
+ adapter.post(path, ::JSON.dump(params))
146
+ rescue Faraday::ClientError => e
147
+ handle_exception(e)
148
+ end
149
+
150
+ def handle_exception e
151
+ raise e
152
+ end
153
+
154
+ def adapter
155
+ Faraday.new(url: endpoint, builder: middleware, request: {timeout: timeout})
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,31 @@
1
+ module Attune
2
+ module Configurable
3
+ KEYS = [
4
+ :endpoint,
5
+ :middleware,
6
+ :disabled,
7
+ :timeout
8
+ ]
9
+
10
+ # The HTTP endpoint to connect to
11
+ attr_accessor :endpoint
12
+
13
+ # Middleware used by faraday
14
+ attr_accessor :middleware
15
+
16
+ # FIXME
17
+ attr_accessor :disabled
18
+
19
+ # Time (in seconds) to wait for requests to finish
20
+ attr_accessor :timeout
21
+
22
+ # @example configure
23
+ # Attune.configure do |c|
24
+ # c.endpoint = "http://example.com:8080/"
25
+ # c.timeout = 5
26
+ # end
27
+ def configure
28
+ yield self
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,33 @@
1
+ require 'attune/param_flattener'
2
+ require "attune/json_logger"
3
+
4
+ module Attune
5
+ # Default options
6
+ module Default
7
+ extend Configurable
8
+
9
+ ENDPOINT = "http://localhost/".freeze
10
+
11
+ MIDDLEWARE = Faraday::Builder.new do |builder|
12
+ # Needed for encoding of BATCH GET requests
13
+ builder.use Attune::ParamFlattener
14
+
15
+ # Allow one retry per request
16
+ builder.request :retry, 1
17
+
18
+ # Log all requests
19
+ builder.use Attune::JsonLogger
20
+
21
+ # Raise exceptions for HTTP 4xx/5xx
22
+ builder.response :raise_error
23
+ builder.adapter Faraday.default_adapter
24
+ end
25
+
26
+ configure do |c|
27
+ c.endpoint = ENDPOINT
28
+ c.middleware = MIDDLEWARE
29
+ c.disabled = false
30
+ c.timeout = 1
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,38 @@
1
+ require 'benchmark'
2
+ require 'securerandom'
3
+ require 'logger'
4
+
5
+ module Attune
6
+ class JsonLogger < Faraday::Middleware
7
+ def initialize app, logger=nil
8
+ super(app)
9
+ @logger = logger || Logger.new(STDERR)
10
+ end
11
+ def call(env)
12
+ time = (Time.now.to_f * 1000).to_i
13
+ response = nil
14
+ elapsed_time = Benchmark.realtime do
15
+ response = @app.call(env)
16
+ end
17
+ log(
18
+ ref: nil,
19
+ v: 1,
20
+ protocol: env[:url].scheme,
21
+ host: env[:url].host,
22
+ path: env[:url].path,
23
+ t: time,
24
+ r_id: SecureRandom.uuid,
25
+ status: response.status,
26
+ ua: env[:request_headers]['User-Agent'],
27
+ method: env[:method],
28
+ perf: {
29
+ total: elapsed_time * 1000
30
+ }
31
+ )
32
+ response
33
+ end
34
+ def log(data)
35
+ @logger.info JSON.dump(data)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,17 @@
1
+ module Attune
2
+ # Faraday 0.8 can't make resuests like /?ids=123&ids=456 and forces the form
3
+ # /?ids[]=123&ids[]=456 (this is fixed in faraday 0.9)
4
+ #
5
+ # Fortunately faraday's middleware is quite powerful. This just strips the
6
+ # array syntax from the request.
7
+ class ParamFlattener < Faraday::Middleware
8
+ def call(env)
9
+ url = env[:url]
10
+
11
+ # replaces ?foo[]=123 with ?foo=123
12
+ url.query = url.query.gsub('%5B%5D', '') if url.query
13
+
14
+ @app.call(env)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ module Attune
2
+ VERSION = "0.0.1"
3
+ end
data/lib/attune.rb ADDED
@@ -0,0 +1,12 @@
1
+ require 'faraday'
2
+
3
+ require "attune/version"
4
+ require "attune/configurable"
5
+ require "attune/default"
6
+ require "attune/client"
7
+
8
+ module Attune
9
+ def self.client
10
+ Client.new
11
+ end
12
+ end
@@ -0,0 +1,87 @@
1
+ require 'spec_helper'
2
+
3
+ describe Attune::Client do
4
+ let(:client){ described_class.new(options) }
5
+ subject { client }
6
+
7
+ context "with defaults" do
8
+ let(:options){ {} }
9
+ specify { expect(subject.endpoint).to eq(Attune::Default::ENDPOINT) }
10
+ specify { expect(subject.middleware).to eq(Attune::Default::MIDDLEWARE) }
11
+ end
12
+ context "with custom endpoint" do
13
+ let(:endpoint){ 'http://example.com/' }
14
+ let(:options){ {endpoint: endpoint} }
15
+ specify { expect(subject.endpoint).to eq(endpoint) }
16
+ end
17
+
18
+ let(:options){ {endpoint: 'http://example.com:8080/', middleware: middleware} }
19
+ let(:stubs) { Faraday::Adapter::Test::Stubs.new }
20
+ let(:middleware) do
21
+ Faraday::Builder.new do |builder|
22
+ builder.use Attune::ParamFlattener
23
+ builder.adapter :test, stubs
24
+ end
25
+ end
26
+
27
+ it "can create_anonymous generating an id" do
28
+ stubs.post("anonymous", %[{"user_agent":"Mozilla/5.0"}]){ [200, {location: 'urn:id:abcd123'}, nil] }
29
+ id = client.create_anonymous(user_agent: 'Mozilla/5.0')
30
+ stubs.verify_stubbed_calls
31
+
32
+ expect(id).to eq('abcd123')
33
+ end
34
+
35
+ it "can bind" do
36
+ stubs.put("bindings/anonymous=abcd123&customer=foobar"){ [200, {}, nil] }
37
+ client.bind('abcd123', 'foobar')
38
+ stubs.verify_stubbed_calls
39
+ end
40
+
41
+ it "can create_anonymous using existing id" do
42
+ stubs.put("anonymous/abcd123", %[{"user_agent":"Mozilla/5.0"}]){ [200, {}, nil] }
43
+ id = client.create_anonymous(id: 'abcd123', user_agent: 'Mozilla/5.0')
44
+ stubs.verify_stubbed_calls
45
+
46
+ expect(id).to eq('abcd123')
47
+ end
48
+
49
+ it "can get_rankings" do
50
+ stubs.get("rankings/anonymous=abcd123&view=b%2Fmens-pants&entity_collection=products&entities=1001%2C%2C1002%2C%2C1003%2C%2C1004&ip=none"){ [200, {}, %[{"ranking":["1004","1003","1002","1001"]}]] }
51
+ rankings = client.get_rankings(
52
+ id: 'abcd123',
53
+ view: 'b/mens-pants',
54
+ collection: 'products',
55
+ entities: %w[1001, 1002, 1003, 1004]
56
+ )
57
+ stubs.verify_stubbed_calls
58
+
59
+ expect(rankings).to eq(%W[1004 1003 1002 1001])
60
+ end
61
+
62
+ it "can multi_get_rankings" do
63
+ stubs.get("/rankings?ids=anonymous%3D0cddbc0-6114-11e3-949a-0800200c9a66%26view%3Db%252Fmens-pants%26entity_collection%3Dproducts%26entities%3D1001%252C%252C1002%252C%252C1003%252C%252C1004%26ip%3Dnone&ids=anonymous%3D0cddbc0-6114-11e3-949a-0800200c9a66%26view%3Db%252Fmens-pants%26entity_collection%3Dproducts%26entities%3D2001%252C%252C2002%252C%252C2003%252C%252C2004%26ip%3Dnone") do
64
+ [200, {}, %[{"results":{"fake0":{"ranking":["1004","1003","1002","1001"]},"fake1":{"ranking":["2004","2003","2002","2001"]}}}]]
65
+ end
66
+ rankings = client.multi_get_rankings([
67
+ {
68
+ id: '0cddbc0-6114-11e3-949a-0800200c9a66',
69
+ view: 'b/mens-pants',
70
+ collection: 'products',
71
+ entities: %w[1001, 1002, 1003, 1004]
72
+ },
73
+ {
74
+ id: '0cddbc0-6114-11e3-949a-0800200c9a66',
75
+ view: 'b/mens-pants',
76
+ collection: 'products',
77
+ entities: %w[2001, 2002, 2003, 2004]
78
+ }
79
+ ])
80
+ stubs.verify_stubbed_calls
81
+
82
+ expect(rankings).to eq [
83
+ %W[1004 1003 1002 1001],
84
+ %W[2004 2003 2002 2001]
85
+ ]
86
+ end
87
+ end
@@ -0,0 +1,29 @@
1
+ require 'pry'
2
+ require 'spec_helper'
3
+
4
+ describe Attune::JsonLogger do
5
+ let(:logger){ double(:logger) }
6
+ let(:connection) do
7
+ Faraday.new(url: 'http://example.com/') do |builder|
8
+ builder.use described_class, logger
9
+ builder.adapter :test do |stubs|
10
+ stubs.get("/test"){ [200, {}, "foobar"] }
11
+ end
12
+ end
13
+ end
14
+
15
+ it "logs as expected" do
16
+ logged = ""
17
+ logger.stub(:info){|s| logged << s }
18
+
19
+ Benchmark.stub(:realtime).and_yield.and_return(0.12)
20
+ SecureRandom.stub(:uuid).and_return("eaa45af2-efc3-45ef-90da-9bcb56758e77")
21
+ Time.stub(:now).and_return(12345)
22
+
23
+ response = connection.get("/test")
24
+ response.status.should == 200
25
+ response.body.should == "foobar"
26
+
27
+ expect(logged).to eq %[{"ref":null,"v":1,"protocol":"http","host":"example.com","path":"/test","t":12345000,"r_id":"eaa45af2-efc3-45ef-90da-9bcb56758e77","status":200,"ua":"Faraday v0.8.8","method":"get","perf":{"total":120.0}}]
28
+ end
29
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+ describe Attune do
3
+ describe 'client' do
4
+ subject { Attune.client }
5
+ it{ should be_a Attune::Client }
6
+ end
7
+ describe 'defaults' do
8
+ subject { Attune::Default }
9
+ specify { expect(subject.endpoint).to eq 'http://localhost/' }
10
+ specify { expect(subject.disabled).to eq false }
11
+ end
12
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ describe "remote requests" do
4
+ let(:endpoint){ ENV['REMOTE_ENDPOINT'] }
5
+ before { pending "REMOTE_ENDPOINT required for remote spec" unless endpoint }
6
+ let!(:client){ Attune::Client.new(endpoint: endpoint) }
7
+
8
+ it "can create an anonymous user" do
9
+ id = client.create_anonymous(user_agent: 'Mozilla/5.0')
10
+ id.should =~ /[a-z0-9\-]+/
11
+ end
12
+
13
+ it "can create an anonymous user with an id" do
14
+ id = client.create_anonymous(id: '123456', user_agent: 'Mozilla/5.0')
15
+ id.should == '123456'
16
+ end
17
+
18
+ it "can bind an anonymous user" do
19
+ id = client.create_anonymous(id: '123456', user_agent: 'Mozilla/5.0')
20
+ client.bind(id, '654321')
21
+ end
22
+
23
+ describe "get_rankings" do
24
+ let(:entities){ [202875,202876,202874,202900,202902,202898,202905,200182,200181,185940,188447,185932,190589,1238689589] }
25
+ it "can get rankings" do
26
+ id = client.create_anonymous(id: '123456', user_agent: 'Mozilla/5.0')
27
+ client.bind(id, '654321')
28
+ result = client.get_rankings(id: '123456', view: 'b/mens-pants', collection: 'products', entities: entities)
29
+ result.should be_an Array
30
+ result.sort.should == entities.map(&:to_s).sort
31
+ end
32
+
33
+ it "can batch get rankings" do
34
+ id = client.create_anonymous(id: '123456', user_agent: 'Mozilla/5.0')
35
+ client.bind(id, '654321')
36
+ results = client.multi_get_rankings([id: '123456', view: 'b/mens-pants', collection: 'products', entities: entities])
37
+ results.should be_an Array
38
+
39
+ result, = *results
40
+ result.sort.should == entities.map(&:to_s).sort
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,19 @@
1
+ require 'attune'
2
+
3
+ # This file was generated by the `rspec --init` command. Conventionally, all
4
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
5
+ # Require this file using `require "spec_helper"` to ensure that it is only
6
+ # loaded once.
7
+ #
8
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
9
+ RSpec.configure do |config|
10
+ config.treat_symbols_as_metadata_keys_with_true_values = true
11
+ config.run_all_when_everything_filtered = true
12
+ config.filter_run :focus
13
+
14
+ # Run specs in random order to surface order dependencies. If you find an
15
+ # order dependency and want to debug it, you can fix the order by providing
16
+ # the seed, which is printed after each run.
17
+ # --seed 1234
18
+ config.order = 'random'
19
+ end
metadata ADDED
@@ -0,0 +1,155 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: attune
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - John Hawthorn
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-12-17 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.2'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '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: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: yard
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: redcarpet
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ! '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: Client for the Attune product ranking API.
98
+ email:
99
+ - john@freerunningtechnologies.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - .gitignore
105
+ - .rspec
106
+ - .yardopts
107
+ - Gemfile
108
+ - Guardfile
109
+ - LICENSE.txt
110
+ - README.md
111
+ - Rakefile
112
+ - attune.gemspec
113
+ - lib/attune.rb
114
+ - lib/attune/client.rb
115
+ - lib/attune/configurable.rb
116
+ - lib/attune/default.rb
117
+ - lib/attune/json_logger.rb
118
+ - lib/attune/param_flattener.rb
119
+ - lib/attune/version.rb
120
+ - spec/attune/client_spec.rb
121
+ - spec/attune/json_logger_spec.rb
122
+ - spec/attune_spec.rb
123
+ - spec/remote_spec.rb
124
+ - spec/spec_helper.rb
125
+ homepage: https://github.com/freerunningtech/attune-ruby
126
+ licenses:
127
+ - MIT
128
+ metadata: {}
129
+ post_install_message:
130
+ rdoc_options: []
131
+ require_paths:
132
+ - lib
133
+ required_ruby_version: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ! '>='
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ required_rubygems_version: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - ! '>='
141
+ - !ruby/object:Gem::Version
142
+ version: '0'
143
+ requirements: []
144
+ rubyforge_project:
145
+ rubygems_version: 2.1.9
146
+ signing_key:
147
+ specification_version: 4
148
+ summary: Client for the Attune product ranking API.
149
+ test_files:
150
+ - spec/attune/client_spec.rb
151
+ - spec/attune/json_logger_spec.rb
152
+ - spec/attune_spec.rb
153
+ - spec/remote_spec.rb
154
+ - spec/spec_helper.rb
155
+ has_rdoc: