searchapi 1.0.0
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.
- checksums.yaml +7 -0
- data/README.md +175 -0
- data/bin/test +14 -0
- data/lib/searchapi/error_result.rb +50 -0
- data/lib/searchapi/google_finance_options.rb +40 -0
- data/lib/searchapi/google_finance_request.rb +26 -0
- data/lib/searchapi/google_finance_result.rb +262 -0
- data/lib/searchapi/google_flights_options.rb +85 -0
- data/lib/searchapi/google_flights_request.rb +25 -0
- data/lib/searchapi/google_flights_result.rb +275 -0
- data/lib/searchapi/google_images_options.rb +64 -0
- data/lib/searchapi/google_images_request.rb +26 -0
- data/lib/searchapi/google_images_result.rb +133 -0
- data/lib/searchapi/google_light_options.rb +43 -0
- data/lib/searchapi/google_light_request.rb +26 -0
- data/lib/searchapi/google_light_result.rb +198 -0
- data/lib/searchapi/google_local_options.rb +40 -0
- data/lib/searchapi/google_local_request.rb +26 -0
- data/lib/searchapi/google_local_result.rb +85 -0
- data/lib/searchapi/google_news_light_options.rb +47 -0
- data/lib/searchapi/google_news_light_request.rb +26 -0
- data/lib/searchapi/google_news_light_result.rb +94 -0
- data/lib/searchapi/google_news_options.rb +53 -0
- data/lib/searchapi/google_news_portal_options.rb +40 -0
- data/lib/searchapi/google_news_portal_request.rb +89 -0
- data/lib/searchapi/google_news_portal_result.rb +137 -0
- data/lib/searchapi/google_news_request.rb +26 -0
- data/lib/searchapi/google_news_result.rb +129 -0
- data/lib/searchapi/google_scholar_options.rb +48 -0
- data/lib/searchapi/google_scholar_request.rb +26 -0
- data/lib/searchapi/google_scholar_result.rb +195 -0
- data/lib/searchapi/google_search_options.rb +67 -0
- data/lib/searchapi/google_search_request.rb +26 -0
- data/lib/searchapi/google_search_result.rb +301 -0
- data/lib/searchapi/helpers.rb +10 -0
- data/lib/searchapi/instagram_profile_options.rb +31 -0
- data/lib/searchapi/instagram_profile_request.rb +26 -0
- data/lib/searchapi/instagram_profile_result.rb +117 -0
- data/lib/searchapi/module_methods.rb +80 -0
- data/lib/searchapi/request.rb +25 -0
- data/lib/searchapi/response_methods.rb +14 -0
- data/lib/searchapi/tiktok_profile_options.rb +31 -0
- data/lib/searchapi/tiktok_profile_request.rb +26 -0
- data/lib/searchapi/tiktok_profile_result.rb +71 -0
- data/lib/searchapi/version.rb +3 -0
- data/lib/searchapi/youtube_search_options.rb +62 -0
- data/lib/searchapi/youtube_search_request.rb +26 -0
- data/lib/searchapi/youtube_search_result.rb +411 -0
- data/lib/searchapi.rb +70 -0
- data/searchapi.gemspec +37 -0
- metadata +164 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: d08a566b8b60588ec83cfb8c18f535de5fe329325dfafb1d8120a61f105afeac
|
|
4
|
+
data.tar.gz: 4a59d8ca1b3921e1d419abd37789ca5098b61f4dd0a5465c1674b76445cff156
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 680bf0570d6b8b814cf7256c5bc3df04d0a3e60f3c38cc7ff420044a51e15836835c8b40ead4c8a613c69016512206cc8f72fe41602124127b8130883d1408bd
|
|
7
|
+
data.tar.gz: 1dc489acd03a4540dcf63121ae4c4ece11f550f795c0e86a6f4326420c263dee4b0ac0bfc92b805ede693c24232f6d62dbf135bbb64424177963d52c342a7e45
|
data/README.md
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# SearchAPI Ruby Gem
|
|
2
|
+
|
|
3
|
+
A Ruby client for the [SearchAPI.io](https://searchapi.io) API, providing access to Google Search, YouTube, Instagram, TikTok, and more.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Add to your Gemfile:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
gem 'searchapi'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or install directly:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
gem install searchapi
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Configuration
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
require 'searchapi'
|
|
23
|
+
|
|
24
|
+
SearchAPI.api_key ENV['SEARCH_API_KEY']
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Available APIs
|
|
28
|
+
|
|
29
|
+
| API | Method | Description |
|
|
30
|
+
|-----|--------|-------------|
|
|
31
|
+
| Google Search | `SearchAPI.google(query)` | Full Google search with all features |
|
|
32
|
+
| Google Light | `SearchAPI.google_light(query)` | Fast, lightweight Google search |
|
|
33
|
+
| Google News | `SearchAPI.news(query)` | Google News articles |
|
|
34
|
+
| Google News Portal | `SearchAPI.news_portal(query)` | Google News Portal with topics |
|
|
35
|
+
| Google News Light | `SearchAPI.news_light(query)` | Fast, lightweight news search |
|
|
36
|
+
| Google Images | `SearchAPI.images(query)` | Google Image search |
|
|
37
|
+
| Google Local | `SearchAPI.local(query)` | Local business search |
|
|
38
|
+
| Google Scholar | `SearchAPI.scholar(query)` | Academic papers and citations |
|
|
39
|
+
| Google Finance | `SearchAPI.finance(symbol)` | Stock quotes and financial data |
|
|
40
|
+
| Google Flights | `SearchAPI.flights(options)` | Flight search and booking |
|
|
41
|
+
| YouTube | `SearchAPI.youtube(query)` | YouTube video search |
|
|
42
|
+
| Instagram | `SearchAPI.instagram(username)` | Instagram profile data |
|
|
43
|
+
| TikTok | `SearchAPI.tiktok(username)` | TikTok profile data |
|
|
44
|
+
|
|
45
|
+
## Quick Examples
|
|
46
|
+
|
|
47
|
+
### Google Search
|
|
48
|
+
|
|
49
|
+
```ruby
|
|
50
|
+
response = SearchAPI.google('ruby programming')
|
|
51
|
+
response.result.organic_results.each do |result|
|
|
52
|
+
puts "#{ result.title } - #{ result.link }"
|
|
53
|
+
end
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Google News
|
|
57
|
+
|
|
58
|
+
```ruby
|
|
59
|
+
response = SearchAPI.news('technology', time_period: :last_week)
|
|
60
|
+
response.result.each do |article|
|
|
61
|
+
puts "#{ article.title } (#{ article.source })"
|
|
62
|
+
end
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Google Images
|
|
66
|
+
|
|
67
|
+
```ruby
|
|
68
|
+
response = SearchAPI.images('sunset', size: :large, color: :orange)
|
|
69
|
+
response.result.each do |image|
|
|
70
|
+
puts "#{ image.title } - #{ image.original.link }"
|
|
71
|
+
end
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Google Finance
|
|
75
|
+
|
|
76
|
+
```ruby
|
|
77
|
+
response = SearchAPI.finance('AAPL:NASDAQ')
|
|
78
|
+
puts "#{ response.result.summary.title }: $#{ response.result.summary.price }"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Google Flights
|
|
82
|
+
|
|
83
|
+
```ruby
|
|
84
|
+
options = SearchAPI::GoogleFlightsOptions.build do
|
|
85
|
+
departure_id 'JFK'
|
|
86
|
+
arrival_id 'LAX'
|
|
87
|
+
outbound_date '2026-06-15'
|
|
88
|
+
flight_type :one_way
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
response = SearchAPI.flights(options)
|
|
92
|
+
puts "Cheapest: $#{ response.result.cheapest&.price }"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### YouTube Search
|
|
96
|
+
|
|
97
|
+
```ruby
|
|
98
|
+
response = SearchAPI.youtube('ruby tutorial')
|
|
99
|
+
response.result.videos.each do |video|
|
|
100
|
+
puts "#{ video.title } by #{ video.channel.title }"
|
|
101
|
+
end
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Social Media Profiles
|
|
105
|
+
|
|
106
|
+
```ruby
|
|
107
|
+
# Instagram
|
|
108
|
+
response = SearchAPI.instagram('instagram')
|
|
109
|
+
puts "#{ response.result.profile.username }: #{ response.result.followers } followers"
|
|
110
|
+
|
|
111
|
+
# TikTok
|
|
112
|
+
response = SearchAPI.tiktok('tiktok')
|
|
113
|
+
puts "#{ response.result.profile.name }: #{ response.result.hearts } hearts"
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Using Options
|
|
117
|
+
|
|
118
|
+
Each API supports options via a block or hash:
|
|
119
|
+
|
|
120
|
+
```ruby
|
|
121
|
+
# Block syntax
|
|
122
|
+
options = SearchAPI::GoogleSearchOptions.build do
|
|
123
|
+
device :mobile
|
|
124
|
+
gl 'us'
|
|
125
|
+
hl 'en'
|
|
126
|
+
time_period :last_week
|
|
127
|
+
end
|
|
128
|
+
response = SearchAPI.google('news', options)
|
|
129
|
+
|
|
130
|
+
# Hash syntax
|
|
131
|
+
response = SearchAPI.google('news', { device: :mobile, gl: 'us' })
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Options are case-insensitive and normalized automatically:
|
|
135
|
+
|
|
136
|
+
```ruby
|
|
137
|
+
# These are equivalent
|
|
138
|
+
SearchAPI::GoogleFinanceOptions.build { window :'1d' } # => '1D'
|
|
139
|
+
SearchAPI::GoogleFinanceOptions.build { window :'1D' } # => '1D'
|
|
140
|
+
SearchAPI::GoogleFinanceOptions.build { window :MAX } # => 'MAX'
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Response Structure
|
|
144
|
+
|
|
145
|
+
All responses return a Faraday response with a `result` accessor:
|
|
146
|
+
|
|
147
|
+
```ruby
|
|
148
|
+
response = SearchAPI.google('test')
|
|
149
|
+
response.success? # HTTP success
|
|
150
|
+
response.result.success? # API success (no error field)
|
|
151
|
+
response.result.search_metadata
|
|
152
|
+
response.result.search_parameters
|
|
153
|
+
response.result.organic_results
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Results are enumerable where it makes sense:
|
|
157
|
+
|
|
158
|
+
```ruby
|
|
159
|
+
response = SearchAPI.google('ruby')
|
|
160
|
+
response.result.each { |r| puts r.title } # Iterates organic_results
|
|
161
|
+
response.result.first # First organic result
|
|
162
|
+
response.result.count # Number of organic results
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Documentation
|
|
166
|
+
|
|
167
|
+
See the `/readme` directory for detailed documentation on each API endpoint.
|
|
168
|
+
|
|
169
|
+
## Contributing
|
|
170
|
+
|
|
171
|
+
Bug reports and pull requests are welcome on GitHub at [https://github.com/EndlessInternational/searchapi](https://github.com/EndlessInternational/searchapi).
|
|
172
|
+
|
|
173
|
+
## License
|
|
174
|
+
|
|
175
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/bin/test
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'bundler/setup'
|
|
4
|
+
|
|
5
|
+
$LOAD_PATH.unshift File.expand_path( '../lib', __dir__ )
|
|
6
|
+
$LOAD_PATH.unshift File.expand_path( '../test', __dir__ )
|
|
7
|
+
|
|
8
|
+
test_files = if ARGV.empty?
|
|
9
|
+
Dir.glob( File.expand_path( '../test/**/*_test.rb', __dir__ ) )
|
|
10
|
+
else
|
|
11
|
+
ARGV.map { | arg | File.expand_path( arg, Dir.pwd ) }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
test_files.each { | file | require file }
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
module SearchAPI
|
|
2
|
+
class ErrorResult
|
|
3
|
+
|
|
4
|
+
attr_reader :error_type, :error_description
|
|
5
|
+
|
|
6
|
+
def initialize( status_code, attributes = nil )
|
|
7
|
+
@error_type, @error_description = status_code_to_error( status_code )
|
|
8
|
+
if attributes&.respond_to?( :[] ) && attributes[ :error ]
|
|
9
|
+
@error_description = attributes[ :error ]
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def success?
|
|
14
|
+
false
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def status_code_to_error( status_code )
|
|
20
|
+
case status_code
|
|
21
|
+
when 200
|
|
22
|
+
[ :unexpected_error,
|
|
23
|
+
"The response was successful but it did not include a valid payload." ]
|
|
24
|
+
when 400
|
|
25
|
+
[ :invalid_request_error,
|
|
26
|
+
"The request is invalid due to malformed parameters, malformed JSON, or " \
|
|
27
|
+
"missing required fields." ]
|
|
28
|
+
when 401
|
|
29
|
+
[ :authentication_error,
|
|
30
|
+
"The API key is missing or invalid." ]
|
|
31
|
+
when 403
|
|
32
|
+
[ :forbidden_error,
|
|
33
|
+
"The API key is valid but has insufficient permissions." ]
|
|
34
|
+
when 404
|
|
35
|
+
[ :not_found_error,
|
|
36
|
+
"The requested resource was not found." ]
|
|
37
|
+
when 429
|
|
38
|
+
[ :rate_limit_error,
|
|
39
|
+
"The rate limit has been exceeded." ]
|
|
40
|
+
when 500..599
|
|
41
|
+
[ :server_error,
|
|
42
|
+
"The SearchAPI server encountered an error while processing the request." ]
|
|
43
|
+
else
|
|
44
|
+
[ :unknown_error,
|
|
45
|
+
"The SearchAPI service returned an unexpected status code: '#{ status_code }'." ]
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module SearchAPI
|
|
2
|
+
class GoogleFinanceOptions
|
|
3
|
+
include DynamicSchema::Definable
|
|
4
|
+
include Helpers
|
|
5
|
+
|
|
6
|
+
WINDOWS = %w[ 1D 5D 1M 6M YTD 1Y 5Y MAX ]
|
|
7
|
+
NORMALIZE_UPCASE = ->(v) { v.to_s.upcase }
|
|
8
|
+
|
|
9
|
+
schema do
|
|
10
|
+
# query (stock symbol, index, currency pair, etc.)
|
|
11
|
+
q String
|
|
12
|
+
|
|
13
|
+
# language
|
|
14
|
+
hl String
|
|
15
|
+
|
|
16
|
+
# time window for historical data
|
|
17
|
+
window String, in: WINDOWS, normalize: NORMALIZE_UPCASE
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.build( options = nil, &block )
|
|
21
|
+
new( api_options: builder.build( options, &block ) )
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.build!( options = nil, &block )
|
|
25
|
+
new( api_options: builder.build!( options, &block ) )
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def initialize( options = {}, api_options: nil )
|
|
29
|
+
@options = self.class.builder.build( options || {} )
|
|
30
|
+
@options = api_options.merge( @options ) if api_options
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def to_h
|
|
34
|
+
result = @options.to_h
|
|
35
|
+
result[ :engine ] = 'google_finance'
|
|
36
|
+
result
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module SearchAPI
|
|
2
|
+
class GoogleFinanceRequest < Request
|
|
3
|
+
|
|
4
|
+
def quote( symbol, options = nil, &block )
|
|
5
|
+
if options
|
|
6
|
+
options = options.is_a?( GoogleFinanceOptions ) ? options : GoogleFinanceOptions.build!( options.to_h )
|
|
7
|
+
options = options.to_h
|
|
8
|
+
else
|
|
9
|
+
options = { engine: 'google_finance' }
|
|
10
|
+
end
|
|
11
|
+
options[ :q ] = symbol.to_s
|
|
12
|
+
|
|
13
|
+
response = get( "#{ BASE_URI }/search", options, &block )
|
|
14
|
+
attributes = ( JSON.parse( response.body, symbolize_names: true ) rescue nil )
|
|
15
|
+
|
|
16
|
+
result = if response.success?
|
|
17
|
+
GoogleFinanceResult.new( attributes || {} )
|
|
18
|
+
else
|
|
19
|
+
ErrorResult.new( response.status, attributes )
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
ResponseMethods.install( response, result )
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
require 'forwardable'
|
|
2
|
+
|
|
3
|
+
module SearchAPI
|
|
4
|
+
|
|
5
|
+
# Google Finance Search metadata
|
|
6
|
+
GoogleFinanceMetadataSchema = DynamicSchema::Struct.define do
|
|
7
|
+
id String
|
|
8
|
+
status String
|
|
9
|
+
created_at String, as: :created_at
|
|
10
|
+
request_time_taken Float, as: :request_time_taken
|
|
11
|
+
parsing_time_taken Float, as: :parsing_time_taken
|
|
12
|
+
total_time_taken Float, as: :total_time_taken
|
|
13
|
+
request_url String, as: :request_url
|
|
14
|
+
html_url String, as: :html_url
|
|
15
|
+
json_url String, as: :json_url
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class GoogleFinanceMetadata < GoogleFinanceMetadataSchema
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Google Finance Search parameters
|
|
22
|
+
GoogleFinanceParametersSchema = DynamicSchema::Struct.define do
|
|
23
|
+
engine String
|
|
24
|
+
q String
|
|
25
|
+
hl String
|
|
26
|
+
window String
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
class GoogleFinanceParameters < GoogleFinanceParametersSchema
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Price change
|
|
33
|
+
GoogleFinancePriceChangeSchema = DynamicSchema::Struct.define do
|
|
34
|
+
percentage Float
|
|
35
|
+
amount Float
|
|
36
|
+
movement String
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
class GoogleFinancePriceChange < GoogleFinancePriceChangeSchema
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Summary
|
|
43
|
+
GoogleFinanceSummarySchema = DynamicSchema::Struct.define do
|
|
44
|
+
title String
|
|
45
|
+
stock String
|
|
46
|
+
exchange String
|
|
47
|
+
price Float
|
|
48
|
+
currency String
|
|
49
|
+
date String
|
|
50
|
+
price_change GoogleFinancePriceChange, as: :price_change
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
class GoogleFinanceSummary < GoogleFinanceSummarySchema
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Key event
|
|
57
|
+
GoogleFinanceKeyEventSchema = DynamicSchema::Struct.define do
|
|
58
|
+
title String
|
|
59
|
+
link String
|
|
60
|
+
source String
|
|
61
|
+
source_date String, as: :source_date
|
|
62
|
+
date String
|
|
63
|
+
price_change GoogleFinancePriceChange, as: :price_change
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
class GoogleFinanceKeyEvent < GoogleFinanceKeyEventSchema
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Graph point
|
|
70
|
+
GoogleFinanceGraphPointSchema = DynamicSchema::Struct.define do
|
|
71
|
+
price Float
|
|
72
|
+
currency String
|
|
73
|
+
date String
|
|
74
|
+
volume Integer
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
class GoogleFinanceGraphPoint < GoogleFinanceGraphPointSchema
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Knowledge graph tag
|
|
81
|
+
GoogleFinanceTagSchema = DynamicSchema::Struct.define do
|
|
82
|
+
title String
|
|
83
|
+
description String
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
class GoogleFinanceTag < GoogleFinanceTagSchema
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Knowledge graph stat
|
|
90
|
+
GoogleFinanceStatSchema = DynamicSchema::Struct.define do
|
|
91
|
+
label String
|
|
92
|
+
description String
|
|
93
|
+
value String
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
class GoogleFinanceStat < GoogleFinanceStatSchema
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Company about
|
|
100
|
+
GoogleFinanceCompanyAboutSchema = DynamicSchema::Struct.define do
|
|
101
|
+
company String
|
|
102
|
+
description String
|
|
103
|
+
address String
|
|
104
|
+
founded String
|
|
105
|
+
ceo String
|
|
106
|
+
employees Integer
|
|
107
|
+
website String
|
|
108
|
+
wikipedia String
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
class GoogleFinanceCompanyAbout < GoogleFinanceCompanyAboutSchema
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Knowledge graph
|
|
115
|
+
GoogleFinanceKnowledgeGraphSchema = DynamicSchema::Struct.define do
|
|
116
|
+
tags GoogleFinanceTag, array: true
|
|
117
|
+
stats GoogleFinanceStat, array: true
|
|
118
|
+
about GoogleFinanceCompanyAbout
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
class GoogleFinanceKnowledgeGraph < GoogleFinanceKnowledgeGraphSchema
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# News article
|
|
125
|
+
GoogleFinanceArticleSchema = DynamicSchema::Struct.define do
|
|
126
|
+
snippet String
|
|
127
|
+
link String
|
|
128
|
+
source String
|
|
129
|
+
date String
|
|
130
|
+
thumbnail String
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
class GoogleFinanceArticle < GoogleFinanceArticleSchema
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# News category
|
|
137
|
+
GoogleFinanceNewsCategorySchema = DynamicSchema::Struct.define do
|
|
138
|
+
title String
|
|
139
|
+
articles GoogleFinanceArticle, array: true
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
class GoogleFinanceNewsCategory < GoogleFinanceNewsCategorySchema
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Financial metric
|
|
146
|
+
GoogleFinanceMetricSchema = DynamicSchema::Struct.define do
|
|
147
|
+
value Float
|
|
148
|
+
last_year_value Float, as: :last_year_value
|
|
149
|
+
price_change GoogleFinancePriceChange, as: :price_change
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
class GoogleFinanceMetric < GoogleFinanceMetricSchema
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Quarterly financial
|
|
156
|
+
GoogleFinanceQuarterlySchema = DynamicSchema::Struct.define do
|
|
157
|
+
year Integer
|
|
158
|
+
quarter String
|
|
159
|
+
currency String
|
|
160
|
+
revenue GoogleFinanceMetric
|
|
161
|
+
net_income GoogleFinanceMetric, as: :net_income
|
|
162
|
+
earnings_per_share GoogleFinanceMetric, as: :earnings_per_share
|
|
163
|
+
net_profit_margin GoogleFinanceMetric, as: :net_profit_margin
|
|
164
|
+
total_assets GoogleFinanceMetric, as: :total_assets
|
|
165
|
+
total_equity GoogleFinanceMetric, as: :total_equity
|
|
166
|
+
total_liabilities GoogleFinanceMetric, as: :total_liabilities
|
|
167
|
+
free_cash_flow GoogleFinanceMetric, as: :free_cash_flow
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
class GoogleFinanceQuarterly < GoogleFinanceQuarterlySchema
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Annual financial
|
|
174
|
+
GoogleFinanceAnnualSchema = DynamicSchema::Struct.define do
|
|
175
|
+
year Integer
|
|
176
|
+
currency String
|
|
177
|
+
revenue GoogleFinanceMetric
|
|
178
|
+
net_income GoogleFinanceMetric, as: :net_income
|
|
179
|
+
earnings_per_share GoogleFinanceMetric, as: :earnings_per_share
|
|
180
|
+
net_profit_margin GoogleFinanceMetric, as: :net_profit_margin
|
|
181
|
+
total_assets GoogleFinanceMetric, as: :total_assets
|
|
182
|
+
total_equity GoogleFinanceMetric, as: :total_equity
|
|
183
|
+
total_liabilities GoogleFinanceMetric, as: :total_liabilities
|
|
184
|
+
free_cash_flow GoogleFinanceMetric, as: :free_cash_flow
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
class GoogleFinanceAnnual < GoogleFinanceAnnualSchema
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Financials
|
|
191
|
+
GoogleFinanceFinancialsSchema = DynamicSchema::Struct.define do
|
|
192
|
+
quarterly GoogleFinanceQuarterly, array: true
|
|
193
|
+
annual GoogleFinanceAnnual, array: true
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
class GoogleFinanceFinancials < GoogleFinanceFinancialsSchema
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Market stock
|
|
200
|
+
GoogleFinanceMarketStockSchema = DynamicSchema::Struct.define do
|
|
201
|
+
stock String
|
|
202
|
+
exchange String
|
|
203
|
+
company String
|
|
204
|
+
link String
|
|
205
|
+
name String
|
|
206
|
+
price Float
|
|
207
|
+
price_change GoogleFinancePriceChange, as: :price_change
|
|
208
|
+
date String
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
class GoogleFinanceMarketStock < GoogleFinanceMarketStockSchema
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Discover item
|
|
215
|
+
GoogleFinanceDiscoverItemSchema = DynamicSchema::Struct.define do
|
|
216
|
+
stock String
|
|
217
|
+
link String
|
|
218
|
+
name String
|
|
219
|
+
price Float
|
|
220
|
+
price_change GoogleFinancePriceChange, as: :price_change
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
class GoogleFinanceDiscoverItem < GoogleFinanceDiscoverItemSchema
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# Discover section
|
|
227
|
+
GoogleFinanceDiscoverSectionSchema = DynamicSchema::Struct.define do
|
|
228
|
+
title String
|
|
229
|
+
items GoogleFinanceDiscoverItem, array: true
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
class GoogleFinanceDiscoverSection < GoogleFinanceDiscoverSectionSchema
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
# Main Google Finance result
|
|
236
|
+
GoogleFinanceResultSchema = DynamicSchema::Struct.define do
|
|
237
|
+
search_metadata GoogleFinanceMetadata, as: :search_metadata
|
|
238
|
+
search_parameters GoogleFinanceParameters, as: :search_parameters
|
|
239
|
+
summary GoogleFinanceSummary
|
|
240
|
+
key_events GoogleFinanceKeyEvent, array: true, as: :key_events
|
|
241
|
+
graph GoogleFinanceGraphPoint, array: true
|
|
242
|
+
knowledge_graph GoogleFinanceKnowledgeGraph, as: :knowledge_graph
|
|
243
|
+
news GoogleFinanceNewsCategory, array: true
|
|
244
|
+
articles GoogleFinanceArticle, array: true
|
|
245
|
+
financials GoogleFinanceFinancials
|
|
246
|
+
markets Hash
|
|
247
|
+
compare_to GoogleFinanceMarketStock, array: true, as: :compare_to
|
|
248
|
+
discover_more GoogleFinanceDiscoverSection, array: true, as: :discover_more
|
|
249
|
+
error String
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
class GoogleFinanceResult < GoogleFinanceResultSchema
|
|
253
|
+
extend Forwardable
|
|
254
|
+
|
|
255
|
+
def_delegators :summary, :title, :stock, :exchange, :price, :currency
|
|
256
|
+
|
|
257
|
+
def success?
|
|
258
|
+
self.error.nil?
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
module SearchAPI
|
|
2
|
+
class GoogleFlightsOptions
|
|
3
|
+
include DynamicSchema::Definable
|
|
4
|
+
include Helpers
|
|
5
|
+
|
|
6
|
+
FLIGHT_TYPES = %w[ round_trip one_way multi_city ]
|
|
7
|
+
STOPS = %w[ any nonstop one_stop_or_fewer two_stops_or_fewer ]
|
|
8
|
+
SORT_BY = %w[ top_flights price departure_time arrival_time duration emissions ]
|
|
9
|
+
TRAVEL_CLASSES = %w[ economy premium_economy business first_class ]
|
|
10
|
+
|
|
11
|
+
NORMALIZE_DOWNCASE = ->(v) { v.to_s.downcase }
|
|
12
|
+
|
|
13
|
+
schema do
|
|
14
|
+
# locations
|
|
15
|
+
departure_id String
|
|
16
|
+
arrival_id String
|
|
17
|
+
|
|
18
|
+
# dates
|
|
19
|
+
outbound_date String
|
|
20
|
+
return_date String
|
|
21
|
+
|
|
22
|
+
# localization
|
|
23
|
+
hl String
|
|
24
|
+
gl String
|
|
25
|
+
currency String
|
|
26
|
+
|
|
27
|
+
# flight options
|
|
28
|
+
flight_type String, in: FLIGHT_TYPES, normalize: NORMALIZE_DOWNCASE
|
|
29
|
+
stops String, in: STOPS, normalize: NORMALIZE_DOWNCASE
|
|
30
|
+
sort_by String, in: SORT_BY, normalize: NORMALIZE_DOWNCASE
|
|
31
|
+
travel_class String, in: TRAVEL_CLASSES, normalize: NORMALIZE_DOWNCASE
|
|
32
|
+
|
|
33
|
+
# passengers
|
|
34
|
+
adults Integer, in: 1..9
|
|
35
|
+
children Integer, in: 0..9
|
|
36
|
+
infants_in_seat Integer, in: 0..9
|
|
37
|
+
infants_on_lap Integer, in: 0..9
|
|
38
|
+
|
|
39
|
+
# filters
|
|
40
|
+
max_price Integer
|
|
41
|
+
carry_on_bags Integer
|
|
42
|
+
checked_bags Integer
|
|
43
|
+
included_airlines String
|
|
44
|
+
excluded_airlines String
|
|
45
|
+
included_connecting_airports String
|
|
46
|
+
excluded_connecting_airports String
|
|
47
|
+
outbound_times String
|
|
48
|
+
return_times String
|
|
49
|
+
emissions String
|
|
50
|
+
layover_duration_min Integer
|
|
51
|
+
layover_duration_max Integer
|
|
52
|
+
max_flight_duration Integer
|
|
53
|
+
separate_tickets String
|
|
54
|
+
|
|
55
|
+
# pagination/booking
|
|
56
|
+
departure_token String
|
|
57
|
+
booking_token String
|
|
58
|
+
multi_city_json String
|
|
59
|
+
|
|
60
|
+
# display options
|
|
61
|
+
show_cheapest_flights [ TrueClass, FalseClass ]
|
|
62
|
+
show_hidden_flights [ TrueClass, FalseClass ]
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def self.build( options = nil, &block )
|
|
66
|
+
new( api_options: builder.build( options, &block ) )
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def self.build!( options = nil, &block )
|
|
70
|
+
new( api_options: builder.build!( options, &block ) )
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def initialize( options = {}, api_options: nil )
|
|
74
|
+
@options = self.class.builder.build( options || {} )
|
|
75
|
+
@options = api_options.merge( @options ) if api_options
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def to_h
|
|
79
|
+
result = @options.to_h
|
|
80
|
+
result[ :engine ] = 'google_flights'
|
|
81
|
+
result
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module SearchAPI
|
|
2
|
+
class GoogleFlightsRequest < Request
|
|
3
|
+
|
|
4
|
+
def search( options = nil, &block )
|
|
5
|
+
if options
|
|
6
|
+
options = options.is_a?( GoogleFlightsOptions ) ? options : GoogleFlightsOptions.build!( options.to_h )
|
|
7
|
+
options = options.to_h
|
|
8
|
+
else
|
|
9
|
+
options = { engine: 'google_flights' }
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
response = get( "#{ BASE_URI }/search", options, &block )
|
|
13
|
+
attributes = ( JSON.parse( response.body, symbolize_names: true ) rescue nil )
|
|
14
|
+
|
|
15
|
+
result = if response.success?
|
|
16
|
+
GoogleFlightsResult.new( attributes || {} )
|
|
17
|
+
else
|
|
18
|
+
ErrorResult.new( response.status, attributes )
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
ResponseMethods.install( response, result )
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end
|
|
25
|
+
end
|