emporium 0.0.3.alpha → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +37 -2
- data/emporium.gemspec +3 -2
- data/lib/emporium/product.rb +12 -10
- data/lib/emporium/services/amazon.rb +31 -46
- data/lib/emporium/services/google.rb +45 -0
- data/lib/emporium/services/options.rb +31 -0
- data/lib/emporium/services/utilities.rb +29 -0
- data/lib/emporium/services.rb +3 -4
- data/lib/emporium/version.rb +1 -1
- data/spec/emporium/emporium_spec.rb +60 -18
- data/spec/spec_helper.rb +2 -0
- metadata +23 -9
data/README.md
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
Automatic identification and data capture library to read UPC/EAN and get as much information as it possibly can.
|
4
4
|
|
5
|
+
|
5
6
|
## Installation
|
6
7
|
|
7
8
|
Add to your Gemfile and run the `bundle` command to install it.
|
@@ -12,8 +13,11 @@ Add to your Gemfile and run the `bundle` command to install it.
|
|
12
13
|
|
13
14
|
**Requires Ruby 1.9.2 or later.**
|
14
15
|
|
16
|
+
|
15
17
|
## Configuration
|
16
18
|
|
19
|
+
### Amazon
|
20
|
+
|
17
21
|
```ruby
|
18
22
|
service = Emporium::Services::Amazon.configuration do |config|
|
19
23
|
config.access_key = "access_key"
|
@@ -22,6 +26,15 @@ Add to your Gemfile and run the `bundle` command to install it.
|
|
22
26
|
end
|
23
27
|
```
|
24
28
|
|
29
|
+
### Google
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
service = Emporium::Services::Google.configuration do |config|
|
33
|
+
config.access_key = "access_key"
|
34
|
+
config.cme = "cse" # custom search engine
|
35
|
+
end
|
36
|
+
```
|
37
|
+
|
25
38
|
|
26
39
|
## Usage
|
27
40
|
|
@@ -36,7 +49,29 @@ Give it a UPC to fetch a product object. The Code only takes UPC-A digits
|
|
36
49
|
```
|
37
50
|
|
38
51
|
|
39
|
-
|
40
52
|
## Development
|
41
53
|
|
42
|
-
|
54
|
+
Questions or problems? Please post them on the [issue tracker](https://github.com/hugobast/emporium/issues).
|
55
|
+
|
56
|
+
You can contribute changes by forking the project and submitting a pull request. You can ensure the tests passing by running `bundle` and `rake`. Create a yml file under the spec folder that looks like this first:
|
57
|
+
|
58
|
+
```yaml
|
59
|
+
amazon:
|
60
|
+
secret: <secret>
|
61
|
+
access_key: <access_key>
|
62
|
+
associate_tag: <associate_tag>
|
63
|
+
|
64
|
+
google:
|
65
|
+
access_key: <access_key>
|
66
|
+
cse: <custom_search_engine>
|
67
|
+
```
|
68
|
+
|
69
|
+
## Licence
|
70
|
+
|
71
|
+
Copyright (c) 2011 Hugo Bastien
|
72
|
+
|
73
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
74
|
+
|
75
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
76
|
+
|
77
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/emporium.gemspec
CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |s|
|
|
8
8
|
s.authors = ["Hugo Bastien"]
|
9
9
|
s.email = ["hugobast@gmail.com"]
|
10
10
|
s.homepage = "https://github.com/hugobast/emporium"
|
11
|
-
s.summary = %q{Emporium fetches information about a product from it's UPC}
|
12
|
-
s.description = %q{
|
11
|
+
s.summary = %q{Emporium fetches information about a product from it's UPC (GTIN-12)}
|
12
|
+
s.description = %q{Uses Amazon's product advertising API or Google's Shopping Search API}
|
13
13
|
|
14
14
|
s.rubyforge_project = "emporium"
|
15
15
|
|
@@ -20,6 +20,7 @@ Gem::Specification.new do |s|
|
|
20
20
|
|
21
21
|
s.add_dependency 'nokogiri'
|
22
22
|
s.add_dependency 'ruby-hmac'
|
23
|
+
s.add_dependency 'json'
|
23
24
|
|
24
25
|
s.add_development_dependency "rspec"
|
25
26
|
end
|
data/lib/emporium/product.rb
CHANGED
@@ -9,13 +9,22 @@ module Emporium
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def fetch!
|
12
|
+
@service.response
|
12
13
|
create @service.response
|
13
14
|
end
|
14
15
|
|
15
|
-
def use(service)
|
16
|
-
|
16
|
+
def use(service, options={})
|
17
|
+
options.merge!(code: @code.value)
|
18
|
+
@service = service.new(options)
|
17
19
|
end
|
18
|
-
|
20
|
+
|
21
|
+
private
|
22
|
+
def create(hash)
|
23
|
+
hash.each do |key, value|
|
24
|
+
self.instance_variable_set("@#{key.to_s}", value)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
19
28
|
def method_missing(name, *args)
|
20
29
|
if self.instance_variables.include? :"@#{name}"
|
21
30
|
self.instance_variable_get("@#{name}")
|
@@ -23,12 +32,5 @@ module Emporium
|
|
23
32
|
super
|
24
33
|
end
|
25
34
|
end
|
26
|
-
|
27
|
-
private
|
28
|
-
def create(response)
|
29
|
-
response.search("ItemAttributes").children.each do |value|
|
30
|
-
self.instance_variable_set("@#{value.name.downcase}", value.content)
|
31
|
-
end
|
32
|
-
end
|
33
35
|
end
|
34
36
|
end
|
@@ -1,72 +1,57 @@
|
|
1
|
+
require 'hmac'
|
2
|
+
require 'hmac-sha2'
|
3
|
+
require 'base64'
|
4
|
+
require 'nokogiri'
|
5
|
+
|
1
6
|
module Emporium
|
2
7
|
module Services
|
3
8
|
class Amazon
|
9
|
+
include Emporium::Services::Options
|
10
|
+
|
11
|
+
service_attr_accessor :access_key, :secret, :associate_tag
|
12
|
+
|
4
13
|
def initialize(options={})
|
5
14
|
@options = options
|
6
15
|
end
|
7
16
|
|
8
17
|
def response
|
9
|
-
|
10
|
-
message = res.search('Message')
|
11
|
-
raise message.children.first.content unless message.empty?
|
12
|
-
res
|
18
|
+
attributes
|
13
19
|
end
|
14
20
|
|
15
21
|
private
|
16
|
-
|
17
|
-
{
|
18
|
-
"Service" => "AWSECommerceService",
|
19
|
-
"Operation" => "ItemLookup",
|
20
|
-
"IdType" => @options[:type] || "UPC",
|
21
|
-
"ItemId" => @options[:code],
|
22
|
-
"SearchIndex" => @options[:search_index] || "All",
|
23
|
-
"ResponseGroup" => @options[:response_group] || "Medium",
|
24
|
-
"Version" => @options[:version] || "2011-08-01",
|
25
|
-
"AssociateTag" => @@associate_tag,
|
26
|
-
"Timestamp" => Time.now.iso8601,
|
27
|
-
"AWSAccessKeyId" => @@access_key
|
28
|
-
}
|
29
|
-
end
|
22
|
+
include Emporium::Services::Utilities
|
30
23
|
|
31
|
-
def
|
32
|
-
|
24
|
+
def attributes
|
25
|
+
response = ::Nokogiri::XML(open("http://webservices.amazon.com/onca/xml?#{signed_query}"))
|
26
|
+
message = response.search('Message')
|
27
|
+
raise message.children.first.content unless message.empty?
|
28
|
+
hash_from_xml response.search('ItemAttributes')
|
33
29
|
end
|
34
30
|
|
35
31
|
def signed_query
|
36
32
|
digest = HMAC::SHA256.digest(@@secret, request)
|
37
33
|
signature = Base64.encode64(digest).chomp
|
38
|
-
|
39
|
-
end
|
40
|
-
|
41
|
-
def encode(value)
|
42
|
-
CGI.escape(value).gsub("%7E", "~").gsub("+", "%20")
|
34
|
+
query(params.merge "Signature" => signature)
|
43
35
|
end
|
44
36
|
|
45
37
|
def request
|
46
|
-
"GET\nwebservices.amazon.com\n/onca/xml\n#{
|
38
|
+
"GET\nwebservices.amazon.com\n/onca/xml\n#{query(params)}"
|
47
39
|
end
|
48
|
-
class << self
|
49
|
-
|
50
|
-
def configuration
|
51
|
-
yield self
|
52
|
-
end
|
53
|
-
|
54
|
-
def class_option(*symbols)
|
55
|
-
symbols.each do |symbol|
|
56
|
-
class_eval(<<-EOS)
|
57
|
-
def self.#{symbol}
|
58
|
-
@@#{symbol}
|
59
|
-
end
|
60
40
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
41
|
+
def params
|
42
|
+
{
|
43
|
+
'Service' => 'AWSECommerceService',
|
44
|
+
'Operation' => 'ItemLookup',
|
45
|
+
'IdType' => @options[:type] || 'UPC',
|
46
|
+
'ItemId' => @options[:code],
|
47
|
+
'SearchIndex' => @options[:search_index] || 'All',
|
48
|
+
'ResponseGroup' => @options[:response_group] || 'Medium',
|
49
|
+
'Version' => @options[:version] || '2011-08-01',
|
50
|
+
'AssociateTag' => @@associate_tag,
|
51
|
+
'Timestamp' => Time.now.iso8601,
|
52
|
+
'AWSAccessKeyId' => @@access_key
|
53
|
+
}
|
67
54
|
end
|
68
|
-
|
69
|
-
class_option :access_key, :secret, :associate_tag
|
70
55
|
end
|
71
56
|
end
|
72
57
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Emporium
|
4
|
+
module Services
|
5
|
+
class Google
|
6
|
+
include Emporium::Services::Options
|
7
|
+
service_attr_accessor :access_key, :cse
|
8
|
+
|
9
|
+
def initialize(options={})
|
10
|
+
@options = options
|
11
|
+
end
|
12
|
+
|
13
|
+
def response
|
14
|
+
attributes
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
include Emporium::Services::Utilities
|
19
|
+
|
20
|
+
def attributes
|
21
|
+
response = open(request)
|
22
|
+
hash = hash_from_json(response.read)
|
23
|
+
raise message unless hash["totalItems"] > 0
|
24
|
+
hash["items"][0]["product"]
|
25
|
+
end
|
26
|
+
|
27
|
+
def message
|
28
|
+
"q=#{@options[:code]} generated no results"
|
29
|
+
end
|
30
|
+
|
31
|
+
def request
|
32
|
+
"https://www.googleapis.com/shopping/search/v1/public/products?#{query(params)}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def params
|
36
|
+
{
|
37
|
+
'q' => @options[:code],
|
38
|
+
'alt' => @options[:alt] || 'json',
|
39
|
+
'country' => @options[:country] || 'US',
|
40
|
+
'key' => @@access_key
|
41
|
+
}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Emporium
|
2
|
+
module Services
|
3
|
+
|
4
|
+
module Options
|
5
|
+
def self.included(base)
|
6
|
+
base.extend ClassMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def configuration
|
11
|
+
yield self
|
12
|
+
end
|
13
|
+
|
14
|
+
def service_attr_accessor(*symbols)
|
15
|
+
symbols.each do |symbol|
|
16
|
+
class_eval(<<-EOS)
|
17
|
+
def self.#{symbol}
|
18
|
+
@@#{symbol}
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.#{symbol}=(value)
|
22
|
+
@@#{symbol} = value
|
23
|
+
end
|
24
|
+
EOS
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Emporium
|
2
|
+
module Services
|
3
|
+
module Utilities
|
4
|
+
def hash_from_xml(xml)
|
5
|
+
# Only the first level elements for now
|
6
|
+
|
7
|
+
result_hash = {}
|
8
|
+
xml.children.each do |value|
|
9
|
+
result_hash[value.name.downcase.to_sym] = value.content
|
10
|
+
end
|
11
|
+
result_hash
|
12
|
+
end
|
13
|
+
|
14
|
+
def hash_from_json(json)
|
15
|
+
JSON.parse(json)
|
16
|
+
end
|
17
|
+
|
18
|
+
def query(hash)
|
19
|
+
hash.sort.collect {
|
20
|
+
|k, v| [encode(k), encode(v.to_s)].join("=")
|
21
|
+
}.join("&")
|
22
|
+
end
|
23
|
+
|
24
|
+
def encode(value)
|
25
|
+
CGI.escape(value)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/emporium/services.rb
CHANGED
@@ -1,9 +1,8 @@
|
|
1
|
+
require 'emporium/services/options'
|
2
|
+
require 'emporium/services/utilities'
|
1
3
|
require 'emporium/services/amazon'
|
4
|
+
require 'emporium/services/google'
|
2
5
|
|
3
|
-
require 'hmac'
|
4
|
-
require 'hmac-sha2'
|
5
6
|
require 'time'
|
6
7
|
require 'cgi'
|
7
|
-
require 'base64'
|
8
8
|
require 'open-uri'
|
9
|
-
require 'nokogiri'
|
data/lib/emporium/version.rb
CHANGED
@@ -1,18 +1,18 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
Emporium::Services::Amazon.configuration do |config|
|
4
|
-
config.access_key =
|
5
|
-
config.associate_tag =
|
6
|
-
config.secret =
|
4
|
+
config.access_key = AMAZON["access_key"]
|
5
|
+
config.associate_tag = AMAZON["associate_tag"]
|
6
|
+
config.secret = AMAZON["secret"]
|
7
|
+
end
|
8
|
+
|
9
|
+
Emporium::Services::Google.configuration do |config|
|
10
|
+
config.access_key = GOOGLE["access_key"]
|
11
|
+
config.cse = GOOGLE["cse"]
|
7
12
|
end
|
8
13
|
|
9
14
|
describe Emporium::Product do
|
10
15
|
describe "#new" do
|
11
|
-
it "creates an instance from a upc" do
|
12
|
-
product = Emporium::Product.new("036000241457")
|
13
|
-
product.should be_an_instance_of Emporium::Product
|
14
|
-
end
|
15
|
-
|
16
16
|
it "fails to create an instance if no upc is given" do
|
17
17
|
lambda { Emporium::Product.new() }.should raise_error
|
18
18
|
end
|
@@ -24,16 +24,33 @@ describe Emporium::Product do
|
|
24
24
|
|
25
25
|
describe "#fetch!" do
|
26
26
|
product = Emporium::Product.new("610839331574")
|
27
|
-
product.use Emporium::Services::Amazon
|
28
27
|
|
29
|
-
|
30
|
-
|
28
|
+
describe "using the amazon service" do
|
29
|
+
product.use Emporium::Services::Amazon
|
30
|
+
|
31
|
+
it "should fetch product information" do
|
32
|
+
lambda { product.fetch! }.should_not raise_error
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should create attributes for the product" do
|
36
|
+
product.fetch!
|
37
|
+
product.brand.downcase.should match 'asus'
|
38
|
+
end
|
31
39
|
end
|
32
40
|
|
33
|
-
|
34
|
-
product.
|
35
|
-
|
41
|
+
describe "using the google service" do
|
42
|
+
product.use Emporium::Services::Google
|
43
|
+
|
44
|
+
it "should fetch product information" do
|
45
|
+
lambda { product.fetch! }.should_not raise_error
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should create attributes for the product" do
|
49
|
+
product.fetch!
|
50
|
+
product.brand.downcase.should match 'asus'
|
51
|
+
end
|
36
52
|
end
|
53
|
+
|
37
54
|
end
|
38
55
|
|
39
56
|
describe "#use" do
|
@@ -48,20 +65,45 @@ end
|
|
48
65
|
describe Emporium::Services::Amazon do
|
49
66
|
describe "#new" do
|
50
67
|
it "should initialize with options" do
|
51
|
-
options = {some: "option"}
|
68
|
+
options = { some: "option" }
|
52
69
|
service = Emporium::Services::Amazon.new(options)
|
53
70
|
service.instance_variable_get('@options')[:some].should match "option"
|
54
71
|
end
|
55
72
|
end
|
56
73
|
|
57
74
|
describe "#response" do
|
58
|
-
it "should return
|
75
|
+
it "should return a Hash" do
|
59
76
|
service = Emporium::Services::Amazon.new(code: "610839331574")
|
60
|
-
service.response.should
|
77
|
+
service.response.should be_an_instance_of Hash
|
61
78
|
end
|
62
79
|
|
63
80
|
it "should raise an error if nothing is found" do
|
64
|
-
lambda {
|
81
|
+
lambda {
|
82
|
+
Emporium::Services::Amazon.new(code: "036000241452").response
|
83
|
+
}.should raise_error
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe Emporium::Services::Google do
|
89
|
+
describe "#new" do
|
90
|
+
it "should initialize with options" do
|
91
|
+
options = { some: "option" }
|
92
|
+
service = Emporium::Services::Amazon.new(options)
|
93
|
+
service.instance_variable_get('@options')[:some].should match "option"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe "#response" do
|
98
|
+
it "should return a Hash" do
|
99
|
+
service = Emporium::Services::Google.new(code: "610839331574")
|
100
|
+
service.response.should be_an_instance_of Hash
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should return an error if nothing is found" do
|
104
|
+
lambda {
|
105
|
+
service = Emporium::Services::Google.new(code: "ASjh@89a2$").response
|
106
|
+
}.should raise_error
|
65
107
|
end
|
66
108
|
end
|
67
109
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: emporium
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
prerelease:
|
5
|
-
version: 0.0.3
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.3
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Hugo Bastien
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-12-
|
13
|
+
date: 2011-12-08 00:00:00 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: nokogiri
|
@@ -35,7 +35,7 @@ dependencies:
|
|
35
35
|
type: :runtime
|
36
36
|
version_requirements: *id002
|
37
37
|
- !ruby/object:Gem::Dependency
|
38
|
-
name:
|
38
|
+
name: json
|
39
39
|
prerelease: false
|
40
40
|
requirement: &id003 !ruby/object:Gem::Requirement
|
41
41
|
none: false
|
@@ -43,9 +43,20 @@ dependencies:
|
|
43
43
|
- - ">="
|
44
44
|
- !ruby/object:Gem::Version
|
45
45
|
version: "0"
|
46
|
-
type: :
|
46
|
+
type: :runtime
|
47
47
|
version_requirements: *id003
|
48
|
-
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: rspec
|
50
|
+
prerelease: false
|
51
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: "0"
|
57
|
+
type: :development
|
58
|
+
version_requirements: *id004
|
59
|
+
description: Uses Amazon's product advertising API or Google's Shopping Search API
|
49
60
|
email:
|
50
61
|
- hugobast@gmail.com
|
51
62
|
executables: []
|
@@ -66,6 +77,9 @@ files:
|
|
66
77
|
- lib/emporium/product.rb
|
67
78
|
- lib/emporium/services.rb
|
68
79
|
- lib/emporium/services/amazon.rb
|
80
|
+
- lib/emporium/services/google.rb
|
81
|
+
- lib/emporium/services/options.rb
|
82
|
+
- lib/emporium/services/utilities.rb
|
69
83
|
- lib/emporium/version.rb
|
70
84
|
- spec/emporium/emporium_spec.rb
|
71
85
|
- spec/spec_helper.rb
|
@@ -86,16 +100,16 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
86
100
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
101
|
none: false
|
88
102
|
requirements:
|
89
|
-
- - "
|
103
|
+
- - ">="
|
90
104
|
- !ruby/object:Gem::Version
|
91
|
-
version:
|
105
|
+
version: "0"
|
92
106
|
requirements: []
|
93
107
|
|
94
108
|
rubyforge_project: emporium
|
95
109
|
rubygems_version: 1.8.11
|
96
110
|
signing_key:
|
97
111
|
specification_version: 3
|
98
|
-
summary: Emporium fetches information about a product from it's UPC
|
112
|
+
summary: Emporium fetches information about a product from it's UPC (GTIN-12)
|
99
113
|
test_files:
|
100
114
|
- spec/emporium/emporium_spec.rb
|
101
115
|
- spec/spec_helper.rb
|