crown 0.0.10 → 0.0.11

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/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
+