london-bike-hire-cli 1.3.0 → 1.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/.travis.yml +1 -0
- data/README.md +19 -17
- data/lib/london_bike_hire_cli/application.rb +8 -1
- data/lib/london_bike_hire_cli/basic_renderer.rb +15 -22
- data/lib/london_bike_hire_cli/color_helper.rb +24 -0
- data/lib/london_bike_hire_cli/controller.rb +21 -31
- data/lib/london_bike_hire_cli/feed_parser.rb +9 -10
- data/lib/london_bike_hire_cli/queries/stations_by_name.rb +9 -0
- data/lib/london_bike_hire_cli/queries/stations_near.rb +5 -0
- data/lib/london_bike_hire_cli/query_response.rb +4 -14
- data/lib/london_bike_hire_cli/repository/in_memory_store_adapter.rb +69 -0
- data/lib/london_bike_hire_cli/repository/repo.rb +39 -0
- data/lib/london_bike_hire_cli/repository/spatial_search_adapter.rb +28 -0
- data/lib/london_bike_hire_cli/repository/station_repo.rb +35 -0
- data/lib/london_bike_hire_cli/repository/station_store.rb +22 -0
- data/lib/london_bike_hire_cli/station.rb +4 -0
- data/lib/london_bike_hire_cli/station_not_found_error.rb +3 -0
- data/lib/london_bike_hire_cli/version.rb +1 -1
- data/lib/london_bike_hire_cli/views/error.erb +5 -0
- data/lib/london_bike_hire_cli/views/stations.erb +11 -0
- data/lib/london_bike_hire_cli.rb +12 -7
- data/london-bike-hire-cli.gemspec +3 -2
- data/spec/controller_spec.rb +29 -61
- data/spec/feed_parser_spec.rb +10 -12
- data/spec/station_repo_spec.rb +148 -0
- data/spec/support/test_datasource.rb +8 -4
- data/test.rb +1 -0
- metadata +17 -13
- data/lib/london_bike_hire_cli/spatial_search.rb +0 -20
- data/lib/london_bike_hire_cli/station_adapter.rb +0 -19
- data/lib/london_bike_hire_cli/station_repository.rb +0 -51
- data/spec/spatial_search_spec.rb +0 -27
- data/spec/station_adapter_spec.rb +0 -29
- data/spec/station_respository_spec.rb +0 -117
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d671c2c0f1e169a21f29d72549a428bb33611d4c
|
4
|
+
data.tar.gz: 3f32c3f533b6a18741d28e57275a289762b3260d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 664bcbc0ebcbf7fe0ad5b3af52961f186f618928eb84dc4c9c571742052da197e92a6a8bb93addba70927a9c028ecc8b575f646351a89abe00d71643a7a40f86
|
7
|
+
data.tar.gz: 3665a466b1f0d4d2a3d08ca212ebbf401be996c3e99cf8a3f4dd5858732ed7498425cfa6d598a8349a67d379e3b16be0ca6c03279a8f2ca1f3d018b43845ef7e
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.2.0
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
[![Build Status](https://travis-ci.org/rob-murray/london-bike-hire-cli.svg?branch=master)](https://travis-ci.org/rob-murray/london-bike-hire-cli)
|
6
6
|
[![Code Climate](https://codeclimate.com/github/rob-murray/london-bike-hire-cli.png)](https://codeclimate.com/github/rob-murray/london-bike-hire-cli)
|
7
|
-
[![Coverage Status](https://coveralls.io/repos/rob-murray/london-bike-hire-cli/badge.
|
7
|
+
[![Coverage Status](https://coveralls.io/repos/rob-murray/london-bike-hire-cli/badge.svg?branch=master)](https://coveralls.io/r/rob-murray/london-bike-hire-cli?branch=master)
|
8
8
|
[![Dependency Status](https://gemnasium.com/rob-murray/ferver.svg)](https://gemnasium.com/rob-murray/london-bike-hire-cli)
|
9
9
|
[![Gem Version](https://badge.fury.io/rb/london-bike-hire-cli.svg)](http://badge.fury.io/rb/london-bike-hire-cli)
|
10
10
|
[![Haz Commitz Status](http://haz-commitz.herokuapp.com/repos/rob-murray/london-bike-hire-cli.svg)](http://haz-commitz.herokuapp.com/repos/rob-murray/london-bike-hire-cli)
|
@@ -12,17 +12,19 @@
|
|
12
12
|
|
13
13
|
### Description
|
14
14
|
|
15
|
-
This gem provides a command line interface to find information about London's Bike Hire stations, it allows you to find stations by name or their ID and retrieve information about the status of a station.
|
15
|
+
This gem provides a command line interface to find information about London's Bike Hire stations, it allows you to find stations by name or their ID or a location and retrieve information about the status of a station.
|
16
|
+
|
17
|
+
I got fed up digging through [the official map](https://web.barclayscyclehire.tfl.gov.uk/maps) to see which bike stations were free - everyone knows the command line is way more efficient.
|
16
18
|
|
17
19
|
![Boris](boris-on-a-bike_med.jpg?raw=true "Boris Johnson on a bike")
|
18
20
|
|
19
21
|
Here's the spec for **London Bike Hire CLI**:
|
20
22
|
|
21
|
-
* Find all stations
|
22
|
-
* Find
|
23
|
-
* Find
|
24
|
-
* Find by by
|
25
|
-
* Display status of stations
|
23
|
+
* [Find all stations](#list-all-bike-stations)
|
24
|
+
* [Find by ID](#find-a-bike-station-by-id)
|
25
|
+
* [Find nearest station](#find-the-nearest-bike-station)
|
26
|
+
* [Find by name](#find-all-bike-stations-by-name)
|
27
|
+
* [Display status of stations](#results)
|
26
28
|
|
27
29
|
|
28
30
|
### Getting started
|
@@ -38,7 +40,7 @@ $ gem install london-bike-hire-cli
|
|
38
40
|
|
39
41
|
You can run **london-bike-hire-cli** from the command line with just the `lbh` command.
|
40
42
|
|
41
|
-
|
43
|
+
#### Command line help
|
42
44
|
|
43
45
|
For a list of arguments just use the `-h` switch.
|
44
46
|
|
@@ -46,7 +48,7 @@ For a list of arguments just use the `-h` switch.
|
|
46
48
|
$ lbh -h
|
47
49
|
````
|
48
50
|
|
49
|
-
|
51
|
+
#### List all bike stations
|
50
52
|
|
51
53
|
List all however-many-hundred bike stations there are. With pagination.
|
52
54
|
|
@@ -54,7 +56,7 @@ List all however-many-hundred bike stations there are. With pagination.
|
|
54
56
|
$ lbh all
|
55
57
|
````
|
56
58
|
|
57
|
-
|
59
|
+
#### Find the nearest bike station
|
58
60
|
|
59
61
|
Find a the nearest bike station to the specified type below. This will return five stations.
|
60
62
|
|
@@ -76,7 +78,7 @@ $ lbh near --id 123
|
|
76
78
|
|
77
79
|
**Note:** Don't forget to wrap search term in quotes if it contains spaces.
|
78
80
|
|
79
|
-
|
81
|
+
#### Find a bike station by id
|
80
82
|
|
81
83
|
Find a Bike station by it's Tfl station ID. This will return one station.
|
82
84
|
|
@@ -85,7 +87,7 @@ $ lbh find --id {id}
|
|
85
87
|
$ lbh find --id 439
|
86
88
|
````
|
87
89
|
|
88
|
-
|
90
|
+
#### Find all bike stations by name
|
89
91
|
|
90
92
|
Search all bike stations' name attribute for the search value. This will return zero or more results.
|
91
93
|
|
@@ -94,22 +96,22 @@ $ lbh where --{attribute} {search_value}
|
|
94
96
|
$ lbh where --name kings
|
95
97
|
````
|
96
98
|
|
97
|
-
|
99
|
+
#### Results
|
98
100
|
|
99
101
|
```bash
|
100
102
|
$ lbh find --id 439
|
101
103
|
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
102
|
-
|
103
|
-
>>> Dock
|
104
|
+
----- Bike station
|
104
105
|
Id: 439
|
105
106
|
Name: Killick Street, Kings Cross
|
106
107
|
Lat: 51.5338
|
107
108
|
Long: -0.118677
|
108
109
|
Temporary: false
|
109
|
-
Bikes:
|
110
|
-
Docks_free:
|
110
|
+
Bikes: 13
|
111
|
+
Docks_free: 9
|
111
112
|
Docks_total: 22
|
112
113
|
Link to map: https://www.google.co.uk/maps/preview/@51.5338,-0.118677,17z
|
114
|
+
Updated at: 2015-03-31 20:46:01
|
113
115
|
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
114
116
|
```
|
115
117
|
|
@@ -65,7 +65,14 @@ module LondonBikeHireCli
|
|
65
65
|
end
|
66
66
|
|
67
67
|
def station_repository
|
68
|
-
|
68
|
+
register_adapter!
|
69
|
+
Repository::StationRepo.new
|
70
|
+
end
|
71
|
+
|
72
|
+
def register_adapter!
|
73
|
+
Repository::Repo.register(
|
74
|
+
Repository::StationStore.new(FeedParser.new.fetch)
|
75
|
+
)
|
69
76
|
end
|
70
77
|
end
|
71
78
|
end
|
@@ -1,38 +1,31 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require_relative 'color_helper'
|
3
|
+
|
1
4
|
module LondonBikeHireCli
|
2
5
|
class BasicRenderer
|
6
|
+
include ColorHelper
|
7
|
+
|
3
8
|
def initialize(output = STDOUT)
|
4
9
|
@output = output
|
5
10
|
end
|
6
11
|
|
7
|
-
|
8
|
-
output.print ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"
|
9
|
-
output.print "Feed updated: #{context.time_of_feed}\n"
|
12
|
+
attr_reader :context
|
10
13
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
output.print ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"
|
16
|
-
end
|
17
|
-
|
18
|
-
def render_error(context)
|
19
|
-
output.print ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"
|
20
|
-
output.print "Ooops, something went wrong with the query.\n"
|
21
|
-
output.print "The query #{context[:query].inspect}\n"
|
22
|
-
output.print "resulted in error: #{context[:error]}\n"
|
23
|
-
output.print "Try again?\n"
|
14
|
+
def render(template_name, context)
|
15
|
+
@context = context
|
16
|
+
output.print render_template(find_template(template_name))
|
24
17
|
end
|
25
18
|
|
26
19
|
private
|
27
20
|
|
28
21
|
attr_reader :output
|
29
22
|
|
30
|
-
def
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
23
|
+
def render_template(template)
|
24
|
+
ERB.new(template).result(binding).gsub(/^\s+/, '')
|
25
|
+
end
|
26
|
+
|
27
|
+
def find_template(template_name)
|
28
|
+
File.read("lib/london_bike_hire_cli/views/#{template_name}.erb")
|
36
29
|
end
|
37
30
|
end
|
38
31
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module LondonBikeHireCli
|
2
|
+
module ColorHelper
|
3
|
+
def colorize(text, color_name = :default)
|
4
|
+
colour_code = color_code(color_name)
|
5
|
+
"\e[#{colour_code}m#{text}\e[0m"
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def color_code(name)
|
11
|
+
COLOR_MAP[name]
|
12
|
+
end
|
13
|
+
|
14
|
+
COLOR_MAP = {
|
15
|
+
default: 39,
|
16
|
+
red: 31,
|
17
|
+
green: 32,
|
18
|
+
yellow: 33,
|
19
|
+
blue: 34,
|
20
|
+
pink: 35,
|
21
|
+
cyan: 36
|
22
|
+
}.freeze
|
23
|
+
end
|
24
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module LondonBikeHireCli
|
2
2
|
class Controller
|
3
|
-
attr_accessor :geocoder
|
3
|
+
attr_accessor :geocoder
|
4
4
|
|
5
5
|
def initialize(repository: nil, renderer: default_renderer)
|
6
6
|
@repository = repository
|
@@ -10,58 +10,55 @@ module LondonBikeHireCli
|
|
10
10
|
|
11
11
|
def all
|
12
12
|
results = repository.all
|
13
|
-
renderer.render(results)
|
13
|
+
renderer.render(:stations, results)
|
14
14
|
end
|
15
15
|
|
16
16
|
def find_by_id(id)
|
17
|
-
results = repository.
|
18
|
-
renderer.render(results)
|
17
|
+
results = repository.find(id.to_i)
|
18
|
+
renderer.render(:stations, Array(results))
|
19
19
|
|
20
|
-
rescue
|
21
|
-
renderer.
|
20
|
+
rescue StationNotFoundError => e
|
21
|
+
renderer.render(:error, query: "find_by_id: #{id}", error: e)
|
22
22
|
end
|
23
23
|
|
24
24
|
def where(params: {})
|
25
|
-
|
26
|
-
|
25
|
+
by_name_query = Queries::StationsByName.new(params[:name])
|
26
|
+
results = repository.query(by_name_query)
|
27
|
+
renderer.render(:stations, results)
|
27
28
|
|
28
|
-
rescue
|
29
|
-
renderer.
|
29
|
+
rescue StationNotFoundError => e
|
30
|
+
renderer.render(:error, query: "where: #{params}", error: e)
|
30
31
|
end
|
31
32
|
|
32
33
|
def nearest(params: {})
|
33
34
|
unless params[:search_term].nil? ^ params[:id].nil?
|
34
35
|
error = 'Please use either `search_term` or `id` not both.'
|
35
|
-
renderer.
|
36
|
+
renderer.render(:error, query: "nearest: #{params}", error: error)
|
36
37
|
return
|
37
38
|
end
|
38
39
|
|
39
40
|
if search_term = params[:search_term]
|
40
41
|
geocoded_point = geocoder.geocode(search_term)
|
41
|
-
|
42
|
-
|
43
|
-
if search_term = params[:id]
|
42
|
+
elsif search_term = params[:id]
|
44
43
|
begin
|
45
|
-
|
46
|
-
rescue
|
47
|
-
renderer.
|
44
|
+
station = repository.find(search_term.to_i)
|
45
|
+
rescue StationNotFoundError => e
|
46
|
+
renderer.render(:error, query: "where: #{params}", error: e)
|
48
47
|
return
|
49
48
|
end
|
50
49
|
|
51
|
-
geocoded_point =
|
50
|
+
geocoded_point = station.position
|
52
51
|
end
|
53
52
|
|
54
53
|
unless geocoded_point
|
55
54
|
error = 'Unable to Geocode location.'
|
56
|
-
renderer.
|
55
|
+
renderer.render(:error, query: "nearest: #{params}", error: error)
|
57
56
|
return
|
58
57
|
end
|
59
58
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
results = repository.find_by_id(*nearest_ids)
|
64
|
-
renderer.render(results)
|
59
|
+
near_query = Queries::StationsNear.new(geocoded_point[:lat], geocoded_point[:long], DEFAULT_SEARCH_LIMIT)
|
60
|
+
results = repository.query(near_query)
|
61
|
+
renderer.render(:stations, results)
|
65
62
|
end
|
66
63
|
|
67
64
|
private
|
@@ -71,12 +68,5 @@ module LondonBikeHireCli
|
|
71
68
|
def default_renderer
|
72
69
|
BasicRenderer.new
|
73
70
|
end
|
74
|
-
|
75
|
-
def init_spatial_service(all_stations)
|
76
|
-
@spatial_service ||= begin
|
77
|
-
datasource = StationAdapter.new(all_stations).to_triples
|
78
|
-
SpatialSearch.new(datasource)
|
79
|
-
end
|
80
|
-
end
|
81
71
|
end
|
82
72
|
end
|
@@ -14,23 +14,21 @@ module LondonBikeHireCli
|
|
14
14
|
|
15
15
|
private
|
16
16
|
|
17
|
-
|
17
|
+
attr_reader :feed_time
|
18
18
|
|
19
19
|
def parse_xml(xml_doc)
|
20
|
-
stations =
|
20
|
+
stations = []
|
21
|
+
parse_feed_time(xml_doc)
|
21
22
|
|
22
23
|
xml_doc.root.elements.each do |node|
|
23
|
-
|
24
|
-
station_id = station[:id].to_i
|
25
|
-
stations[station_id] = Station.new(station) unless station_id == 0
|
24
|
+
stations << parse_station(node)
|
26
25
|
end
|
27
|
-
stations[:last_update] = parse_feed_time(xml_doc)
|
28
26
|
|
29
|
-
stations
|
27
|
+
QueryResponse.new(last_update: feed_time, results: stations)
|
30
28
|
end
|
31
29
|
|
32
30
|
def parse_feed_time(xml_doc)
|
33
|
-
Time.at xml_doc.root['lastUpdate'].to_i / 1000
|
31
|
+
@feed_time ||= Time.at xml_doc.root['lastUpdate'].to_i / 1000
|
34
32
|
end
|
35
33
|
|
36
34
|
# <stations lastUpdate="1407153481506" version="2.0">
|
@@ -64,13 +62,14 @@ module LondonBikeHireCli
|
|
64
62
|
station[:lat] = node.text.to_f if node.node_name.eql? 'lat'
|
65
63
|
station[:long] = node.text.to_f if node.node_name.eql? 'long'
|
66
64
|
station[:temporary] = parse_bool(node.text) if node.node_name.eql? 'temporary'
|
65
|
+
station[:updated_at] = feed_time
|
67
66
|
end
|
68
67
|
|
69
|
-
station
|
68
|
+
Station.new(station)
|
70
69
|
end
|
71
70
|
|
72
71
|
def parse_bool(value)
|
73
|
-
value == 'true'
|
72
|
+
!!(value == 'true')
|
74
73
|
end
|
75
74
|
end
|
76
75
|
end
|
@@ -1,22 +1,12 @@
|
|
1
|
-
require '
|
1
|
+
require 'delegate'
|
2
2
|
|
3
3
|
module LondonBikeHireCli
|
4
|
-
class QueryResponse
|
5
|
-
|
6
|
-
|
7
|
-
def_delegators :results, :[], :each, :map, :first, :size
|
4
|
+
class QueryResponse < SimpleDelegator
|
5
|
+
attr_reader :last_update
|
8
6
|
|
9
7
|
def initialize(last_update: Time.now, results: [])
|
8
|
+
super(results)
|
10
9
|
@last_update = last_update
|
11
|
-
@results = results
|
12
|
-
end
|
13
|
-
|
14
|
-
def time_of_feed
|
15
|
-
last_update.strftime('%Y-%m-%d %H:%M:%S')
|
16
10
|
end
|
17
|
-
|
18
|
-
private
|
19
|
-
|
20
|
-
attr_reader :results, :last_update
|
21
11
|
end
|
22
12
|
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module LondonBikeHireCli
|
2
|
+
module Repository
|
3
|
+
# Adapter that stores records in memory.
|
4
|
+
# Supports multiple record types
|
5
|
+
#
|
6
|
+
class InMemoryStoreAdapter
|
7
|
+
def initialize(items = [])
|
8
|
+
@counter = 0
|
9
|
+
@map = {}
|
10
|
+
add_items(items)
|
11
|
+
end
|
12
|
+
|
13
|
+
def create(record)
|
14
|
+
@counter = @counter + 1
|
15
|
+
record.id ||= @counter
|
16
|
+
map_for(record)[record.id] = record
|
17
|
+
end
|
18
|
+
|
19
|
+
def update(record)
|
20
|
+
map_for(record)[record.id] = record
|
21
|
+
end
|
22
|
+
|
23
|
+
def delete(record)
|
24
|
+
map_for(record).delete record.id
|
25
|
+
end
|
26
|
+
|
27
|
+
def find(klass, id)
|
28
|
+
map_for_class(klass).fetch id
|
29
|
+
end
|
30
|
+
|
31
|
+
def find_by_ids(klass, *ids)
|
32
|
+
map_for_class(klass).values_at(*ids)
|
33
|
+
end
|
34
|
+
|
35
|
+
def all(klass)
|
36
|
+
map_for_class(klass).values
|
37
|
+
end
|
38
|
+
|
39
|
+
def query(klass, selector)
|
40
|
+
# TODO fix introspection
|
41
|
+
send "query_#{underscore(selector.class.name)}", klass, selector
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def add_items(items)
|
47
|
+
items.each { |item| create(item) }
|
48
|
+
end
|
49
|
+
|
50
|
+
def map_for_class(klass)
|
51
|
+
@map[klass.to_s.to_sym] ||= {}
|
52
|
+
end
|
53
|
+
|
54
|
+
def map_for(record)
|
55
|
+
map_for_class(record.class)
|
56
|
+
end
|
57
|
+
|
58
|
+
# TODO fix
|
59
|
+
def underscore(camel_cased_word)
|
60
|
+
demod_word = camel_cased_word.to_s.split('::').last
|
61
|
+
demod_word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
|
62
|
+
demod_word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
|
63
|
+
demod_word.tr!('-', '_')
|
64
|
+
demod_word.downcase!
|
65
|
+
demod_word
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module LondonBikeHireCli
|
2
|
+
module Repository
|
3
|
+
# Generic Repository delegating to adapter
|
4
|
+
#
|
5
|
+
module Repo
|
6
|
+
class << self
|
7
|
+
attr_reader :adapter
|
8
|
+
|
9
|
+
def register(adapter)
|
10
|
+
@adapter = adapter
|
11
|
+
end
|
12
|
+
|
13
|
+
def find(klass, id)
|
14
|
+
adapter.find klass, id
|
15
|
+
end
|
16
|
+
|
17
|
+
def all(klass)
|
18
|
+
adapter.all klass
|
19
|
+
end
|
20
|
+
|
21
|
+
def create(model)
|
22
|
+
adapter.create(model)
|
23
|
+
end
|
24
|
+
|
25
|
+
def update(model)
|
26
|
+
adapter.update(model)
|
27
|
+
end
|
28
|
+
|
29
|
+
def delete(model)
|
30
|
+
adapter.delete model
|
31
|
+
end
|
32
|
+
|
33
|
+
def query(klass, selector)
|
34
|
+
adapter.query(klass, selector)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require "kdtree"
|
2
|
+
|
3
|
+
module LondonBikeHireCli
|
4
|
+
module Repository
|
5
|
+
class SpatialSearchAdapter
|
6
|
+
def initialize(stations = [])
|
7
|
+
init_spatial_store(stations)
|
8
|
+
end
|
9
|
+
|
10
|
+
# Public: Return the IDs of the nearest n stations
|
11
|
+
# lat, long - latitude, longitude of point to search
|
12
|
+
# limit - The maximum number of results
|
13
|
+
#
|
14
|
+
def nearest(lat, long, limit)
|
15
|
+
spatial_store.nearestk(lat, long, limit)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
attr_reader :spatial_store
|
21
|
+
|
22
|
+
def init_spatial_store(stations)
|
23
|
+
stations_triples = stations.map { |station| [station.lat, station.long, station.id] }
|
24
|
+
@spatial_store = Kdtree.new(stations_triples)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module LondonBikeHireCli
|
2
|
+
module Repository
|
3
|
+
# Specialisation for Bike Stations; delegates all to Repository
|
4
|
+
#
|
5
|
+
class StationRepo
|
6
|
+
def save(record)
|
7
|
+
Repo.save(record)
|
8
|
+
end
|
9
|
+
|
10
|
+
def all
|
11
|
+
Repo.all object_class
|
12
|
+
end
|
13
|
+
|
14
|
+
def find(id)
|
15
|
+
Repo.find object_class, id
|
16
|
+
end
|
17
|
+
|
18
|
+
def delete(record)
|
19
|
+
Repo.delete record
|
20
|
+
end
|
21
|
+
|
22
|
+
def query(selector)
|
23
|
+
Repo.query object_class, selector
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def object_class
|
29
|
+
# TODO fix reflection
|
30
|
+
# @object_class ||= self.to_s.match(/^(.+)Repo/)[1].constantize
|
31
|
+
Station
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module LondonBikeHireCli
|
2
|
+
module Repository
|
3
|
+
# Specialisation for Bike Stations; implements specific queries
|
4
|
+
#
|
5
|
+
class StationStore < InMemoryStoreAdapter
|
6
|
+
def query_stations_by_name(klass, query)
|
7
|
+
all(klass).select { |station| station.name.downcase.include?(query.normalized_query) }
|
8
|
+
end
|
9
|
+
|
10
|
+
def query_stations_near(klass, query)
|
11
|
+
matched_ids = spatial_search_adapter(klass).nearest(query.lat, query.long, query.limit)
|
12
|
+
find_by_ids(klass, *matched_ids)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def spatial_search_adapter(klass)
|
18
|
+
@spatial_search_adapter ||= SpatialSearchAdapter.new all(klass)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<%= colorize('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>', :yellow) %>
|
2
|
+
<% context.each do |station| %>
|
3
|
+
----- <%= colorize('Bike station', :green) %>
|
4
|
+
<% station.each_pair.each do |attr_name, attr_value| %>
|
5
|
+
<% next if attr_name == :updated_at %>
|
6
|
+
<%= attr_name.capitalize %>: <%= colorize(attr_value, :cyan) %>
|
7
|
+
<% end %>
|
8
|
+
Link to map: <%= colorize(station.map_link, :pink) %>
|
9
|
+
Updated at: <%= colorize(station.display_updated_at, :red) %>
|
10
|
+
<% end %>
|
11
|
+
<%= colorize('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>', :yellow) %>
|