skimlinks 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE.txt +1 -1
- data/README.md +190 -2
- data/lib/skimlinks.rb +20 -0
- data/lib/skimlinks/client.rb +208 -0
- data/lib/skimlinks/merchant.rb +35 -0
- data/lib/skimlinks/merchant_search.rb +106 -0
- data/lib/skimlinks/product.rb +42 -0
- data/lib/skimlinks/product_search.rb +83 -0
- data/lib/skimlinks/search_helpers.rb +30 -0
- data/lib/skimlinks/version.rb +1 -1
- data/skimlinks.gemspec +12 -1
- data/spec/fixtures/vcr_cassettes/Skimlinks_MerchantSearch.yml +22026 -0
- data/spec/fixtures/vcr_cassettes/Skimlinks_ProductSearch.yml +4914 -0
- data/spec/skimlinks/client_spec.rb +87 -0
- data/spec/skimlinks/merchant_search_spec.rb +88 -0
- data/spec/skimlinks/merchant_spec.rb +4 -0
- data/spec/skimlinks/product_search_spec.rb +44 -0
- data/spec/skimlinks/product_spec.rb +4 -0
- data/spec/skimlinks/skimlinks_spec.rb +7 -0
- data/spec/spec_helper.rb +21 -0
- metadata +100 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5d176c89122667e84f50c2a3e4aadc7e7b308fec
|
4
|
+
data.tar.gz: 8f2a3b4dfedc721dd626adcd4f67659d5496d621
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bb8c3bafe621d4f58ceb6536350a40b390ab81ac347703d861931fa0d3ca652651545fedd379a3bfee8f0ed9be202dafe932be94cf63fcf6505fee678b907cc9
|
7
|
+
data.tar.gz: d5609beacaba2147aaa5d63c453b91576a226953430e958ead5e1b2cbf54117650d4e71bfef2a930322f1594be803b63d0763753c8b49a66e11e66dcf3626c7a
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -1,5 +1,193 @@
|
|
1
1
|
# Skimlinks
|
2
2
|
|
3
|
-
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/skimlinks.png)](http://badge.fury.io/rb/skimlinks)
|
4
|
+
[![Build Status](https://secure.travis-ci.org/krautcomputing/skimlinks.png)](http://travis-ci.org/krautcomputing/skimlinks)
|
5
|
+
[![Dependency Status](https://gemnasium.com/krautcomputing/skimlinks.png)](https://gemnasium.com/krautcomputing/skimlinks)
|
6
|
+
[![Code Climate](https://codeclimate.com/github/krautcomputing/skimlinks.png)](https://codeclimate.com/github/krautcomputing/skimlinks)
|
4
7
|
|
5
|
-
|
8
|
+
A simple wrapper around the [Skimlinks APIs](http://skimlinks.com/apis)
|
9
|
+
|
10
|
+
Read the accompanying [blog post](http://www.krautcomputing.com/blog/2013/01/11/new-gem-skimlinks/).
|
11
|
+
|
12
|
+
## Requirements
|
13
|
+
|
14
|
+
Requires Ruby 1.9.2 or higher
|
15
|
+
|
16
|
+
## Installation
|
17
|
+
|
18
|
+
Add this line to your application's Gemfile:
|
19
|
+
|
20
|
+
gem 'skimlinks'
|
21
|
+
|
22
|
+
And then execute:
|
23
|
+
|
24
|
+
$ bundle
|
25
|
+
|
26
|
+
Or install it yourself as:
|
27
|
+
|
28
|
+
$ gem install skimlinks
|
29
|
+
|
30
|
+
## APIs
|
31
|
+
|
32
|
+
Skimlinks offers the following APIs:
|
33
|
+
|
34
|
+
* The [Product API](http://api-products.skimlinks.com/doc/) to search for products and product categories
|
35
|
+
* The [Merchant API](http://api-merchants.skimlinks.com/doc/) to search for merchants and merchant categories
|
36
|
+
* The [Link API](http://go.redirectingat.com/doc/) to convert regular URLs into affiliate URLs
|
37
|
+
* The [Reporting API](https://api-reports.skimlinks.com/doc/) to receive a history of your earned commissions
|
38
|
+
|
39
|
+
This gem currently only implements access to the Product API and Merchant API.
|
40
|
+
|
41
|
+
## Usage
|
42
|
+
|
43
|
+
### Configuration
|
44
|
+
|
45
|
+
Add configurations to `config/initializers/skimlinks.rb`:
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
Skimlinks.configure do |config|
|
49
|
+
config.api_key = 'foobar' # Your API key (get it here: https://accounts.skimlinks.com/productapi) (mandatory)
|
50
|
+
config.cache = Rails.cache # Set to an instance of ActiveSupport::Cache::Store to cache the API requests. (optional, defaults to nil)
|
51
|
+
config.format = :json # Currently no other setting is supported. In the future it will be possible to set this to :xml to communicate with the API via XML. (optional, defaults to :json)
|
52
|
+
config.cache_ttl = 10.minutes # Set to higher/lower value to cache requests shorter/longer. (optional, defaults to 1 day)
|
53
|
+
end
|
54
|
+
```
|
55
|
+
|
56
|
+
### Product API
|
57
|
+
|
58
|
+
#### Get a list of product categories
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
Skimlinks::ProductSearch.new.categories
|
62
|
+
|
63
|
+
=> {
|
64
|
+
=> "Animals" => 1, # Category name => category ID
|
65
|
+
=> "Animals > Live Animals" => 2,
|
66
|
+
=> "Animals > Pet Supplies" => 3,
|
67
|
+
=> "Animals > Pet Supplies > Bird Supplies" => 4,
|
68
|
+
=> "Animals > Pet Supplies > Bird Supplies > Bird Cages & Stands" => 5,
|
69
|
+
=> "Animals > Pet Supplies > Bird Supplies > Bird Food" => 6,
|
70
|
+
=> ...
|
71
|
+
=> }
|
72
|
+
```
|
73
|
+
|
74
|
+
#### Get a nested list of product categories
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
Skimlinks::ProductSearch.new.nested_categories
|
78
|
+
|
79
|
+
=> {
|
80
|
+
=> "Animals" => {
|
81
|
+
=> "Live Animals" => nil,
|
82
|
+
=> "Pet Supplies" => {
|
83
|
+
=> "Bird Supplies" => {
|
84
|
+
=> "Bird Cages & Stands" => nil,
|
85
|
+
=> "Bird Food" => nil,
|
86
|
+
=> ...
|
87
|
+
=> }
|
88
|
+
=> }
|
89
|
+
=> }
|
90
|
+
=> }
|
91
|
+
```
|
92
|
+
|
93
|
+
#### Search for products
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
Skimlinks::ProductSearch.new(
|
97
|
+
query: 'justin bieber', # Search query (mandatory)
|
98
|
+
page: 1, # Page (optional, defaults to 1)
|
99
|
+
rows: 10, # Number of rows to return (optional, max. 300, defaults to 10)
|
100
|
+
min_price: 100, # Minimum price (including decimal digits, i.e. 100 = $1.00) (optional)
|
101
|
+
max_price: 500, # Maximum price (including decimal digits, i.e. 500 = $5.00) (optional)
|
102
|
+
country: 'uk', # Restrict search to products of a specific country (optional)
|
103
|
+
merchant_id: 8286, # Restrict search to products of a specific merchant (optional)
|
104
|
+
category: 'Toys & Games' # Restrict search to products of a specific category (optional)
|
105
|
+
).products
|
106
|
+
|
107
|
+
=> [
|
108
|
+
=> #<Skimlinks::Product:0x007ff694913fb0 @description="Justin Bieber (Boyfriend)", @name="Justin Bieber Boyfriend Poster", @currency="gbp", @product_id="8286|33428272", @id="8116651", @merchant_id=8286, @price=219, @url="http://www.play.com/Product.aspx?r=GADG&title=33428272", @category="Toys & Games", @image_urls=[#<URI::HTTP:0x007ff694910540 URL:http://images.productserve.com/preview/1418/569238291.jpg>], @country="UK", @merchant_name="Play.com">,
|
109
|
+
=> #<Skimlinks::Product:0x007ff694cb98e0 @description="Justin Bieber (Live) Poster", @name="Justin Bieber (Live) Poster", @currency="gbp", @product_id="8286|20468288", @id="7979941", @merchant_id=8286, @price=219, @url="http://www.play.com/Product.aspx?r=GADG&title=20468288", @category="Toys & Games", @image_urls=[#<URI::HTTP:0x007ff694cb9cc8 URL:http://images.productserve.com/preview/1418/153122801.jpg>], @country="UK", @merchant_name="Play.com">,
|
110
|
+
=> #<Skimlinks::Product:0x007ff694f60fd0 @description="Justin Bieber (Hoodie) Mini Poster", @name="Justin Bieber (Hoodie) Mini Poster", @currency="gbp", @product_id="8286|20418241", @id="7975531", @merchant_id=8286, @price=219, @url="http://www.play.com/Product.aspx?r=GADG&title=20418241", @category="Toys & Games", @image_urls=[#<URI::HTTP:0x007ff694f610c0 URL:http://images.productserve.com/preview/1418/148546541.jpg>], @country="UK", @merchant_name="Play.com">,
|
111
|
+
=> ...
|
112
|
+
=> ]
|
113
|
+
```
|
114
|
+
|
115
|
+
### Merchant API
|
116
|
+
|
117
|
+
#### Search for merchants
|
118
|
+
|
119
|
+
```ruby
|
120
|
+
Skimlinks::MerchantSearch.new(
|
121
|
+
category_ids: [1, 2, 3] # Return only merchants in the specificed categories (optional)
|
122
|
+
).merchants
|
123
|
+
|
124
|
+
=> [
|
125
|
+
=> #<Skimlinks::Merchant:0x00000103d33b60 @id=17738, @name="*NEW!* High Commission Payout!", @preferred={}, @updated_at=2012-12-16 01:02:00 +0100, @average_conversion_rate="0", @average_commission="0", @logo_url="http://s.skimresources.com/logos/17738.jpg", @domains={"9682"=>"mykegelsecret.com", "45143"=>"kegelmasters.com"}, @categories={"37"=>"health & beauty", "1"=>"adult & mature", "38"=>"health & beauty;cosmetics", "39"=>"health & beauty;health products"}, @countries=["united states"], @product_count=0>,
|
126
|
+
=> #<Skimlinks::Merchant:0x00000103cd7428 @id=41004, @name="Adultsextoys.com - A Huge Range Of Adult Products", @preferred={}, @updated_at=2012-12-16 01:02:00 +0100, @average_conversion_rate="0", @average_commission="0", @logo_url="http://s.skimresources.com/logos/41004.jpg", @domains={"40457"=>"adultsextoys.com.au"}, @categories={"1"=>"adult & mature"}, @countries=["australia"], @product_count=0>,
|
127
|
+
=> #<Skimlinks::Merchant:0x00000103cb2dd0 @id=68079, @name="Adultshop", @preferred={}, @updated_at=2012-12-16 01:02:00 +0100, @average_conversion_rate="0", @average_commission="0", @logo_url="http://s.skimresources.com/logos/68079.jpg", @domains={"68133"=>"shop.adultshop.de"}, @categories={"1"=>"adult & mature"}, @countries=["germany"], @product_count=0>,
|
128
|
+
=> ...
|
129
|
+
=> ]
|
130
|
+
```
|
131
|
+
|
132
|
+
#### Get a single merchant
|
133
|
+
|
134
|
+
```ruby
|
135
|
+
Skimlinks::MerchantSearch.new.merchant(
|
136
|
+
12678 # Merchant ID, get it from calling Skimlinks::MerchantSearch.merchants first (mandatory)
|
137
|
+
)
|
138
|
+
|
139
|
+
=> #<Skimlinks::Merchant:0x00000105204f30 @id=12678, @name="Amazon US", @preferred={"commission"=>"8.5% General products\r\n4% Electronics", "commissionDetails"=>"Was 6% --> NOW 8.5% General products!\r\nWas 3% --> NOW 4% Electronics!", "description"=>"Amazon.com is the global leader in e-commerce. They launch new product categories and stores around the world as it offers customers greater selection, lower prices, more in-stock merchandise, and a best-in-class shopping experience.", "ecpc"=>"0.00", "featured_commission"=>nil, "pp_enabled"=>"1"}, @updated_at=2012-12-07 01:02:00 +0100, @average_conversion_rate="5.42%", @average_commission="6.36%", @logo_url="http://s.skimresources.com/logos/12678.jpeg", @domains={"6309"=>"amazon.com", "47172"=>"wireless.amazon.com", "119814"=>"amazonsupply.com"}, @categories={"12"=>"consumer electronics;mobiles, pdas & satnav", "50"=>"phones, tv & broadband subscriptions", "8"=>"consumer electronics", "9"=>"consumer electronics;audio, tv & home theatre", "10"=>"consumer electronics;cameras & photos", "11"=>"consumer electronics;gadgets & geeks", "13"=>"consumer electronics;mp3 players & accessories", "18"=>"fashion & accessories", "19"=>"fashion & accessories;belts & bags", "20"=>"fashion & accessories;children's clothing", "21"=>"fashion & accessories;jewelry", "22"=>"fashion & accessories;lingerie & sleepwear", "23"=>"fashion & accessories;men's clothing", "24"=>"fashion & accessories;shoes", "25"=>"fashion & accessories;women's clothing", "33"=>"gifts", "34"=>"gifts;chocolate", "35"=>"gifts;flowers", "36"=>"gifts;novelty", "40"=>"home & garden", "41"=>"home & garden;bed & bath", "42"=>"home & garden;diy", "43"=>"home & garden;furniture & interior design", "44"=>"home & garden;garden", "45"=>"home & garden;home appliances", "37"=>"health & beauty", "38"=>"health & beauty;cosmetics", "39"=>"health & beauty;health products"}, @countries=["united states"], @product_count=0>
|
140
|
+
```
|
141
|
+
|
142
|
+
#### Get a list of merchant categories
|
143
|
+
|
144
|
+
```ruby
|
145
|
+
Skimlinks::MerchantSearch.new.categories
|
146
|
+
|
147
|
+
=> {
|
148
|
+
=> "adult & mature" => 1, # Category name => category ID
|
149
|
+
=> "arts, crafts & hobbies" => 2,
|
150
|
+
=> "automotive, cars & bikes" => 3,
|
151
|
+
=> "baby & parenting supplies" => 4,
|
152
|
+
=> "books & magazines" => 5,
|
153
|
+
=> "charities & non-profit" => 6,
|
154
|
+
=> "computers & software" => 7,
|
155
|
+
=> "consumer electronics" => 8,
|
156
|
+
=> "consumer electronics > audio, tv & home theatre" => 9,
|
157
|
+
=> "consumer electronics > cameras & photos" => 10,
|
158
|
+
=> ...
|
159
|
+
=> }
|
160
|
+
```
|
161
|
+
|
162
|
+
#### Get a nested list of merchant categories
|
163
|
+
|
164
|
+
```ruby
|
165
|
+
Skimlinks::MerchantSearch.new.nested_categories
|
166
|
+
|
167
|
+
=> {
|
168
|
+
=> "adult & mature" => nil,
|
169
|
+
=> "arts, crafts & hobbies" => nil,
|
170
|
+
=> "automotive, cars & bikes" => nil,
|
171
|
+
=> "baby & parenting supplies" => nil,
|
172
|
+
=> "books & magazines" => nil,
|
173
|
+
=> "charities & non-profit" => nil,
|
174
|
+
=> "computers & software" => nil,
|
175
|
+
=> "consumer electronics" => {
|
176
|
+
=> "audio, tv & home theatre" => nil,
|
177
|
+
=> "cameras & photos" => nil,
|
178
|
+
=> ...
|
179
|
+
=> }
|
180
|
+
=> }
|
181
|
+
```
|
182
|
+
|
183
|
+
## TODO
|
184
|
+
|
185
|
+
* Implement access to Link API and Reporting API
|
186
|
+
|
187
|
+
## Contributing
|
188
|
+
|
189
|
+
1. Fork it
|
190
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
191
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
192
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
193
|
+
5. Create new Pull Request
|
data/lib/skimlinks.rb
CHANGED
@@ -1,4 +1,24 @@
|
|
1
|
+
require 'gem_config'
|
2
|
+
require 'active_support/cache'
|
3
|
+
|
1
4
|
module Skimlinks
|
5
|
+
include GemConfig::Base
|
6
|
+
|
7
|
+
ApiError = Class.new(StandardError)
|
8
|
+
InvalidParameters = Class.new(StandardError)
|
9
|
+
|
10
|
+
with_configuration do
|
11
|
+
has :api_key, classes: String
|
12
|
+
has :format, values: :json, default: :json
|
13
|
+
has :cache, classes: ActiveSupport::Cache::Store
|
14
|
+
has :cache_ttl, classes: Numeric, default: 1.day
|
15
|
+
end
|
2
16
|
end
|
3
17
|
|
4
18
|
require 'skimlinks/version'
|
19
|
+
require 'skimlinks/client'
|
20
|
+
require 'skimlinks/merchant'
|
21
|
+
require 'skimlinks/product'
|
22
|
+
require 'skimlinks/search_helpers'
|
23
|
+
require 'skimlinks/merchant_search'
|
24
|
+
require 'skimlinks/product_search'
|
@@ -0,0 +1,208 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'rest-client'
|
3
|
+
require 'mechanize'
|
4
|
+
|
5
|
+
module Skimlinks
|
6
|
+
class Client
|
7
|
+
API_ENDPOINTS = {
|
8
|
+
product_api: 'http://api-product.skimlinks.com/',
|
9
|
+
merchant_api: 'http://api-merchants.skimlinks.com/merchants/',
|
10
|
+
link_api: 'http://go.productwidgets.com/'
|
11
|
+
}
|
12
|
+
|
13
|
+
attr_accessor *Skimlinks.configuration.rules.keys
|
14
|
+
|
15
|
+
def initialize(args = {})
|
16
|
+
config = Skimlinks.configuration.current.merge(args)
|
17
|
+
|
18
|
+
Skimlinks.configuration.rules.keys.each do |key|
|
19
|
+
self.send "#{key}=", config[key]
|
20
|
+
end
|
21
|
+
|
22
|
+
@product_api = RestClient::Resource.new(API_ENDPOINTS[:product_api])
|
23
|
+
@merchant_api = RestClient::Resource.new(API_ENDPOINTS[:merchant_api])
|
24
|
+
@mechanize = Mechanize.new do |m|
|
25
|
+
m.agent.redirect_ok = false
|
26
|
+
m.agent.http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Product API
|
31
|
+
|
32
|
+
def product_search(args)
|
33
|
+
product_count_and_products(args).last
|
34
|
+
end
|
35
|
+
|
36
|
+
def product_count(args)
|
37
|
+
product_count_and_products(args.merge(rows: 0)).first
|
38
|
+
end
|
39
|
+
|
40
|
+
def product_categories
|
41
|
+
@product_categories ||= product_api('categories')['skimlinksProductAPI']['categories']
|
42
|
+
end
|
43
|
+
|
44
|
+
# Merchant API
|
45
|
+
|
46
|
+
def merchant_categories
|
47
|
+
@merchant_categories ||= merchant_api('categories')
|
48
|
+
end
|
49
|
+
|
50
|
+
def merchant_category_ids
|
51
|
+
@merchant_category_ids ||= flatten(self.merchant_categories).grep(/^\d+$/).uniq.map(&:to_i)
|
52
|
+
end
|
53
|
+
|
54
|
+
def merchants_in_category(category_id)
|
55
|
+
[].tap do |merchants|
|
56
|
+
start, found = 0, nil
|
57
|
+
|
58
|
+
while found.nil? || start < found
|
59
|
+
data = merchant_api('category', category_id, 'limit', 200, 'start', start)
|
60
|
+
|
61
|
+
merchants.concat data['merchants'] if data['merchants'].present?
|
62
|
+
|
63
|
+
start = data['numStarted'].to_i + data['numReturned'].to_i
|
64
|
+
found = data['numFound']
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def merchant_search(query, preferred = false)
|
70
|
+
[].tap do |merchants|
|
71
|
+
start, found = 0, nil
|
72
|
+
|
73
|
+
while found.nil? || start < found
|
74
|
+
data = merchant_api('search', query, 'limit', 200, 'start', start, preferred ? '?filter_by=preferred' : nil)
|
75
|
+
|
76
|
+
merchants.concat data['merchants'] if data['merchants'].present?
|
77
|
+
|
78
|
+
start = data['numStarted'].to_i + data['numReturned'].to_i
|
79
|
+
found = data['numFound']
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Link API
|
85
|
+
|
86
|
+
def affiliate(url, publisher_id)
|
87
|
+
link_api url, publisher_id
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def flatten(object)
|
93
|
+
case object
|
94
|
+
when Hash
|
95
|
+
object.to_a.map { |v| flatten(v) }.flatten
|
96
|
+
when Array
|
97
|
+
object.flatten.map { |v| flatten(v) }
|
98
|
+
else
|
99
|
+
object
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def returning_json(&block)
|
104
|
+
JSON.parse block.call
|
105
|
+
end
|
106
|
+
|
107
|
+
def returning_xml(&block)
|
108
|
+
Nokogiri::XML block.call
|
109
|
+
end
|
110
|
+
|
111
|
+
def product_count_and_products(args)
|
112
|
+
api_query = []
|
113
|
+
api_query << %(id:(#{args[:ids].join(' ')})) if args[:ids].present?
|
114
|
+
api_query << %((#{%w(title description).map { |field| %(#{field}:"#{args[:query]}") }.join(' OR ')})) if args[:query].present?
|
115
|
+
api_query << %(price:[#{args[:min_price].presence || '*'} TO #{args[:max_price].presence || '*'}]) if args[:min_price].present? || args[:max_price].present?
|
116
|
+
api_query << %(categoryId:(#{args[:category_ids].join(' ')})) if args[:category_ids].present?
|
117
|
+
api_query << %(merchantId:"#{args[:merchant_id]}") if args[:merchant_id].present?
|
118
|
+
api_query << %(country:"#{args[:country]}") if args[:country].present?
|
119
|
+
api_query << %(currency:"#{args[:currency]}") if args[:currency].present?
|
120
|
+
|
121
|
+
# TODO: Check for categoryId 0, '' or nil, missing categoryId
|
122
|
+
|
123
|
+
query_params = {
|
124
|
+
q: api_query.join(' AND ')
|
125
|
+
}
|
126
|
+
query_params[:rows] = args[:rows] if args[:rows].present?
|
127
|
+
query_params[:start] = args[:start] if args[:start].present?
|
128
|
+
|
129
|
+
product_data = product_api('query', query_params)['skimlinksProductAPI']
|
130
|
+
|
131
|
+
[product_data['numFound'], product_data['products']]
|
132
|
+
end
|
133
|
+
|
134
|
+
def get(api, path, params = {})
|
135
|
+
raise Skimlinks::InvalidParameters, 'Only JSON format is supported right now.' unless Skimlinks.configuration.format == :json
|
136
|
+
|
137
|
+
do_get = lambda do
|
138
|
+
returning_json do
|
139
|
+
api[URI.escape(path)].get params: params
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
if Skimlinks.configuration.cache.nil?
|
144
|
+
do_get.call
|
145
|
+
else
|
146
|
+
cache_key = [
|
147
|
+
'skimlinks',
|
148
|
+
'api',
|
149
|
+
Digest::MD5.hexdigest(api.to_s + path + params.to_s)
|
150
|
+
].join(':')
|
151
|
+
cache_options = Skimlinks.configuration.cache_ttl > 0 ? { expires_in: Skimlinks.configuration.cache_ttl } : {}
|
152
|
+
Skimlinks.configuration.cache.fetch cache_key, cache_options do
|
153
|
+
do_get.call
|
154
|
+
end
|
155
|
+
end
|
156
|
+
rescue RestClient::Exception => e
|
157
|
+
message = [e.message].tap do |message_parts|
|
158
|
+
error = JSON.parse(e.response)['skimlinksProductAPI']['message'] rescue nil
|
159
|
+
message_parts << error if error.present?
|
160
|
+
end.join(' - ')
|
161
|
+
raise Skimlinks::ApiError, message
|
162
|
+
end
|
163
|
+
|
164
|
+
def product_api(method, params = {})
|
165
|
+
raise Skimlinks::InvalidParameters, 'API key not configured' if Skimlinks.configuration.api_key.blank?
|
166
|
+
|
167
|
+
params = params.reverse_merge(
|
168
|
+
format: Skimlinks.configuration.format,
|
169
|
+
key: Skimlinks.configuration.api_key
|
170
|
+
)
|
171
|
+
|
172
|
+
get(@product_api, method, params).tap do |response|
|
173
|
+
raise Skimlinks::InvalidParameters, 'API key is invalid' if response.is_a?(Array) && response.first =~ /^Invalid API key/
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def merchant_api(method, *params)
|
178
|
+
raise Skimlinks::InvalidParameters, 'API key not configured' if Skimlinks.configuration.api_key.blank?
|
179
|
+
|
180
|
+
path = [
|
181
|
+
Skimlinks.configuration.format,
|
182
|
+
Skimlinks.configuration.api_key,
|
183
|
+
method,
|
184
|
+
*params.compact
|
185
|
+
].join('/')
|
186
|
+
|
187
|
+
get(@merchant_api, path).tap do |response|
|
188
|
+
raise Skimlinks::InvalidParameters, 'API key is invalid' if response.is_a?(Array) && response.first =~ /^Invalid API key/
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def link_api(url, publisher_id)
|
193
|
+
query_params = { url: CGI.escape(url), id: publisher_id, xs: 1 }
|
194
|
+
path = [API_ENDPOINTS[:link_api], URI.encode_www_form(query_params)].join('?')
|
195
|
+
response = @mechanize.head(path)
|
196
|
+
|
197
|
+
raise Skimlinks::ApiError, "Unexpected response code: #{response.code}" unless response.code == '302'
|
198
|
+
|
199
|
+
redirect_location = response['location']
|
200
|
+
case redirect_location
|
201
|
+
when url, %r(http://www\.google\.com/search\?q=)
|
202
|
+
nil
|
203
|
+
else
|
204
|
+
redirect_location
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'active_support/hash_with_indifferent_access'
|
2
|
+
|
3
|
+
module Skimlinks
|
4
|
+
class Merchant
|
5
|
+
attr_accessor :id, :name, :preferred, :updated_at, :average_conversion_rate, :average_commission, :logo_url, :domains, :categories, :countries, :product_count
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def build_from_api_response(merchant_data)
|
9
|
+
merchant_data.map do |merchant|
|
10
|
+
self.new \
|
11
|
+
id: merchant['merchantID'].to_i,
|
12
|
+
name: merchant['merchantName'].presence,
|
13
|
+
preferred: HashWithIndifferentAccess.new(merchant['preferred']),
|
14
|
+
updated_at: merchant['dateUpdated'].present? ? Time.parse(merchant['dateUpdated']) : nil,
|
15
|
+
average_conversion_rate: merchant['averageConversionRate'].presence,
|
16
|
+
average_commission: merchant['averageCommission'].presence,
|
17
|
+
logo_url: merchant['logo'].presence,
|
18
|
+
domains: HashWithIndifferentAccess.new(merchant['domains']),
|
19
|
+
categories: HashWithIndifferentAccess.new(merchant['categories']),
|
20
|
+
countries: Array(merchant['countries'].presence)
|
21
|
+
end.sort_by(&:name)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(args = {})
|
26
|
+
args.each do |k, v|
|
27
|
+
self.send "#{k}=", v
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def preferred?
|
32
|
+
self.preferred.present?
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|