skimlinks 1.0.0 → 1.0.1
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 +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
|
+
[](http://badge.fury.io/rb/skimlinks)
|
4
|
+
[](http://travis-ci.org/krautcomputing/skimlinks)
|
5
|
+
[](https://gemnasium.com/krautcomputing/skimlinks)
|
6
|
+
[](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
|