google-search 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/History.rdoc +8 -0
- data/Manifest +70 -0
- data/README.rdoc +95 -0
- data/Rakefile +16 -0
- data/examples/image.rb +21 -0
- data/examples/images.html +1 -0
- data/examples/web.rb +28 -0
- data/google-search.gemspec +33 -0
- data/lib/google-search.rb +29 -0
- data/lib/google-search/item.rb +10 -0
- data/lib/google-search/item/base.rb +78 -0
- data/lib/google-search/item/blog.rb +34 -0
- data/lib/google-search/item/book.rb +40 -0
- data/lib/google-search/item/image.rb +40 -0
- data/lib/google-search/item/local.rb +107 -0
- data/lib/google-search/item/news.rb +46 -0
- data/lib/google-search/item/patent.rb +40 -0
- data/lib/google-search/item/video.rb +46 -0
- data/lib/google-search/item/web.rb +22 -0
- data/lib/google-search/response.rb +92 -0
- data/lib/google-search/search.rb +11 -0
- data/lib/google-search/search/base.rb +212 -0
- data/lib/google-search/search/blog.rb +14 -0
- data/lib/google-search/search/book.rb +8 -0
- data/lib/google-search/search/image.rb +93 -0
- data/lib/google-search/search/local.rb +8 -0
- data/lib/google-search/search/mixins.rb +4 -0
- data/lib/google-search/search/mixins/filter.rb +26 -0
- data/lib/google-search/search/mixins/order_by.rb +36 -0
- data/lib/google-search/search/mixins/safety_level.rb +40 -0
- data/lib/google-search/search/news.rb +66 -0
- data/lib/google-search/search/patent.rb +37 -0
- data/lib/google-search/search/video.rb +15 -0
- data/lib/google-search/search/web.rb +15 -0
- data/lib/google-search/version.rb +6 -0
- data/spec/fixtures/400-response.json +1 -0
- data/spec/fixtures/blog-response.json +1 -0
- data/spec/fixtures/book-response.json +1 -0
- data/spec/fixtures/image-response.json +1 -0
- data/spec/fixtures/invalid-response.json +1 -0
- data/spec/fixtures/local-response.json +1 -0
- data/spec/fixtures/news-response.json +1 -0
- data/spec/fixtures/patent-response.json +1 -0
- data/spec/fixtures/video-response.json +1 -0
- data/spec/fixtures/web-2-response.json +1 -0
- data/spec/fixtures/web-response.json +1 -0
- data/spec/item_blog_spec.rb +26 -0
- data/spec/item_book_spec.rb +18 -0
- data/spec/item_image_spec.rb +21 -0
- data/spec/item_local_spec.rb +30 -0
- data/spec/item_news_spec.rb +19 -0
- data/spec/item_patent_spec.rb +19 -0
- data/spec/item_spec.rb +10 -0
- data/spec/item_video_spec.rb +20 -0
- data/spec/item_web_spec.rb +18 -0
- data/spec/response_spec.rb +63 -0
- data/spec/search_blog_spec.rb +0 -0
- data/spec/search_book_spec.rb +0 -0
- data/spec/search_image_spec.rb +71 -0
- data/spec/search_local_spec.rb +0 -0
- data/spec/search_news_spec.rb +33 -0
- data/spec/search_patent_spec.rb +27 -0
- data/spec/search_spec.rb +89 -0
- data/spec/search_video_spec.rb +42 -0
- data/spec/search_web_spec.rb +42 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +13 -0
- data/tasks/docs.rake +18 -0
- data/tasks/gemspec.rake +3 -0
- data/tasks/spec.rake +25 -0
- 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
|