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,219 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require_relative 'demo_base'
|
3
|
+
require_relative 'open_weather_map'
|
4
|
+
|
5
|
+
# This is demonstration of TLAW (The Last API Wrapper) library's behavior
|
6
|
+
# and opinions.
|
7
|
+
#
|
8
|
+
# All of below functionality is created by this API wrapper definition:
|
9
|
+
# TODO URL
|
10
|
+
|
11
|
+
# OK, first thing is: all API wrappers created with TLAW, are
|
12
|
+
# _discoverable_ by design. In fact, you can understand all you need to
|
13
|
+
# use the API just from IRB, no need to go to rdoc.info, dig in code,
|
14
|
+
# or, my favourite thing, "see arguments explanation at original API
|
15
|
+
# site".
|
16
|
+
|
17
|
+
# Let's see:
|
18
|
+
|
19
|
+
p TLAW::Examples::OpenWeatherMap
|
20
|
+
# => #<TLAW::Examples::OpenWeatherMap: call-sequence: TLAW::Examples::OpenWeatherMap.new(appid:, lang: "en", units: :standard); namespaces: current, find, forecast; docs: .describe>
|
21
|
+
|
22
|
+
# Let's try this .describe thing which inspect recommends:
|
23
|
+
|
24
|
+
p TLAW::Examples::OpenWeatherMap.describe
|
25
|
+
# TLAW::Examples::OpenWeatherMap.new(appid:, lang: "en", units: :standard)
|
26
|
+
# API for [OpenWeatherMap](http://openweathermap.org/). Only parts
|
27
|
+
# available for free are implemented (as only them could be tested).
|
28
|
+
#
|
29
|
+
# See full docs at http://openweathermap.org/api
|
30
|
+
#
|
31
|
+
# @param appid You need to receive it at http://openweathermap.org/appid (free)
|
32
|
+
# @param lang Language of API responses (affects weather description only).
|
33
|
+
# See http://openweathermap.org/current#multi for list of supported languages. (default = "en")
|
34
|
+
# @param units Units for temperature and other values. Standard is Kelvin.
|
35
|
+
# Possible values: :standard, :metric, :imperial (default = :standard)
|
36
|
+
#
|
37
|
+
# Namespaces:
|
38
|
+
#
|
39
|
+
# .current()
|
40
|
+
# Allows to obtain current weather at one place, designated
|
41
|
+
# by city, location or zip code.
|
42
|
+
#
|
43
|
+
# .find()
|
44
|
+
# Allows to find some place (and weather in it) by set of input
|
45
|
+
# parameters.
|
46
|
+
#
|
47
|
+
# .forecast()
|
48
|
+
# Allows to obtain weather forecast for 5 days with 3-hour
|
49
|
+
# frequency.
|
50
|
+
|
51
|
+
# Note that this multiline output is produced by `p`! So, in IRB/pry
|
52
|
+
# session it would be exactly the same: you just say "something.describe",
|
53
|
+
# and it is just printed the most convenient way.
|
54
|
+
|
55
|
+
# Let's look closer to some of those namespaces:
|
56
|
+
p TLAW::Examples::OpenWeatherMap.namespaces[:current]
|
57
|
+
# => #<TLAW::Examples::OpenWeatherMap::Current: call-sequence: current(); endpoints: city, city_id, location, zip, group; docs: .describe>
|
58
|
+
|
59
|
+
# .describe, anyone?
|
60
|
+
p TLAW::Examples::OpenWeatherMap.namespaces[:current].describe
|
61
|
+
# .current()
|
62
|
+
# Allows to obtain current weather at one place, designated
|
63
|
+
# by city, location or zip code.
|
64
|
+
#
|
65
|
+
# Docs: http://openweathermap.org/current
|
66
|
+
#
|
67
|
+
#
|
68
|
+
# Endpoints:
|
69
|
+
#
|
70
|
+
# .city(city, country_code=nil)
|
71
|
+
# Current weather by city name (with optional country code
|
72
|
+
# specification).
|
73
|
+
#
|
74
|
+
# .city_id(city_id)
|
75
|
+
# Current weather by city id. Recommended by OpenWeatherMap
|
76
|
+
# docs.
|
77
|
+
#
|
78
|
+
# .location(lat, lng)
|
79
|
+
# Current weather by geographic coordinates.
|
80
|
+
#
|
81
|
+
# .zip(zip, country_code=nil)
|
82
|
+
# Current weather by ZIP code (with optional country code
|
83
|
+
# specification).
|
84
|
+
#
|
85
|
+
# .group(city_ids)
|
86
|
+
# Current weather in several cities by their ids.
|
87
|
+
|
88
|
+
# And further:
|
89
|
+
p TLAW::Examples::OpenWeatherMap
|
90
|
+
.namespaces[:current].endpoints[:city]
|
91
|
+
# => #<TLAW::Examples::OpenWeatherMap::Current::City: call-sequence: city(city, country_code=nil); docs: .describe>
|
92
|
+
|
93
|
+
p TLAW::Examples::OpenWeatherMap
|
94
|
+
.namespaces[:current].endpoints[:city].describe
|
95
|
+
# .city(city, country_code=nil)
|
96
|
+
# Current weather by city name (with optional country code
|
97
|
+
# specification).
|
98
|
+
#
|
99
|
+
# Docs: http://openweathermap.org/current#name
|
100
|
+
#
|
101
|
+
# @param city City name
|
102
|
+
# @param country_code ISO 3166 2-letter country code
|
103
|
+
|
104
|
+
# Note, that all above classes and methods are generated at moment of
|
105
|
+
# API definition, so there is no cumbersome dispatching at runtime:
|
106
|
+
|
107
|
+
p TLAW::Examples::OpenWeatherMap.instance_methods(false)
|
108
|
+
# => [:current, :find, :forecast]
|
109
|
+
p TLAW::Examples::OpenWeatherMap::Current.instance_methods(false)
|
110
|
+
# => [:city, :city_id, :location, :zip, :group]
|
111
|
+
p TLAW::Examples::OpenWeatherMap::Current.instance_method(:city).parameters
|
112
|
+
# => [[:req, :city], [:opt, :country_code]]
|
113
|
+
|
114
|
+
# E.g. namespace is a class, providing methods for all the child
|
115
|
+
# namespaces and endpoints! And all params are just method params.
|
116
|
+
|
117
|
+
# OK, let's go for some real things, not just documentation reading.
|
118
|
+
|
119
|
+
# You need to create key here: http://openweathermap.org/appid
|
120
|
+
# And run the script this way:
|
121
|
+
#
|
122
|
+
# OPEN_WEATHER_MAP={your_id} examples/open_weather_map_demo.rb
|
123
|
+
#
|
124
|
+
weather = TLAW::Examples::OpenWeatherMap
|
125
|
+
.new(appid: ENV['OPEN_WEATHER_MAP'], units: :metric)
|
126
|
+
p weather
|
127
|
+
# => #<TLAW::Examples::OpenWeatherMap.new(appid: {your id}, lang: nil, units: :metric) namespaces: current, find, forecast; docs: .describe>
|
128
|
+
|
129
|
+
# Looks familiar and nothing new.
|
130
|
+
|
131
|
+
p weather.current
|
132
|
+
# => #<current() endpoints: city, city_id, location, zip, group; docs: .describe>
|
133
|
+
|
134
|
+
# Saem.
|
135
|
+
|
136
|
+
pp weather.current.city('Kharkiv')
|
137
|
+
# {"weather.id"=>800,
|
138
|
+
# "weather.main"=>"Clear",
|
139
|
+
# "weather.description"=>"clear sky",
|
140
|
+
# "weather.icon"=>"http://openweathermap.org/img/w/01n.png",
|
141
|
+
# "base"=>"cmc stations",
|
142
|
+
# "main.temp"=>23,
|
143
|
+
# "main.pressure"=>1013,
|
144
|
+
# "main.humidity"=>40,
|
145
|
+
# "main.temp_min"=>23,
|
146
|
+
# "main.temp_max"=>23,
|
147
|
+
# "wind.speed"=>2,
|
148
|
+
# "wind.deg"=>190,
|
149
|
+
# "clouds.all"=>0,
|
150
|
+
# "dt"=>2016-09-05 20:30:00 +0300,
|
151
|
+
# "sys.type"=>1,
|
152
|
+
# "sys.id"=>7355,
|
153
|
+
# "sys.message"=>0.0115,
|
154
|
+
# "sys.country"=>"UA",
|
155
|
+
# "sys.sunrise"=>2016-09-05 05:57:26 +0300,
|
156
|
+
# "sys.sunset"=>2016-09-05 19:08:13 +0300,
|
157
|
+
# "id"=>706483,
|
158
|
+
# "name"=>"Kharkiv",
|
159
|
+
# "cod"=>200,
|
160
|
+
# "coord"=>#<Geo::Coord 50.000000,36.250000>}
|
161
|
+
|
162
|
+
# Whoa!
|
163
|
+
#
|
164
|
+
# What we see here (except for "OK, it works")?
|
165
|
+
#
|
166
|
+
# Several pretty improtant things:
|
167
|
+
#
|
168
|
+
# * TLAW response processing is _highly opinionated_. It tends do flatten
|
169
|
+
# all the hashes: original has something like
|
170
|
+
# {weather: {...}, main: {...}, sys: {...}
|
171
|
+
# * It is done, again, for the sake of _discoverability_. You, like, see
|
172
|
+
# at once all things API response proposes; you can do `response.keys`
|
173
|
+
# to understand what you got instead of `response.keys`, hm,
|
174
|
+
# `response['weather'].class`, ah, ok, `response['weather'].keys` and
|
175
|
+
# so on;
|
176
|
+
# * TLAW allows easy postprocessing of response: our example API parses
|
177
|
+
# timestamps into proper ruby times, and (just to promote related gem),
|
178
|
+
# converts "coord.lat" and "coord.lng" to one instance of a Geo::Coord,
|
179
|
+
# from https://github.com/zverok/geo_coord
|
180
|
+
#
|
181
|
+
|
182
|
+
# Finally, one HUGE design decision related to "opinionated response
|
183
|
+
# processing":
|
184
|
+
pp weather.forecast.city('Kharkiv')
|
185
|
+
# {"city.id"=>706483,
|
186
|
+
# "city.name"=>"Kharkiv",
|
187
|
+
# "city.country"=>"UA",
|
188
|
+
# "city.population"=>0,
|
189
|
+
# "city.sys.population"=>0,
|
190
|
+
# "cod"=>"200",
|
191
|
+
# "message"=>0.0276,
|
192
|
+
# "cnt"=>40,
|
193
|
+
# "list"=>
|
194
|
+
# #<TLAW::DataTable[dt, main.temp, main.temp_min, main.temp_max, main.pressure, main.sea_level, main.grnd_level, main.humidity, main.temp_kf, weather.id, weather.main, weather.description, weather.icon, clouds.all, wind.speed, wind.deg, sys.pod] x 40>,
|
195
|
+
# "city.coord"=>#<Geo::Coord 50.000000,36.250000>}
|
196
|
+
|
197
|
+
# Hmm? What is this DataTable thingy? It is (loosy) implementation of
|
198
|
+
# DataFrame data type. You can think of it as an array of homogenous
|
199
|
+
# hashes -- which could be considered the main type of useful JSON API
|
200
|
+
# data.
|
201
|
+
forecasts = weather.forecast.city('Kharkiv')['list']
|
202
|
+
|
203
|
+
p forecasts.count
|
204
|
+
# => 40
|
205
|
+
p forecasts.keys
|
206
|
+
# => ["dt", "main.temp", "main.temp_min", "main.temp_max", "main.pressure", "main.sea_level", "main.grnd_level", "main.humidity", "main.temp_kf", "weather.id", "weather.main", "weather.description", "weather.icon", "clouds.all", "wind.speed", "wind.deg", "sys.pod"]
|
207
|
+
p forecasts.first
|
208
|
+
# => {"dt"=>2016-09-06 00:00:00 +0300, "main.temp"=>12.67, "main.temp_min"=>12.67, "main.temp_max"=>15.84, "main.pressure"=>1006.67, "main.sea_level"=>1026.62, "main.grnd_level"=>1006.67, "main.humidity"=>74, "main.temp_kf"=>-3.17, "weather.id"=>800, "weather.main"=>"Clear", "weather.description"=>"clear sky", "weather.icon"=>"http://openweathermap.org/img/w/01n.png", "clouds.all"=>0, "wind.speed"=>1.26, "wind.deg"=>218.509, "sys.pod"=>"n"}
|
209
|
+
|
210
|
+
p forecasts['dt'].first(3)
|
211
|
+
# => [2016-09-06 00:00:00 +0300, 2016-09-06 03:00:00 +0300, 2016-09-06 06:00:00 +0300]
|
212
|
+
|
213
|
+
p forecasts.columns('dt', 'main.temp').to_a.first(3)
|
214
|
+
# => [{"dt"=>2016-09-06 00:00:00 +0300, "main.temp"=>12.67}, {"dt"=>2016-09-06 03:00:00 +0300, "main.temp"=>11.65}, {"dt"=>2016-09-06 06:00:00 +0300, "main.temp"=>12.41}]
|
215
|
+
|
216
|
+
# All of it works and you can check it by yourself.
|
217
|
+
#
|
218
|
+
# Again, EVERYTHING you can see in this example is created by short and
|
219
|
+
# focused API definition: TODO URL
|
@@ -0,0 +1,133 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require_relative 'demo_base'
|
3
|
+
|
4
|
+
# This example demonstrates how TLAW allows you to define and redefine
|
5
|
+
# API wrappers on the fly—to the extent you need and without much
|
6
|
+
# bothering—and still have all the goodies.
|
7
|
+
|
8
|
+
# For example, you have pretty large and complicated TheMoviesDatabase API:
|
9
|
+
# http://docs.themoviedb.apiary.io/
|
10
|
+
# ...and all you want is just to search for movies and get their posters.
|
11
|
+
# All existing TMDB Ruby wrappers (I know at least three) are strange.
|
12
|
+
#
|
13
|
+
# What you'll do?
|
14
|
+
#
|
15
|
+
# That's what:
|
16
|
+
|
17
|
+
class TMDB < TLAW::API
|
18
|
+
define do
|
19
|
+
base 'http://api.themoviedb.org/3'
|
20
|
+
param :api_key, required: true
|
21
|
+
param :language, default: 'en'
|
22
|
+
|
23
|
+
namespace :movies, '/movie' do
|
24
|
+
namespace :[], '/{id}' do
|
25
|
+
param :id, required: true
|
26
|
+
|
27
|
+
endpoint :images
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
namespace :search do
|
32
|
+
endpoint :movie do
|
33
|
+
param :query, required: true, keyword: false
|
34
|
+
|
35
|
+
post_process_items('results') {
|
36
|
+
post_process 'release_date', &Date.method(:parse)
|
37
|
+
}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# You need to run it like TMDB={your_key} ./examples/tmdb_demo.rb
|
44
|
+
tmdb = TMDB.new(api_key: ENV['TMDB'])
|
45
|
+
|
46
|
+
pp tmdb.search.movie('guardians of the galaxy')
|
47
|
+
# {"page"=>1,
|
48
|
+
# "results"=>
|
49
|
+
# #<TLAW::DataTable[poster_path, adult, overview, release_date, genre_ids, id, original_title, original_language, title, backdrop_path, popularity, vote_count, video, vote_average] x 2>,
|
50
|
+
# "total_results"=>2,
|
51
|
+
# "total_pages"=>1}
|
52
|
+
|
53
|
+
pp tmdb.search.movie('guardians of the galaxy')['results'].first
|
54
|
+
# {"poster_path"=>"/y31QB9kn3XSudA15tV7UWQ9XLuW.jpg",
|
55
|
+
# "adult"=>false,
|
56
|
+
# "overview"=>
|
57
|
+
# "Light years from Earth, 26 years after being abducted, Peter Quill finds himself the prime target of a manhunt after discovering an orb wanted by Ronan the Accuser.",
|
58
|
+
# "release_date"=>#<Date: 2014-07-30 ((2456869j,0s,0n),+0s,2299161j)>,
|
59
|
+
# "genre_ids"=>[28, 878, 12],
|
60
|
+
# "id"=>118340,
|
61
|
+
# "original_title"=>"Guardians of the Galaxy",
|
62
|
+
# "original_language"=>"en",
|
63
|
+
# "title"=>"Guardians of the Galaxy",
|
64
|
+
# "backdrop_path"=>"/bHarw8xrmQeqf3t8HpuMY7zoK4x.jpg",
|
65
|
+
# "popularity"=>12.287455,
|
66
|
+
# "vote_count"=>5067,
|
67
|
+
# "video"=>false,
|
68
|
+
# "vote_average"=>7.96}
|
69
|
+
|
70
|
+
# OK, now we have an id
|
71
|
+
pp tmdb.movies[118340].images
|
72
|
+
|
73
|
+
# Note, that [] is also namespace accessor here :) With param. See API
|
74
|
+
# description above.
|
75
|
+
|
76
|
+
pp tmdb.movies[118340].images['posters'].last
|
77
|
+
# {"aspect_ratio"=>0.666666666666667,
|
78
|
+
# "file_path"=>"/6YUodKKkqIIDx6Hk7ZkaVOxnWND.jpg",
|
79
|
+
# "height"=>1500,
|
80
|
+
# "iso_639_1"=>"ru",
|
81
|
+
# "vote_average"=>0.0,
|
82
|
+
# "vote_count"=>0,
|
83
|
+
# "width"=>1000}
|
84
|
+
|
85
|
+
# Hmm, maybe we need some path post-processing?.. How about adding it
|
86
|
+
# right now? Assuming our API is already described by someone else...
|
87
|
+
tmdb.class.define do
|
88
|
+
namespace :movies do
|
89
|
+
namespace :[] do
|
90
|
+
endpoint :images do
|
91
|
+
post_process_items 'posters' do
|
92
|
+
post_process('file_path') { |p| 'https://image.tmdb.org/t/p/original' + p }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
pp tmdb.movies[118340].images['posters'].last
|
100
|
+
# Ah, much better!
|
101
|
+
#
|
102
|
+
# {"aspect_ratio"=>0.666666666666667,
|
103
|
+
# "file_path"=>
|
104
|
+
# "https://image.tmdb.org/t/p/original/6YUodKKkqIIDx6Hk7ZkaVOxnWND.jpg",
|
105
|
+
# "height"=>1500,
|
106
|
+
# "iso_639_1"=>"ru",
|
107
|
+
# "vote_average"=>0.0,
|
108
|
+
# "vote_count"=>0,
|
109
|
+
# "width"=>1000}
|
110
|
+
|
111
|
+
|
112
|
+
# Note, that despite not adding a bit of documentation, you still have
|
113
|
+
# your API wrapper discoverable:
|
114
|
+
p TMDB
|
115
|
+
# #<TMDB: call-sequence: TMDB.new(api_key:, language: "en"); namespaces: movies, search; docs: .describe>
|
116
|
+
p tmdb.movies.describe
|
117
|
+
# .movies()
|
118
|
+
#
|
119
|
+
#
|
120
|
+
# Namespaces:
|
121
|
+
#
|
122
|
+
# .[](id)
|
123
|
+
|
124
|
+
p tmdb.movies.namespaces[:[]].describe
|
125
|
+
# .[](id)
|
126
|
+
# @param id
|
127
|
+
#
|
128
|
+
# Endpoints:
|
129
|
+
#
|
130
|
+
# .images()
|
131
|
+
|
132
|
+
# So, you can still investigate, navigate and get meaningful API errors
|
133
|
+
# with backtraces... While you spent only like 15 lines on API description.
|
@@ -0,0 +1,105 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require_relative 'demo_base'
|
3
|
+
|
4
|
+
# That's an example of TLAW's strength.
|
5
|
+
#
|
6
|
+
# Urbandictionary API is really small (just two endpoints, only one of
|
7
|
+
# which is actually useful, because /random is more like a joke).
|
8
|
+
#
|
9
|
+
# But you still need to remember the protocol, parse the answer and so
|
10
|
+
# on.
|
11
|
+
#
|
12
|
+
# There is even separate gem: https://github.com/ryangreenberg/urban_dictionary
|
13
|
+
# Its `lib` folder contains 7 files, 9 classes/modules and 300 lines of
|
14
|
+
# code, I kid you not.
|
15
|
+
#
|
16
|
+
# I have no intention to offend that gem's author! I just saying that's
|
17
|
+
# what you get when you need to design everything from scratch, like HTTP
|
18
|
+
# client and params processing and response parsing and whatnot.
|
19
|
+
#
|
20
|
+
# Oh, and there is another one: https://github.com/tmiller/urban
|
21
|
+
#
|
22
|
+
# But when somebody really need them (for chatbots), they use neither,
|
23
|
+
# just redefine everything from scratch with rough net connection and
|
24
|
+
# response parsing (because both of aforementioned gems are too thick
|
25
|
+
# wrappers to rely on them):
|
26
|
+
# * https://github.com/jimmycuadra/lita-urban-dictionary
|
27
|
+
# * https://github.com/cinchrb/cinch-urbandictionary
|
28
|
+
#
|
29
|
+
# Here is our version (17 codelines, including namespacing and bit of
|
30
|
+
# docs, API definition itself takes like 7 lines only):
|
31
|
+
#
|
32
|
+
module TLAW
|
33
|
+
module Examples
|
34
|
+
class UrbanDictionary < TLAW::API
|
35
|
+
define do
|
36
|
+
desc %Q{
|
37
|
+
Really small API. Described as "official but undocumented"
|
38
|
+
by some.
|
39
|
+
}
|
40
|
+
|
41
|
+
base 'http://api.urbandictionary.com/v0'
|
42
|
+
|
43
|
+
endpoint :define, '/define?term={term}' do
|
44
|
+
param :term, required: true
|
45
|
+
end
|
46
|
+
|
47
|
+
endpoint :random, '/random'
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Usage (response is clear as tears, could be integrated anywhere):
|
54
|
+
|
55
|
+
d = TLAW::Examples::UrbanDictionary.new
|
56
|
+
|
57
|
+
p d.describe
|
58
|
+
# TLAW::Examples::UrbanDictionary.new()
|
59
|
+
# Really small API. Described as "official but undocumented"
|
60
|
+
# by some.
|
61
|
+
#
|
62
|
+
# Endpoints:
|
63
|
+
#
|
64
|
+
# .define(term)
|
65
|
+
#
|
66
|
+
# .random()
|
67
|
+
|
68
|
+
res = d.define('trollface')
|
69
|
+
|
70
|
+
pp res
|
71
|
+
# {"tags"=>
|
72
|
+
# ["troll",
|
73
|
+
# "trolling",
|
74
|
+
# "meme",
|
75
|
+
# "4chan",
|
76
|
+
# "troll face",
|
77
|
+
# "coolface",
|
78
|
+
# "trollfacing",
|
79
|
+
# "trolls",
|
80
|
+
# "backpfeifengesicht",
|
81
|
+
# "derp"],
|
82
|
+
# "result_type"=>"exact",
|
83
|
+
# "list"=>
|
84
|
+
# #<TLAW::DataTable[definition, permalink, thumbs_up, author, word, defid, current_vote, example, thumbs_down] x 7>,
|
85
|
+
# "sounds"=>[]}
|
86
|
+
|
87
|
+
pp res['list'].columns('word', 'thumbs_up', 'thumbs_down').to_a
|
88
|
+
# [{"word"=>"Trollface", "thumbs_up"=>418, "thumbs_down"=>99},
|
89
|
+
# {"word"=>"Troll Face", "thumbs_up"=>481, "thumbs_down"=>318},
|
90
|
+
# {"word"=>"Troll Face", "thumbs_up"=>340, "thumbs_down"=>184},
|
91
|
+
# {"word"=>"trollface", "thumbs_up"=>115, "thumbs_down"=>35},
|
92
|
+
# {"word"=>"trollface", "thumbs_up"=>94, "thumbs_down"=>30},
|
93
|
+
# {"word"=>"trollface", "thumbs_up"=>61, "thumbs_down"=>54},
|
94
|
+
# {"word"=>"Troll Face", "thumbs_up"=>81, "thumbs_down"=>181}]
|
95
|
+
|
96
|
+
pp d.random['list'].columns('word', 'example').first(3).to_a
|
97
|
+
# [{"word"=>"pH", "example"=>"Get that ph out of the shower.\r\n\r\n "},
|
98
|
+
# {"word"=>"spocking",
|
99
|
+
# "example"=>
|
100
|
+
# "The dirty bitch couldn’t get enough so I gave her a damn good spocking"},
|
101
|
+
# {"word"=>"mormon",
|
102
|
+
# "example"=>
|
103
|
+
# "Oh my gosh are the Smiths mormons?! We better have a party so they can bring some frickin' sweet green jello!..."}]
|
104
|
+
|
105
|
+
# That's it ¯\_(ツ)_/¯
|