dpickett-ramazon_advertising 0.1.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.
- data/.document +5 -0
- data/.gitignore +7 -0
- data/LICENSE +20 -0
- data/README.rdoc +44 -0
- data/Rakefile +85 -0
- data/VERSION +1 -0
- data/features/friendly_errors.feature +15 -0
- data/features/generate_root_browse_nodes.feature +12 -0
- data/features/retrieving_a_product.feature +23 -0
- data/features/searching_for_products.feature +15 -0
- data/features/step_definitions/auth_steps.rb +8 -0
- data/features/step_definitions/browse_node_steps.rb +36 -0
- data/features/step_definitions/error_steps.rb +14 -0
- data/features/step_definitions/product_steps.rb +48 -0
- data/features/step_definitions/ramazon_advertising_steps.rb +0 -0
- data/features/support/env.rb +7 -0
- data/features/support/ramazon_advertising.example.yml +2 -0
- data/lib/ramazon/abstract_element.rb +18 -0
- data/lib/ramazon/browse_node.rb +32 -0
- data/lib/ramazon/configuration.rb +66 -0
- data/lib/ramazon/error.rb +12 -0
- data/lib/ramazon/image.rb +18 -0
- data/lib/ramazon/price.rb +18 -0
- data/lib/ramazon/product.rb +144 -0
- data/lib/ramazon/product_collection.rb +27 -0
- data/lib/ramazon/rails_additions.rb +99 -0
- data/lib/ramazon/request.rb +82 -0
- data/lib/ramazon/signatory.rb +10 -0
- data/lib/ramazon_advertising.rb +34 -0
- data/lib/root_nodes.yml +64 -0
- data/lib/tasks/ramazon.rake +9 -0
- data/ramazon_advertising.gemspec +92 -0
- data/spec/ramazon/configuration_spec.rb +21 -0
- data/spec/spec_helper.rb +11 -0
- metadata +157 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Dan Pickett
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
= ramazon_advertising
|
2
|
+
|
3
|
+
Ruby + Amazon Advertising == Car RAmazon_Advertising - Say Car RAmazon_Advertising!
|
4
|
+
|
5
|
+
Ruh roh' Rorge it's an object oriented approach to Amazon's overly complicated product api that has a new name every 2 weeks.
|
6
|
+
|
7
|
+
Complete with the ability to use nokogiri selectors for results
|
8
|
+
|
9
|
+
Currently only supports product search and retrieval. Requests are signed properly. More soon!
|
10
|
+
|
11
|
+
Ramazon::Configuration.access_key = "Your Access Key"
|
12
|
+
Ramazon::Configuration.secret_key = "Your Secret Key"
|
13
|
+
|
14
|
+
@products = Ramazon::Product.find(:item_id => "B000NU2CY4", :response_group => "Medium")
|
15
|
+
@products[0].title
|
16
|
+
@products[0].asin
|
17
|
+
@products[0].upc
|
18
|
+
@products[0].large_image.url
|
19
|
+
@products[0].url
|
20
|
+
|
21
|
+
#you can also use a nokogiri search string to get elements that don't have built-in accessors
|
22
|
+
@products[0].get("ItemAttributes Actor").collect{|a| a.content}
|
23
|
+
|
24
|
+
== What's under the hood?
|
25
|
+
|
26
|
+
* HTTParty
|
27
|
+
* HappyMapper
|
28
|
+
* will_paginate
|
29
|
+
* nokogiri
|
30
|
+
|
31
|
+
== Note on Patches/Pull Requests
|
32
|
+
|
33
|
+
* Fork the project.
|
34
|
+
* Make your feature addition or bug fix.
|
35
|
+
* Add tests for it. This is important so I don't break it in a
|
36
|
+
future version unintentionally.
|
37
|
+
* Commit, do not mess with rakefile, version, or history.
|
38
|
+
(if you want to have your own version, that is fine but
|
39
|
+
bump version in a commit by itself I can ignore when I pull)
|
40
|
+
* Send me a pull request. Bonus points for topic branches.
|
41
|
+
|
42
|
+
== Copyright
|
43
|
+
|
44
|
+
Copyright (c) 2009 Dan Pickett. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
require 'lib/ramazon_advertising'
|
5
|
+
load File.join(File.dirname(__FILE__), 'lib', 'tasks', 'ramazon.rake')
|
6
|
+
|
7
|
+
begin
|
8
|
+
require 'jeweler'
|
9
|
+
Jeweler::Tasks.new do |gem|
|
10
|
+
gem.name = "ramazon_advertising"
|
11
|
+
gem.summary = %Q{TODO: one-line summary of your gem}
|
12
|
+
gem.description = %Q{TODO: longer description of your gem}
|
13
|
+
gem.email = "dpickett@enlightsolutions.com"
|
14
|
+
gem.homepage = "http://github.com/dpickett/ramazon_advertising"
|
15
|
+
gem.authors = ["Dan Pickett"]
|
16
|
+
gem.add_dependency("jnunemaker-httparty", ">= 0.4.3")
|
17
|
+
gem.add_dependency("jnunemaker-happymapper", ">= 0.2.5")
|
18
|
+
gem.add_dependency("mislav-will_paginate", ">= 2.3.11")
|
19
|
+
gem.add_dependency("nokogiri", ">= 1.3.3")
|
20
|
+
gem.add_dependency("configatron", ">= 2.4.1")
|
21
|
+
gem.add_dependency("rails", ">= 2.3.3")
|
22
|
+
gem.add_dependency("ruby-hmac", ">= 0.3.2")
|
23
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
24
|
+
end
|
25
|
+
|
26
|
+
rescue LoadError
|
27
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
28
|
+
end
|
29
|
+
|
30
|
+
require 'spec/rake/spectask'
|
31
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
32
|
+
spec.libs << 'lib' << 'spec'
|
33
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
34
|
+
end
|
35
|
+
|
36
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
37
|
+
spec.libs << 'lib' << 'spec'
|
38
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
39
|
+
spec.rcov = true
|
40
|
+
end
|
41
|
+
|
42
|
+
task :default => :spec
|
43
|
+
|
44
|
+
begin
|
45
|
+
require 'cucumber/rake/task'
|
46
|
+
Cucumber::Rake::Task.new(:cucumber)
|
47
|
+
rescue LoadError
|
48
|
+
task :cucumber do
|
49
|
+
abort "Cucumber is not available. In order to run features, you must: sudo gem install cucumber"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
task :default => :test
|
54
|
+
|
55
|
+
require 'rake/rdoctask'
|
56
|
+
Rake::RDocTask.new do |rdoc|
|
57
|
+
if File.exist?('VERSION.yml')
|
58
|
+
config = YAML.load(File.read('VERSION.yml'))
|
59
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
60
|
+
else
|
61
|
+
version = ""
|
62
|
+
end
|
63
|
+
|
64
|
+
rdoc.rdoc_dir = 'rdoc'
|
65
|
+
rdoc.title = "ramazon_advertising #{version}"
|
66
|
+
rdoc.rdoc_files.include('README*')
|
67
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
68
|
+
end
|
69
|
+
|
70
|
+
begin
|
71
|
+
require "YARD"
|
72
|
+
if File.exist?('VERSION.yml')
|
73
|
+
config = YAML.load(File.read('VERSION.yml'))
|
74
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
75
|
+
else
|
76
|
+
version = ""
|
77
|
+
end
|
78
|
+
|
79
|
+
YARD::Rake::YardocTask.new do |t|
|
80
|
+
end
|
81
|
+
rescue LoadError
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.1
|
@@ -0,0 +1,15 @@
|
|
1
|
+
@errors
|
2
|
+
Feature: Friendly errors
|
3
|
+
As a user of ramazon_advertising
|
4
|
+
I want to get friendly error messages
|
5
|
+
So that I know I did something wrong
|
6
|
+
|
7
|
+
Background:
|
8
|
+
Given I have a valid access key
|
9
|
+
And I have a valid secret key
|
10
|
+
|
11
|
+
Scenario: I don't specify a keyword for search
|
12
|
+
When I perform the product search
|
13
|
+
Then I should get an error
|
14
|
+
And the error should have a "code" of "AWS.MinimumParameterRequirement"
|
15
|
+
And the error should have a "message"
|
@@ -0,0 +1,12 @@
|
|
1
|
+
Feature: As a user of the Ramazon Advertising API
|
2
|
+
I want a list of browse nodes
|
3
|
+
So that I have starting points for browse node traversal
|
4
|
+
|
5
|
+
Scenario: Retrieving browse nodes
|
6
|
+
Given I want browse nodes to be stored in a temporary file
|
7
|
+
And the browse node temporary file doesn't exist
|
8
|
+
When I retrieve root nodes
|
9
|
+
Then I should get a temporary file for root nodes
|
10
|
+
And I should have a "Books" root node
|
11
|
+
And I should have a "Grocery" root node
|
12
|
+
And I should have a list of root nodes
|
@@ -0,0 +1,23 @@
|
|
1
|
+
Feature: Retrieving a product
|
2
|
+
As a user of the Ramazon_advertising_api
|
3
|
+
I want to get specific information
|
4
|
+
In order to leverage the data provided by the api
|
5
|
+
|
6
|
+
Background:
|
7
|
+
Given I have a valid access key
|
8
|
+
And I have a valid secret key
|
9
|
+
|
10
|
+
When I try to find the asin "B000NU2CY4"
|
11
|
+
Then I should get a product
|
12
|
+
And the product should have the "title" "Gladiator [Blu-ray]"
|
13
|
+
And the product should have a "manufacturer"
|
14
|
+
And the product should have a "product_group"
|
15
|
+
And the product should have a "sales_rank"
|
16
|
+
And the product should have a "large_image"
|
17
|
+
And the product should have a "list_price"
|
18
|
+
And the product should have a "upc"
|
19
|
+
And the product should have a "lowest_new_price"
|
20
|
+
And the product should have a "new_count"
|
21
|
+
And the product should have a "used_count"
|
22
|
+
|
23
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
Feature: Search for products
|
2
|
+
As a user of the Ramazon_advertising api
|
3
|
+
I want to perform a search
|
4
|
+
In order to find products my customers will want to purchase
|
5
|
+
|
6
|
+
Background:
|
7
|
+
Given I have a valid access key
|
8
|
+
And I have a valid secret key
|
9
|
+
|
10
|
+
Given I am searching with the "search_index" of "DVD"
|
11
|
+
And I am searching with the "browse_node" of "130"
|
12
|
+
When I perform the product search
|
13
|
+
Then I should get a list of products
|
14
|
+
And the list of products should have more than 1 product
|
15
|
+
And each product should have the "product_group" "DVD"
|
@@ -0,0 +1,36 @@
|
|
1
|
+
Given /^I want browse nodes to be stored in a temporary file$/ do
|
2
|
+
@browse_root_filename = File.join(File.dirname(__FILE__), '..', 'support', 'root_nodes.yml')
|
3
|
+
end
|
4
|
+
|
5
|
+
Given /^the browse node temporary file doesn't exist$/ do
|
6
|
+
FileUtils.rm_f @browse_root_filename
|
7
|
+
end
|
8
|
+
|
9
|
+
When /^I retrieve root nodes$/ do
|
10
|
+
if @browse_root_filename
|
11
|
+
Ramazon::BrowseNode.generate_root_nodes @browse_root_filename
|
12
|
+
else
|
13
|
+
Ramazon::BrowseNode.generate_root_nodes
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
Then /^I should get a temporary file for root nodes$/ do
|
18
|
+
FileTest.exists?(@browse_root_filename).should be_true
|
19
|
+
end
|
20
|
+
|
21
|
+
Then /^I should have a "([^\"]*)" root node$/ do |name|
|
22
|
+
get_root_nodes[name].should_not be_nil
|
23
|
+
end
|
24
|
+
|
25
|
+
Then /^I should have a list of root nodes$/ do
|
26
|
+
get_root_nodes.should_not be_empty
|
27
|
+
end
|
28
|
+
|
29
|
+
def get_root_nodes
|
30
|
+
if @browse_root_filename
|
31
|
+
nodes = Ramazon::BrowseNode.root_nodes(@browse_root_filename)
|
32
|
+
else
|
33
|
+
nodes = Ramazon::BrowseNode.root_nodes
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
Then /^I should get an error$/ do
|
2
|
+
@error.should_not be_nil
|
3
|
+
end
|
4
|
+
|
5
|
+
Then /^the error should have a "([^\"]*)" of "([^\"]*)"$/ do |attr, value|
|
6
|
+
@error.send(attr).should eql(value)
|
7
|
+
end
|
8
|
+
|
9
|
+
Then /^the error should have a "([^\"]*)"$/ do |attr|
|
10
|
+
@error.send(attr).should_not be_nil
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
|
@@ -0,0 +1,48 @@
|
|
1
|
+
When /^I try to find the asin "([^\"]*)"$/ do |asin|
|
2
|
+
@products = Ramazon::Product.find(:item_id => asin, :response_group => "Medium")
|
3
|
+
@product = @products[0]
|
4
|
+
end
|
5
|
+
|
6
|
+
Given /^I am searching with the "([^\"]*)" of "([^\"]*)"$/ do |attr, value|
|
7
|
+
@search_options ||= {}
|
8
|
+
@search_options[attr] = value
|
9
|
+
end
|
10
|
+
|
11
|
+
When /^I perform the product search$/ do
|
12
|
+
begin
|
13
|
+
@search_options ||= {}
|
14
|
+
@products = Ramazon::Product.find(@search_options)
|
15
|
+
rescue Ramazon::Error => e
|
16
|
+
@error = e
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
Then /^I should get a list of products$/ do
|
21
|
+
raise @error if @error
|
22
|
+
@products.should_not be_empty
|
23
|
+
end
|
24
|
+
|
25
|
+
Then /^the list of products should have more than (\d+) product$/ do |count|
|
26
|
+
@products.should have_at_least(count.to_i + 1).items
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
Then /^each product should have the "([^\"]*)" "([^\"]*)"$/ do |attr, value|
|
31
|
+
@products.each do |p|
|
32
|
+
p.send(attr).should eql(value)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
Then /^I should get a product$/ do
|
37
|
+
raise @error if @error
|
38
|
+
@product.should_not be_nil
|
39
|
+
end
|
40
|
+
|
41
|
+
Then /^the product should have the "([^\"]*)" "([^\"]*)"$/ do |attr, value|
|
42
|
+
@product.send(attr).should eql(value)
|
43
|
+
end
|
44
|
+
|
45
|
+
Then /^the product should have a "([^\"]*)"$/ do |attr|
|
46
|
+
@product.send(attr).should_not be_nil
|
47
|
+
end
|
48
|
+
|
File without changes
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Ramazon
|
2
|
+
module AbstractElement
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def abstract_element(name, type, options = {})
|
9
|
+
element(name, type, options.merge(:parser => :abstract_parse, :raw => true))
|
10
|
+
end
|
11
|
+
|
12
|
+
def abstract_parse(xml, options = {})
|
13
|
+
tag XML::Parser.string(xml).parse.root.name
|
14
|
+
parse(xml)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Ramazon
|
2
|
+
class BrowseNode
|
3
|
+
include HTTParty
|
4
|
+
|
5
|
+
DEFAULT_ROOT_FILE = File.join(File.dirname(__FILE__), '..', 'root_nodes.yml')
|
6
|
+
def self.generate_root_nodes(file_name = DEFAULT_ROOT_FILE)
|
7
|
+
if Ramazon::Configuration.locale == :us
|
8
|
+
doc = Nokogiri::HTML(get('http://www.amazon.com').body)
|
9
|
+
root_nodes = {}
|
10
|
+
doc.search(".navSaMenu .navSaChildItem a").each do |element|
|
11
|
+
if element["href"] =~ /node=(\d+)\&/
|
12
|
+
root_nodes[element.content] = $1
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
unless root_nodes.empty?
|
17
|
+
FileUtils.rm_f(file_name)
|
18
|
+
File.open(file_name, 'w') do |f|
|
19
|
+
f.write(root_nodes.to_yaml)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
else
|
23
|
+
#todo correlate ECS locales to actual amazon.* urls
|
24
|
+
raise "generating root nodes for locale's other than the US is not supported"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.root_nodes(file_name = DEFAULT_ROOT_FILE)
|
29
|
+
@root_nodes ||= File.open(file_name) { |yf| YAML::load(yf) }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Ramazon
|
2
|
+
# the class that persists configuration information for Amazon requests
|
3
|
+
class Configuration
|
4
|
+
class << self
|
5
|
+
LOCALES = [:ca, :de, :fr, :jp, :uk, :us]
|
6
|
+
|
7
|
+
# set the locale for future requests
|
8
|
+
# will raise an exception if the locale isn't in the Configuration::LOCALES collection
|
9
|
+
def locale=(locale)
|
10
|
+
if LOCALES.include?(locale)
|
11
|
+
configatron.ramazon.locale = locale
|
12
|
+
else
|
13
|
+
raise "unknown locale"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# get the current locale (defaults to the us)
|
18
|
+
def locale
|
19
|
+
configatron.ramazon.locale || :us
|
20
|
+
end
|
21
|
+
|
22
|
+
# get the current access key
|
23
|
+
def access_key
|
24
|
+
configatron.ramazon.access_key
|
25
|
+
end
|
26
|
+
|
27
|
+
# set the current access key
|
28
|
+
# @param key [String] access key you're using to access the advertising api
|
29
|
+
def access_key=(key)
|
30
|
+
configatron.ramazon.access_key = key
|
31
|
+
end
|
32
|
+
|
33
|
+
# get the current secret key that is used for request signing
|
34
|
+
def secret_key
|
35
|
+
configatron.ramazon.secret_key
|
36
|
+
end
|
37
|
+
|
38
|
+
# set the secret key so that requests can be appropriately signed
|
39
|
+
# @param key [String] secret key you're using to sign advertising requests
|
40
|
+
def secret_key=(key)
|
41
|
+
configatron.ramazon.secret_key = key
|
42
|
+
end
|
43
|
+
|
44
|
+
# get the correct host based on your locale
|
45
|
+
def base_uri
|
46
|
+
if locale == :us
|
47
|
+
"http://ecs.amazonaws.com"
|
48
|
+
else
|
49
|
+
"http://ecs.amazonaws.#{locale}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# get the full path including locale specific host and /onca/xml path
|
54
|
+
def uri
|
55
|
+
"#{base_uri}#{path}"
|
56
|
+
end
|
57
|
+
|
58
|
+
# get the path where requests should be dispatched to
|
59
|
+
def path
|
60
|
+
"/onca/xml"
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Ramazon
|
2
|
+
# An image object returned by Amazon's product data
|
3
|
+
# Available accessors include
|
4
|
+
# +url+::
|
5
|
+
# The url of the image
|
6
|
+
# +height+::
|
7
|
+
# The height of the image
|
8
|
+
# +width+::
|
9
|
+
# The width of the image
|
10
|
+
class Image
|
11
|
+
include HappyMapper
|
12
|
+
include Ramazon::AbstractElement
|
13
|
+
tag "Image"
|
14
|
+
element :url, String, :tag => "URL"
|
15
|
+
element :height, Integer, :tag => "Height"
|
16
|
+
element :width, Integer, :tag => "Width"
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Ramazon
|
2
|
+
# A price object returned by Amazon's product data
|
3
|
+
# Available accessors include
|
4
|
+
# +amount+::
|
5
|
+
# The unformatted ammount of the price
|
6
|
+
# +currency_code+::
|
7
|
+
# The currency of the offer
|
8
|
+
# +formatted_price+::
|
9
|
+
# The formatted price of the offer
|
10
|
+
class Price
|
11
|
+
include HappyMapper
|
12
|
+
include Ramazon::AbstractElement
|
13
|
+
|
14
|
+
element :amount, String, :tag => "Amount"
|
15
|
+
element :currency_code, String, :tag => "CurrencyCode"
|
16
|
+
element :formatted_price, String, :tag => "FormattedPrice"
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
module Ramazon
|
2
|
+
# Find and get product details with this class
|
3
|
+
# Currently supports the following accessors
|
4
|
+
# (all other elements can be accessed via nokogiri selectors and the get method)
|
5
|
+
# +asin+::
|
6
|
+
# Amazon Identifier
|
7
|
+
# +upc+::
|
8
|
+
# UPC ID
|
9
|
+
# +title+::
|
10
|
+
# Title of the product
|
11
|
+
# +product_group+::
|
12
|
+
# The category/product_group of the product
|
13
|
+
# +manufacturer+::
|
14
|
+
# The manufacturer of the product
|
15
|
+
# +brand+::
|
16
|
+
# The brand of the product
|
17
|
+
# +url+::
|
18
|
+
# The Amazon URL of the product
|
19
|
+
# +small_image+::
|
20
|
+
# The small image that Amazon provides (NOTE: Returns Ramazon::Image object)
|
21
|
+
# +medium_image+::
|
22
|
+
# The medium image that Amazon provides (NOTE: Returns Ramazon::Image object)
|
23
|
+
# +large_image+::
|
24
|
+
# The large image that Amazon provides (NOTE: Returns Ramazon::Image object)
|
25
|
+
# +list_price+::
|
26
|
+
# The list price of the item (NOTE: Returns Ramazon::Price object)
|
27
|
+
# +lowest_new_price+::
|
28
|
+
# The lowest new price from the offer summary (NOTE: Returns Ramazon::Price object)
|
29
|
+
# +sales_rank+::
|
30
|
+
# The sales rank of the product
|
31
|
+
# +new_count+::
|
32
|
+
# The quantity of new item offers
|
33
|
+
# +used_count+::
|
34
|
+
# The quantity of used item offers
|
35
|
+
# +collectible_count+::
|
36
|
+
# The quantity of collectible item offers
|
37
|
+
# +refurbished_count+::
|
38
|
+
# The quantity of refurbished item offers
|
39
|
+
# +release_date+::
|
40
|
+
# The release date of the product
|
41
|
+
# +original_release_date+::
|
42
|
+
# The original release date of the product
|
43
|
+
# @example find an individual item
|
44
|
+
# @products = Ramazon::Product.find(:item_id => "B000NU2CY4", :response_group => "Medium")
|
45
|
+
# @products[0].title
|
46
|
+
# @products[0].asin
|
47
|
+
# @products[0].upc
|
48
|
+
# @products[0].large_image.url
|
49
|
+
# @products[0].url
|
50
|
+
#
|
51
|
+
|
52
|
+
class Product
|
53
|
+
include HappyMapper
|
54
|
+
include Ramazon::AbstractElement
|
55
|
+
tag 'Item'
|
56
|
+
|
57
|
+
element :asin, String, :tag => 'ASIN'
|
58
|
+
element :upc, String, :tag => 'ItemAttributes/UPC'
|
59
|
+
element :title, String, :tag => 'Title', :deep => true
|
60
|
+
element :product_group, String, :tag => 'ItemAttributes/ProductGroup'
|
61
|
+
element :manufacturer, String, :tag => 'ItemAttributes/Manufacturer'
|
62
|
+
element :brand, String, :tag => 'ItemAttributes/Brand'
|
63
|
+
element :url, String, :tag => 'DetailPageURL'
|
64
|
+
abstract_element :small_image, Ramazon::Image, :tag => "SmallImage"
|
65
|
+
abstract_element :large_image, Ramazon::Image, :tag => "LargeImage"
|
66
|
+
abstract_element :medium_image, Ramazon::Image, :tag => "MediumImage"
|
67
|
+
abstract_element :tiny_image, Ramazon::Image, :tag => "TinyImage"
|
68
|
+
abstract_element :thumb_image, Ramazon::Image, :tag => "ThumbImage"
|
69
|
+
abstract_element :list_price, Ramazon::Price, :tag => "ItemAttributes/ListPrice"
|
70
|
+
abstract_element :lowest_new_price, Ramazon::Price, :tag => "OfferSummary/LowestNewPrice"
|
71
|
+
|
72
|
+
element :sales_rank, Integer, :tag => "SalesRank"
|
73
|
+
|
74
|
+
element :new_count, Integer, :tag => "OfferSummary/TotalNew"
|
75
|
+
element :used_count, Integer, :tag => "OfferSummary/TotalUsed"
|
76
|
+
element :collectible_count, Integer, :tag => "OfferSummary/TotalCollectible"
|
77
|
+
element :refurbished_count, Integer, :tag => "OfferSummary/TotalRefurbished"
|
78
|
+
|
79
|
+
element :release_date, Date, :tag => 'ItemAttributes/ReleaseDate'
|
80
|
+
element :original_release_date, Date, :tag => 'ItemAttributes/OriginalReleaseDate'
|
81
|
+
|
82
|
+
|
83
|
+
# Creates the worker that performs the delta indexing
|
84
|
+
# @param options Amazon request options (you can use an underscore convention)
|
85
|
+
# (ie. passing the :response_group option will be converted to "ResponseGroup")
|
86
|
+
# <tt>:item_id</tt> - the ASIN or UPC you're looking for
|
87
|
+
# @return [Array] array of Ramazon::Product objects
|
88
|
+
def self.find(*args)
|
89
|
+
options = args.extract_options!
|
90
|
+
if options[:item_id]
|
91
|
+
item_lookup(options[:item_id], options)
|
92
|
+
else
|
93
|
+
options[:operation] ||= "ItemSearch"
|
94
|
+
options[:search_index] ||= "Blended"
|
95
|
+
options[:item_page] ||= 1
|
96
|
+
res = Ramazon::Request.new(options).submit
|
97
|
+
Ramazon::ProductCollection.create_from_results(options[:item_page] || 1, 10, res)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Performs an item lookup
|
102
|
+
# @param item_id the ASIN or UPC you're looking for
|
103
|
+
# @options additional Amazon request options (i.e. :response_group)
|
104
|
+
def self.item_lookup(item_id, options = {})
|
105
|
+
req = Ramazon::Request.new({:item_id => item_id,
|
106
|
+
:operation => "ItemLookup"}.merge(options))
|
107
|
+
res = req.submit
|
108
|
+
|
109
|
+
Ramazon::ProductCollection.create_from_results(1,1,res)
|
110
|
+
end
|
111
|
+
|
112
|
+
# assembles the available images for the object
|
113
|
+
# @return [Hash] hash of symbolized image_name => Ramazon::Image pairs
|
114
|
+
def images
|
115
|
+
if !@images
|
116
|
+
@images = {}
|
117
|
+
@images[:thumb] = self.thumb_image if self.thumb_image
|
118
|
+
@images[:tiny_image] = self.tiny_image if self.tiny_image
|
119
|
+
@images[:small] = self.small_image if self.small_image
|
120
|
+
@images[:medium] = self.medium_image if self.medium_image
|
121
|
+
@images[:large] = self.large_image if self.large_image
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
attr_accessor :xml_doc
|
126
|
+
def self.parse(xml, options = {})
|
127
|
+
node = XML::Parser.string(xml.to_s).parse.root
|
128
|
+
node.find("//Item").collect do |n|
|
129
|
+
p = super(n.to_s)
|
130
|
+
p.xml_doc = Nokogiri::XML.parse(n.to_s)
|
131
|
+
p
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# perform a nokogiri search on the product's XML
|
136
|
+
# @param args Passes directly to a Nokogiri::Xml.parse(xml).search method
|
137
|
+
# @example find the actor
|
138
|
+
# @product = Ramazon::Product.find(:item_id => "B000NU2CY4", :response_group => "Medium")[0]
|
139
|
+
# @product.get("ItemAttributes Actor").collect{|a| a.content}
|
140
|
+
def get(*args)
|
141
|
+
result = @xml_doc.search(args)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Ramazon
|
2
|
+
class ProductCollection < WillPaginate::Collection
|
3
|
+
include HappyMapper
|
4
|
+
|
5
|
+
def self.create_from_results(page, per_page, body)
|
6
|
+
results = Items.parse(body, {})[0]
|
7
|
+
col = create(page, 10, results.total_results || 0) do |pager|
|
8
|
+
pager.replace(results.products)
|
9
|
+
end
|
10
|
+
|
11
|
+
col
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
class Items
|
17
|
+
include HappyMapper
|
18
|
+
tag 'Items'
|
19
|
+
|
20
|
+
element :total_results, Integer, :tag => 'TotalResults'
|
21
|
+
element :total_pages, Integer, :tag => 'TotalPages'
|
22
|
+
has_many :products,
|
23
|
+
Ramazon::Product,
|
24
|
+
:tag => "Item",
|
25
|
+
:raw => true
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
#this code courtesy of Pat Allan and Rails Core
|
2
|
+
module Ramazon
|
3
|
+
module HashExcept
|
4
|
+
# Returns a new hash without the given keys.
|
5
|
+
def except(*keys)
|
6
|
+
rejected = Set.new(respond_to?(:convert_key) ? keys.map { |key| convert_key(key) } : keys)
|
7
|
+
reject { |key,| rejected.include?(key) }
|
8
|
+
end
|
9
|
+
|
10
|
+
# Replaces the hash without only the given keys.
|
11
|
+
def except!(*keys)
|
12
|
+
replace(except(*keys))
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
Hash.send(
|
18
|
+
:include, Ramazon::HashExcept
|
19
|
+
) unless Hash.instance_methods.include?("except")
|
20
|
+
|
21
|
+
module Ramazon
|
22
|
+
module ArrayExtractOptions
|
23
|
+
def extract_options!
|
24
|
+
last.is_a?(::Hash) ? pop : {}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
Array.send(
|
30
|
+
:include, Ramazon::ArrayExtractOptions
|
31
|
+
) unless Array.instance_methods.include?("extract_options!")
|
32
|
+
|
33
|
+
module Ramazon
|
34
|
+
module ClassAttributeMethods
|
35
|
+
def cattr_reader(*syms)
|
36
|
+
syms.flatten.each do |sym|
|
37
|
+
next if sym.is_a?(Hash)
|
38
|
+
class_eval(<<-EOS, __FILE__, __LINE__)
|
39
|
+
unless defined? @@#{sym}
|
40
|
+
@@#{sym} = nil
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.#{sym}
|
44
|
+
@@#{sym}
|
45
|
+
end
|
46
|
+
|
47
|
+
def #{sym}
|
48
|
+
@@#{sym}
|
49
|
+
end
|
50
|
+
EOS
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def cattr_writer(*syms)
|
55
|
+
options = syms.extract_options!
|
56
|
+
syms.flatten.each do |sym|
|
57
|
+
class_eval(<<-EOS, __FILE__, __LINE__)
|
58
|
+
unless defined? @@#{sym}
|
59
|
+
@@#{sym} = nil
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.#{sym}=(obj)
|
63
|
+
@@#{sym} = obj
|
64
|
+
end
|
65
|
+
|
66
|
+
#{"
|
67
|
+
def #{sym}=(obj)
|
68
|
+
@@#{sym} = obj
|
69
|
+
end
|
70
|
+
" unless options[:instance_writer] == false }
|
71
|
+
EOS
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def cattr_accessor(*syms)
|
76
|
+
cattr_reader(*syms)
|
77
|
+
cattr_writer(*syms)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
Class.extend(
|
83
|
+
Ramazon::ClassAttributeMethods
|
84
|
+
) unless Class.respond_to?(:cattr_reader)
|
85
|
+
|
86
|
+
module Ramazon
|
87
|
+
module MetaClass
|
88
|
+
def metaclass
|
89
|
+
class << self
|
90
|
+
self
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
unless Object.new.respond_to?(:metaclass)
|
97
|
+
Object.send(:include, Ramazon::MetaClass)
|
98
|
+
end
|
99
|
+
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Ramazon
|
2
|
+
#the object used to prepare and submit requests to amazon
|
3
|
+
class Request
|
4
|
+
include HTTParty
|
5
|
+
attr_accessor :options
|
6
|
+
|
7
|
+
def initialize(options = {})
|
8
|
+
self.options = options.merge(default_options)
|
9
|
+
end
|
10
|
+
|
11
|
+
def submit
|
12
|
+
@response = self.class.get(signed_url)
|
13
|
+
if valid?
|
14
|
+
@response.body
|
15
|
+
else
|
16
|
+
raise self.errors[0], "The following response errors were returned: #{self.errors.collect{|e| e.to_s}}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Error checking for responses (will return true if errors are present)
|
21
|
+
# @returns [boolean] whether the response yielded errors
|
22
|
+
def valid?
|
23
|
+
@response && errors.empty?
|
24
|
+
end
|
25
|
+
|
26
|
+
#errors returned from amazon
|
27
|
+
def errors
|
28
|
+
@errors ||= Ramazon::Error.parse(@response.body) || []
|
29
|
+
end
|
30
|
+
|
31
|
+
#use this if any type of caching is desired (TBD)
|
32
|
+
def unsigned_path
|
33
|
+
qs = ""
|
34
|
+
|
35
|
+
sorted_params.each do |key, value|
|
36
|
+
qs << "&#{key}=#{URI.encode(value.to_s)}"
|
37
|
+
end
|
38
|
+
|
39
|
+
qs.size > 0 ? qs[1..qs.size] : qs
|
40
|
+
end
|
41
|
+
|
42
|
+
def signed_url
|
43
|
+
self.options[:timestamp] = Time.now.utc.strftime("%Y-%m-%dT%H:%M:%S")
|
44
|
+
encoded_param_strings = []
|
45
|
+
sorted_params.each do |p|
|
46
|
+
encoded_param_strings << "#{p[0]}=#{CGI::escape(p[1].to_s)}"
|
47
|
+
end
|
48
|
+
string_to_sign =
|
49
|
+
"GET
|
50
|
+
#{Ramazon::Configuration.base_uri.gsub("http://", "")}
|
51
|
+
#{Ramazon::Configuration.path}
|
52
|
+
#{encoded_param_strings.join("&")}"
|
53
|
+
|
54
|
+
signature = Ramazon::Signatory.sign(string_to_sign)
|
55
|
+
encoded_param_strings << "Signature=" + signature
|
56
|
+
|
57
|
+
"#{Ramazon::Configuration.uri}?#{encoded_param_strings.join("&")}"
|
58
|
+
end
|
59
|
+
|
60
|
+
def sorted_params
|
61
|
+
params.sort {|a, b| a <=> b}
|
62
|
+
end
|
63
|
+
|
64
|
+
def params
|
65
|
+
formatted_params = {}
|
66
|
+
self.options.each do |key, value|
|
67
|
+
formatted_params[key.to_s.classify] = value.is_a?(Array) ? value.join(",") : value
|
68
|
+
end
|
69
|
+
|
70
|
+
formatted_params
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
def default_options
|
75
|
+
{
|
76
|
+
:service => "AWSECommerceService",
|
77
|
+
:version => "2009-07-01",
|
78
|
+
:AWSAccessKeyId => Ramazon::Configuration.access_key
|
79
|
+
}
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module Ramazon
|
2
|
+
class Signatory
|
3
|
+
def self.sign(string_to_sign)
|
4
|
+
sha1 = HMAC::SHA256.digest(Ramazon::Configuration.secret_key, string_to_sign)
|
5
|
+
|
6
|
+
#Base64 encoding adds a linefeed to the end of the string so chop the last character!
|
7
|
+
CGI.escape(Base64.encode64(sha1).chomp)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
+
|
3
|
+
require "rubygems"
|
4
|
+
|
5
|
+
require "fileutils"
|
6
|
+
require "base64"
|
7
|
+
require "hmac-sha2"
|
8
|
+
require "digest/sha1"
|
9
|
+
require "digest/sha2"
|
10
|
+
require "cgi"
|
11
|
+
|
12
|
+
require "will_paginate"
|
13
|
+
require "httparty"
|
14
|
+
require "happymapper"
|
15
|
+
require "nokogiri"
|
16
|
+
require "configatron"
|
17
|
+
require "active_support/inflector"
|
18
|
+
|
19
|
+
require "ramazon/rails_additions"
|
20
|
+
|
21
|
+
require "ramazon/abstract_element"
|
22
|
+
|
23
|
+
require "ramazon/signatory"
|
24
|
+
require "ramazon/configuration"
|
25
|
+
require "ramazon/error"
|
26
|
+
require "ramazon/request"
|
27
|
+
|
28
|
+
require "ramazon/image"
|
29
|
+
require "ramazon/price"
|
30
|
+
require "ramazon/product"
|
31
|
+
require "ramazon/product_collection"
|
32
|
+
require "ramazon/browse_node"
|
33
|
+
|
34
|
+
configatron.ramazon.locale = :us
|
data/lib/root_nodes.yml
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
---
|
2
|
+
Apparel (Kids & Baby): "1040662"
|
3
|
+
Baby: "165796011"
|
4
|
+
Gourmet Food: "3370831"
|
5
|
+
Home Appliances: "361395011"
|
6
|
+
Camera & Photo: "502394"
|
7
|
+
All Sports & Outdoors: "3375251"
|
8
|
+
Health & Personal Care: "3760901"
|
9
|
+
Cell Phones & Service: "301185"
|
10
|
+
Home Audio & Theater: "667846011"
|
11
|
+
Power & Hand Tools: "328182011"
|
12
|
+
Golf: "3410851"
|
13
|
+
Team Sports: "706809011"
|
14
|
+
Action Sports: "706812011"
|
15
|
+
Vacuums & Storage: "510080"
|
16
|
+
Office Products & Supplies: "1064954"
|
17
|
+
Magazines: "1263069011"
|
18
|
+
Industrial & Scientific: "16310091"
|
19
|
+
Jewelry: "3367581"
|
20
|
+
Pet Supplies: "12923371"
|
21
|
+
Home Improvement: "228013"
|
22
|
+
Bedding & Bath: "1057792"
|
23
|
+
TV & Video: "1266092011"
|
24
|
+
Computers & Accessories: "541966"
|
25
|
+
Books: "4"
|
26
|
+
Motorcycle & ATV: "346333011"
|
27
|
+
Outdoor Power Equipment: "551242"
|
28
|
+
Outdoor Recreation: "706814011"
|
29
|
+
Natural & Organic: "51537011"
|
30
|
+
"Furniture & D\xC3\xA9cor": "1057794"
|
31
|
+
Newspapers: "1263068011"
|
32
|
+
MP3 & Media Players: "172630"
|
33
|
+
Accessories: "1268192011"
|
34
|
+
MP3 Downloads: "163856011"
|
35
|
+
Kindle Books: "1286228011"
|
36
|
+
Toys & Games: "165793011"
|
37
|
+
Beauty: "3760911"
|
38
|
+
Grocery: "16310101"
|
39
|
+
Blogs: "401358011"
|
40
|
+
Video Games: "471306"
|
41
|
+
Cycling: "3403201"
|
42
|
+
Watches: "377110011"
|
43
|
+
Movies: "163414"
|
44
|
+
Sewing, Craft & Hobby: "12890711"
|
45
|
+
Software: "229548"
|
46
|
+
Kindle Store: "133141011"
|
47
|
+
Game Downloads: "979455011"
|
48
|
+
Video On Demand: "16261631"
|
49
|
+
Movies & TV: "130"
|
50
|
+
Textbooks: "465600"
|
51
|
+
Shoes: "672123011"
|
52
|
+
Apparel & Accessories: "1036592"
|
53
|
+
Kitchen & Dining: "284507"
|
54
|
+
Amazon Shorts: "13993911"
|
55
|
+
Blu-ray: "193640011"
|
56
|
+
Car Electronics & GPS: "1077068"
|
57
|
+
PC Games: "229575"
|
58
|
+
Computer Components: "193870011"
|
59
|
+
Music: "173425"
|
60
|
+
Automotive: "15684181"
|
61
|
+
Fan Gear: "3386071"
|
62
|
+
Exercise & Fitness: "3407731"
|
63
|
+
Patio, Lawn & Garden: "286168"
|
64
|
+
Musical Instruments: "11091801"
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{ramazon_advertising}
|
5
|
+
s.version = "0.1.1"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Dan Pickett"]
|
9
|
+
s.date = %q{2009-09-08}
|
10
|
+
s.description = %q{TODO: longer description of your gem}
|
11
|
+
s.email = %q{dpickett@enlightsolutions.com}
|
12
|
+
s.extra_rdoc_files = [
|
13
|
+
"LICENSE",
|
14
|
+
"README.rdoc"
|
15
|
+
]
|
16
|
+
s.files = [
|
17
|
+
".document",
|
18
|
+
".gitignore",
|
19
|
+
"LICENSE",
|
20
|
+
"README.rdoc",
|
21
|
+
"Rakefile",
|
22
|
+
"VERSION",
|
23
|
+
"features/friendly_errors.feature",
|
24
|
+
"features/generate_root_browse_nodes.feature",
|
25
|
+
"features/retrieving_a_product.feature",
|
26
|
+
"features/searching_for_products.feature",
|
27
|
+
"features/step_definitions/auth_steps.rb",
|
28
|
+
"features/step_definitions/browse_node_steps.rb",
|
29
|
+
"features/step_definitions/error_steps.rb",
|
30
|
+
"features/step_definitions/product_steps.rb",
|
31
|
+
"features/step_definitions/ramazon_advertising_steps.rb",
|
32
|
+
"features/support/env.rb",
|
33
|
+
"features/support/ramazon_advertising.example.yml",
|
34
|
+
"lib/ramazon/abstract_element.rb",
|
35
|
+
"lib/ramazon/browse_node.rb",
|
36
|
+
"lib/ramazon/configuration.rb",
|
37
|
+
"lib/ramazon/error.rb",
|
38
|
+
"lib/ramazon/image.rb",
|
39
|
+
"lib/ramazon/price.rb",
|
40
|
+
"lib/ramazon/product.rb",
|
41
|
+
"lib/ramazon/product_collection.rb",
|
42
|
+
"lib/ramazon/rails_additions.rb",
|
43
|
+
"lib/ramazon/request.rb",
|
44
|
+
"lib/ramazon/signatory.rb",
|
45
|
+
"lib/ramazon_advertising.rb",
|
46
|
+
"lib/root_nodes.yml",
|
47
|
+
"lib/tasks/ramazon.rake",
|
48
|
+
"ramazon_advertising.gemspec",
|
49
|
+
"spec/ramazon/configuration_spec.rb",
|
50
|
+
"spec/spec_helper.rb"
|
51
|
+
]
|
52
|
+
s.homepage = %q{http://github.com/dpickett/ramazon_advertising}
|
53
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
54
|
+
s.require_paths = ["lib"]
|
55
|
+
s.rubygems_version = %q{1.3.5}
|
56
|
+
s.summary = %q{TODO: one-line summary of your gem}
|
57
|
+
s.test_files = [
|
58
|
+
"spec/ramazon/configuration_spec.rb",
|
59
|
+
"spec/spec_helper.rb"
|
60
|
+
]
|
61
|
+
|
62
|
+
if s.respond_to? :specification_version then
|
63
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
64
|
+
s.specification_version = 3
|
65
|
+
|
66
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
67
|
+
s.add_runtime_dependency(%q<jnunemaker-httparty>, [">= 0.4.3"])
|
68
|
+
s.add_runtime_dependency(%q<jnunemaker-happymapper>, [">= 0.2.5"])
|
69
|
+
s.add_runtime_dependency(%q<mislav-will_paginate>, [">= 2.3.11"])
|
70
|
+
s.add_runtime_dependency(%q<nokogiri>, [">= 1.3.3"])
|
71
|
+
s.add_runtime_dependency(%q<configatron>, [">= 2.4.1"])
|
72
|
+
s.add_runtime_dependency(%q<rails>, [">= 2.3.3"])
|
73
|
+
s.add_runtime_dependency(%q<ruby-hmac>, [">= 0.3.2"])
|
74
|
+
else
|
75
|
+
s.add_dependency(%q<jnunemaker-httparty>, [">= 0.4.3"])
|
76
|
+
s.add_dependency(%q<jnunemaker-happymapper>, [">= 0.2.5"])
|
77
|
+
s.add_dependency(%q<mislav-will_paginate>, [">= 2.3.11"])
|
78
|
+
s.add_dependency(%q<nokogiri>, [">= 1.3.3"])
|
79
|
+
s.add_dependency(%q<configatron>, [">= 2.4.1"])
|
80
|
+
s.add_dependency(%q<rails>, [">= 2.3.3"])
|
81
|
+
s.add_dependency(%q<ruby-hmac>, [">= 0.3.2"])
|
82
|
+
end
|
83
|
+
else
|
84
|
+
s.add_dependency(%q<jnunemaker-httparty>, [">= 0.4.3"])
|
85
|
+
s.add_dependency(%q<jnunemaker-happymapper>, [">= 0.2.5"])
|
86
|
+
s.add_dependency(%q<mislav-will_paginate>, [">= 2.3.11"])
|
87
|
+
s.add_dependency(%q<nokogiri>, [">= 1.3.3"])
|
88
|
+
s.add_dependency(%q<configatron>, [">= 2.4.1"])
|
89
|
+
s.add_dependency(%q<rails>, [">= 2.3.3"])
|
90
|
+
s.add_dependency(%q<ruby-hmac>, [">= 0.3.2"])
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Ramazon::Configuration do
|
4
|
+
it "should default to the us" do
|
5
|
+
Ramazon::Configuration.locale.should eql(:us)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should raise an exception if a set a locale that doesn't exist" do
|
9
|
+
lambda {Ramazon::Configuration.locale = :ffsf}.should raise_error
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should derive a url of the locale" do
|
13
|
+
Ramazon::Configuration.locale = :de
|
14
|
+
Ramazon::Configuration.base_uri.should =~ /#{Ramazon::Configuration.locale.to_s}/
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should use .com for a us address" do
|
18
|
+
Ramazon::Configuration.locale = :us
|
19
|
+
Ramazon::Configuration.base_uri.should =~ /\.com/
|
20
|
+
end
|
21
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,157 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dpickett-ramazon_advertising
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Dan Pickett
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-09-08 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: jnunemaker-httparty
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.4.3
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: jnunemaker-happymapper
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.2.5
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: mislav-will_paginate
|
37
|
+
type: :runtime
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 2.3.11
|
44
|
+
version:
|
45
|
+
- !ruby/object:Gem::Dependency
|
46
|
+
name: nokogiri
|
47
|
+
type: :runtime
|
48
|
+
version_requirement:
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 1.3.3
|
54
|
+
version:
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: configatron
|
57
|
+
type: :runtime
|
58
|
+
version_requirement:
|
59
|
+
version_requirements: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: 2.4.1
|
64
|
+
version:
|
65
|
+
- !ruby/object:Gem::Dependency
|
66
|
+
name: rails
|
67
|
+
type: :runtime
|
68
|
+
version_requirement:
|
69
|
+
version_requirements: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: 2.3.3
|
74
|
+
version:
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: ruby-hmac
|
77
|
+
type: :runtime
|
78
|
+
version_requirement:
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: 0.3.2
|
84
|
+
version:
|
85
|
+
description: "TODO: longer description of your gem"
|
86
|
+
email: dpickett@enlightsolutions.com
|
87
|
+
executables: []
|
88
|
+
|
89
|
+
extensions: []
|
90
|
+
|
91
|
+
extra_rdoc_files:
|
92
|
+
- LICENSE
|
93
|
+
- README.rdoc
|
94
|
+
files:
|
95
|
+
- .document
|
96
|
+
- .gitignore
|
97
|
+
- LICENSE
|
98
|
+
- README.rdoc
|
99
|
+
- Rakefile
|
100
|
+
- VERSION
|
101
|
+
- features/friendly_errors.feature
|
102
|
+
- features/generate_root_browse_nodes.feature
|
103
|
+
- features/retrieving_a_product.feature
|
104
|
+
- features/searching_for_products.feature
|
105
|
+
- features/step_definitions/auth_steps.rb
|
106
|
+
- features/step_definitions/browse_node_steps.rb
|
107
|
+
- features/step_definitions/error_steps.rb
|
108
|
+
- features/step_definitions/product_steps.rb
|
109
|
+
- features/step_definitions/ramazon_advertising_steps.rb
|
110
|
+
- features/support/env.rb
|
111
|
+
- features/support/ramazon_advertising.example.yml
|
112
|
+
- lib/ramazon/abstract_element.rb
|
113
|
+
- lib/ramazon/browse_node.rb
|
114
|
+
- lib/ramazon/configuration.rb
|
115
|
+
- lib/ramazon/error.rb
|
116
|
+
- lib/ramazon/image.rb
|
117
|
+
- lib/ramazon/price.rb
|
118
|
+
- lib/ramazon/product.rb
|
119
|
+
- lib/ramazon/product_collection.rb
|
120
|
+
- lib/ramazon/rails_additions.rb
|
121
|
+
- lib/ramazon/request.rb
|
122
|
+
- lib/ramazon/signatory.rb
|
123
|
+
- lib/ramazon_advertising.rb
|
124
|
+
- lib/root_nodes.yml
|
125
|
+
- lib/tasks/ramazon.rake
|
126
|
+
- ramazon_advertising.gemspec
|
127
|
+
- spec/ramazon/configuration_spec.rb
|
128
|
+
- spec/spec_helper.rb
|
129
|
+
has_rdoc: false
|
130
|
+
homepage: http://github.com/dpickett/ramazon_advertising
|
131
|
+
post_install_message:
|
132
|
+
rdoc_options:
|
133
|
+
- --charset=UTF-8
|
134
|
+
require_paths:
|
135
|
+
- lib
|
136
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
137
|
+
requirements:
|
138
|
+
- - ">="
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
version: "0"
|
141
|
+
version:
|
142
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
143
|
+
requirements:
|
144
|
+
- - ">="
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
version: "0"
|
147
|
+
version:
|
148
|
+
requirements: []
|
149
|
+
|
150
|
+
rubyforge_project:
|
151
|
+
rubygems_version: 1.2.0
|
152
|
+
signing_key:
|
153
|
+
specification_version: 3
|
154
|
+
summary: "TODO: one-line summary of your gem"
|
155
|
+
test_files:
|
156
|
+
- spec/ramazon/configuration_spec.rb
|
157
|
+
- spec/spec_helper.rb
|