epok 0.2.0 → 0.5.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 +4 -4
- data/CLAUDE.md +31 -15
- data/Gemfile +1 -0
- data/Gemfile.lock +38 -15
- data/README.md +58 -14
- data/bin/console +0 -2
- data/epok.gemspec +0 -1
- data/lib/epok/api.rb +59 -10
- data/lib/epok/collection.rb +5 -9
- data/lib/epok/object.rb +33 -4
- data/lib/epok/version.rb +1 -1
- data/lib/epok.rb +55 -7
- data/test/epok/api_test.rb +65 -0
- data/test/epok/categories_test.rb +26 -0
- data/test/epok/collection_test.rb +22 -0
- data/test/epok/datos_utiles_test.rb +39 -0
- data/test/epok/geocode_test.rb +36 -0
- data/test/epok/geocoder_test.rb +24 -4
- data/test/epok/object_test.rb +37 -0
- data/test/epok/search_test.rb +21 -2
- data/test/epok_test.rb +4 -11
- data/test/test_helper.rb +1 -3
- metadata +6 -24
- data/lib/epok/collectable.rb +0 -17
- data/lib/epok/geocoder.rb +0 -21
- data/lib/epok/search.rb +0 -17
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ee8d9d659f3f1da4d0e290c2fe57173ca577ccaf6ae8c9ab5d6fb0df5a41045d
|
|
4
|
+
data.tar.gz: d075af0af60603bfc513f65f26f6656cc9a43e3e3a6dd25da47245cd96a0ea05
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d57ad60dcd6be25dcd1ad82039809fccc8d9039ed31439e3209fd2c85cdbe82cccf83a9e9c9846989f182c9290ccd60d440a69d3aa673f500f160cd31cf55ee2
|
|
7
|
+
data.tar.gz: 7b850241d36d95455e480cb26222233927447bab1cffe547388a53c5d0c92c6d61ae387c3c70db9aae7eb784623c38a5bf220f693f6fde06fc39a10cd01c83fb
|
data/CLAUDE.md
CHANGED
|
@@ -14,18 +14,34 @@ bundle exec swarf lib/ # complexity vs. coverage report, worst first
|
|
|
14
14
|
Cassettes are gitignored, so the first test run hits the live API.
|
|
15
15
|
The API rejects curl's default User-Agent, but Ruby's Net::HTTP works fine.
|
|
16
16
|
|
|
17
|
-
##
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
17
|
+
## API notes
|
|
18
|
+
|
|
19
|
+
Official docs are Swagger specs on the city's 3scale portal. The HTML at
|
|
20
|
+
https://datosabiertos-apis.buenosaires.gob.ar/BA_Root/Documentacion is
|
|
21
|
+
JavaScript-rendered; fetch the JSON directly:
|
|
22
|
+
|
|
23
|
+
- Index: https://datosabiertos-apis.buenosaires.gob.ar/api_docs/services.json
|
|
24
|
+
- EPOk ("Busquedas de Lugares"): .../api_docs/services/10.json
|
|
25
|
+
- USIG Geocoder: .../api_docs/services/8.json
|
|
26
|
+
- Datos Útiles: .../api_docs/services/9.json
|
|
27
|
+
|
|
28
|
+
The specs list `datosabiertos-*-apis.buenosaires.gob.ar` hosts that return
|
|
29
|
+
404. The live hosts are `epok.buenosaires.gob.ar` for EPOk and
|
|
30
|
+
`ws.usig.buenosaires.gob.ar` for USIG (REST index with docs at
|
|
31
|
+
https://ws.usig.buenosaires.gob.ar/rest/).
|
|
32
|
+
|
|
33
|
+
- `/buscar/` also takes `categoria` (comma-separated), `clase`, `bbox`,
|
|
34
|
+
`start`, `limit` and `totalFull`.
|
|
35
|
+
- `/reverseGeocoderLugares/` takes `srid` (default 4326) and `radio`
|
|
36
|
+
(default 1 metre, hence our 500 default).
|
|
37
|
+
- Unknown ids return `200 {}`, unknown categories return an empty list.
|
|
38
|
+
- Object coordinates are projected (Gauss-Krüger Buenos Aires). USIG's
|
|
39
|
+
`/rest/convertir_coordenadas` turns them into longitude and latitude.
|
|
40
|
+
- `ws.usig.buenosaires.gob.ar/datos_utiles?x=&y=` answers with empty strings
|
|
41
|
+
outside the city; `Epok.datos_utiles` turns those into nil.
|
|
42
|
+
- `servicios.usig.buenosaires.gob.ar/normalizar/?direccion=&geocodificar=true`
|
|
43
|
+
matches across the whole AMBA, CABA first. Coordinates come back as
|
|
44
|
+
strings for CABA and floats elsewhere. No match is `200` with an empty
|
|
45
|
+
list and an `errorMessage`; `Epok.geocode` raises NotFound with it.
|
|
46
|
+
- A December 2025 city report says "API USIG" is being replaced by
|
|
47
|
+
"API Servicios Geo" during 2026. The USIG hosts above may move.
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,31 +1,47 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
epok (0.
|
|
4
|
+
epok (0.5.0)
|
|
5
5
|
|
|
6
6
|
GEM
|
|
7
7
|
remote: https://rubygems.org/
|
|
8
8
|
specs:
|
|
9
9
|
addressable (2.9.0)
|
|
10
10
|
public_suffix (>= 2.0.2, < 8.0)
|
|
11
|
-
ansi (1.6.0)
|
|
12
11
|
bigdecimal (4.1.3)
|
|
13
|
-
builder (3.3.0)
|
|
14
12
|
crack (1.0.1)
|
|
15
13
|
bigdecimal
|
|
16
14
|
rexml
|
|
15
|
+
erb (6.0.7)
|
|
17
16
|
hashdiff (1.2.1)
|
|
17
|
+
io-console (0.9.3)
|
|
18
|
+
irb (1.18.0)
|
|
19
|
+
pp (>= 0.6.0)
|
|
20
|
+
prism (>= 1.3.0)
|
|
21
|
+
rdoc (>= 4.0.0)
|
|
22
|
+
reline (>= 0.4.2)
|
|
23
|
+
logger (1.7.0)
|
|
18
24
|
minitest (5.27.0)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
ruby-progressbar
|
|
25
|
+
pp (0.6.4)
|
|
26
|
+
prettyprint
|
|
27
|
+
prettyprint (0.2.0)
|
|
28
|
+
prism (1.9.0)
|
|
24
29
|
public_suffix (7.0.5)
|
|
25
30
|
rake (13.4.2)
|
|
31
|
+
rbs (4.2.0)
|
|
32
|
+
logger
|
|
33
|
+
prism (>= 1.6.0)
|
|
34
|
+
tsort
|
|
35
|
+
rdoc (8.0.0)
|
|
36
|
+
erb
|
|
37
|
+
prism (>= 1.6.0)
|
|
38
|
+
rbs (>= 4.0.0)
|
|
39
|
+
tsort
|
|
40
|
+
reline (0.7.0)
|
|
41
|
+
io-console (~> 0.5)
|
|
26
42
|
rexml (3.4.4)
|
|
27
|
-
ruby-progressbar (1.13.0)
|
|
28
43
|
swarf (0.2.0)
|
|
44
|
+
tsort (0.2.0)
|
|
29
45
|
vcr (6.4.0)
|
|
30
46
|
webmock (3.26.4)
|
|
31
47
|
addressable (>= 2.8.0)
|
|
@@ -38,8 +54,8 @@ PLATFORMS
|
|
|
38
54
|
|
|
39
55
|
DEPENDENCIES
|
|
40
56
|
epok!
|
|
57
|
+
irb
|
|
41
58
|
minitest (~> 5.0)
|
|
42
|
-
minitest-reporters (~> 1.3, >= 1.3.6)
|
|
43
59
|
rake (~> 13.0)
|
|
44
60
|
swarf
|
|
45
61
|
vcr (~> 6.0)
|
|
@@ -47,19 +63,26 @@ DEPENDENCIES
|
|
|
47
63
|
|
|
48
64
|
CHECKSUMS
|
|
49
65
|
addressable (2.9.0) sha256=7fdf6ac3660f7f4e867a0838be3f6cf722ace541dd97767fa42bc6cfa980c7af
|
|
50
|
-
ansi (1.6.0) sha256=ac9ea0c0ea8d32fb4e271348e609963ac78882f34b73836c2a02b3622e666658
|
|
51
66
|
bigdecimal (4.1.3) sha256=61ebe1e5e559bdc3cc6f2c0ee7f427321fc838f59611c294356eb04d6e21cf66
|
|
52
|
-
builder (3.3.0) sha256=497918d2f9dca528fdca4b88d84e4ef4387256d984b8154e9d5d3fe5a9c8835f
|
|
53
67
|
crack (1.0.1) sha256=ff4a10390cd31d66440b7524eb1841874db86201d5b70032028553130b6d4c7e
|
|
54
|
-
epok (0.
|
|
68
|
+
epok (0.5.0)
|
|
69
|
+
erb (6.0.7) sha256=c5ca6dc25b0ef974a44dc8f59fe847577122483b1968a38dec305c60bf91ee92
|
|
55
70
|
hashdiff (1.2.1) sha256=9c079dbc513dfc8833ab59c0c2d8f230fa28499cc5efb4b8dd276cf931457cd1
|
|
71
|
+
io-console (0.9.3) sha256=f555049461b0afb78a5448b5be8e7c7b48371347cd48abfc22814764160000dd
|
|
72
|
+
irb (1.18.0) sha256=de9454a0703a54704b9811a5ef31a60c86949fbf4013fcf244fabc7c775248e3
|
|
73
|
+
logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
|
|
56
74
|
minitest (5.27.0) sha256=2d3b17f8a36fe7801c1adcffdbc38233b938eb0b4966e97a6739055a45fa77d5
|
|
57
|
-
|
|
75
|
+
pp (0.6.4) sha256=dfcb0fce700c41456265922884f9fe195d7fbb0674a3578e6c0f69588e82b570
|
|
76
|
+
prettyprint (0.2.0) sha256=2bc9e15581a94742064a3cc8b0fb9d45aae3d03a1baa6ef80922627a0766f193
|
|
77
|
+
prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85
|
|
58
78
|
public_suffix (7.0.5) sha256=1a8bb08f1bbea19228d3bed6e5ed908d1cb4f7c2726d18bd9cadf60bc676f623
|
|
59
79
|
rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701
|
|
80
|
+
rbs (4.2.0) sha256=51f7b886dcc05bc09e10b901daa6a81829f6adc03101d6ca9ea4aac6103e0674
|
|
81
|
+
rdoc (8.0.0) sha256=03bf8c08a9639658855a0cfd77c0abca8325c227693f7f33f82957811348c469
|
|
82
|
+
reline (0.7.0) sha256=5b012d8e55dbf9d450f12bde2cf7d15ff546ae80b3f8f3b30e570d431815583d
|
|
60
83
|
rexml (3.4.4) sha256=19e0a2c3425dfbf2d4fc1189747bdb2f849b6c5e74180401b15734bc97b5d142
|
|
61
|
-
ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33
|
|
62
84
|
swarf (0.2.0) sha256=b4306a244fe4b096cfe263e452e666156856eac2467ae5d770639ef11388b375
|
|
85
|
+
tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f
|
|
63
86
|
vcr (6.4.0) sha256=077ac92cc16efc5904eb90492a18153b5e6ca5398046d8a249a7c96a9ea24ae6
|
|
64
87
|
webmock (3.26.4) sha256=8d8da206d217ebe6968cfb09c77f4533c23074e1432bad865f3994eacbaad50d
|
|
65
88
|
|
data/README.md
CHANGED
|
@@ -20,29 +20,73 @@ Or install it yourself as:
|
|
|
20
20
|
|
|
21
21
|
## Usage
|
|
22
22
|
|
|
23
|
+
"I'm at Callao y Corrientes, what's around me?" in five requests:
|
|
24
|
+
|
|
23
25
|
```ruby
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
require "epok"
|
|
27
|
+
|
|
28
|
+
# 1. Turn a typed address into coordinates.
|
|
29
|
+
here = Epok.geocode("callao y corrientes").first
|
|
30
|
+
puts "You are at #{here.address}"
|
|
31
|
+
|
|
32
|
+
# 2. Which neighbourhood and comuna is that?
|
|
33
|
+
area = Epok.datos_utiles(here.location)
|
|
34
|
+
puts "That's #{area.barrio}, #{area.comuna}"
|
|
35
|
+
|
|
36
|
+
# 3. What's within 300 metres, by category?
|
|
37
|
+
nearby = Epok.geocoder(here.location, %w[estaciones_de_subte farmacias], radius: 300)
|
|
38
|
+
nearby.sort_by(&:distance).first(5).each do |place|
|
|
39
|
+
puts " #{place.distance.round} m #{place.name} (#{place.kind})"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# 4. Drill into one: full record and coordinates for a map pin.
|
|
43
|
+
closest = nearby.min_by(&:distance)
|
|
44
|
+
puts "Pin: #{closest.location.y}, #{closest.location.x}"
|
|
45
|
+
closest.content.each { |key, value| puts " #{key}: #{value}" }
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
You are at CALLAO AV. y CORRIENTES AV., CABA
|
|
50
|
+
That's San Nicolas, Comuna 3
|
|
51
|
+
3 m CALLAO - MAESTRO ALFREDO BRAVO (Línea B) (Estación de Subte (Metro))
|
|
52
|
+
42 m Farmacia en CORRIENTES AV. 1820 (Farmacia)
|
|
53
|
+
73 m Farmacia en CORRIENTES AV. 1835 (Farmacia)
|
|
54
|
+
98 m Farmacia en CALLAO AV. 477 (Farmacia)
|
|
55
|
+
111 m Farmacia en CORRIENTES AV. 1880 (Farmacia)
|
|
56
|
+
Pin: -34.604419, -58.392318
|
|
57
|
+
Nombre: CALLAO - MAESTRO ALFREDO BRAVO (Línea B)
|
|
58
|
+
Línea: B
|
|
59
|
+
Estado: Estación Malabia cerrada por obras de renovación integral.
|
|
60
|
+
Cabeceras: J. M. de Rosas - L. N. Alem
|
|
61
|
+
Tiempo: 27 min.
|
|
62
|
+
```
|
|
26
63
|
|
|
27
|
-
|
|
28
|
-
=> #<Epok::Geocoder:0x00007f8719ab4f08 @x=-58.38157, @y=-34.603738, @categories="farmacias">
|
|
64
|
+
Steps 1 to 3 are one request each; step 4 is two (the record, then the coordinate conversion). The content hash is whatever the city publishes for that category: subway stations carry line, status and travel time, pharmacies carry phone and delivery details.
|
|
29
65
|
|
|
30
|
-
|
|
31
|
-
=> {"Nombre"=>"Farmacia en PELLEGRINI, CARLOS 765", "Teléfono"=>"43223198 43220298"}
|
|
66
|
+
### The rest
|
|
32
67
|
|
|
33
|
-
|
|
34
|
-
=> #<Epok::Search:0x00007f871a09ac60 @query="cgp">
|
|
68
|
+
**Search by text**, optionally filtered by category and capped:
|
|
35
69
|
|
|
36
|
-
|
|
37
|
-
|
|
70
|
+
```ruby
|
|
71
|
+
Epok.search("cgp", categories: "sedes_de_comunas", limit: 3).map(&:name)
|
|
72
|
+
# => ["Sede Comunal 4", "Sede Comunal 9", "Sede Comunal 10"]
|
|
73
|
+
```
|
|
38
74
|
|
|
39
|
-
|
|
40
|
-
=> "Sede Comunal 4"
|
|
75
|
+
**Categories** are what the geocoder and the search filter take. There are 167, each with an id, a display name and a description:
|
|
41
76
|
|
|
42
|
-
|
|
43
|
-
|
|
77
|
+
```ruby
|
|
78
|
+
Epok.categories.find { |c| c.id == "farmacias" }
|
|
79
|
+
# => #<struct Epok::Category id="farmacias", name="Farmacias", description="">
|
|
44
80
|
```
|
|
45
81
|
|
|
82
|
+
**Collections** from `search` and `geocoder` are `Enumerable` and lazy: nothing is requested until you iterate.
|
|
83
|
+
|
|
84
|
+
**Objects** know their `id`, `name`, `kind`, `category` and, from the geocoder, `distance` in metres straight from the listing. `content`, `normalized_address` and `location` fetch the full record on first use; `location` also converts the city's projected coordinates through USIG. `Epok::Object.new("farmacias|1082")` looks one up by id.
|
|
85
|
+
|
|
86
|
+
**Locations** are `Epok::Location.new(x: longitude, y: latitude)`. `geocode` returns every match across the metro area, city first, as `Address` structs with `address`, `partido`, `localidad` and `location`. `datos_utiles` returns nil for the city fields outside CABA and fills `partido_amba` and `localidad_amba` instead.
|
|
87
|
+
|
|
88
|
+
**Errors**: every request failure raises `Epok::Error`. An unknown id or address raises `Epok::NotFound`.
|
|
89
|
+
|
|
46
90
|
## Development
|
|
47
91
|
|
|
48
92
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/bin/console
CHANGED
data/epok.gemspec
CHANGED
|
@@ -18,7 +18,6 @@ Gem::Specification.new do |spec|
|
|
|
18
18
|
spec.require_paths = ["lib"]
|
|
19
19
|
|
|
20
20
|
spec.add_development_dependency "minitest", "~> 5.0"
|
|
21
|
-
spec.add_development_dependency "minitest-reporters", "~> 1.3", ">= 1.3.6"
|
|
22
21
|
spec.add_development_dependency "rake", "~> 13.0"
|
|
23
22
|
spec.add_development_dependency "vcr", "~> 6.0"
|
|
24
23
|
spec.add_development_dependency "webmock", "~> 3.5", ">= 3.5.1"
|
data/lib/epok/api.rb
CHANGED
|
@@ -2,25 +2,74 @@ require "net/http"
|
|
|
2
2
|
require "json"
|
|
3
3
|
|
|
4
4
|
module Epok
|
|
5
|
+
class Error < StandardError; end
|
|
6
|
+
class NotFound < Error; end
|
|
7
|
+
|
|
5
8
|
class API
|
|
6
|
-
|
|
9
|
+
EPOK_URL = "https://epok.buenosaires.gob.ar".freeze
|
|
10
|
+
USIG_URL = "https://ws.usig.buenosaires.gob.ar".freeze
|
|
11
|
+
USIG_SERVICES_URL = "https://servicios.usig.buenosaires.gob.ar".freeze
|
|
12
|
+
TIMEOUT = 10
|
|
7
13
|
|
|
8
14
|
def self.object(id)
|
|
9
|
-
|
|
15
|
+
object = get(EPOK_URL, "/getObjectContent/", id: id)
|
|
16
|
+
raise NotFound, "no object with id #{id}" if object.empty?
|
|
17
|
+
|
|
18
|
+
object
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.search(query, categories = nil, limit = nil)
|
|
22
|
+
params = { texto: query, categoria: categories, limit: limit }.compact
|
|
23
|
+
|
|
24
|
+
get(EPOK_URL, "/buscar/", params)["instancias"]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.geocoder(x, y, categories, radius)
|
|
28
|
+
get(EPOK_URL, "/reverseGeocoderLugares/",
|
|
29
|
+
x: x, y: y, categorias: categories, radio: radius)["instancias"]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.categories
|
|
33
|
+
get(EPOK_URL, "/getCategorias/", {})["categorias"]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.geocode(text)
|
|
37
|
+
response = get(USIG_SERVICES_URL, "/normalizar/", direccion: text, geocodificar: true)
|
|
38
|
+
addresses = response["direccionesNormalizadas"]
|
|
39
|
+
raise NotFound, response["errorMessage"] if addresses.empty?
|
|
40
|
+
|
|
41
|
+
addresses
|
|
10
42
|
end
|
|
11
43
|
|
|
12
|
-
def self.
|
|
13
|
-
|
|
44
|
+
def self.datos_utiles(x, y)
|
|
45
|
+
get(USIG_URL, "/datos_utiles", x: x, y: y)
|
|
14
46
|
end
|
|
15
47
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
48
|
+
# Converts EPOk's projected coordinates (Gauss-Krüger Buenos Aires) to
|
|
49
|
+
# longitude and latitude through the city's USIG service.
|
|
50
|
+
def self.to_lonlat(x, y)
|
|
51
|
+
response = get(USIG_URL, "/rest/convertir_coordenadas", x: x, y: y, output: "lonlat")
|
|
52
|
+
raise Error, "USIG could not convert (#{x}, #{y})" unless response["tipo_resultado"] == "Ok"
|
|
53
|
+
|
|
54
|
+
response["resultado"].values_at("x", "y").map(&:to_f)
|
|
20
55
|
end
|
|
21
56
|
|
|
22
|
-
def self.get(path)
|
|
23
|
-
|
|
57
|
+
def self.get(host, path, params)
|
|
58
|
+
uri = URI("#{host}#{path}")
|
|
59
|
+
uri.query = URI.encode_www_form(params)
|
|
60
|
+
|
|
61
|
+
response = Net::HTTP.start(uri.host, uri.port, use_ssl: true,
|
|
62
|
+
open_timeout: TIMEOUT, read_timeout: TIMEOUT) do |http|
|
|
63
|
+
http.get(uri.request_uri)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
unless response.is_a?(Net::HTTPSuccess)
|
|
67
|
+
raise Error, "EPOk responded #{response.code} to #{uri}"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
JSON.parse(response.body)
|
|
71
|
+
rescue SystemCallError, SocketError, Timeout::Error, OpenSSL::SSL::SSLError, JSON::ParserError => e
|
|
72
|
+
raise Error, "#{e.class}: #{e.message}"
|
|
24
73
|
end
|
|
25
74
|
private_class_method :get
|
|
26
75
|
end
|
data/lib/epok/collection.rb
CHANGED
|
@@ -2,22 +2,18 @@ module Epok
|
|
|
2
2
|
class Collection
|
|
3
3
|
include Enumerable
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def initialize(data, id = "id")
|
|
8
|
-
@data = collection(data, id)
|
|
5
|
+
def initialize(&fetch)
|
|
6
|
+
@fetch = fetch
|
|
9
7
|
end
|
|
10
8
|
|
|
11
9
|
def each(&block)
|
|
12
|
-
|
|
10
|
+
objects.each(&block)
|
|
13
11
|
end
|
|
14
12
|
|
|
15
13
|
private
|
|
16
14
|
|
|
17
|
-
def
|
|
18
|
-
|
|
19
|
-
Object.new(item[id])
|
|
20
|
-
end
|
|
15
|
+
def objects
|
|
16
|
+
@objects ||= @fetch.call.map { |item| Object.new(item) }
|
|
21
17
|
end
|
|
22
18
|
end
|
|
23
19
|
end
|
data/lib/epok/object.rb
CHANGED
|
@@ -1,19 +1,48 @@
|
|
|
1
1
|
module Epok
|
|
2
2
|
class Object
|
|
3
|
-
attr_reader :id
|
|
3
|
+
attr_reader :id, :distance
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
# Accepts either an id ("farmacias|1082") or a result hash from a search
|
|
6
|
+
# or geocoder listing. Listing attributes are available immediately; the
|
|
7
|
+
# full content is fetched on first use.
|
|
8
|
+
def initialize(attributes)
|
|
9
|
+
attributes = { "id" => attributes } if attributes.is_a?(String)
|
|
10
|
+
|
|
11
|
+
@id = attributes.fetch("id")
|
|
12
|
+
@name = attributes["nombre"]&.strip
|
|
13
|
+
@kind = attributes["clase"]
|
|
14
|
+
@distance = attributes["distancia"]&.to_f
|
|
7
15
|
end
|
|
8
16
|
|
|
9
17
|
def name
|
|
10
|
-
content["Nombre"]
|
|
18
|
+
@name ||= content["Nombre"]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def kind
|
|
22
|
+
@kind ||= object["clase"]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def category
|
|
26
|
+
id.split("|").first
|
|
11
27
|
end
|
|
12
28
|
|
|
13
29
|
def normalized_address
|
|
14
30
|
object["direccionNormalizada"]
|
|
15
31
|
end
|
|
16
32
|
|
|
33
|
+
# Longitude and latitude as a Location, or nil when EPOk has no
|
|
34
|
+
# centroid for the object. Costs one extra request the first time.
|
|
35
|
+
def location
|
|
36
|
+
return @location if defined?(@location)
|
|
37
|
+
|
|
38
|
+
centroid = object.dig("ubicacion", "centroide")
|
|
39
|
+
@location = centroid && begin
|
|
40
|
+
x, y = centroid[/\(([^)]*)\)/, 1].split.map(&:to_f)
|
|
41
|
+
lon, lat = API.to_lonlat(x, y)
|
|
42
|
+
Location.new(x: lon, y: lat)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
17
46
|
def content
|
|
18
47
|
object["contenido"].each_with_object({}) do |entry, hash|
|
|
19
48
|
name, value = entry.values_at("nombre", "valor")
|
data/lib/epok/version.rb
CHANGED
data/lib/epok.rb
CHANGED
|
@@ -1,17 +1,65 @@
|
|
|
1
1
|
require "epok/api"
|
|
2
|
-
require "epok/collectable"
|
|
3
2
|
require "epok/collection"
|
|
4
|
-
require "epok/geocoder"
|
|
5
3
|
require "epok/object"
|
|
6
|
-
require "epok/search"
|
|
7
4
|
require "epok/version"
|
|
8
5
|
|
|
9
6
|
module Epok
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
Location = Struct.new(:x, :y, keyword_init: true)
|
|
8
|
+
Category = Struct.new(:id, :name, :description, keyword_init: true)
|
|
9
|
+
DatosUtiles = Struct.new(
|
|
10
|
+
:barrio, :comuna, :comisaria, :comisaria_vecinal,
|
|
11
|
+
:area_hospitalaria, :region_sanitaria, :distrito_escolar,
|
|
12
|
+
:partido_amba, :localidad_amba,
|
|
13
|
+
keyword_init: true
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
Address = Struct.new(:address, :partido, :localidad, :location, keyword_init: true)
|
|
17
|
+
|
|
18
|
+
DEFAULT_RADIUS = 500
|
|
19
|
+
|
|
20
|
+
def self.search(query, categories: nil, limit: nil)
|
|
21
|
+
categories = Array(categories).join(",") if categories
|
|
22
|
+
|
|
23
|
+
Collection.new { API.search(query, categories, limit) }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.geocoder(location, categories, radius: DEFAULT_RADIUS)
|
|
27
|
+
categories = Array(categories).join(",")
|
|
28
|
+
|
|
29
|
+
Collection.new { API.geocoder(location.x, location.y, categories, radius) }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.geocode(text)
|
|
33
|
+
API.geocode(text).map do |address|
|
|
34
|
+
x, y = address["coordenadas"].values_at("x", "y").map(&:to_f)
|
|
35
|
+
|
|
36
|
+
Address.new(
|
|
37
|
+
address: address["direccion"],
|
|
38
|
+
partido: address["nombre_partido"],
|
|
39
|
+
localidad: address["nombre_localidad"],
|
|
40
|
+
location: Location.new(x: x, y: y)
|
|
41
|
+
)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def self.datos_utiles(location)
|
|
46
|
+
datos = API.datos_utiles(location.x, location.y)
|
|
47
|
+
|
|
48
|
+
DatosUtiles.new(
|
|
49
|
+
DatosUtiles.members.to_h do |member|
|
|
50
|
+
value = datos[member.to_s].to_s.strip.squeeze(" ")
|
|
51
|
+
[member, value.empty? ? nil : value]
|
|
52
|
+
end
|
|
53
|
+
)
|
|
12
54
|
end
|
|
13
55
|
|
|
14
|
-
def self.
|
|
15
|
-
|
|
56
|
+
def self.categories
|
|
57
|
+
API.categories.map do |category|
|
|
58
|
+
Category.new(
|
|
59
|
+
id: category["nombre_normalizado"],
|
|
60
|
+
name: category["nombre"],
|
|
61
|
+
description: category["descripcion"]
|
|
62
|
+
)
|
|
63
|
+
end
|
|
16
64
|
end
|
|
17
65
|
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
require "test_helper"
|
|
2
|
+
|
|
3
|
+
module Epok
|
|
4
|
+
class APITest < Minitest::Test
|
|
5
|
+
include WebMock::API
|
|
6
|
+
|
|
7
|
+
def setup
|
|
8
|
+
VCR.eject_cassette
|
|
9
|
+
VCR.turn_off!
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def teardown
|
|
13
|
+
WebMock.reset!
|
|
14
|
+
VCR.turn_on!
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def test_that_a_server_error_raises
|
|
18
|
+
stub_request(:get, /epok/).to_return(status: 500, body: "boom")
|
|
19
|
+
|
|
20
|
+
error = assert_raises(Error) { Epok.search("x").first }
|
|
21
|
+
assert_match "500", error.message
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def test_that_a_timeout_raises
|
|
25
|
+
stub_request(:get, /epok/).to_timeout
|
|
26
|
+
|
|
27
|
+
assert_raises(Error) { Epok.categories }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def test_that_a_non_json_body_raises
|
|
31
|
+
stub_request(:get, /epok/).to_return(status: 200, body: "<html>")
|
|
32
|
+
|
|
33
|
+
assert_raises(Error) { Epok.search("x").first }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def test_that_a_missing_object_raises_not_found
|
|
37
|
+
stub_request(:get, /getObjectContent/).to_return(status: 200, body: "{}")
|
|
38
|
+
|
|
39
|
+
assert_raises(NotFound) { Object.new("nope|1").content }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def test_that_an_object_without_a_centroid_has_no_location
|
|
43
|
+
stub_request(:get, /getObjectContent/)
|
|
44
|
+
.to_return(status: 200, body: '{"contenido": [], "ubicacion": null}')
|
|
45
|
+
|
|
46
|
+
object = Object.new("espacios_verdes_publicos|1")
|
|
47
|
+
|
|
48
|
+
assert_nil object.location
|
|
49
|
+
assert_nil object.location
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def test_that_a_failed_coordinate_conversion_raises
|
|
53
|
+
stub_request(:get, /getObjectContent/)
|
|
54
|
+
.to_return(status: 200, body: '{"ubicacion": {"centroide": "POINT (1 2)"}}')
|
|
55
|
+
stub_request(:get, /usig/)
|
|
56
|
+
.to_return(status: 200, body: '{"tipo_resultado":"Error","resultado":{"x":"","y":""}}')
|
|
57
|
+
|
|
58
|
+
assert_raises(Error) { Object.new("x|1").location }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def test_that_not_found_is_an_error
|
|
62
|
+
assert_operator NotFound, :<, Error
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require "test_helper"
|
|
2
|
+
|
|
3
|
+
module Epok
|
|
4
|
+
class CategoriesTest < Minitest::Test
|
|
5
|
+
def setup
|
|
6
|
+
VCR.insert_cassette("categories")
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def teardown
|
|
10
|
+
VCR.eject_cassette("categories")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def test_that_categories_are_listed
|
|
14
|
+
categories = Epok.categories
|
|
15
|
+
|
|
16
|
+
assert_operator categories.size, :>, 100
|
|
17
|
+
assert categories.all? { |category| category.is_a?(Category) }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def test_that_a_category_has_an_id_and_a_name
|
|
21
|
+
pharmacies = Epok.categories.find { |category| category.id == "farmacias" }
|
|
22
|
+
|
|
23
|
+
assert_equal "Farmacias", pharmacies.name
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require "test_helper"
|
|
2
|
+
|
|
3
|
+
module Epok
|
|
4
|
+
class CollectionTest < Minitest::Test
|
|
5
|
+
def test_that_a_collection_is_enumerable
|
|
6
|
+
collection = Collection.new { [{"id" => "a|1"}, {"id" => "a|2"}] }
|
|
7
|
+
|
|
8
|
+
assert_equal ["a|1", "a|2"], collection.map(&:id)
|
|
9
|
+
assert_equal 2, collection.count
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def test_that_a_collection_fetches_lazily_and_once
|
|
13
|
+
calls = 0
|
|
14
|
+
collection = Collection.new { calls += 1; [{"id" => "a|1"}] }
|
|
15
|
+
|
|
16
|
+
assert_equal 0, calls
|
|
17
|
+
collection.first
|
|
18
|
+
collection.to_a
|
|
19
|
+
assert_equal 1, calls
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
require "test_helper"
|
|
2
|
+
|
|
3
|
+
module Epok
|
|
4
|
+
class DatosUtilesTest < Minitest::Test
|
|
5
|
+
def setup
|
|
6
|
+
VCR.insert_cassette("datos_utiles")
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def teardown
|
|
10
|
+
VCR.eject_cassette("datos_utiles")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def test_that_datos_utiles_returns_the_barrio_and_comuna
|
|
14
|
+
datos = Epok.datos_utiles(Location.new(x: -58.381570, y: -34.603738))
|
|
15
|
+
|
|
16
|
+
assert_equal "San Nicolas", datos.barrio
|
|
17
|
+
assert_equal "Comuna 1", datos.comuna
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def test_that_datos_utiles_returns_the_other_jurisdictions
|
|
21
|
+
datos = Epok.datos_utiles(Location.new(x: -58.381570, y: -34.603738))
|
|
22
|
+
|
|
23
|
+
assert_equal "3", datos.comisaria
|
|
24
|
+
assert_equal "1B", datos.comisaria_vecinal
|
|
25
|
+
assert_equal "HTAL. DR. J.M. RAMOS MEJÍA", datos.area_hospitalaria
|
|
26
|
+
assert_equal "I (Este)", datos.region_sanitaria
|
|
27
|
+
assert_equal "Distrito Escolar I", datos.distrito_escolar
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def test_that_outside_the_city_fields_are_nil_and_amba_is_filled
|
|
31
|
+
datos = Epok.datos_utiles(Location.new(x: -58.60, y: -34.60))
|
|
32
|
+
|
|
33
|
+
assert_nil datos.barrio
|
|
34
|
+
assert_nil datos.comuna
|
|
35
|
+
assert_equal "Tres de Febrero", datos.partido_amba
|
|
36
|
+
assert_equal "Ciudad Jardín Lomas de El Palomar", datos.localidad_amba
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
require "test_helper"
|
|
2
|
+
|
|
3
|
+
module Epok
|
|
4
|
+
class GeocodeTest < Minitest::Test
|
|
5
|
+
def setup
|
|
6
|
+
VCR.insert_cassette("geocode")
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def teardown
|
|
10
|
+
VCR.eject_cassette("geocode")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def test_that_an_address_is_normalized_and_located
|
|
14
|
+
address = Epok.geocode("cabildo 1234").first
|
|
15
|
+
|
|
16
|
+
assert_equal "CABILDO AV. 1234, CABA", address.address
|
|
17
|
+
assert_in_delta(-58.448466, address.location.x)
|
|
18
|
+
assert_in_delta(-34.568290, address.location.y)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def test_that_an_address_has_a_partido_and_localidad
|
|
22
|
+
addresses = Epok.geocode("cabildo 1234")
|
|
23
|
+
|
|
24
|
+
assert_equal "CABA", addresses.first.partido
|
|
25
|
+
assert_equal "CABA", addresses.first.localidad
|
|
26
|
+
assert_equal "La Matanza", addresses[1].partido
|
|
27
|
+
assert_equal "Villa Madero", addresses[1].localidad
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def test_that_an_unknown_address_raises_not_found
|
|
31
|
+
error = assert_raises(NotFound) { Epok.geocode("xyzzy 999") }
|
|
32
|
+
|
|
33
|
+
assert_equal "Calle inexistente: xyzzy 999", error.message
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
data/test/epok/geocoder_test.rb
CHANGED
|
@@ -3,7 +3,7 @@ require "test_helper"
|
|
|
3
3
|
module Epok
|
|
4
4
|
class GeocoderTest < Minitest::Test
|
|
5
5
|
def setup
|
|
6
|
-
VCR.insert_cassette("geocoder"
|
|
6
|
+
VCR.insert_cassette("geocoder")
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
def teardown
|
|
@@ -18,12 +18,32 @@ module Epok
|
|
|
18
18
|
assert_match "Línea D", result.name
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
+
def test_that_geocoder_results_have_a_distance
|
|
22
|
+
assert_operator result.distance, :<, 500
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def test_that_geocoder_accepts_a_radius
|
|
26
|
+
near = Epok.geocoder(obelisco, "estaciones_de_subte", radius: 100)
|
|
27
|
+
far = Epok.geocoder(obelisco, "estaciones_de_subte", radius: 1000)
|
|
28
|
+
|
|
29
|
+
assert_operator near.count, :<, far.count
|
|
30
|
+
assert near.all? { |station| station.distance <= 100 }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def test_that_geocoder_accepts_several_categories
|
|
34
|
+
results = Epok.geocoder(obelisco, %w[estaciones_de_subte farmacias])
|
|
35
|
+
|
|
36
|
+
assert_equal %w[estaciones_de_subte farmacias].sort, results.map(&:category).uniq.sort
|
|
37
|
+
end
|
|
38
|
+
|
|
21
39
|
private
|
|
22
40
|
|
|
23
41
|
def result
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
42
|
+
Epok.geocoder(obelisco, "estaciones_de_subte").first
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def obelisco
|
|
46
|
+
Location.new(x: -58.381570, y: -34.603738)
|
|
27
47
|
end
|
|
28
48
|
end
|
|
29
49
|
end
|
data/test/epok/object_test.rb
CHANGED
|
@@ -14,10 +14,26 @@ module Epok
|
|
|
14
14
|
assert_equal "BIBLIOTECA ANMAT", object.name
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
+
def test_that_an_object_has_a_kind
|
|
18
|
+
assert_equal "Dependencias Culturales", object.kind
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def test_that_an_object_has_a_category
|
|
22
|
+
assert_equal "dependencias_culturales", object.category
|
|
23
|
+
end
|
|
24
|
+
|
|
17
25
|
def test_that_an_object_has_a_normalized_address
|
|
18
26
|
assert_equal "DE MAYO AV. 869", object.normalized_address
|
|
19
27
|
end
|
|
20
28
|
|
|
29
|
+
def test_that_an_object_has_a_location
|
|
30
|
+
location = object.location
|
|
31
|
+
|
|
32
|
+
assert_instance_of Location, location
|
|
33
|
+
assert_in_delta(-58.38, location.x, 0.01)
|
|
34
|
+
assert_in_delta(-34.61, location.y, 0.01)
|
|
35
|
+
end
|
|
36
|
+
|
|
21
37
|
def test_that_an_object_has_content
|
|
22
38
|
expected_content = {
|
|
23
39
|
"Nombre" => "BIBLIOTECA ANMAT",
|
|
@@ -36,6 +52,27 @@ module Epok
|
|
|
36
52
|
assert_equal expected_content, object.content
|
|
37
53
|
end
|
|
38
54
|
|
|
55
|
+
def test_that_listing_attributes_do_not_fetch
|
|
56
|
+
object = Object.new(
|
|
57
|
+
"id" => "farmacias|1082",
|
|
58
|
+
"nombre" => "Farmacia en CERRITO 342 ",
|
|
59
|
+
"clase" => "Farmacia",
|
|
60
|
+
"distancia" => "112.31"
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
VCR.eject_cassette
|
|
64
|
+
VCR.turned_off do
|
|
65
|
+
assert_equal "Farmacia en CERRITO 342", object.name
|
|
66
|
+
assert_equal "Farmacia", object.kind
|
|
67
|
+
assert_equal "farmacias", object.category
|
|
68
|
+
assert_in_delta 112.31, object.distance
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def test_that_an_object_built_from_an_id_has_no_distance
|
|
73
|
+
assert_nil object.distance
|
|
74
|
+
end
|
|
75
|
+
|
|
39
76
|
private
|
|
40
77
|
|
|
41
78
|
def object
|
data/test/epok/search_test.rb
CHANGED
|
@@ -18,11 +18,30 @@ module Epok
|
|
|
18
18
|
assert_match "San Miguel de Garicoits", result.name
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
+
def test_that_search_encodes_the_query
|
|
22
|
+
assert_match "Peña", Epok.search("plaza peña").first.name
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def test_that_search_filters_by_category
|
|
26
|
+
results = Epok.search("san martin", categories: "estaciones_de_subte")
|
|
27
|
+
|
|
28
|
+
assert_equal ["estaciones_de_subte"], results.map(&:category).uniq
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def test_that_search_filters_by_several_categories
|
|
32
|
+
results = Epok.search("san martin", categories: %w[estaciones_de_subte estaciones_de_ferrocarril])
|
|
33
|
+
|
|
34
|
+
assert_equal %w[estaciones_de_ferrocarril estaciones_de_subte], results.map(&:category).uniq.sort
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def test_that_search_limits_results
|
|
38
|
+
assert_equal 2, Epok.search("san martin", limit: 2).count
|
|
39
|
+
end
|
|
40
|
+
|
|
21
41
|
private
|
|
22
42
|
|
|
23
43
|
def result
|
|
24
|
-
|
|
25
|
-
results.first
|
|
44
|
+
Epok.search("garicoits").first
|
|
26
45
|
end
|
|
27
46
|
end
|
|
28
47
|
end
|
data/test/epok_test.rb
CHANGED
|
@@ -1,20 +1,13 @@
|
|
|
1
1
|
require "test_helper"
|
|
2
2
|
|
|
3
3
|
class EpokTest < Minitest::Test
|
|
4
|
-
def
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
assert_instance_of Epok::Search, search
|
|
8
|
-
assert_equal "garicoits", search.query
|
|
4
|
+
def test_that_search_builds_a_collection
|
|
5
|
+
assert_instance_of Epok::Collection, Epok.search("garicoits")
|
|
9
6
|
end
|
|
10
7
|
|
|
11
|
-
def
|
|
8
|
+
def test_that_geocoder_builds_a_collection
|
|
12
9
|
obelisco = Epok::Location.new(x: -58.381570, y: -34.603738)
|
|
13
|
-
geocoder = Epok.geocoder(obelisco, "farmacias")
|
|
14
10
|
|
|
15
|
-
assert_instance_of Epok::
|
|
16
|
-
assert_equal(-58.381570, geocoder.x)
|
|
17
|
-
assert_equal(-34.603738, geocoder.y)
|
|
18
|
-
assert_equal "farmacias", geocoder.categories
|
|
11
|
+
assert_instance_of Epok::Collection, Epok.geocoder(obelisco, "farmacias")
|
|
19
12
|
end
|
|
20
13
|
end
|
data/test/test_helper.rb
CHANGED
|
@@ -4,12 +4,10 @@ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
|
|
|
4
4
|
|
|
5
5
|
require "epok"
|
|
6
6
|
require "minitest/autorun"
|
|
7
|
-
require "minitest/reporters"
|
|
8
7
|
require "vcr"
|
|
9
8
|
|
|
10
|
-
Minitest::Reporters.use!
|
|
11
|
-
|
|
12
9
|
VCR.configure do |config|
|
|
13
10
|
config.cassette_library_dir = "test/fixtures"
|
|
14
11
|
config.hook_into :webmock
|
|
12
|
+
config.default_cassette_options = { record: :new_episodes }
|
|
15
13
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: epok
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ariel Rzezak
|
|
@@ -23,26 +23,6 @@ dependencies:
|
|
|
23
23
|
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '5.0'
|
|
26
|
-
- !ruby/object:Gem::Dependency
|
|
27
|
-
name: minitest-reporters
|
|
28
|
-
requirement: !ruby/object:Gem::Requirement
|
|
29
|
-
requirements:
|
|
30
|
-
- - "~>"
|
|
31
|
-
- !ruby/object:Gem::Version
|
|
32
|
-
version: '1.3'
|
|
33
|
-
- - ">="
|
|
34
|
-
- !ruby/object:Gem::Version
|
|
35
|
-
version: 1.3.6
|
|
36
|
-
type: :development
|
|
37
|
-
prerelease: false
|
|
38
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
39
|
-
requirements:
|
|
40
|
-
- - "~>"
|
|
41
|
-
- !ruby/object:Gem::Version
|
|
42
|
-
version: '1.3'
|
|
43
|
-
- - ">="
|
|
44
|
-
- !ruby/object:Gem::Version
|
|
45
|
-
version: 1.3.6
|
|
46
26
|
- !ruby/object:Gem::Dependency
|
|
47
27
|
name: rake
|
|
48
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -112,12 +92,14 @@ files:
|
|
|
112
92
|
- epok.gemspec
|
|
113
93
|
- lib/epok.rb
|
|
114
94
|
- lib/epok/api.rb
|
|
115
|
-
- lib/epok/collectable.rb
|
|
116
95
|
- lib/epok/collection.rb
|
|
117
|
-
- lib/epok/geocoder.rb
|
|
118
96
|
- lib/epok/object.rb
|
|
119
|
-
- lib/epok/search.rb
|
|
120
97
|
- lib/epok/version.rb
|
|
98
|
+
- test/epok/api_test.rb
|
|
99
|
+
- test/epok/categories_test.rb
|
|
100
|
+
- test/epok/collection_test.rb
|
|
101
|
+
- test/epok/datos_utiles_test.rb
|
|
102
|
+
- test/epok/geocode_test.rb
|
|
121
103
|
- test/epok/geocoder_test.rb
|
|
122
104
|
- test/epok/object_test.rb
|
|
123
105
|
- test/epok/search_test.rb
|
data/lib/epok/collectable.rb
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
require "forwardable"
|
|
2
|
-
|
|
3
|
-
module Epok
|
|
4
|
-
module Collectable
|
|
5
|
-
def self.included(base)
|
|
6
|
-
base.class_eval do
|
|
7
|
-
extend Forwardable
|
|
8
|
-
|
|
9
|
-
def_delegators :results, :entries, :first
|
|
10
|
-
end
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def collection(object)
|
|
14
|
-
Collection.new object
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
data/lib/epok/geocoder.rb
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
module Epok
|
|
2
|
-
Location = Struct.new(:x, :y, keyword_init: true)
|
|
3
|
-
|
|
4
|
-
class Geocoder
|
|
5
|
-
include Collectable
|
|
6
|
-
|
|
7
|
-
attr_reader :x, :y, :categories
|
|
8
|
-
|
|
9
|
-
def initialize(location, categories)
|
|
10
|
-
@x = location.x
|
|
11
|
-
@y = location.y
|
|
12
|
-
@categories = categories
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
private
|
|
16
|
-
|
|
17
|
-
def results
|
|
18
|
-
collection API.geocoder(x, y, categories)
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
end
|