ramazon_advertising 0.3.2
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 +46 -0
- data/Rakefile +84 -0
- data/VERSION +1 -0
- data/features/friendly_errors.feature +15 -0
- data/features/generate_root_browse_nodes.feature +13 -0
- data/features/getting_offer_details.feature +30 -0
- data/features/getting_search_bins.feature +22 -0
- data/features/retrieve_browse_node_information.feature +16 -0
- data/features/retrieving_a_product.feature +32 -0
- data/features/searching_for_products.feature +47 -0
- data/features/step_definitions/auth_steps.rb +8 -0
- data/features/step_definitions/browse_node_steps.rb +55 -0
- data/features/step_definitions/error_steps.rb +14 -0
- data/features/step_definitions/product_collection_steps.rb +20 -0
- data/features/step_definitions/product_steps.rb +75 -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 +69 -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/merchant.rb +21 -0
- data/lib/ramazon/offer.rb +27 -0
- data/lib/ramazon/price.rb +18 -0
- data/lib/ramazon/product.rb +303 -0
- data/lib/ramazon/product_collection.rb +30 -0
- data/lib/ramazon/rails_additions.rb +99 -0
- data/lib/ramazon/request.rb +82 -0
- data/lib/ramazon/search_bin.rb +11 -0
- data/lib/ramazon/search_bin_parameter.rb +9 -0
- data/lib/ramazon/search_bin_set.rb +11 -0
- data/lib/ramazon/signatory.rb +10 -0
- data/lib/ramazon_advertising.rb +41 -0
- data/lib/root_nodes.yml +64 -0
- data/lib/tasks/ramazon.rake +9 -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,46 @@
|
|
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
|
+
More documentation at http://rdoc.info/projects/dpickett/ramazon_advertising
|
25
|
+
|
26
|
+
== What's under the hood?
|
27
|
+
|
28
|
+
* HTTParty
|
29
|
+
* HappyMapper
|
30
|
+
* will_paginate
|
31
|
+
* nokogiri
|
32
|
+
|
33
|
+
== Note on Patches/Pull Requests
|
34
|
+
|
35
|
+
* Fork the project.
|
36
|
+
* Make your feature addition or bug fix.
|
37
|
+
* Add tests for it. This is important so I don't break it in a
|
38
|
+
future version unintentionally.
|
39
|
+
* Commit, do not mess with rakefile, version, or history.
|
40
|
+
(if you want to have your own version, that is fine but
|
41
|
+
bump version in a commit by itself I can ignore when I pull)
|
42
|
+
* Send me a pull request. Bonus points for topic branches.
|
43
|
+
|
44
|
+
== Copyright
|
45
|
+
|
46
|
+
Copyright (c) 2009 Dan Pickett. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,84 @@
|
|
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{Amazon Advertising Wrapper - Use nokogiri to target the elements you want}
|
12
|
+
gem.description = %Q{ Amazon Advertising Wrapper - Use nokogiri to target the elements you want. An object oriented approach}
|
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("ruby-hmac", ">= 0.3.2")
|
22
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
23
|
+
end
|
24
|
+
|
25
|
+
rescue LoadError
|
26
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
27
|
+
end
|
28
|
+
|
29
|
+
require 'spec/rake/spectask'
|
30
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
31
|
+
spec.libs << 'lib' << 'spec'
|
32
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
33
|
+
end
|
34
|
+
|
35
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
36
|
+
spec.libs << 'lib' << 'spec'
|
37
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
38
|
+
spec.rcov = true
|
39
|
+
end
|
40
|
+
|
41
|
+
task :default => :spec
|
42
|
+
|
43
|
+
begin
|
44
|
+
require 'cucumber/rake/task'
|
45
|
+
Cucumber::Rake::Task.new(:cucumber)
|
46
|
+
rescue LoadError
|
47
|
+
task :cucumber do
|
48
|
+
abort "Cucumber is not available. In order to run features, you must: sudo gem install cucumber"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
task :default => :test
|
53
|
+
|
54
|
+
require 'rake/rdoctask'
|
55
|
+
Rake::RDocTask.new do |rdoc|
|
56
|
+
if File.exist?('VERSION.yml')
|
57
|
+
config = YAML.load(File.read('VERSION.yml'))
|
58
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
59
|
+
else
|
60
|
+
version = ""
|
61
|
+
end
|
62
|
+
|
63
|
+
rdoc.rdoc_dir = 'rdoc'
|
64
|
+
rdoc.title = "ramazon_advertising #{version}"
|
65
|
+
rdoc.rdoc_files.include('README*')
|
66
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
67
|
+
end
|
68
|
+
|
69
|
+
begin
|
70
|
+
require "YARD"
|
71
|
+
if File.exist?('VERSION.yml')
|
72
|
+
config = YAML.load(File.read('VERSION.yml'))
|
73
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
74
|
+
else
|
75
|
+
version = ""
|
76
|
+
end
|
77
|
+
|
78
|
+
YARD::Rake::YardocTask.new do |t|
|
79
|
+
end
|
80
|
+
rescue LoadError
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.3.2
|
@@ -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,13 @@
|
|
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 root 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
|
13
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
Feature: Retrieving offer details
|
2
|
+
As a user of the Ramazon_advertising api
|
3
|
+
I want to get offer details
|
4
|
+
In order to get pricing information about a given product
|
5
|
+
|
6
|
+
Background:
|
7
|
+
Given I have a valid access key
|
8
|
+
And I have a valid secret key
|
9
|
+
|
10
|
+
Scenario: Getting offers on a DVD
|
11
|
+
Given I am searching with the "item_id" of "B000NTPDSW"
|
12
|
+
And I am searching with the "condition" of "Used"
|
13
|
+
And I am searching with the "response_group" of "Medium,OfferListings"
|
14
|
+
And I am searching with the "merchant_id" of "All"
|
15
|
+
When I perform the product search
|
16
|
+
Then I should get a product
|
17
|
+
And the product should have "offers"
|
18
|
+
And each of the product's "offers" should have a "price"
|
19
|
+
And each of the product's "offers" should have a "condition"
|
20
|
+
And each of the product's "offers" should have a "sub_condition"
|
21
|
+
And the product should have "used_offers"
|
22
|
+
And each of the product's "used_offers" should have a "condition" of "Used"
|
23
|
+
|
24
|
+
Scenario: Getting offers by subcondition hash
|
25
|
+
Given I am searching with the "item_id" of "B000NTPDSW"
|
26
|
+
And I am searching with the "response_group" of "Medium,OfferListings"
|
27
|
+
When I perform the product search
|
28
|
+
Then I should get a product
|
29
|
+
And the product should have "offers_by_condition"
|
30
|
+
And the product should have "lowest_offers"
|
@@ -0,0 +1,22 @@
|
|
1
|
+
Feature: As a user of the Ramazon Advertising wrapper
|
2
|
+
I want to get search bins
|
3
|
+
So I can efficiently narrow down results
|
4
|
+
|
5
|
+
Scenario: Getting DVD search bins
|
6
|
+
Given I am searching with the "search_index" of "DVD"
|
7
|
+
And I am searching with the "response_group" of "Medium,SearchBins"
|
8
|
+
And I am searching with the "browse_node" of "130"
|
9
|
+
When I perform the product search
|
10
|
+
Then I should get a list of products
|
11
|
+
And the list of products should have more than 1 product
|
12
|
+
And the list of products should have "search_bin_sets"
|
13
|
+
And each "search_bin_set" in the list of products should have "search_bins"
|
14
|
+
|
15
|
+
Scenario: Getting Genre search bins
|
16
|
+
Given I am searching with the "search_index" of "DVD"
|
17
|
+
And I am searching with the "response_group" of "Medium,SearchBins"
|
18
|
+
And I am searching with the "browse_node" of "405391011"
|
19
|
+
When I perform the product search
|
20
|
+
Then I should get a list of products
|
21
|
+
And the list of products should have "search_bin_sets"
|
22
|
+
And each "search_bin_set" in the list of products should have "search_bins"
|
@@ -0,0 +1,16 @@
|
|
1
|
+
Feature: As a user of the ramazon advertising api
|
2
|
+
I want to get browse node information
|
3
|
+
So I can retrieve Amazon product data more effectively
|
4
|
+
|
5
|
+
Scenario: Fetch "Other Video Games" Node
|
6
|
+
Given I want browse node information for the node "294940"
|
7
|
+
When I retrieve the browse node
|
8
|
+
Then the browse node should have a name
|
9
|
+
And the browse node should have "children"
|
10
|
+
And the browse node should have a "child_hash"
|
11
|
+
|
12
|
+
Scenario: Fetch "DVD" Node
|
13
|
+
Given I want browse node information for the node "130"
|
14
|
+
When I retrieve the browse node
|
15
|
+
Then the browse node should have a name
|
16
|
+
And the browse node should have "children"
|
@@ -0,0 +1,32 @@
|
|
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
|
+
Scenario: Finding a DVD
|
11
|
+
When I try to find the asin "B000NU2CY4"
|
12
|
+
Then I should get a product
|
13
|
+
And the product should have the "title" "Gladiator [Blu-ray]"
|
14
|
+
And the product should have a "manufacturer"
|
15
|
+
And the product should have a "product_group"
|
16
|
+
And the product should have a "sales_rank"
|
17
|
+
And the product should have a "large_image"
|
18
|
+
And the product should have a "list_price"
|
19
|
+
And the product should have a "upc"
|
20
|
+
And the product should have a "lowest_new_price"
|
21
|
+
And the product should have a "new_count"
|
22
|
+
And the product should have a "used_count"
|
23
|
+
|
24
|
+
Scenario: Finding a Video Game with a deep category tree
|
25
|
+
When I try to find the asin "B001COQW14"
|
26
|
+
Then I should get a product
|
27
|
+
And the product should have a category tree for "Categories"
|
28
|
+
|
29
|
+
Scenario: Finding a Movie with a Genre tree
|
30
|
+
When I try to find the asin "B002CMLIJ6"
|
31
|
+
Then I should get a product
|
32
|
+
And the product should have a category tree for "Genres"
|
@@ -0,0 +1,47 @@
|
|
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
|
+
Scenario: Finding dvds
|
11
|
+
Given I am searching with the "search_index" of "DVD"
|
12
|
+
And I am searching with the "browse_node" of "130"
|
13
|
+
When I perform the product search
|
14
|
+
Then I should get a list of products
|
15
|
+
And the list of products should have more than 1 product
|
16
|
+
And each product should have the "product_group" "DVD"
|
17
|
+
|
18
|
+
Scenario: Finding video games
|
19
|
+
Given I am searching with the "search_index" of "VideoGames"
|
20
|
+
And I am searching with the "response_group" of "Medium,BrowseNodes"
|
21
|
+
And I am searching with the "browse_node" of "294940"
|
22
|
+
When I perform the product search
|
23
|
+
Then I should get a list of products
|
24
|
+
And the list of products should have more than 1 product
|
25
|
+
|
26
|
+
Scenario: Finding multiple browse nodes
|
27
|
+
Given I am searching with the "search_index" of "VideoGames"
|
28
|
+
And I am searching with the "response_group" of "Medium"
|
29
|
+
And I am searching with the "browse_node" of "11075221"
|
30
|
+
When I perform the product search
|
31
|
+
Then I should get a list of products
|
32
|
+
And the list of products should have more than 1 product
|
33
|
+
And each product should have the "product_group" "Video Games"
|
34
|
+
|
35
|
+
Scenario: Finding All Offers optimization
|
36
|
+
Given I am searching with the "search_index" of "VideoGames"
|
37
|
+
And I am searching with the "response_group" of "Medium,OfferListings"
|
38
|
+
And I am searching with the "merchant_id" of "All"
|
39
|
+
And I am searching with the "condition" of "All"
|
40
|
+
And I am searching with the "offer_page" of "1"
|
41
|
+
And I am searching with the "browse_node" of "11075221"
|
42
|
+
When I perform the product search
|
43
|
+
Then I should get a list of products
|
44
|
+
And each product should have "offer_pages"
|
45
|
+
And each product should have "has_first_page_of_full_offers"
|
46
|
+
|
47
|
+
|
@@ -0,0 +1,55 @@
|
|
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
|
+
Given /^I want browse node information for the node "([^\"]*)"$/ do |node_id|
|
10
|
+
@node_id = node_id
|
11
|
+
end
|
12
|
+
|
13
|
+
When /^I retrieve root nodes$/ do
|
14
|
+
if @browse_root_filename
|
15
|
+
Ramazon::BrowseNode.generate_root_nodes @browse_root_filename
|
16
|
+
else
|
17
|
+
Ramazon::BrowseNode.generate_root_nodes
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
When /^I retrieve the browse node$/ do
|
22
|
+
@node = Ramazon::BrowseNode.find(@node_id)
|
23
|
+
end
|
24
|
+
|
25
|
+
Then /^the browse node should have a name$/ do
|
26
|
+
@node.name.should_not be_nil
|
27
|
+
end
|
28
|
+
|
29
|
+
Then /^the browse node should have (a\s)?"(.*)"$/ do |a, attr|
|
30
|
+
@node.send(attr).should_not be_nil
|
31
|
+
if @node.send(attr).respond_to?(:empty?)
|
32
|
+
@node.send(attr).should_not be_empty
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
Then /^I should get a temporary file for root nodes$/ do
|
37
|
+
FileTest.exists?(@browse_root_filename).should be_true
|
38
|
+
end
|
39
|
+
|
40
|
+
Then /^I should have a "([^\"]*)" root node$/ do |name|
|
41
|
+
get_root_nodes[name].should_not be_nil
|
42
|
+
end
|
43
|
+
|
44
|
+
Then /^I should have a list of root nodes$/ do
|
45
|
+
get_root_nodes.should_not be_empty
|
46
|
+
end
|
47
|
+
|
48
|
+
def get_root_nodes
|
49
|
+
if @browse_root_filename
|
50
|
+
nodes = Ramazon::BrowseNode.root_nodes(@browse_root_filename)
|
51
|
+
else
|
52
|
+
nodes = Ramazon::BrowseNode.root_nodes
|
53
|
+
end
|
54
|
+
|
55
|
+
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,20 @@
|
|
1
|
+
Then /^the list of products should have "([^\"]*)"$/ do |attr|
|
2
|
+
@products.send(attr).should_not be_nil
|
3
|
+
if @products.respond_to?(:empty?)
|
4
|
+
@products.should_not be_empty
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
Then /^each "([^\"]*)" in the list of products should have "([^\"]*)"$/ do |collection, attr|
|
9
|
+
collection = collection.pluralize
|
10
|
+
@products.send(collection).should_not be_nil
|
11
|
+
@products.send(collection).should_not be_empty
|
12
|
+
|
13
|
+
@products.send(collection).each do |i|
|
14
|
+
i.send(attr).should_not be_nil
|
15
|
+
if i.send(attr).respond_to?(:empty?)
|
16
|
+
i.send(attr).should_not be_empty
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|