google-search 1.0.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.
Files changed (71) hide show
  1. data/History.rdoc +8 -0
  2. data/Manifest +70 -0
  3. data/README.rdoc +95 -0
  4. data/Rakefile +16 -0
  5. data/examples/image.rb +21 -0
  6. data/examples/images.html +1 -0
  7. data/examples/web.rb +28 -0
  8. data/google-search.gemspec +33 -0
  9. data/lib/google-search.rb +29 -0
  10. data/lib/google-search/item.rb +10 -0
  11. data/lib/google-search/item/base.rb +78 -0
  12. data/lib/google-search/item/blog.rb +34 -0
  13. data/lib/google-search/item/book.rb +40 -0
  14. data/lib/google-search/item/image.rb +40 -0
  15. data/lib/google-search/item/local.rb +107 -0
  16. data/lib/google-search/item/news.rb +46 -0
  17. data/lib/google-search/item/patent.rb +40 -0
  18. data/lib/google-search/item/video.rb +46 -0
  19. data/lib/google-search/item/web.rb +22 -0
  20. data/lib/google-search/response.rb +92 -0
  21. data/lib/google-search/search.rb +11 -0
  22. data/lib/google-search/search/base.rb +212 -0
  23. data/lib/google-search/search/blog.rb +14 -0
  24. data/lib/google-search/search/book.rb +8 -0
  25. data/lib/google-search/search/image.rb +93 -0
  26. data/lib/google-search/search/local.rb +8 -0
  27. data/lib/google-search/search/mixins.rb +4 -0
  28. data/lib/google-search/search/mixins/filter.rb +26 -0
  29. data/lib/google-search/search/mixins/order_by.rb +36 -0
  30. data/lib/google-search/search/mixins/safety_level.rb +40 -0
  31. data/lib/google-search/search/news.rb +66 -0
  32. data/lib/google-search/search/patent.rb +37 -0
  33. data/lib/google-search/search/video.rb +15 -0
  34. data/lib/google-search/search/web.rb +15 -0
  35. data/lib/google-search/version.rb +6 -0
  36. data/spec/fixtures/400-response.json +1 -0
  37. data/spec/fixtures/blog-response.json +1 -0
  38. data/spec/fixtures/book-response.json +1 -0
  39. data/spec/fixtures/image-response.json +1 -0
  40. data/spec/fixtures/invalid-response.json +1 -0
  41. data/spec/fixtures/local-response.json +1 -0
  42. data/spec/fixtures/news-response.json +1 -0
  43. data/spec/fixtures/patent-response.json +1 -0
  44. data/spec/fixtures/video-response.json +1 -0
  45. data/spec/fixtures/web-2-response.json +1 -0
  46. data/spec/fixtures/web-response.json +1 -0
  47. data/spec/item_blog_spec.rb +26 -0
  48. data/spec/item_book_spec.rb +18 -0
  49. data/spec/item_image_spec.rb +21 -0
  50. data/spec/item_local_spec.rb +30 -0
  51. data/spec/item_news_spec.rb +19 -0
  52. data/spec/item_patent_spec.rb +19 -0
  53. data/spec/item_spec.rb +10 -0
  54. data/spec/item_video_spec.rb +20 -0
  55. data/spec/item_web_spec.rb +18 -0
  56. data/spec/response_spec.rb +63 -0
  57. data/spec/search_blog_spec.rb +0 -0
  58. data/spec/search_book_spec.rb +0 -0
  59. data/spec/search_image_spec.rb +71 -0
  60. data/spec/search_local_spec.rb +0 -0
  61. data/spec/search_news_spec.rb +33 -0
  62. data/spec/search_patent_spec.rb +27 -0
  63. data/spec/search_spec.rb +89 -0
  64. data/spec/search_video_spec.rb +42 -0
  65. data/spec/search_web_spec.rb +42 -0
  66. data/spec/spec.opts +2 -0
  67. data/spec/spec_helper.rb +13 -0
  68. data/tasks/docs.rake +18 -0
  69. data/tasks/gemspec.rake +3 -0
  70. data/tasks/spec.rake +25 -0
  71. metadata +168 -0
