tlaw 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.codeclimate.yml +11 -0
- data/.yardopts +2 -0
- data/LICENSE.txt +22 -0
- data/README.md +438 -0
- data/examples/demo_base.rb +10 -0
- data/examples/forecast_io.rb +113 -0
- data/examples/forecast_io_demo.rb +72 -0
- data/examples/open_weather_map.rb +266 -0
- data/examples/open_weather_map_demo.rb +219 -0
- data/examples/tmdb_demo.rb +133 -0
- data/examples/urbandictionary_demo.rb +105 -0
- data/lib/tlaw.rb +67 -0
- data/lib/tlaw/api.rb +58 -0
- data/lib/tlaw/api_path.rb +137 -0
- data/lib/tlaw/data_table.rb +116 -0
- data/lib/tlaw/dsl.rb +511 -0
- data/lib/tlaw/endpoint.rb +132 -0
- data/lib/tlaw/namespace.rb +159 -0
- data/lib/tlaw/param.rb +155 -0
- data/lib/tlaw/param/type.rb +113 -0
- data/lib/tlaw/param_set.rb +111 -0
- data/lib/tlaw/response_processor.rb +124 -0
- data/lib/tlaw/util.rb +45 -0
- data/lib/tlaw/version.rb +7 -0
- data/tlaw.gemspec +53 -0
- metadata +265 -0
@@ -0,0 +1,113 @@
|
|
1
|
+
module TLAW
|
2
|
+
module Examples
|
3
|
+
class ForecastIO < TLAW::API
|
4
|
+
define do
|
5
|
+
base 'https://api.forecast.io/forecast/{api_key}'
|
6
|
+
|
7
|
+
desc %Q{
|
8
|
+
The Forecast API allows you to look up the weather anywhere on
|
9
|
+
the globe, returning (where available):
|
10
|
+
|
11
|
+
* Current conditions
|
12
|
+
* Minute-by-minute forecasts out to 1 hour
|
13
|
+
* Hour-by-hour forecasts out to 48 hours
|
14
|
+
* Day-by-day forecasts out to 7 days
|
15
|
+
}
|
16
|
+
|
17
|
+
docs 'https://developer.forecast.io/docs/v2'
|
18
|
+
|
19
|
+
param :api_key, required: true,
|
20
|
+
desc: 'Register at https://developer.forecast.io/register to obtain it'
|
21
|
+
param :units, enum: %i[us si ca uk2 auto], default: :us,
|
22
|
+
desc: %Q{
|
23
|
+
Response units. Values:
|
24
|
+
* `:us` is default;
|
25
|
+
* `:si` is meters/celsius/hectopascals;
|
26
|
+
* `:ca` is identical to si, except that `windSpeed` is in
|
27
|
+
kilometers per hour.
|
28
|
+
* `:uk2` is identical to si, except that `windSpeed` is in
|
29
|
+
miles per hour, and `nearestStormDistance` and `visibility`
|
30
|
+
are in miles, as in the US.
|
31
|
+
* `auto` selects the relevant units automatically, based
|
32
|
+
on geographic location.
|
33
|
+
}
|
34
|
+
|
35
|
+
param :lang, default: :en,
|
36
|
+
desc: %Q{
|
37
|
+
Return summary properties in the desired language. (2-letters code)
|
38
|
+
}
|
39
|
+
|
40
|
+
endpoint :forecast, '/{lat},{lng}' do
|
41
|
+
desc %Q{Forecast for the next week.}
|
42
|
+
|
43
|
+
docs 'https://developer.forecast.io/docs/v2#forecast_call'
|
44
|
+
|
45
|
+
param :lat, :to_f, required: true, desc: 'Latitude'
|
46
|
+
param :lng, :to_f, required: true, desc: 'Longitude'
|
47
|
+
|
48
|
+
param :exclude, Array,
|
49
|
+
desc: %Q{
|
50
|
+
Exclude some number of data blocks from the API response.
|
51
|
+
This is useful for reducing latency and saving cache space.
|
52
|
+
Should be a list (without spaces) of any of the following:
|
53
|
+
currently, minutely, hourly, daily, alerts, flags.
|
54
|
+
}
|
55
|
+
|
56
|
+
param :extended_hourly, field: :extend,
|
57
|
+
enum: {false => nil, true => 'hourly'},
|
58
|
+
desc: %Q{
|
59
|
+
When present, return hourly data for the next seven days,
|
60
|
+
rather than the next two.
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
endpoint :time_machine, '/{lat},{lng},{at}' do
|
65
|
+
desc %Q{
|
66
|
+
Observed weather at a given time (for many places, up to 60
|
67
|
+
years in the past).
|
68
|
+
|
69
|
+
For future dates, returns numerical forecast for the next week
|
70
|
+
and seasonal averages beyond that.
|
71
|
+
}
|
72
|
+
|
73
|
+
docs 'https://developer.forecast.io/docs/v2#time_call'
|
74
|
+
|
75
|
+
param :lat, :to_f, required: true, desc: 'Latitude'
|
76
|
+
param :lng, :to_f, required: true, desc: 'Longitude'
|
77
|
+
param :at, :to_time, format: :to_i, required: true,
|
78
|
+
desc: 'Date in past or future.'
|
79
|
+
|
80
|
+
param :exclude, Array,
|
81
|
+
desc: %Q{
|
82
|
+
Exclude some number of data blocks from the API response.
|
83
|
+
This is useful for reducing latency and saving cache space.
|
84
|
+
Should be a list (without spaces) of any of the following:
|
85
|
+
currently, minutely, hourly, daily, alerts, flags.
|
86
|
+
}
|
87
|
+
end
|
88
|
+
|
89
|
+
post_process 'currently.time', &Time.method(:at)
|
90
|
+
|
91
|
+
post_process_items('minutely.data') {
|
92
|
+
post_process 'time', &Time.method(:at)
|
93
|
+
}
|
94
|
+
|
95
|
+
post_process_items('hourly.data') {
|
96
|
+
post_process 'time', &Time.method(:at)
|
97
|
+
}
|
98
|
+
|
99
|
+
post_process_items('daily.data') {
|
100
|
+
post_process 'time', &Time.method(:at)
|
101
|
+
post_process 'sunriseTime', &Time.method(:at)
|
102
|
+
post_process 'sunsetTime', &Time.method(:at)
|
103
|
+
}
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# TODO: X-Forecast-API-Calls header is useful!
|
108
|
+
# TODO: The Forecast Data API supports HTTP compression.
|
109
|
+
# We heartily recommend using it, as it will make responses much
|
110
|
+
# smaller over the wire. To enable it, simply add an
|
111
|
+
# Accept-Encoding: gzip header to your request.
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require_relative 'demo_base'
|
3
|
+
require_relative 'forecast_io'
|
4
|
+
|
5
|
+
# This wrapper demo demonstrates that even a simple (one call, several
|
6
|
+
# params) API could benefit from TLAW by param types checking &
|
7
|
+
# sky-level discoverability.
|
8
|
+
|
9
|
+
p TLAW::Examples::ForecastIO
|
10
|
+
# #<TLAW::Examples::ForecastIO: call-sequence: TLAW::Examples::ForecastIO.new(api_key:, units: :us, lang: :en); endpoints: forecast, time_machine; docs: .describe>
|
11
|
+
|
12
|
+
p TLAW::Examples::ForecastIO.describe
|
13
|
+
# TLAW::Examples::ForecastIO.new(api_key:, units: :us, lang: :en)
|
14
|
+
# The Forecast API allows you to look up the weather anywhere on
|
15
|
+
# the globe, returning (where available):
|
16
|
+
#
|
17
|
+
# * Current conditions
|
18
|
+
# * Minute-by-minute forecasts out to 1 hour
|
19
|
+
# * Hour-by-hour forecasts out to 48 hours
|
20
|
+
# * Day-by-day forecasts out to 7 days
|
21
|
+
#
|
22
|
+
# Docs: https://developer.forecast.io/docs/v2
|
23
|
+
#
|
24
|
+
# @param api_key Register at https://developer.forecast.io/register to obtain it
|
25
|
+
# @param units
|
26
|
+
# Response units. Values:
|
27
|
+
# * `:us` is default;
|
28
|
+
# * `:si` is meters/celsius/hectopascals;
|
29
|
+
# * `:ca` is identical to si, except that `windSpeed` is in
|
30
|
+
# kilometers per hour.
|
31
|
+
# * `:uk2` is identical to si, except that `windSpeed` is in
|
32
|
+
# miles per hour, and `nearestStormDistance` and `visibility`
|
33
|
+
# are in miles, as in the US.
|
34
|
+
# * `auto` selects the relevant units automatically, based
|
35
|
+
# on geographic location.
|
36
|
+
#
|
37
|
+
# Possible values: :us, :si, :ca, :uk2, :auto (default = :us)
|
38
|
+
# @param lang
|
39
|
+
# Return summary properties in the desired language. (2-letters code)
|
40
|
+
# (default = :en)
|
41
|
+
#
|
42
|
+
# Endpoints:
|
43
|
+
#
|
44
|
+
# .forecast(lat, lng, exclude: nil, extended_hourly: nil)
|
45
|
+
# Forecast for the next week.
|
46
|
+
#
|
47
|
+
# .time_machine(lat, lng, at, exclude: nil)
|
48
|
+
# Observed weather at a given time (for many places, up to 60
|
49
|
+
# years in the past).
|
50
|
+
|
51
|
+
# You need to create key here: https://developer.forecast.io/register
|
52
|
+
# And run the script this way:
|
53
|
+
#
|
54
|
+
# FORECAST_IO={your_id} examples/forecast_io_demo.rb
|
55
|
+
#
|
56
|
+
|
57
|
+
weather = TLAW::Examples::ForecastIO
|
58
|
+
.new(api_key: ENV['FORECAST_IO'], units: :si)
|
59
|
+
|
60
|
+
res = weather.forecast(40.7127, -74.0059, extended_hourly: true)
|
61
|
+
pp res['minutely.data'].first
|
62
|
+
# {"time"=>2016-09-12 21:20:00 +0300,
|
63
|
+
# "precipIntensity"=>0,
|
64
|
+
# "precipProbability"=>0}
|
65
|
+
|
66
|
+
res = weather.time_machine(49.999892, 36.242392, Date.parse('2020-02-01'))
|
67
|
+
pp res['daily.data'].columns('time', 'temperatureMin', 'temperatureMax', 'dewPoint', 'moonPhase').first
|
68
|
+
# {"time"=>2020-02-01 00:00:00 +0200,
|
69
|
+
# "temperatureMin"=>-6.61,
|
70
|
+
# "temperatureMax"=>-4.37,
|
71
|
+
# "dewPoint"=>-7.7,
|
72
|
+
# "moonPhase"=>0.23}
|
@@ -0,0 +1,266 @@
|
|
1
|
+
require 'geo/coord'
|
2
|
+
|
3
|
+
module TLAW
|
4
|
+
module Examples
|
5
|
+
class OpenWeatherMap < TLAW::API
|
6
|
+
define do
|
7
|
+
desc %Q{
|
8
|
+
API for [OpenWeatherMap](http://openweathermap.org/). Only parts
|
9
|
+
available for free are implemented (as only them could be tested).
|
10
|
+
}
|
11
|
+
|
12
|
+
docs 'http://openweathermap.org/api'
|
13
|
+
|
14
|
+
base 'http://api.openweathermap.org/data/2.5'
|
15
|
+
|
16
|
+
param :appid, required: true,
|
17
|
+
desc: 'You need to receive it at http://openweathermap.org/appid (free)'
|
18
|
+
|
19
|
+
param :lang, default: 'en',
|
20
|
+
desc: %Q{Language of API responses (affects weather description only).
|
21
|
+
See http://openweathermap.org/current#multi for list of supported languages.}
|
22
|
+
|
23
|
+
param :units, enum: %i[standard metric imperial], default: :standard,
|
24
|
+
desc: 'Units for temperature and other values. Standard is Kelvin.'
|
25
|
+
|
26
|
+
namespace :current, '/weather' do
|
27
|
+
desc %Q{
|
28
|
+
Allows to obtain current weather at one place, designated
|
29
|
+
by city, location or zip code.
|
30
|
+
}
|
31
|
+
|
32
|
+
docs 'http://openweathermap.org/current'
|
33
|
+
|
34
|
+
endpoint :city, '?q={city}{,country_code}' do
|
35
|
+
desc %Q{
|
36
|
+
Current weather by city name (with optional country code
|
37
|
+
specification).
|
38
|
+
}
|
39
|
+
|
40
|
+
docs 'http://openweathermap.org/current#name'
|
41
|
+
|
42
|
+
param :city, required: true, desc: 'City name'
|
43
|
+
param :country_code, desc: 'ISO 3166 2-letter country code'
|
44
|
+
end
|
45
|
+
|
46
|
+
endpoint :city_id, '?id={city_id}' do
|
47
|
+
desc %Q{
|
48
|
+
Current weather by city id. Recommended by OpenWeatherMap
|
49
|
+
docs.
|
50
|
+
|
51
|
+
List of city ID city.list.json.gz can be downloaded at
|
52
|
+
http://bulk.openweathermap.org/sample/
|
53
|
+
}
|
54
|
+
|
55
|
+
docs 'http://openweathermap.org/current#cityid'
|
56
|
+
|
57
|
+
param :city_id, required: true, desc: 'City ID (as defined by OpenWeatherMap)'
|
58
|
+
end
|
59
|
+
|
60
|
+
endpoint :location, '?lat={lat}&lon={lng}' do
|
61
|
+
desc %Q{
|
62
|
+
Current weather by geographic coordinates.
|
63
|
+
}
|
64
|
+
|
65
|
+
docs 'http://openweathermap.org/current#geo'
|
66
|
+
|
67
|
+
param :lat, :to_f, required: true, desc: 'Latitude'
|
68
|
+
param :lng, :to_f, required: true, desc: 'Longitude'
|
69
|
+
end
|
70
|
+
|
71
|
+
endpoint :zip, '?zip={zip}{,country_code}' do
|
72
|
+
desc %Q{
|
73
|
+
Current weather by ZIP code (with optional country code
|
74
|
+
specification).
|
75
|
+
}
|
76
|
+
|
77
|
+
docs 'http://openweathermap.org/current#zip'
|
78
|
+
|
79
|
+
param :zip, required: true, desc: 'ZIP code'
|
80
|
+
param :country_code, desc: 'ISO 3166 2-letter country code'
|
81
|
+
end
|
82
|
+
|
83
|
+
endpoint :group, '/../group?id={city_ids}' do
|
84
|
+
desc %Q{
|
85
|
+
Current weather in several cities by their ids.
|
86
|
+
|
87
|
+
List of city ID city.list.json.gz can be downloaded at
|
88
|
+
http://bulk.openweathermap.org/sample/
|
89
|
+
}
|
90
|
+
|
91
|
+
docs 'http://openweathermap.org/current#cities'
|
92
|
+
|
93
|
+
param :city_ids, :to_a, required: true
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
namespace :find do
|
98
|
+
desc %Q{
|
99
|
+
Allows to find some place (and weather in it) by set of input
|
100
|
+
parameters.
|
101
|
+
}
|
102
|
+
|
103
|
+
docs 'http://openweathermap.org/current#accuracy'
|
104
|
+
|
105
|
+
endpoint :by_name, '?q={start_with}{,country_code}' do
|
106
|
+
desc %Q{
|
107
|
+
Looks for cities by beginning of their names.
|
108
|
+
}
|
109
|
+
|
110
|
+
docs 'http://openweathermap.org/current#accuracy'
|
111
|
+
|
112
|
+
param :start_with, required: true, desc: 'Beginning of city name'
|
113
|
+
param :country_code, desc: 'ISO 3166 2-letter country code'
|
114
|
+
|
115
|
+
param :cnt, :to_i, range: 1..50, default: 10,
|
116
|
+
desc: 'Max number of results to return'
|
117
|
+
|
118
|
+
param :accurate, field: :type,
|
119
|
+
enum: {true => 'accurate', false => 'like'},
|
120
|
+
default: true,
|
121
|
+
desc: %Q{Accuracy level of result.
|
122
|
+
true returns exact match values (accurate).
|
123
|
+
false returns results by searching for that substring (like).
|
124
|
+
}
|
125
|
+
end
|
126
|
+
|
127
|
+
endpoint :around, '?lat={lat}&lon={lng}' do
|
128
|
+
desc %Q{
|
129
|
+
Looks for cities around geographical coordinates.
|
130
|
+
}
|
131
|
+
|
132
|
+
docs 'http://openweathermap.org/current#cycle'
|
133
|
+
|
134
|
+
param :lat, :to_f, required: true, desc: 'Latitude'
|
135
|
+
param :lng, :to_f, required: true, desc: 'Longitude'
|
136
|
+
|
137
|
+
param :cnt, :to_i, range: 1..50, default: 10,
|
138
|
+
desc: 'Max number of results to return'
|
139
|
+
|
140
|
+
param :cluster, enum: {true => 'yes', false: 'no'},
|
141
|
+
default: true,
|
142
|
+
desc: 'Use server clustering of points'
|
143
|
+
end
|
144
|
+
|
145
|
+
# Real path is api/bbox/city - not inside /find, but logically
|
146
|
+
# we want to place it here
|
147
|
+
endpoint :inside, '/../box/city?bbox={lng_left},{lat_bottom},{lng_right},{lat_top},{zoom}' do
|
148
|
+
desc %Q{
|
149
|
+
Looks for cities inside specified rectangle zone.
|
150
|
+
}
|
151
|
+
|
152
|
+
docs 'http://openweathermap.org/current#rectangle'
|
153
|
+
|
154
|
+
param :lat_top, :to_f, required: true, keyword: true
|
155
|
+
param :lat_bottom, :to_f, required: true, keyword: true
|
156
|
+
param :lng_left, :to_f, required: true, keyword: true
|
157
|
+
param :lng_right, :to_f, required: true, keyword: true
|
158
|
+
param :zoom, :to_i, default: 10, keyword: true,
|
159
|
+
desc: 'Map zoom level.'
|
160
|
+
|
161
|
+
param :cluster, enum: {true => 'yes', false: 'no'},
|
162
|
+
default: true,
|
163
|
+
desc: 'Use server clustering of points'
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
# http://openweathermap.org/forecast5
|
168
|
+
namespace :forecast do
|
169
|
+
desc %Q{
|
170
|
+
Allows to obtain weather forecast for 5 days with 3-hour
|
171
|
+
frequency.
|
172
|
+
|
173
|
+
NB: OpenWeatherMap also implement [16-day forecast](http://openweathermap.org/forecast16),
|
174
|
+
but it have no free option and can not be tested. That's why
|
175
|
+
we don't implement it.
|
176
|
+
}
|
177
|
+
|
178
|
+
docs 'http://openweathermap.org/forecast5'
|
179
|
+
|
180
|
+
endpoint :city, '?q={city}{,country_code}' do
|
181
|
+
desc %Q{
|
182
|
+
Weather forecast by city name (with optional country code
|
183
|
+
specification).
|
184
|
+
}
|
185
|
+
|
186
|
+
docs 'http://openweathermap.org/forecast5#name5'
|
187
|
+
|
188
|
+
param :city, required: true, desc: 'City name'
|
189
|
+
param :country_code, desc: 'ISO 3166 2-letter country code'
|
190
|
+
end
|
191
|
+
|
192
|
+
endpoint :city_id, '?id={city_id}' do
|
193
|
+
desc %Q{
|
194
|
+
Current weather by city id. Recommended by OpenWeatherMap
|
195
|
+
docs.
|
196
|
+
|
197
|
+
List of city ID city.list.json.gz can be downloaded at
|
198
|
+
http://bulk.openweathermap.org/sample/
|
199
|
+
}
|
200
|
+
|
201
|
+
docs 'http://openweathermap.org/forecast5#cityid5'
|
202
|
+
|
203
|
+
param :city_id, required: true, desc: 'City ID (as defined by OpenWeatherMap)'
|
204
|
+
end
|
205
|
+
|
206
|
+
endpoint :location, '?lat={lat}&lon={lng}' do
|
207
|
+
desc %Q{
|
208
|
+
Weather forecast by geographic coordinates.
|
209
|
+
}
|
210
|
+
|
211
|
+
docs 'http://openweathermap.org/forecast5#geo5'
|
212
|
+
|
213
|
+
param :lat, :to_f, required: true, desc: 'Latitude'
|
214
|
+
param :lng, :to_f, required: true, desc: 'Longitude'
|
215
|
+
end
|
216
|
+
|
217
|
+
post_process { |e|
|
218
|
+
e['city.coord'] = Geo::Coord.new(e['city.coord.lat'], e['city.coord.lon']) \
|
219
|
+
if e['city.coord.lat'] && e['city.coord.lon']
|
220
|
+
}
|
221
|
+
post_process('city.coord.lat') { nil }
|
222
|
+
post_process('city.coord.lon') { nil }
|
223
|
+
end
|
224
|
+
|
225
|
+
# OpenWeatherMap reports most of logical errors with HTTP code
|
226
|
+
# 200 and responses like {cod: "500", message: "Error message"}
|
227
|
+
post_process { |h|
|
228
|
+
!h.key?('cod') || (200..400).cover?(h['cod'].to_i) or
|
229
|
+
fail "#{h['cod']}: #{h['message']}"
|
230
|
+
}
|
231
|
+
|
232
|
+
WEATHER_POST_PROCESSOR = lambda do |*|
|
233
|
+
# Most of the time there is exactly one weather item...
|
234
|
+
# ...but sometimes there are two. So, flatterning them looks
|
235
|
+
# more reasonable than having DataTable of 1-2 rows.
|
236
|
+
post_process { |h|
|
237
|
+
h['weather2'] = h['weather'].last if h['weather'] && h['weather'].count > 1
|
238
|
+
}
|
239
|
+
post_process('weather', &:first)
|
240
|
+
|
241
|
+
post_process('dt', &Time.method(:at))
|
242
|
+
post_process('dt_txt') { nil } # TODO: we need cleaner way to say "remove this"
|
243
|
+
post_process('sys.sunrise', &Time.method(:at))
|
244
|
+
post_process('sys.sunset', &Time.method(:at))
|
245
|
+
|
246
|
+
# https://github.com/zverok/geo_coord promo here!
|
247
|
+
post_process { |e|
|
248
|
+
e['coord'] = Geo::Coord.new(e['coord.lat'], e['coord.lon']) if e['coord.lat'] && e['coord.lon']
|
249
|
+
}
|
250
|
+
post_process('coord.lat') { nil }
|
251
|
+
post_process('coord.lon') { nil }
|
252
|
+
|
253
|
+
# See http://openweathermap.org/weather-conditions#How-to-get-icon-URL
|
254
|
+
post_process('weather.icon') { |i| "http://openweathermap.org/img/w/#{i}.png" }
|
255
|
+
end
|
256
|
+
|
257
|
+
# For endpoints returning weather in one place
|
258
|
+
instance_eval(&WEATHER_POST_PROCESSOR)
|
259
|
+
|
260
|
+
# For endpoints returning list of weathers (forecast or several
|
261
|
+
# cities).
|
262
|
+
post_process_items('list', &WEATHER_POST_PROCESSOR)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|