ruby-aaws 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,102 @@
1
+ # $Id: locale.rb,v 1.3 2008/06/07 17:28:51 ianmacd Exp $
2
+ #
3
+
4
+ catch :done do
5
+
6
+ begin
7
+ require 'net/geoip'
8
+ rescue LoadError
9
+ throw :done
10
+ end
11
+
12
+ module Amazon
13
+
14
+ # Use of this module requires the use of the GeoIP library from
15
+ # MaxMind[http://www.maxmind.com/]. It also requires the
16
+ # net-geoip[http://rubyforge.org/scm/?group_id=947] Ruby module to
17
+ # interface with it.
18
+ #
19
+ # Load this library as follows:
20
+ #
21
+ # require 'amazon/locale'
22
+ #
23
+ module Locale
24
+
25
+ # These country lists are obviously not complete.
26
+
27
+ # ISO 3166[http://www.iso.ch/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/iso_3166-1_decoding_table.html]
28
+ # codes of countries likely to want to shop in the *CA* locale.
29
+ #
30
+ CA = %w[ ca ]
31
+
32
+ # ISO 3166 codes of countries likely to want to shop in the *DE* locale.
33
+ #
34
+ DE = %w[ at ch de ]
35
+
36
+ # ISO 3166 codes of countries likely to want to shop in the *FR* locale.
37
+ #
38
+ FR = %w[ fr ]
39
+
40
+ # ISO 3166 codes of countries likely to want to shop in the *JP* locale.
41
+ #
42
+ JP = %w[ jp ]
43
+
44
+ # ISO 3166 codes of countries likely to want to shop in the *UK* locale.
45
+ #
46
+ UK = %w[ ad al ba be cy cz dk ee es fi fo gg gi gr gl hu ie im is it je
47
+ li lt lu lv mk mt nl no pl pt ro se si sk sm uk ]
48
+
49
+ # ISO 3166 codes of countries likely to want to shop in the *US* locale.
50
+ # Any countries not explicitly listed above default to the *US* locale.
51
+ #
52
+ US = %w[ mx us ]
53
+
54
+ # Exception class for geolocation errors.
55
+ #
56
+ class GeoError < StandardError; end
57
+
58
+ def Locale.localise(code)
59
+ code.downcase!
60
+
61
+ return 'ca' if CA.include? code
62
+ return 'de' if DE.include? code
63
+ return 'fr' if FR.include? code
64
+ return 'jp' if JP.include? code
65
+ return 'uk' if UK.include? code
66
+
67
+ 'us'
68
+ end
69
+ private_class_method :localise
70
+
71
+
72
+ # This will attempt to return a reasonable locale (*ca*, *de*, *fr*,
73
+ # *jp*, *uk* or *us*) to use for _host_.
74
+ #
75
+ # Example:
76
+ #
77
+ # get_locale_by_name( 'xs1.xs4all.nl' ) => "uk"
78
+ #
79
+ def Locale.get_locale_by_name(host)
80
+ cc = Net::GeoIP.new.country_code_by_name( host )
81
+ raise GeoError, "invalid host: #{host}" unless cc
82
+ localise( cc )
83
+ end
84
+
85
+ # This will attempt to return a reasonable locale (*ca*, *de*, *fr*,
86
+ # *jp*, *uk* or *us*) to use for the IP address _address_.
87
+ #
88
+ # Example:
89
+ #
90
+ # get_locale_by_addr( '217.110.207.55' ) => "de"
91
+ #
92
+ def Locale.get_locale_by_addr(address)
93
+ cc = Net::GeoIP.new.country_code_by_addr( address )
94
+ raise GeoError, "invalid address: #{address}" unless cc
95
+ localise( cc )
96
+ end
97
+
98
+ end
99
+
100
+ end
101
+
102
+ end
data/lib/amazon.rb ADDED
@@ -0,0 +1,99 @@
1
+ # $Id: amazon.rb,v 1.18 2008/08/17 12:00:39 ianmacd Exp $
2
+ #
3
+
4
+ module Amazon
5
+
6
+ NAME = 'Ruby/Amazon'
7
+ @@config = {}
8
+
9
+ # Prints debugging messages and works like printf, except that it prints
10
+ # only when Ruby is run with the -d switch.
11
+ #
12
+ def Amazon.dprintf(format='', *args)
13
+ $stderr.printf( format + "\n", *args ) if $DEBUG
14
+ end
15
+
16
+
17
+ # Encode a string, such that it is suitable for HTTP transmission.
18
+ #
19
+ def Amazon.url_encode(string)
20
+
21
+ # Shamelessly plagiarised from Wakou Aoyama's cgi.rb.
22
+ #
23
+ string.gsub( /([^ a-zA-Z0-9_.-]+)/n ) do
24
+ '%' + $1.unpack( 'H2' * $1.size ).join( '%' ).upcase
25
+ end.tr( ' ', '+' )
26
+ end
27
+
28
+
29
+ # Convert a string from CamelCase to ruby_case.
30
+ #
31
+ def Amazon.uncamelise(str)
32
+ # Avoid modifying by reference.
33
+ #
34
+ str = str.dup
35
+
36
+ # Don't mess with string if all caps.
37
+ #
38
+ str.gsub!( /(.+?)(([A-Z][a-z]|[A-Z]+$))/, "\\1_\\2" ) if str =~ /[a-z]/
39
+
40
+ # Convert to lower case.
41
+ #
42
+ str.downcase
43
+ end
44
+
45
+
46
+ # A Class for dealing with configuration files, such as
47
+ # <tt>/etc/amazonrc</tt> and <tt>~/.amazonrc</tt>.
48
+ #
49
+ class Config < Hash
50
+
51
+ def initialize
52
+
53
+ config_files = [ File.join( '', 'etc', 'amazonrc' ) ]
54
+
55
+ # Figure out where home is. The subsequent locations are for Windows.
56
+ # [ruby-core:12347]
57
+ #
58
+ home = ENV['HOME'] || ENV['HOMEDRIVE'] + ENV['HOMEPATH'] ||
59
+ ENV['USERPROFILE']
60
+
61
+ if home
62
+ config_files << File.expand_path( File.join( '~', '.amazonrc' ) )
63
+ end
64
+
65
+ config_files.each do |cf|
66
+ if File.exists?( cf ) && File.readable?( cf )
67
+
68
+ Amazon.dprintf( 'Opening %s ...', cf )
69
+
70
+ File.open( cf ) { |f| lines = f.readlines }.each do |line|
71
+ line.chomp!
72
+
73
+ # Skip comments and blank lines.
74
+ #
75
+ next if line =~ /^(#|$)/
76
+
77
+ Amazon.dprintf( 'Read: %s', line )
78
+
79
+ # Store these, because we'll probably find a use for these later.
80
+ #
81
+ begin
82
+ match = line.match( /^(\S+)\s*=\s*(['"]?)([^'"]+)(['"]?)/ )
83
+ key, begin_quote, val, end_quote = match[1, 4]
84
+ raise ConfigError if begin_quote != end_quote
85
+ rescue NoMethodError, ConfigError
86
+ raise ConfigError, "bad config line: #{line}"
87
+ end
88
+
89
+ self[key] = val
90
+
91
+ end
92
+ end
93
+
94
+ end
95
+
96
+ end
97
+ end
98
+
99
+ end
data/test/setup.rb ADDED
@@ -0,0 +1,31 @@
1
+ # $Id: setup.rb,v 1.3 2008/06/22 11:50:23 ianmacd Exp $
2
+ #
3
+
4
+ # Attempt to load Ruby/AWS using RubyGems.
5
+ #
6
+ begin
7
+ require 'rubygems'
8
+ gem 'ruby-aws'
9
+ rescue LoadError
10
+ # Either we don't have RubyGems or we don't have a gem of Ruby/AWS.
11
+ end
12
+
13
+ # Require the essential library, be it via RubyGems or the default way.
14
+ #
15
+ require 'amazon/aws/search'
16
+
17
+ include Amazon::AWS
18
+ include Amazon::AWS::Search
19
+
20
+ class AWSTest < Test::Unit::TestCase
21
+
22
+ def setup
23
+ @rg = ResponseGroup.new( :Small )
24
+ @req = Request.new
25
+ @req.locale = 'uk'
26
+ @req.cache = false
27
+ end
28
+
29
+ undef_method :default_test
30
+
31
+ end
data/test/tc_amazon.rb ADDED
@@ -0,0 +1,20 @@
1
+ # $Id: tc_amazon.rb,v 1.1 2008/05/19 10:17:26 ianmacd Exp $
2
+ #
3
+
4
+ require 'test/unit'
5
+ require './setup'
6
+
7
+ class TestAmazonBasics < AWSTest
8
+
9
+ def test_uncamelise
10
+ str = 'ALongStringWithACRONYM'
11
+ uncamel_str = 'a_long_string_with_acronym'
12
+
13
+ # Ensure uncamelisation of strings occurs properly.
14
+ #
15
+ assert_equal( uncamel_str, Amazon::uncamelise( str ) )
16
+ assert_equal( 'asin', Amazon::uncamelise( 'ASIN' ) )
17
+
18
+ end
19
+
20
+ end
data/test/tc_aws.rb ADDED
@@ -0,0 +1,118 @@
1
+ # $Id: tc_aws.rb,v 1.8 2008/08/12 09:50:48 ianmacd Exp $
2
+ #
3
+
4
+ require 'test/unit'
5
+ require './setup'
6
+ require 'fileutils'
7
+ require 'tmpdir'
8
+ require 'amazon/locale'
9
+
10
+ class TestAWSBasics < AWSTest
11
+
12
+ CACHE_DIR = Amazon::AWS::Cache::DEFAULT_CACHE_DIR
13
+ CACHE_PATH = File.join( Dir.tmpdir, 'aws_cache' )
14
+
15
+ def test_version
16
+ v = '1.8.6'
17
+ assert( RUBY_VERSION >= v, "Ruby version is lower than #{v}." )
18
+ end
19
+
20
+ def test_cache
21
+
22
+ # Assure we can create a cache with a non-default path.
23
+ #
24
+ c = Cache.new( CACHE_PATH )
25
+ assert( CACHE_PATH, c.path )
26
+ assert( File.directory?( c.path ) )
27
+ FileUtils.rmdir( c.path )
28
+
29
+ c = Cache.new
30
+
31
+ # Ensure that default cache path has been chosen.
32
+ #
33
+ assert( CACHE_DIR, c.path )
34
+
35
+ # Ensure that cache directory has been created.
36
+ #
37
+ assert( File.directory?( c.path ) )
38
+
39
+ f = File.join( c.path, 'foobar' )
40
+
41
+ # Just over a day ago.
42
+ #
43
+ t = Time.now - 86460
44
+
45
+ FileUtils.touch f, { :mtime => t }
46
+ assert( File.exist?( f ) )
47
+
48
+ # Ensure expired file is properly flushed.
49
+ #
50
+ c.flush_expired
51
+ assert( ! File.exist?( f ) )
52
+
53
+ FileUtils.touch f
54
+ assert( File.exist?( f ) )
55
+
56
+ # Ensure unexpired file is properly retained.
57
+ #
58
+ c.flush_expired
59
+ assert( File.exist?( f ) )
60
+
61
+ # Ensure file is properly flushed.
62
+ #
63
+ c.flush_all
64
+ assert( ! File.exist?( f ) )
65
+
66
+ h = Help.new( :ResponseGroup, :Large )
67
+ h_rg = ResponseGroup.new( :Help )
68
+
69
+ # Ensure that file is not cached when no cache is desired.
70
+ #
71
+ @req.cache = false
72
+ resp = @req.search( h, h_rg )
73
+ assert_equal( 0, Dir.glob( File.join( c.path, '*' ) ).size )
74
+
75
+ # Ensure that file is cached when cache is desired.
76
+ #
77
+ @req.cache = c
78
+ resp = @req.search( h, h_rg )
79
+ assert_equal( 1, Dir.glob( File.join( c.path, '*' ) ).size )
80
+
81
+ # Flush it away.
82
+ #
83
+ c.flush_all
84
+ assert_equal( 0, Dir.glob( File.join( c.path, '*' ) ).size )
85
+
86
+ FileUtils.rmdir( c.path )
87
+
88
+ # Turn off caching for future tests. This happens in the setup method,
89
+ # anyway, but it can't hurt to tidy up after ourselves.
90
+ #
91
+ @req.cache = false
92
+
93
+ end
94
+
95
+ def test_exceptions
96
+ assert_raise( Amazon::AWS::HTTPError) { raise Amazon::AWS::HTTPError }
97
+ end
98
+
99
+ include Amazon
100
+
101
+ def test_geo
102
+
103
+ require 'net/geoip'
104
+
105
+ assert_equal( 'de', Locale.get_locale_by_name( 'www.marxer.org' ) )
106
+ assert_equal( 'jp', Locale.get_locale_by_name( 'ruby-lang.org' ) )
107
+ assert_equal( 'uk', Locale.get_locale_by_name( 'xs1.xs4all.nl' ) )
108
+ assert_equal( 'uk', Locale.get_locale_by_name( 'caliban.org' ) )
109
+ assert_equal( 'us', Locale.get_locale_by_name( 'google.com' ) )
110
+
111
+ # Ensure non-existent hostname causes an Amazon::Locale::GeoError.
112
+ #
113
+ assert_raise( Amazon::Locale::GeoError ) do
114
+ Locale.get_locale_by_name( 'marxer.org' )
115
+ end
116
+ end
117
+
118
+ end
@@ -0,0 +1,21 @@
1
+ # $Id: tc_item_search.rb,v 1.1 2008/05/19 10:17:26 ianmacd Exp $
2
+ #
3
+
4
+ require 'test/unit'
5
+ require './setup'
6
+
7
+ class TestItemSearch < AWSTest
8
+
9
+ def test_item_search
10
+ is = ItemSearch.new( 'Books', { 'Title' => 'Ruby' } )
11
+ response = @req.search( is, @rg )
12
+
13
+ results = response.kernel
14
+
15
+ # Ensure we got some actual results back.
16
+ #
17
+ assert( results.size > 0 )
18
+
19
+ end
20
+
21
+ end
@@ -0,0 +1,58 @@
1
+ # $Id: tc_multiple_operation.rb,v 1.1 2008/05/19 10:17:26 ianmacd Exp $
2
+ #
3
+
4
+ require 'test/unit'
5
+ require './setup'
6
+
7
+ class TestMultipleOperation < AWSTest
8
+
9
+ def test_item_search
10
+ il = ItemLookup.new( 'ASIN', { 'ItemId' => 'B000AE4QEC',
11
+ 'MerchantId' => 'Amazon' },
12
+ { 'ItemId' => 'B000051WBE',
13
+ 'MerchantId' => 'Amazon' } )
14
+ is = ItemSearch.new( 'Books', { 'Title' => 'Ruby' } )
15
+
16
+ mo = MultipleOperation.new( is, il )
17
+
18
+ response = @req.search( mo, @rg )
19
+
20
+ mor = response.multi_operation_response[0]
21
+
22
+ # Ensure we received a MultiOperationResponse.
23
+ #
24
+ assert_instance_of( Amazon::AWS::AWSObject::MultiOperationResponse, mor )
25
+
26
+ # Ensure response contains an ItemSearchResponse.
27
+ #
28
+ assert_instance_of( Amazon::AWS::AWSObject::ItemSearchResponse,
29
+ mor.item_search_response[0] )
30
+
31
+ # Ensure response also contains an ItemLookupResponse.
32
+ #
33
+ assert_instance_of( Amazon::AWS::AWSObject::ItemLookupResponse,
34
+ mor.item_lookup_response[0] )
35
+
36
+ is_set = response.multi_operation_response.item_search_response[0].items
37
+ il_set = response.multi_operation_response.item_lookup_response[0].items
38
+ is_arr = is_set.item
39
+ il_arr1 = il_set[0].item
40
+ il_arr2 = il_set[1].item
41
+
42
+ # Ensure that there's one <ItemSet> for the ItemSearch.
43
+ #
44
+ assert_equal( 1, is_set.size )
45
+
46
+ # Ensure that there are two <ItemSet>s for the ItemLookup, because it was
47
+ # a batched operation.
48
+ #
49
+ assert_equal( 2, il_set.size )
50
+
51
+ # Assure that all item sets have some results.
52
+ #
53
+ assert( is_arr.size > 0 )
54
+ assert( il_arr1.size > 0 )
55
+ assert( il_arr2.size > 0 )
56
+ end
57
+
58
+ end
@@ -0,0 +1,58 @@
1
+ # $Id: tc_operation_request.rb,v 1.1 2008/05/19 10:17:26 ianmacd Exp $
2
+ #
3
+
4
+ require 'test/unit'
5
+ require './setup'
6
+
7
+ class TestOperationRequest < AWSTest
8
+
9
+ def test_operation_request
10
+ is = ItemSearch.new( 'Books', { 'Title' => 'Ruby' } )
11
+ response = @req.search( is, ResponseGroup.new( 'Request' ) )
12
+
13
+ # Same again with Symbols.
14
+ #
15
+ is = ItemSearch.new( :Books, { :Title => 'Ruby' } )
16
+ response = @req.search( is, ResponseGroup.new( :Request ) )
17
+
18
+ # Make sure undocumented AWSObject#results provides an accurate shortcut
19
+ # to the most interesting part of the data returned by AWS.
20
+ #
21
+ assert_equal( response.item_search_response[0].items[0].item,
22
+ response.kernel )
23
+
24
+ # Ensure response is an Amazon::AWS::AWSObject.
25
+ #
26
+ assert_instance_of( Amazon::AWS::AWSObject, response )
27
+
28
+ # Ensure non-existent instance variables return nil.
29
+ #
30
+ assert_nil( response.foo_bar_baz )
31
+
32
+ # Ensure top level of response is an Amazon::AWS::AWSArray.
33
+ #
34
+ assert_instance_of( Amazon::AWS::AWSArray, response.item_search_response )
35
+
36
+ # Ensure delegation of method from AWSArray to single element.
37
+ #
38
+ assert_equal( response.item_search_response[0].operation_request,
39
+ response.item_search_response.operation_request )
40
+
41
+ # Test for correct user-agent in response.
42
+ #
43
+ assert_equal( Amazon::AWS::USER_AGENT,
44
+ response.item_search_response[0].operation_request[0].
45
+ http_headers[0].header[0].attrib['value'] )
46
+
47
+ # Ensure that the correct version of the AWS API was requested.
48
+ #
49
+ response.item_search_response[0].operation_request[0].arguments[0].
50
+ argument.each do |arg|
51
+ next unless arg.attrib['name'] == 'Version'
52
+ assert_equal( Amazon::AWS::SERVICE['Version'], arg.attrib['value'] )
53
+ break
54
+ end
55
+
56
+ end
57
+
58
+ end
@@ -0,0 +1,103 @@
1
+ # $Id: tc_serialisation.rb,v 1.2 2008/06/22 21:18:50 ianmacd Exp $
2
+ #
3
+
4
+ require 'test/unit'
5
+ require './setup'
6
+ require 'yaml'
7
+ require 'tempfile'
8
+
9
+ class AWSSerialisationTest < AWSTest
10
+
11
+ def test_yaml_load
12
+
13
+ results_file = Tempfile.new( 'ruby_aws' )
14
+
15
+ # Serialise some results.
16
+ #
17
+ is = ItemSearch.new( 'Music', { 'Artist' => 'Voice Of The Beehive' } )
18
+ response = @req.search( is, @rg )
19
+ results = response.kernel
20
+
21
+ YAML.dump( results, results_file )
22
+ results = nil
23
+
24
+ # Remove knowledge of Amazon::AWS::AWSObject, so that YAML.load knows
25
+ # nothing of its subclasses.
26
+ #
27
+ Amazon::AWS.module_eval( %Q( remove_const :AWSObject ) )
28
+
29
+ # Silence warnings about redefined constants.
30
+ #
31
+ v = $VERBOSE
32
+ $VERBOSE = nil
33
+
34
+ # Reload Amazon::AWS and friends.
35
+ #
36
+ load 'amazon/aws.rb'
37
+
38
+ # Reset warning status.
39
+ #
40
+ $VERBOSE = v
41
+
42
+ # Demonstrate that normal YAML.load can't cope with instantiating objects
43
+ # from classes it knows nothing about.
44
+ #
45
+ results_file.open
46
+ results = YAML.load( results_file )
47
+ assert_instance_of( YAML::Object, results[0] )
48
+
49
+ # Ensure that AWSObject.yaml_load does the right thing.
50
+ #
51
+ results_file.open
52
+ results = Amazon::AWS::AWSObject.yaml_load( results_file )
53
+ assert_instance_of( Amazon::AWS::AWSObject::Item, results[0] )
54
+ end
55
+
56
+
57
+ def test_marshal_load
58
+
59
+ results_file = Tempfile.new( 'ruby_aws' )
60
+
61
+ # Serialise some results.
62
+ #
63
+ is = ItemSearch.new( 'Music', { 'Artist' => 'Voice Of The Beehive' } )
64
+ response = @req.search( is, @rg )
65
+ results = response.kernel
66
+
67
+ results_file.puts Marshal.dump( results )
68
+ results = nil
69
+
70
+ # Remove knowledge of Amazon::AWS::AWSObject, so that Marshal.load knows
71
+ # nothing of its subclasses.
72
+ #
73
+ Amazon::AWS.module_eval( %Q( remove_const :AWSObject ) )
74
+
75
+ # Silence warnings about redefined constants.
76
+ #
77
+ v = $VERBOSE
78
+ $VERBOSE = nil
79
+
80
+ # Reload Amazon::AWS and friends.
81
+ #
82
+ load 'amazon/aws.rb'
83
+
84
+ # Reset warning status.
85
+ #
86
+ $VERBOSE = v
87
+
88
+ # Demonstrate that normal Marshal.load can't cope with instantiating
89
+ # objects from classes it knows nothing about.
90
+ #
91
+ results_file.open
92
+
93
+ assert_raise ArgumentError do
94
+ Marshal.load( results_file )
95
+ end
96
+
97
+ # Ensure that Amazon::AWS::AWSObject.load does the right thing.
98
+ #
99
+ results_file.open
100
+ results = Amazon::AWS::AWSObject.load( results_file )
101
+ assert_instance_of( Amazon::AWS::AWSObject::Item, results[0] )
102
+ end
103
+ end