@@ -0,0 +1,40 @@
1
+
2
+ module Google
3
+ class Search
4
+ class Item
5
+ class Image < self
6
+
7
+ ##
8
+ # Image id.
9
+
10
+ attr_reader :id
11
+
12
+ ##
13
+ # Context uri.
14
+
15
+ attr_reader :context_uri
16
+
17
+ ##
18
+ # Image width in pixels.
19
+
20
+ attr_reader :width
21
+
22
+ ##
23
+ # Image height in pixels.
24
+
25
+ attr_reader :height
26
+
27
+ ##
28
+ # Initialize with _hash_.
29
+
30
+ def initialize hash
31
+ super
32
+ @id = hash['imageId']
33
+ @context_uri = hash['originalContextUrl']
34
+ @width = hash['width'].to_i
35
+ @height = hash['height'].to_i
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,107 @@
1
+
2
+ module Google
3
+ class Search
4
+ class Item
5
+ class Local < self
6
+
7
+ ##
8
+ # Country.
9
+
10
+ attr_reader :country
11
+
12
+ ##
13
+ # Region.
14
+
15
+ attr_reader :region
16
+
17
+ ##
18
+ # City.
19
+
20
+ attr_reader :city
21
+
22
+ ##
23
+ # Type.
24
+
25
+ attr_reader :type
26
+
27
+ ##
28
+ # Accuracy.
29
+
30
+ attr_reader :accuracy
31
+
32
+ ##
33
+ # Max age in seconds.
34
+
35
+ attr_reader :max_age
36
+
37
+ ##
38
+ # Google maps directions uri.
39
+
40
+ attr_reader :directions_uri
41
+
42
+ ##
43
+ # Google maps directions to here uri.
44
+
45
+ attr_reader :directions_to_here_uri
46
+
47
+ ##
48
+ # Google maps directions from here uri.
49
+
50
+ attr_reader :directions_from_here_uri
51
+
52
+ ##
53
+ # Longitude float.
54
+
55
+ attr_reader :long
56
+
57
+ ##
58
+ # Latitude float.
59
+
60
+ attr_reader :lat
61
+
62
+ ##
63
+ # Viewport mode.
64
+
65
+ attr_reader :viewport_mode
66
+
67
+ ##
68
+ # Phone numbers array.
69
+
70
+ attr_reader :phone_numbers
71
+
72
+ ##
73
+ # Street address.
74
+
75
+ attr_reader :street_address
76
+
77
+ ##
78
+ # Address lines array.
79
+
80
+ attr_reader :address_lines
81
+
82
+ ##
83
+ # Initialize with _hash_.
84
+
85
+ def initialize hash
86
+ super
87
+ @country = hash['country']
88
+ @region = hash['region']
89
+ @city = hash['city']
90
+ @type = hash['listingType']
91
+ @accuracy = hash['accuracy'].to_i
92
+ @max_age = hash['maxAge']
93
+ @directions_uri = hash['ddUrl']
94
+ @directions_to_here_uri = hash['ddUrlToHere']
95
+ @directions_from_here_uri = hash['ddUrlFromHere']
96
+ @thumbnail_uri = hash['staticMapUrl']
97
+ @long = hash['lng'].to_f
98
+ @lat = hash['lat'].to_f
99
+ @viewport_mode = hash['viewportmode']
100
+ @phone_numbers = hash['phoneNumbers'].map { |phone| phone['number'] }
101
+ @street_address = hash['streetAddress']
102
+ @address_lines = hash['addressLines']
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,46 @@
1
+
2
+ module Google
3
+ class Search
4
+ class Item
5
+ class News < self
6
+
7
+ ##
8
+ # Published DateTime.
9
+
10
+ attr_reader :published
11
+
12
+ ##
13
+ # Publisher.
14
+
15
+ attr_reader :publisher
16
+
17
+ ##
18
+ # Location.
19
+
20
+ attr_reader :location
21
+
22
+ ##
23
+ # Language.
24
+
25
+ attr_reader :language
26
+
27
+ ##
28
+ # Redirect uri.
29
+
30
+ attr_reader :redirect_uri
31
+
32
+ ##
33
+ # Initialize with _hash_.
34
+
35
+ def initialize hash
36
+ super
37
+ @location = hash['location']
38
+ @published = DateTime.parse hash['publishedDate']
39
+ @language = hash['language']
40
+ @publisher = hash['publisher']
41
+ @redirect_uri = hash['signedRedirectUrl']
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,40 @@
1
+
2
+ module Google
3
+ class Search
4
+ class Item
5
+ class Patent < self
6
+
7
+ ##
8
+ # Patent id.
9
+
10
+ attr_reader :id
11
+
12
+ ##
13
+ # Patent status.
14
+
15
+ attr_reader :status
16
+
17
+ ##
18
+ # Assignee.
19
+
20
+ attr_reader :assignee
21
+
22
+ ##
23
+ # Application DateTime.
24
+
25
+ attr_reader :application_date
26
+
27
+ ##
28
+ # Initialize with _hash_.
29
+
30
+ def initialize hash
31
+ super
32
+ @id = hash['patentNumber'].to_i
33
+ @application_date = DateTime.parse hash['applicationDate']
34
+ @assignee = hash['assignee']
35
+ @status = hash['patentStatus']
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,46 @@
1
+
2
+ module Google
3
+ class Search
4
+ class Item
5
+ class Video < self
6
+
7
+ ##
8
+ # Rating float.
9
+
10
+ attr_reader :rating
11
+
12
+ ##
13
+ # Video type.
14
+
15
+ attr_reader :type
16
+
17
+ ##
18
+ # Publisher.
19
+
20
+ attr_reader :publisher
21
+
22
+ ##
23
+ # Published DateTime.
24
+
25
+ attr_reader :published
26
+
27
+ ##
28
+ # Duration in seconds.
29
+
30
+ attr_reader :duration
31
+
32
+ ##
33
+ # Initialize with _hash_.
34
+
35
+ def initialize hash
36
+ super
37
+ @rating = hash['rating'].to_f
38
+ @type = hash['videoType']
39
+ @publisher = hash['publisher']
40
+ @published = DateTime.parse hash['published']
41
+ @duration = hash['duration'].to_i
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,22 @@
1
+
2
+ module Google
3
+ class Search
4
+ class Item
5
+ class Web < self
6
+
7
+ ##
8
+ # Cached uri.
9
+
10
+ attr_reader :cache_uri
11
+
12
+ ##
13
+ # Initialize with _hash_.
14
+
15
+ def initialize hash
16
+ super
17
+ @cache_uri = hash['cacheUrl']
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,92 @@
1
+
2
+ module Google
3
+ class Search
4
+ class Response
5
+
6
+ #--
7
+ # Mixins
8
+ #++
9
+
10
+ include Enumerable
11
+
12
+ ##
13
+ # Response status code.
14
+
15
+ attr_reader :status
16
+
17
+ ##
18
+ # Response details.
19
+
20
+ attr_reader :details
21
+
22
+ ##
23
+ # Raw JSON string.
24
+
25
+ attr_accessor :raw
26
+
27
+ ##
28
+ # Hash parsed from raw JSON string.
29
+
30
+ attr_reader :hash
31
+
32
+ ##
33
+ # Items populated by the JSON hash.
34
+
35
+ attr_reader :items
36
+
37
+ ##
38
+ # Estimated number of results.
39
+
40
+ attr_reader :estimated_count
41
+
42
+ ##
43
+ # Current page index.
44
+
45
+ attr_reader :page
46
+
47
+ ##
48
+ # Size of response.
49
+
50
+ attr_reader :size
51
+
52
+ ##
53
+ # Initialize with _hash_.
54
+
55
+ def initialize hash
56
+ @page = 0
57
+ @hash = hash
58
+ @size = (hash['responseSize'] || :large).to_sym
59
+ @items = []
60
+ @status = hash['responseStatus']
61
+ @details = hash['responseDetails']
62
+ if valid?
63
+ if hash['responseData'].include? 'cursor'
64
+ @estimated_count = hash['responseData']['cursor']['estimatedResultCount'].to_i
65
+ @page = hash['responseData']['cursor']['currentPageIndex'].to_i
66
+ end
67
+ @hash['responseData']['results'].each_with_index do |result, i|
68
+ item_class = Google::Search::Item.class_for result['GsearchResultClass']
69
+ result['index'] = i + Google::Search.size_for(size) * page
70
+ items << item_class.new(result)
71
+ end
72
+ end
73
+ end
74
+
75
+ ##
76
+ # Iterate each item with _block_.
77
+
78
+ def each_item &block
79
+ items.each { |item| yield item }
80
+ end
81
+ alias :each :each_item
82
+
83
+ ##
84
+ # Check if the response is valid.
85
+
86
+ def valid?
87
+ hash['responseStatus'] == 200
88
+ end
89
+
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,11 @@
1
+
2
+ require 'google-search/search/mixins'
3
+ require 'google-search/search/base'
4
+ require 'google-search/search/web'
5
+ require 'google-search/search/blog'
6
+ require 'google-search/search/image'
7
+ require 'google-search/search/local'
8
+ require 'google-search/search/news'
9
+ require 'google-search/search/video'
10
+ require 'google-search/search/book'
11
+ require 'google-search/search/patent'
@@ -0,0 +1,212 @@
1
+
2
+ module Google
3
+ class Search
4
+
5
+ #--
6
+ # Mixins
7
+ #++
8
+
9
+ include Enumerable
10
+
11
+ #--
12
+ # Constants
13
+ #++
14
+
15
+ URI = 'http://www.google.com/uds'
16
+
17
+ #--
18
+ # Exceptions
19
+ #++
20
+
21
+ class Error < StandardError; end
22
+
23
+ ##
24
+ # Version. Defaults to 1.0
25
+
26
+ attr_accessor :version
27
+
28
+ ##
29
+ # Search type symbol.
30
+
31
+ attr_accessor :type
32
+
33
+ ##
34
+ # Offset. Defaults to 0
35
+
36
+ attr_accessor :offset
37
+
38
+ ##
39
+ # Language. Defaults to :en
40
+
41
+ attr_accessor :language
42
+
43
+ ##
44
+ # Weither or not a search request has been sent.
45
+
46
+ attr_accessor :sent
47
+
48
+ ##
49
+ # Query. Defaults to nil
50
+
51
+ attr_accessor :query
52
+
53
+ ##
54
+ # API Key. Defaults to :notsupplied
55
+
56
+ attr_accessor :api_key
57
+
58
+ ##
59
+ # Size. Defaults to :large
60
+ #
61
+ # - :small = 4
62
+ # - :large = 8
63
+ #
64
+
65
+ attr_accessor :size
66
+
67
+ ##
68
+ # Additional options. All those listed above
69
+ # are deleted. The remaining represent query
70
+ # string key / value pairs.
71
+
72
+ attr_reader :options
73
+
74
+ ##
75
+ # Initialize search _type_ with _options_. Optionally
76
+ # a block may be passed, and the Search instance will
77
+ # be yielded to it.
78
+
79
+ def initialize options = {}, &block
80
+ @type = self.class.to_s.split('::').last.downcase.to_sym
81
+ @version = options.delete(:version) || 1.0
82
+ @offset = options.delete(:offset) || 0
83
+ @size = options.delete(:size) || :large
84
+ @language = options.delete(:language) || :en
85
+ @query = options.delete(:query)
86
+ @api_key = options.delete(:api_key) || :notsupplied
87
+ @options = options
88
+ raise Error, 'Do not initialize Google::Search; Use a subclass such as Google::Search::Web' if @type == :search
89
+ yield self if block
90
+ end
91
+
92
+ ##
93
+ # Set a response _block_ which is called every time
94
+ # #get_response is called. Useful for reporting etc.
95
+
96
+ def each_response &block
97
+ @each_response = block
98
+ end
99
+
100
+ ##
101
+ # Iterate each item with _block_.
102
+
103
+ def each_item &block
104
+ response = self.next.response
105
+ if response.valid?
106
+ response.each { |item| yield item }
107
+ each_item &block
108
+ end
109
+ end
110
+ alias :each :each_item
111
+
112
+ ##
113
+ # Return all items.
114
+
115
+ def all_items
116
+ select { true }
117
+ end
118
+ alias :all :all_items
119
+
120
+ ##
121
+ # Return uri.
122
+
123
+ def get_uri
124
+ URI + "/G#{@type}Search?" +
125
+ (get_uri_params + options.to_a).
126
+ map { |key, value| "#{key}=#{Search.url_encode(value)}" unless value.nil? }.compact.join('&')
127
+ end
128
+
129
+ #:nodoc:
130
+
131
+ def get_uri_params
132
+ validate(:query) { |query| query.respond_to?(:to_str) && !query.to_str.empty? }
133
+ validate(:version) { |version| Numeric === version }
134
+ [[:start, offset],
135
+ [:rsz, size],
136
+ [:hl, language],
137
+ [:key, api_key],
138
+ [:v, version],
139
+ [:q, query]]
140
+ end
141
+
142
+ ##
143
+ # Prepare for next request.
144
+
145
+ def next
146
+ @offset += Search.size_for(size) if sent
147
+ self
148
+ end
149
+
150
+ ##
151
+ # Return raw JSON response string.
152
+
153
+ def get_raw
154
+ @sent = true
155
+ open(get_uri).read
156
+ end
157
+
158
+ ##
159
+ # Return hash parsed from the raw JSON response.
160
+
161
+ def get_hash
162
+ Search.json_decode get_raw
163
+ end
164
+
165
+ ##
166
+ # Return Response object wrapping the JSON
167
+ # response hash.
168
+
169
+ def get_response
170
+ raw = get_raw
171
+ hash = Search.json_decode raw
172
+ hash['responseSize'] = size
173
+ response = Response.new hash
174
+ response.raw = raw
175
+ @each_response.call response if @each_response
176
+ response
177
+ end
178
+ alias :response :get_response
179
+
180
+ ##
181
+ # Return int for size _sym_.
182
+
183
+ def self.size_for sym
184
+ { :small => 4,
185
+ :large => 8 }[sym]
186
+ end
187
+
188
+ #:nodoc:
189
+
190
+ def validate meth, &block
191
+ value = send meth
192
+ raise Error, "invalid #{type} #{meth} #{value.inspect}", caller unless yield value
193
+ end
194
+
195
+ ##
196
+ # Decode JSON _string_.
197
+
198
+ def self.json_decode string
199
+ JSON.parse string
200
+ end
201
+
202
+ ##
203
+ # Url encode _string_.
204
+
205
+ def self.url_encode string
206
+ string.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
207
+ '%' + $1.unpack('H2' * $1.size).join('%').upcase
208
+ }.tr(' ', '+')
209
+ end
210
+
211
+ end
212
+ end