google-search 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
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