tlaw 0.0.1
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/.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
|