Permalink
Browse files

New functionality - load_from_amazon - loads Amazon data directly int…

…o your application's objects. See README for more.
  • Loading branch information...
1 parent 8b8010b commit e8d53cde77a8601201ecc441362904b2993136c0 @cbeck cbeck committed Oct 12, 2008
Showing with 193 additions and 15 deletions.
  1. +6 −0 CHANGELOG
  2. +80 −3 README
  3. +1 −1 Rakefile
  4. +45 −7 lib/acts_as_amazon_product.rb
  5. +58 −0 test/acts_as_amazon_product_test.rb
  6. +3 −4 test/config-example.yml
View
@@ -1,3 +1,9 @@
+= 1.4
+=== 11th October, 2008
+* Added load_from_amazon and load_from_amazon! methods to load up Amazon attributes into a local object directly (see the README for usage)
+* Added new tests for the above functionality
+* Changed the response group so that it is a passed-in option, with the default being 'Medium' rather than having it hardcoded.
+
= 1.3
=== 11th August, 2008
* (Thanks to David Eisinger for changes in this release)
View
@@ -13,20 +13,30 @@ Run the following to create the migration:
== Using acts_as_amazon_product
-Add the "acts_as" line to your model. Only :access_key is required. The :asin and
+Add the "acts_as" line to your model. Only :access_key is required. The :asin and
:name assignments override what attributes in your class should be used for a direct
-lookup (item_looup) or general search (item_search). If no value is assigned to :asin
+lookup (item_lookup) or general search (item_search). If no value is assigned to :asin
then a general search is made and the first item (sorted by salesrank) is assigned
(this functionality will likely change).
+There are two ways to use Acts as Amazon Product. The first is to simply allow all of the Amazon data for your product
+to be stored in the AmazonProduct object:
+
require 'acts_as_amazon_product'
class Book < ActiveRecord::Base
acts_as_amazon_product :asin => 'isbn', :name => 'title',
:access_key => '0123456', :associate_tag => 'assoc-20'
end
+
+Other interesting options for the acts_as_amazon call:
-You can now access the Amazon data in your views like so (see):
+:search_index => 'Books' (default value if not specified)
+:response_group => 'Medium' (default value if not specified)
+:auto_load_fields => see below
+:ignore_fields => see below
+
+You can now access the Amazon data in your views like so:
@book = Book.new(:title => 'Getting Things Done')
@book.amazon.isbn
@@ -36,6 +46,73 @@ You can now access the Amazon data in your views like so (see):
or
@book.amazon.get('itemattributes/foobar')
+
+The second way to use Acts as Amazon Product is to load the Amazon data directly into an existing object in your application.
+You can elect to have the Amazon data loaded into a new, unsaved object, or go one step further and allow
+Acts as Amazon Product to load the data and save the loaded object all in one shot. The mapping between the
+Amazon fields and your object are declared by passing a hash called auto_load_fields to the acts_as_amazon_product
+call as shown below:
+
+ require 'acts_as_amazon_product'
+
+ class LocalBook < ActiveRecord::Base
+ acts_as_amazon_product(
+ :asin => 'isbn', :name => 'title',
+ :access_key => '0123456', :associate_tag => 'assoc-20',
+ :auto_load_fields => {:title => 'title', :isbn => 'asin', :publisher_name => 'manufacturer', :author => 'author'}
+ )
+ end
+
+The keys in the auto_load_fields hash are the fields in your object, and the values are their Amazon equivalents.
+In the true Rails convention over configuration spirit, we have included some of the most popular auto load fields as
+defaults. If your object contains the following attributes, they will automatically be loaded with their Amazon equivalents:
+
+:auto_load_fields => {:title => 'title', :isbn => 'asin', :publisher_name => 'manufacturer',
+ :author => 'author', :binding => 'binding', :list_price => 'listprice/amount', :pages => 'numberofpages',
+ :small_image_url => 'smallimage/url', :medium_image_url => 'mediumimage/url',
+ :large_image_url => 'largeimage/url', :detail_url => 'detailpageurl'}
+
+Don't worry if your object does not have some or most of these fields; Acts as Amazon Product is smart enough to check to see
+if the method exists before attempting to load up the Amazon value.
+
+Now, if by some coincidence you have some fields in your local object that have the same name as our auto_load defaults, you
+can choose to specifically ignore those fields using, you guessed it, :ignore_fields. :ignore_fields is a simple array of
+fields in your object that you do NOT want to be replaced with the Amazon equivalent. You would specify them like this:
+
+ acts_as_amazon_product(
+ :asin => 'isbn', :name => 'title',
+ :access_key => '0123456', :associate_tag => 'assoc-20',
+ :ignore_fields => [:small_image_url, :medium_image_url]
+ )
+
+In this instance, the object in question would load Amazon info for any of the default fields that the object has, with the
+exception of the small_image_url and medium_image_url values.
+
+To load the Amazon values into your object for local storage, you may call the following methods:
+
+load_from_amazon(title_or_asin, by_title = false)
+load_from_amazon!(title_or_asin, by_title = false)
+
+The two methods do exactly the same thing, except the first simply returns a new, unsaved object with its Amazon-like fields
+loaded with their appropriate Amazon values. The second version (load_from_amazon!) goes one step further and saves the
+object for good measure.
+
+You may pass either an isbn (asin) or a title in. If you are passing a title, you need to specify that you are passing a
+title by also passing true as the second parameter so we will perform an item_search rather than an item_lookup. Note that
+passing a title is risky business, since currently the code will take the first result returned and load that into your
+object. So unless your title is unique, I would recommend sticking with the isbn (asin) value. Since that is the default,
+all you need to do would be something like this:
+
+@local_book = LocalBook.load_from_amazon('1590598415')
+
+If your object was similar to the LocalBook object in our example, for instance, you would not need to specify the
+auto_load_fields in the acts_as call at all. The title, isbn, publisher_name and author methods would automatically
+be called once you call load_from_amazon, and their values populated appropriately.
+
+It is important to note that if you call load_from_amazon with an invalid asin, you will simply get back an empty object.
+
+Now, calling load_from_amazon still allows you to take advantage of the other Acts as Amazon features, like the dynamic
+loading of Amazon attributes on the fly by using the various amazon.foo methods as described above.
== Unit tests
View
@@ -10,7 +10,7 @@ include FileUtils
NAME = "acts_as_amazon_product"
# REV = File.read(".svn/entries")[/committed-rev="(\d+)"/, 1] rescue nil
# VERS = ENV['VERSION'] || ("1.1" + (REV ? ".#{REV}" : ""))
-VERS = ENV['VERSION'] || "1.3.1"
+VERS = ENV['VERSION'] || "1.4"
CLEAN.include ['**/.*.sw?', '*.gem', '.config', 'test/test.log']
desc 'Default: run unit tests.'
@@ -22,17 +22,27 @@ def acts_as_amazon_product(options = {})
:name => 'name',
:access_key => ENV['AMAZON_ACCESS_KEY_ID'],
:associate_tag => ENV['AMAZON_ASSOCIATE_TAG'],
- :search_index => 'Books'
+ :search_index => 'Books',
+ :response_group => 'Medium',
+ :auto_load_fields => {:title => 'title', :isbn => 'asin', :publisher_name => 'manufacturer',
+ :author => 'author', :binding => 'binding', :list_price => 'listprice/amount', :pages => 'numberofpages',
+ :small_image_url => 'smallimage/url', :medium_image_url => 'mediumimage/url',
+ :large_image_url => 'largeimage/url', :detail_url => 'detailpageurl'},
+ :ignore_fields => []
}
options = defaults.merge options
- Amazon::Ecs.options = {:aWS_access_key_id => options[:access_key], :associate_tag => options[:associate_tag] }
+ Amazon::Ecs.options = {:aWS_access_key_id => options[:access_key],
+ :associate_tag => options[:associate_tag], :response_group => options[:response_group]}
write_inheritable_attribute(:amazon_asin, options[:asin])
write_inheritable_attribute(:amazon_name, options[:name])
write_inheritable_attribute(:amazon_search_index, options[:search_index])
write_inheritable_attribute(:amazon_associate_key, options[:associate_key])
+ write_inheritable_attribute(:auto_load_fields, options[:auto_load_fields])
+ write_inheritable_attribute(:ignore_fields, options[:ignore_fields])
class_inheritable_reader :amazon_asin, :amazon_name, :amazon_search_index, :amazon_associate_key
+ class_inheritable_reader :auto_load_fields, :ignore_fields
has_one :amazon_product, :as => :amazonable #, :dependent => :delete
include Netphase::Acts::Amazonable::InstanceMethods
@@ -43,6 +53,37 @@ def acts_as_amazon_product(options = {})
# This module contains class methods
module SingletonMethods
+ def load_from_amazon(title_or_asin, by_title = false)
+ begin
+ unless by_title
+ res = Amazon::Ecs.item_lookup(title_or_asin)
+ else
+ res = Amazon::Ecs.item_search(title_or_asin)
+ end
+ create_instance_from_amazon(res.items[0])
+ rescue
+ return self.new
+ end
+ end
+
+ def load_from_amazon!(title_or_asin, by_title = false)
+ result = self.load_from_amazon(title_or_asin, by_title)
+ if result
+ result.save
+ return result
+ end
+ end
+
+ private
+ def create_instance_from_amazon(item)
+ newbie = self.new
+ self.auto_load_fields.each do |key, value|
+ if newbie.respond_to?(key.to_s) && !self.ignore_fields.include?(key)
+ newbie.send key.to_s + '=', item.get(value)
+ end
+ end
+ return newbie
+ end
end
@@ -58,15 +99,15 @@ def amazon
begin
if !asin.blank?
# puts "Looking up #{asin}"
- res = Amazon::Ecs.item_lookup(self.send(self.amazon_asin), :response_group => 'Medium')
+ res = Amazon::Ecs.item_lookup(self.send(self.amazon_asin))
self.amazon_product =
AmazonProduct.new(:xml => res.doc.to_html, :asin => res.doc.at('asin').inner_html)
self.amazon_product.save
elsif !name.blank?
# puts "Searching for #{name}"
res = Amazon::Ecs.item_search(self.send(self.amazon_name),
- :search_index => self.amazon_search_index, :response_group => 'Medium') #, :sort => 'salesrank'
+ :search_index => self.amazon_search_index) #, :sort => 'salesrank'
res = res.doc.at('items/item')
self.amazon_product =
AmazonProduct.new(:xml => res.to_html,
@@ -84,9 +125,6 @@ def amazon
self.amazon_product
end
- #def method_missing(method, *args)
- #end
-
def after_save
unless self.amazon_product.nil?
self.amazon_product.destroy
@@ -16,6 +16,7 @@
ActiveRecord::Base.connection.drop_table :books rescue nil
ActiveRecord::Base.connection.drop_table :movies rescue nil
ActiveRecord::Base.connection.drop_table :magazines rescue nil
+ActiveRecord::Base.connection.drop_table :local_books rescue nil
ActiveRecord::Base.connection.create_table :books do |t|
t.column :title, :string
@@ -33,6 +34,15 @@
t.column :asin, :string
end
+ActiveRecord::Base.connection.create_table :local_books do |t|
+ t.column :title, :string
+ t.column :author, :string
+ t.column :isbn, :string
+ t.column :publisher_name, :string
+ t.column :small_image_url, :string
+ t.column :medium_image_url, :string
+end
+
ActiveRecord::Base.connection.create_table :amazon_products do |t| # , :id => false
t.column :asin, :string
t.column :xml, :text
@@ -56,6 +66,13 @@ class Magazine < ActiveRecord::Base
:associate_tag => @@associate_tag
end
+class LocalBook < ActiveRecord::Base
+ acts_as_amazon_product(
+ :asin => 'isbn', :name => 'title',
+ :access_key => @@access_key, :associate_tag => @@associate_tag, :ignore_fields => [:small_image_url, :medium_image_url]
+ )
+end
+
AmazonProduct.delete_all
class ActAsAmazonProductTest < Test::Unit::TestCase
@@ -68,6 +85,9 @@ def setup
@movie_dh = Movie.create(:name=>'Live Free or Die Hard', :asin=>'B000VNMMRA')
Magazine.delete_all
@mag_lci = Magazine.create(:name => 'La Cucina Italiana')
+ LocalBook.delete_all
+ @local_rails = LocalBook.load_from_amazon('1590598415')
+ @local_roots = LocalBook.load_from_amazon!('Roots', true)
end
def test_isbn
@@ -140,4 +160,42 @@ def test_method_missing
def test_method_missing_with_separator
assert_equal 'Bruce Willis | Timothy Olyphant | Justin Long | Maggie Q | Cliff Curtis', @movie_dh.amazon.actor(' | ')
end
+
+ def test_load_local_book
+ assert_not_nil(@local_rails.title)
+ assert_not_nil(@local_rails.isbn)
+ assert_not_nil(@local_rails.publisher_name)
+ assert_not_nil(@local_rails.author)
+ assert_not_nil(@local_roots.title)
+ assert_not_nil(@local_roots.isbn)
+ assert_not_nil(@local_roots.publisher_name)
+ assert_not_nil(@local_roots.author)
+ assert_equal "Practical Rails Social Networking Sites (Expert's Voice)", @local_rails.title
+ assert_equal '0882667033', @local_roots.isbn
+ end
+
+ def test_new_versus_saved_load
+ assert_equal @local_rails.new_record?, true
+ assert_equal @local_roots.new_record?, false
+ end
+
+ def test_lack_of_initial_amazon_product_for_local
+ @local_woody = LocalBook.load_from_amazon!('0736412662')
+ assert_nil AmazonProduct.find_by_amazonable_id(@local_woody.id)
+ end
+
+ def test_ignore_fields
+ assert_nil @local_rails.small_image_url
+ assert_nil @local_rails.medium_image_url
+ assert_nil @local_roots.small_image_url
+ assert_nil @local_roots.medium_image_url
+ end
+
+ def test_locals_load_amazon_attributes_if_needed
+ assert_not_nil @local_rails.amazon.binding
+ assert_not_nil @local_roots.amazon.binding
+ assert_equal @local_rails.amazon.binding, 'Paperback'
+ assert_equal @local_roots.amazon.binding, 'Paperback'
+ end
+
end
@@ -1,8 +1,7 @@
-database:
+database:
adapter: sqlite3
dbfile: test/test.db
-
+
amazon:
access_key: 1AAAABBBBCCCCDDDDEE2
- associate_tag: assoc-20
-
+ associate_tag: assoc-20

0 comments on commit e8d53cd

Please sign in to comment.