crown 0.0.10 → 0.0.11

Sign up to get free protection for your applications and to get access to all the features.
data/ChangeLog CHANGED
@@ -1,3 +1,7 @@
1
+ == 0.0.11 / 2011-11-11
2
+
3
+ * bugfix. add amazon/ecs.rb in the project and modify the Line 141 for character corruption.
4
+
1
5
  == 0.0.10 / 2011-05-13
2
6
 
3
7
  * bugfix.
data/Rakefile CHANGED
@@ -12,7 +12,6 @@ begin
12
12
  gem.authors = ["clown"]
13
13
  gem.add_dependency('json')
14
14
  gem.add_dependency('nokogiri')
15
- gem.add_dependency('amazon-ecs')
16
15
  gem.add_development_dependency "thoughtbot-shoulda"
17
16
  # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
18
17
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.10
1
+ 0.0.11
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{crown}
8
- s.version = "0.0.10"
8
+ s.version = "0.0.11"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["clown"]
12
- s.date = %q{2011-05-12}
11
+ s.authors = [%q{clown}]
12
+ s.date = %q{2011-11-10}
13
13
  s.description = %q{crown is uncategorized ruby libraries, which is added according to author's mood :-D}
14
14
  s.email = %q{tt.clown@gmail.com}
15
15
  s.extra_rdoc_files = [
@@ -35,6 +35,7 @@ Gem::Specification.new do |s|
35
35
  "example/twcount.rb",
36
36
  "lib/crown.rb",
37
37
  "lib/crown/amazon.rb",
38
+ "lib/crown/amazon/ecs.rb",
38
39
  "lib/crown/backtype.rb",
39
40
  "lib/crown/buzzurl.rb",
40
41
  "lib/crown/delicious.rb",
@@ -54,13 +55,9 @@ Gem::Specification.new do |s|
54
55
  "test/test_helper.rb"
55
56
  ]
56
57
  s.homepage = %q{http://github.com/clown/crown}
57
- s.require_paths = ["lib"]
58
- s.rubygems_version = %q{1.7.1}
58
+ s.require_paths = [%q{lib}]
59
+ s.rubygems_version = %q{1.8.8}
59
60
  s.summary = %q{uncategorized ruby libraries.}
60
- s.test_files = [
61
- "test/crown_test.rb",
62
- "test/test_helper.rb"
63
- ]
64
61
 
65
62
  if s.respond_to? :specification_version then
66
63
  s.specification_version = 3
@@ -68,18 +65,15 @@ Gem::Specification.new do |s|
68
65
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
69
66
  s.add_runtime_dependency(%q<json>, [">= 0"])
70
67
  s.add_runtime_dependency(%q<nokogiri>, [">= 0"])
71
- s.add_runtime_dependency(%q<amazon-ecs>, [">= 0"])
72
68
  s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
73
69
  else
74
70
  s.add_dependency(%q<json>, [">= 0"])
75
71
  s.add_dependency(%q<nokogiri>, [">= 0"])
76
- s.add_dependency(%q<amazon-ecs>, [">= 0"])
77
72
  s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
78
73
  end
79
74
  else
80
75
  s.add_dependency(%q<json>, [">= 0"])
81
76
  s.add_dependency(%q<nokogiri>, [">= 0"])
82
- s.add_dependency(%q<amazon-ecs>, [">= 0"])
83
77
  s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
84
78
  end
85
79
  end
@@ -36,8 +36,8 @@ module Crown
36
36
  require 'uri'
37
37
  require 'net/http'
38
38
  require 'crown/http'
39
+ require 'crown/amazon/ecs'
39
40
  require 'rubygems'
40
- require 'amazon/ecs'
41
41
  require 'nokogiri'
42
42
  Net::HTTP.version_1_2
43
43
 
@@ -0,0 +1,385 @@
1
+ #--
2
+ # Copyright (c) 2010 Herryanto Siatono
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19
+ # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21
+ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ require 'net/http'
25
+ require 'nokogiri'
26
+ require 'cgi'
27
+ require 'hmac-sha2'
28
+ require 'base64'
29
+ require 'openssl'
30
+
31
+ module Amazon
32
+ class RequestError < StandardError; end
33
+
34
+ class Ecs
35
+ SERVICE_URLS = {
36
+ :us => 'http://ecs.amazonaws.com/onca/xml',
37
+ :uk => 'http://ecs.amazonaws.co.uk/onca/xml',
38
+ :ca => 'http://ecs.amazonaws.ca/onca/xml',
39
+ :de => 'http://ecs.amazonaws.de/onca/xml',
40
+ :jp => 'http://ecs.amazonaws.jp/onca/xml',
41
+ :fr => 'http://ecs.amazonaws.fr/onca/xml',
42
+ :it => 'http://webservices.amazon.it/onca/xml',
43
+ :cn => 'http://webservices.amazon.cn/onca/xml'
44
+ }
45
+
46
+ OPENSSL_DIGEST_SUPPORT = OpenSSL::Digest.constants.include?( 'SHA256' ) ||
47
+ OpenSSL::Digest.constants.include?( :SHA256 )
48
+
49
+ OPENSSL_DIGEST = OpenSSL::Digest::Digest.new( 'sha256' ) if OPENSSL_DIGEST_SUPPORT
50
+
51
+ @@options = {
52
+ :version => "2011-08-01",
53
+ :service => "AWSECommerceService"
54
+ }
55
+
56
+ @@debug = false
57
+
58
+ # Default search options
59
+ def self.options
60
+ @@options
61
+ end
62
+
63
+ # Set default search options
64
+ def self.options=(opts)
65
+ @@options = opts
66
+ end
67
+
68
+ # Get debug flag.
69
+ def self.debug
70
+ @@debug
71
+ end
72
+
73
+ # Set debug flag to true or false.
74
+ def self.debug=(dbg)
75
+ @@debug = dbg
76
+ end
77
+
78
+ def self.configure(&proc)
79
+ raise ArgumentError, "Block is required." unless block_given?
80
+ yield @@options
81
+ end
82
+
83
+ # Search amazon items with search terms. Default search index option is 'Books'.
84
+ # For other search type other than keywords, please specify :type => [search type param name].
85
+ def self.item_search(terms, opts = {})
86
+ opts[:operation] = 'ItemSearch'
87
+ opts[:search_index] = opts[:search_index] || 'Books'
88
+
89
+ type = opts.delete(:type)
90
+ if type
91
+ opts[type.to_sym] = terms
92
+ else
93
+ opts[:keywords] = terms
94
+ end
95
+
96
+ self.send_request(opts)
97
+ end
98
+
99
+ # Search an item by ASIN no.
100
+ def self.item_lookup(item_id, opts = {})
101
+ opts[:operation] = 'ItemLookup'
102
+ opts[:item_id] = item_id
103
+
104
+ self.send_request(opts)
105
+ end
106
+
107
+ # Search a browse node by BrowseNodeId
108
+ def self.browse_node_lookup(browse_node_id, opts = {})
109
+ opts[:operation] = 'BrowseNodeLookup'
110
+ opts[:browse_node_id] = browse_node_id
111
+
112
+ self.send_request(opts)
113
+ end
114
+
115
+ # Generic send request to ECS REST service. You have to specify the :operation parameter.
116
+ def self.send_request(opts)
117
+ opts = self.options.merge(opts) if self.options
118
+
119
+ # Include other required options
120
+ opts[:timestamp] = Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
121
+
122
+ request_url = prepare_url(opts)
123
+ log "Request URL: #{request_url}"
124
+
125
+ res = Net::HTTP.get_response(URI::parse(request_url))
126
+ unless res.kind_of? Net::HTTPSuccess
127
+ raise Amazon::RequestError, "HTTP Response: #{res.code} #{res.message}"
128
+ end
129
+ Response.new(res.body)
130
+ end
131
+
132
+ def self.validate_request(opts)
133
+ raise Amazon::RequestError, "" if opts[:associate_tag]
134
+ end
135
+
136
+ # Response object returned after a REST call to Amazon service.
137
+ class Response
138
+
139
+ # XML input is in string format
140
+ def initialize(xml)
141
+ @doc = Nokogiri::XML(xml, 'nul', 'UTF-8')
142
+ @doc.remove_namespaces!
143
+ # @doc.xpath("//*").each { |elem| elem.name = elem.name.downcase }
144
+ # @doc.xpath("//@*").each { |att| att.name = att.name.downcase }
145
+ end
146
+
147
+ # Return Nokogiri::XML::Document object.
148
+ def doc
149
+ @doc
150
+ end
151
+
152
+ # Return true if request is valid.
153
+ def is_valid_request?
154
+ Element.get(@doc, "//IsValid") == "True"
155
+ end
156
+
157
+ # Return true if response has an error.
158
+ def has_error?
159
+ !(error.nil? || error.empty?)
160
+ end
161
+
162
+ # Return error message.
163
+ def error
164
+ Element.get(@doc, "//Error/Message")
165
+ end
166
+
167
+ # Return error code
168
+ def error_code
169
+ Element.get(@doc, "//Error/Code")
170
+ end
171
+
172
+ # Return an array of Amazon::Element item objects.
173
+ def items
174
+ @items ||= (@doc/"Item").collect { |item| Element.new(item) }
175
+ end
176
+
177
+ # Return the first item (Amazon::Element)
178
+ def first_item
179
+ items.first
180
+ end
181
+
182
+ # Return current page no if :item_page option is when initiating the request.
183
+ def item_page
184
+ @item_page ||= Element.get(@doc, "//ItemPage").to_i
185
+ end
186
+
187
+ # Return total results.
188
+ def total_results
189
+ @total_results ||= Element.get(@doc, "//TotalResults").to_i
190
+ end
191
+
192
+ # Return total pages.
193
+ def total_pages
194
+ @total_pages ||= Element.get(@doc, "//TotalPages").to_i
195
+ end
196
+
197
+ def marshal_dump
198
+ @doc.to_s
199
+ end
200
+
201
+ def marshal_load(xml)
202
+ initialize(xml)
203
+ end
204
+ end
205
+
206
+ protected
207
+ def self.log(s)
208
+ return unless self.debug
209
+ if defined? RAILS_DEFAULT_LOGGER
210
+ RAILS_DEFAULT_LOGGER.error(s)
211
+ elsif defined? LOGGER
212
+ LOGGER.error(s)
213
+ else
214
+ puts s
215
+ end
216
+ end
217
+
218
+ private
219
+ def self.prepare_url(opts)
220
+ country = opts.delete(:country)
221
+ country = (country.nil?) ? 'us' : country
222
+ request_url = SERVICE_URLS[country.to_sym]
223
+ raise Amazon::RequestError, "Invalid country '#{country}'" unless request_url
224
+
225
+ secret_key = opts.delete(:AWS_secret_key)
226
+ request_host = URI.parse(request_url).host
227
+
228
+ qs = ''
229
+
230
+ opts = opts.collect do |a,b|
231
+ [camelize(a.to_s), b.to_s]
232
+ end
233
+
234
+ opts = opts.sort do |c,d|
235
+ c[0].to_s <=> d[0].to_s
236
+ end
237
+
238
+ opts.each do |e|
239
+ log "Adding #{e[0]}=#{e[1]}"
240
+ next unless e[1]
241
+ e[1] = e[1].join(',') if e[1].is_a? Array
242
+ # v = URI.encode(e[1].to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
243
+ v = self.url_encode(e[1].to_s)
244
+ qs << "&" unless qs.length == 0
245
+ qs << "#{e[0]}=#{v}"
246
+ end
247
+
248
+ signature = ''
249
+ unless secret_key.nil?
250
+ request_to_sign="GET\n#{request_host}\n/onca/xml\n#{qs}"
251
+ signature = "&Signature=#{sign_request(request_to_sign, secret_key)}"
252
+ end
253
+
254
+ "#{request_url}?#{qs}#{signature}"
255
+ end
256
+
257
+ def self.url_encode(string)
258
+ string.gsub( /([^a-zA-Z0-9_.~-]+)/ ) do
259
+ '%' + $1.unpack( 'H2' * $1.bytesize ).join( '%' ).upcase
260
+ end
261
+ end
262
+
263
+ def self.camelize(s)
264
+ s.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
265
+ end
266
+
267
+ def self.sign_request(url, key)
268
+ return nil if key.nil?
269
+
270
+ if (OPENSSL_DIGEST_SUPPORT)
271
+ signature = OpenSSL::HMAC.digest(OPENSSL_DIGEST, key, url)
272
+ signature = [signature].pack('m').chomp
273
+ else
274
+ signature = Base64.encode64( HMAC::SHA256.digest(key, url) ).strip
275
+ end
276
+ signature = URI.escape(signature, Regexp.new("[+=]"))
277
+ return signature
278
+ end
279
+ end
280
+
281
+ # Internal wrapper class to provide convenient method to access Nokogiri element value.
282
+ class Element
283
+ class << self
284
+ # Return the text value of an element.
285
+ def get(element, path='.')
286
+ return unless element
287
+ result = element.at_xpath(path)
288
+ result = result.inner_html if result
289
+ result
290
+ end
291
+
292
+ # Return an unescaped text value of an element.
293
+ def get_unescaped(element, path='.')
294
+ result = self.get(element, path)
295
+ CGI::unescapeHTML(result) if result
296
+ end
297
+
298
+ # Return an array of values based on the given path.
299
+ def get_array(element, path='.')
300
+ return unless element
301
+
302
+ result = element/path
303
+ if (result.is_a? Nokogiri::XML::NodeSet) || (result.is_a? Array)
304
+ result.collect { |item| self.get(item) }
305
+ else
306
+ [self.get(result)]
307
+ end
308
+ end
309
+
310
+ # Return child element text values of the given path.
311
+ def get_hash(element, path='.')
312
+ return unless element
313
+
314
+ result = element.at_xpath(path)
315
+ if result
316
+ hash = {}
317
+ result = result.children
318
+ result.each do |item|
319
+ hash[item.name] = item.inner_html
320
+ end
321
+ hash
322
+ end
323
+ end
324
+ end
325
+
326
+ # Pass Nokogiri::XML::Element object
327
+ def initialize(element)
328
+ @element = element
329
+ end
330
+
331
+ # Returns Nokogiri::XML::Element object
332
+ def elem
333
+ @element
334
+ end
335
+
336
+ # Returns a Nokogiri::XML::NodeSet of elements matching the given path. Example: element/"author".
337
+ def /(path)
338
+ elements = @element/path
339
+ return nil if elements.size == 0
340
+ elements
341
+ end
342
+
343
+ # Return an array of Amazon::Element matching the given path
344
+ def get_elements(path)
345
+ elements = self./(path)
346
+ return unless elements
347
+ elements = elements.map{|element| Element.new(element)}
348
+ end
349
+
350
+ # Similar with search_and_convert but always return first element if more than one elements found
351
+ def get_element(path)
352
+ elements = get_elements(path)
353
+ elements[0] if elements
354
+ end
355
+
356
+ # Get the text value of the given path, leave empty to retrieve current element value.
357
+ def get(path='.')
358
+ Element.get(@element, path)
359
+ end
360
+
361
+ # Get the unescaped HTML text of the given path.
362
+ def get_unescaped(path='.')
363
+ Element.get_unescaped(@element, path)
364
+ end
365
+
366
+ # Get the array values of the given path.
367
+ def get_array(path='.')
368
+ Element.get_array(@element, path)
369
+ end
370
+
371
+ # Get the children element text values in hash format with the element names as the hash keys.
372
+ def get_hash(path='.')
373
+ Element.get_hash(@element, path)
374
+ end
375
+
376
+ def attributes
377
+ return unless self.elem
378
+ self.elem.attributes
379
+ end
380
+
381
+ def to_s
382
+ elem.to_s if elem
383
+ end
384
+ end
385
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: crown
3
3
  version: !ruby/object:Gem::Version
4
- hash: 11
4
+ hash: 9
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 10
10
- version: 0.0.10
9
+ - 11
10
+ version: 0.0.11
11
11
  platform: ruby
12
12
  authors:
13
13
  - clown
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-05-12 00:00:00 Z
18
+ date: 2011-11-10 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: json
@@ -45,24 +45,10 @@ dependencies:
45
45
  version: "0"
46
46
  type: :runtime
47
47
  version_requirements: *id002
48
- - !ruby/object:Gem::Dependency
49
- name: amazon-ecs
50
- prerelease: false
51
- requirement: &id003 !ruby/object:Gem::Requirement
52
- none: false
53
- requirements:
54
- - - ">="
55
- - !ruby/object:Gem::Version
56
- hash: 3
57
- segments:
58
- - 0
59
- version: "0"
60
- type: :runtime
61
- version_requirements: *id003
62
48
  - !ruby/object:Gem::Dependency
63
49
  name: thoughtbot-shoulda
64
50
  prerelease: false
65
- requirement: &id004 !ruby/object:Gem::Requirement
51
+ requirement: &id003 !ruby/object:Gem::Requirement
66
52
  none: false
67
53
  requirements:
68
54
  - - ">="
@@ -72,7 +58,7 @@ dependencies:
72
58
  - 0
73
59
  version: "0"
74
60
  type: :development
75
- version_requirements: *id004
61
+ version_requirements: *id003
76
62
  description: crown is uncategorized ruby libraries, which is added according to author's mood :-D
77
63
  email: tt.clown@gmail.com
78
64
  executables: []
@@ -101,6 +87,7 @@ files:
101
87
  - example/twcount.rb
102
88
  - lib/crown.rb
103
89
  - lib/crown/amazon.rb
90
+ - lib/crown/amazon/ecs.rb
104
91
  - lib/crown/backtype.rb
105
92
  - lib/crown/buzzurl.rb
106
93
  - lib/crown/delicious.rb
@@ -147,10 +134,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
147
134
  requirements: []
148
135
 
149
136
  rubyforge_project:
150
- rubygems_version: 1.7.1
137
+ rubygems_version: 1.8.8
151
138
  signing_key:
152
139
  specification_version: 3
153
140
  summary: uncategorized ruby libraries.
154
- test_files:
155
- - test/crown_test.rb
156
- - test/test_helper.rb
141
+ test_files: []
142
+