tower_bridge_lifts 1.0.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.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +7 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +5 -0
  5. data/Gemfile +2 -0
  6. data/README.md +107 -0
  7. data/Rakefile +7 -0
  8. data/bin/tblifts +5 -0
  9. data/lib/tower_bridge_lifts.rb +13 -0
  10. data/lib/tower_bridge_lifts/application.rb +80 -0
  11. data/lib/tower_bridge_lifts/base.rb +118 -0
  12. data/lib/tower_bridge_lifts/helpers.rb +24 -0
  13. data/lib/tower_bridge_lifts/lift.rb +49 -0
  14. data/lib/tower_bridge_lifts/server.rb +36 -0
  15. data/lib/tower_bridge_lifts/templates/lifts.erb +21 -0
  16. data/lib/tower_bridge_lifts/templates/lifts_compact.erb +17 -0
  17. data/lib/tower_bridge_lifts/templates/lifts_group.erb +23 -0
  18. data/lib/tower_bridge_lifts/templates/lifts_group_compact.erb +20 -0
  19. data/lib/tower_bridge_lifts/templates/status.erb +9 -0
  20. data/lib/tower_bridge_lifts/version.rb +4 -0
  21. data/lib/tower_bridge_lifts/views.rb +114 -0
  22. data/reload +4 -0
  23. data/spec/spec_helper.rb +3 -0
  24. data/spec/tower_bridge_lifts_spec.rb +32 -0
  25. data/tower_bridge_lifts.gemspec +31 -0
  26. data/web/apidoc.html +87 -0
  27. data/web/apidoc.yaml +163 -0
  28. data/web/css/print.css +1367 -0
  29. data/web/css/reset.css +125 -0
  30. data/web/css/screen.css +1497 -0
  31. data/web/css/style.css +249 -0
  32. data/web/css/tblifts.css +4 -0
  33. data/web/css/typography.css +14 -0
  34. data/web/fonts/DroidSans-Bold.ttf +0 -0
  35. data/web/fonts/DroidSans.ttf +0 -0
  36. data/web/images/collapse.gif +0 -0
  37. data/web/images/expand.gif +0 -0
  38. data/web/images/explorer_icons.png +0 -0
  39. data/web/images/logo_small.png +0 -0
  40. data/web/images/throbber.gif +0 -0
  41. data/web/images/wordnik_api.png +0 -0
  42. data/web/index.rhtml +42 -0
  43. data/web/lib/backbone-min.js +15 -0
  44. data/web/lib/es5-shim.js +2065 -0
  45. data/web/lib/handlebars-4.0.5.js +4608 -0
  46. data/web/lib/highlight.9.1.0.pack.js +2 -0
  47. data/web/lib/highlight.9.1.0.pack_extended.js +34 -0
  48. data/web/lib/jquery-1.8.0.min.js +2 -0
  49. data/web/lib/jquery.ba-bbq.min.js +18 -0
  50. data/web/lib/jquery.slideto.min.js +1 -0
  51. data/web/lib/jquery.wiggle.min.js +8 -0
  52. data/web/lib/js-yaml.min.js +3 -0
  53. data/web/lib/jsoneditor.min.js +11 -0
  54. data/web/lib/lodash.min.js +102 -0
  55. data/web/lib/marked.js +1272 -0
  56. data/web/lib/object-assign-pollyfill.js +23 -0
  57. data/web/lib/sanitize-html.min.js +6 -0
  58. data/web/lib/swagger-oauth.js +347 -0
  59. data/web/o2c.html +20 -0
  60. data/web/swagger-ui.js +25067 -0
  61. metadata +191 -0
@@ -0,0 +1,36 @@
1
+ require 'sinatra/base'
2
+
3
+ module TowerBridgeLifts
4
+ class Server < Sinatra::Base
5
+
6
+ helpers Helpers
7
+ enable :logging
8
+ set :public_folder, 'web'
9
+
10
+ tblifts = Base.new
11
+ view = View.new
12
+
13
+ get '/' do
14
+ tblifts.fetch if tblifts.expired?
15
+ @tblifts = tblifts
16
+ @view = view
17
+ erb :index, views: settings.public_folder
18
+ end
19
+
20
+
21
+ get '/api/v1/:command' do
22
+ content_type :json
23
+ command = params['command']
24
+ options = Rack::Utils.parse_query(request.query_string)
25
+ options = Hash[ options.map{|k,v| [k.to_sym, (Integer(v) rescue v)]} ]
26
+
27
+ if Base::ALLOWED_COMMANDS.include?(command)
28
+ tblifts.fetch if tblifts.expired?
29
+ view.render(tblifts, command, options, :json)
30
+ else
31
+ error 404, 'oops'
32
+ end
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,21 @@
1
+ <table class=tblifts>
2
+ <thead>
3
+ <tr>
4
+ <th>Date</th>
5
+ <th>Day</th>
6
+ <th>Time</th>
7
+ <th>Dir</th>
8
+ <th>Vessel</th>
9
+ </tr>
10
+ </thead>
11
+ <tbody>
12
+ <% result.each do |lift| %>
13
+ <tr>
14
+ <td><%= lift.date %>
15
+ <td><%= lift.weekday %>
16
+ <td><%= lift.time %>
17
+ <td><%= lift.dir %>
18
+ <td><%= lift.vessel %>
19
+ </tr>
20
+ <% end %>
21
+ </table>
@@ -0,0 +1,17 @@
1
+ <table class=tblifts>
2
+ <thead>
3
+ <tr>
4
+ <th>Date</th>
5
+ <th>Day</th>
6
+ <th>Time</th>
7
+ </tr>
8
+ </thead>
9
+ <tbody>
10
+ <% result.each do |lift| %>
11
+ <tr>
12
+ <td><%= lift.date %>
13
+ <td><%= lift.weekday %>
14
+ <td><%= lift.time %>
15
+ </tr>
16
+ <% end %>
17
+ </table>
@@ -0,0 +1,23 @@
1
+ <table class=tblifts>
2
+ <thead>
3
+ <tr>
4
+ <th>Time</th>
5
+ <th>Dir</th>
6
+ <th>Vessel</th>
7
+ </tr>
8
+ </thead>
9
+ <tbody>
10
+ <% result.each do |date, lifts| %>
11
+ <tr>
12
+ <td colspan=3><%= date %></td>
13
+ </tr>
14
+ <% lifts.each do |lift|%>
15
+ <tr>
16
+ <td><%= lift.time %></td>
17
+ <td><%= lift.dir %></td>
18
+ <td><%= lift.vessel %></td>
19
+ </tr>
20
+ <% end %>
21
+ <% end %>
22
+ </tbody>
23
+ </table>
@@ -0,0 +1,20 @@
1
+ <table class=tblifts>
2
+ <thead>
3
+ <tr>
4
+ <th>Date</th>
5
+ <th>Day</th>
6
+ <th><%= result.values.map(&:count).reduce(:+) %></th>
7
+ <th>Times</th>
8
+ </tr>
9
+ </thead>
10
+ <tbody>
11
+ <% result.each do |date, lifts| %>
12
+ <tr>
13
+ <td><%= lifts.first.date %></td>
14
+ <td><%= lifts.first.weekday %></td>
15
+ <td><%= lifts.count %></td>
16
+ <td><%= lifts.map(&:time).join('&nbsp'*3) %>
17
+ </tr>
18
+ <% end %>
19
+ </tbody>
20
+ </table>
@@ -0,0 +1,9 @@
1
+ <table class=tblifts>
2
+ <tbody>
3
+ <% result.each do |field,value| %>
4
+ <tr>
5
+ <td><%= field %>
6
+ <td><%= value %>
7
+ </tr>
8
+ <% end %>
9
+ </table>
@@ -0,0 +1,4 @@
1
+
2
+ module TowerBridgeLifts
3
+ VERSION = "1.0.0"
4
+ end
@@ -0,0 +1,114 @@
1
+
2
+ require 'json'
3
+ require 'erb'
4
+
5
+ module TowerBridgeLifts
6
+
7
+ class View ## Keeping them here instead of separate files
8
+
9
+ def render(object, command, options, format)
10
+ result = object.error || (options.empty? ? object.send(command) : object.send(command, options))
11
+ view_name = case
12
+ when object.error then 'error'
13
+ when result.nil? || result == [] then 'empty'
14
+ when command == 'lifts' then ( [:lifts] + ([:group, :compact] & options.keys) ).join('_')
15
+ when true then command
16
+ end
17
+ self.send(view_name, result, format)
18
+ end
19
+
20
+ private
21
+
22
+
23
+ def error(result, format)
24
+ result.to_s
25
+ end
26
+
27
+ def empty(result, format)
28
+ msg = 'no lifts scheduled'
29
+ case format
30
+ when :txt then "#{msg}\n"
31
+ when :json then [msg].to_json
32
+ when :html then msg
33
+ end
34
+ end
35
+
36
+ def status(result, format)
37
+ case format
38
+ when :txt then result.map{|k,v| "#{k}: #{v}" }.join("\n")
39
+ when :json then Hash[result.map{|k,v| [k, v.respond_to?(:to_h) ? v.to_h : v] }].to_json
40
+ when :html then template(result, 'status.erb')
41
+ end
42
+ end
43
+
44
+ def time(result, format)
45
+ case format
46
+ when :txt then result
47
+ when :json then [result].to_json
48
+ when :html then result
49
+ end
50
+ end
51
+
52
+ def next_lift(result, format)
53
+ case format
54
+ when :txt then result
55
+ when :json then result.to_h.to_json
56
+ when :html then result
57
+ end
58
+ end
59
+
60
+ def bascules(result, format)
61
+ case format
62
+ when :txt then result
63
+ when :json then [result].to_json
64
+ when :html then result
65
+ end
66
+ end
67
+
68
+ def traffic(result, format)
69
+ case format
70
+ when :txt then result
71
+ when :json then [result].to_json
72
+ when :html then result
73
+ end
74
+ end
75
+
76
+ def lifts(result, format)
77
+ case format
78
+ when :txt then result.map{ |lift| lift.to_s }.join("\n")
79
+ when :json then result.map{ |lift| lift.to_h }.to_json
80
+ when :html then template(result, 'lifts.erb')
81
+ end
82
+ end
83
+
84
+ def lifts_compact(result, format)
85
+ case format
86
+ when :txt then result.map{ |lift| lift.date_weekday_time }.join("\n")
87
+ when :json then result.map{ |lift| lift.to_h }.to_json
88
+ when :html then template(result, 'lifts_compact.erb')
89
+ end
90
+ end
91
+
92
+
93
+ def lifts_group(result, format)
94
+ case format
95
+ when :txt then result.map{ |k,v| [k] + (v.map{|lift| [[lift.time, lift.dir, lift.vessel].join(' ')] }) + [''] }.join("\n")
96
+ when :json then Hash[ result.map{|date, lifts| [date, lifts.map(&:to_h)]} ].to_json
97
+ when :html then template(result, 'lifts_group.erb')
98
+ end
99
+ end
100
+
101
+ def lifts_group_compact(result, format)
102
+ case format
103
+ when :html then template(result, 'lifts_group_compact.erb')
104
+ else
105
+ result.map{ |k,v| ([k] + ["[#{v.count}]"] + (v.map{|lift| lift.time})).join(' ') }.join("\n")
106
+ end
107
+ end
108
+
109
+ def template(result, name)
110
+ ERB.new(File.read(File.join(__dir__, 'templates', name))).result(binding)
111
+ end
112
+
113
+ end
114
+ end
data/reload ADDED
@@ -0,0 +1,4 @@
1
+ #/bin/sh
2
+ gem uninstall tower_bridge_lifts
3
+ gem build tower_bridge_lifts.gemspec
4
+ gem install tower_bridge_lifts-1.0.0.gem
@@ -0,0 +1,3 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+ ENV['RACK_ENV'] = 'test'
3
+ require 'tower_bridge_lifts'
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ describe TowerBridgeLifts do
4
+ it 'gem has a version number' do
5
+ expect(TowerBridgeLifts::VERSION).not_to be nil
6
+ end
7
+ end
8
+
9
+
10
+ describe TowerBridgeLifts::Base do
11
+ subject(:tbl) { described_class.new }
12
+
13
+ it 'instantiates' do
14
+ expect(tbl).to be_instance_of(described_class)
15
+ expect(tbl.updated).not_to be_nil
16
+ end
17
+
18
+ it 'has lifts if no errors' do
19
+ if tbl.error.nil?
20
+ expect(tbl.lifts.map).to all(be_a(TowerBridgeLifts::Lift))
21
+ end
22
+ end
23
+
24
+ it 'has one lift' do
25
+ lift = TowerBridgeLifts::Lift.new(timestamp: Time.now, vessel: "Vessel Name", direction: :up_river)
26
+ tbl.lifts = [lift]
27
+ expect(tbl.lifts.count).to eq(1)
28
+ end
29
+
30
+ end
31
+
32
+
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'tower_bridge_lifts/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "tower_bridge_lifts"
8
+ spec.version = TowerBridgeLifts::VERSION
9
+ spec.authors = ["Andre Parmeggiani"]
10
+ spec.email = ["aaparmeggiani@gmail.com"]
11
+
12
+ spec.summary = "Tower Bridge lift times"
13
+ spec.description = "Provides lift times information, parsed from TowerBridge.org.uk"
14
+ spec.homepage = "https://github.com/aaparmeggiani/tower_bridge_lifts"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0")
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.12"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+ spec.add_development_dependency "rspec", "~> 3.0"
25
+
26
+ spec.add_dependency 'nokogiri', '~> 1.6'
27
+ spec.add_dependency 'sinatra', '~> 1.4'
28
+ spec.add_dependency 'tzinfo', '~> 1.2'
29
+
30
+ end
31
+
data/web/apidoc.html ADDED
@@ -0,0 +1,87 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>Tower Bridge Lifts - API</title>
6
+ <link href='css/typography.css' media='screen' rel='stylesheet' type='text/css'/>
7
+ <link href='css/reset.css' media='screen' rel='stylesheet' type='text/css'/>
8
+ <link href='css/screen.css' media='screen' rel='stylesheet' type='text/css'/>
9
+ <link href='css/reset.css' media='print' rel='stylesheet' type='text/css'/>
10
+ <link href='css/print.css' media='print' rel='stylesheet' type='text/css'/>
11
+
12
+ <script src='lib/object-assign-pollyfill.js' type='text/javascript'></script>
13
+ <script src='lib/jquery-1.8.0.min.js' type='text/javascript'></script>
14
+ <script src='lib/jquery.slideto.min.js' type='text/javascript'></script>
15
+ <script src='lib/jquery.wiggle.min.js' type='text/javascript'></script>
16
+ <script src='lib/jquery.ba-bbq.min.js' type='text/javascript'></script>
17
+ <script src='lib/handlebars-4.0.5.js' type='text/javascript'></script>
18
+ <script src='lib/lodash.min.js' type='text/javascript'></script>
19
+ <script src='lib/backbone-min.js' type='text/javascript'></script>
20
+ <script src='swagger-ui.js' type='text/javascript'></script>
21
+ <script src='lib/highlight.9.1.0.pack.js' type='text/javascript'></script>
22
+ <script src='lib/highlight.9.1.0.pack_extended.js' type='text/javascript'></script>
23
+ <script src='lib/jsoneditor.min.js' type='text/javascript'></script>
24
+ <script src='lib/marked.js' type='text/javascript'></script>
25
+ <script src='lib/swagger-oauth.js' type='text/javascript'></script>
26
+
27
+
28
+ <script type="text/javascript">
29
+ $(function () {
30
+
31
+ hljs.configure({
32
+ highlightSizeThreshold: 5000
33
+ });
34
+
35
+
36
+ window.swaggerUi = new SwaggerUi({
37
+ url: "/apidoc.yaml",
38
+ dom_id: "swagger-ui-container",
39
+ supportedSubmitMethods: ['get', 'post', 'put', 'delete', 'patch'],
40
+ onComplete: function(swaggerApi, swaggerUi){
41
+ if(typeof initOAuth == "function") {
42
+ initOAuth({
43
+ clientId: "your-client-id",
44
+ clientSecret: "your-client-secret-if-required",
45
+ realm: "your-realms",
46
+ appName: "your-app-name",
47
+ scopeSeparator: " ",
48
+ additionalQueryStringParams: {}
49
+ });
50
+ }
51
+
52
+ },
53
+ onFailure: function(data) {
54
+ log("Unable to Load SwaggerUI");
55
+ },
56
+ docExpansion: "list",
57
+ jsonEditor: false,
58
+ defaultModelRendering: 'schema',
59
+ showRequestHeaders: false
60
+ });
61
+
62
+ window.swaggerUi.load();
63
+
64
+ function log() {
65
+ if ('console' in window) {
66
+ console.log.apply(console, arguments);
67
+ }
68
+ }
69
+ });
70
+ </script>
71
+ </head>
72
+
73
+ <body class="swagger-section">
74
+
75
+ <div class="swagger-ui-wrap">
76
+ <div id='header'>
77
+ <div id="api_selector">
78
+ <a href="/">home</a> |
79
+ <a href="/apidoc.html">api</a> |
80
+ <a href="https://github.com/aaparmeggiani/tower_bridge_lifts">github</a>
81
+ </div>
82
+ </div>
83
+ <div id="swagger-ui-container"></div>
84
+ </div>
85
+
86
+ </body>
87
+ </html>
data/web/apidoc.yaml ADDED
@@ -0,0 +1,163 @@
1
+ ---
2
+ swagger: '2.0'
3
+ info:
4
+ version: 1.0.0
5
+ title: Tower Bridge Lifts - API
6
+ description: |
7
+
8
+ Provides lift times information, parsed from [towerbridge.org.uk](http://towerbridge.org.uk/lift-times)
9
+ For more info, please visit the project's [github](https://github.com/aaparmeggiani/tower_bridge_lifts) page.
10
+
11
+ # host:tower-bridge-lifts.herokuapp.com
12
+ basePath: /api/v1
13
+ schemes:
14
+ - http
15
+ produces:
16
+ - application/json
17
+
18
+ paths:
19
+
20
+ /status:
21
+ get:
22
+ summary: bridge status
23
+ description: Returns the status of the bridge.
24
+ responses:
25
+ 200:
26
+ description: successful operation
27
+ schema:
28
+ $ref: '#/definitions/Status'
29
+
30
+ /time:
31
+ get:
32
+ summary: current time
33
+ description: Current local time in London
34
+ responses:
35
+ 200:
36
+ description: successful operation
37
+ schema:
38
+ type: string
39
+
40
+ /lifts:
41
+ get:
42
+ summary: all scheduled lifts
43
+ description: Return all scheduled lifts filtered by the parameters
44
+ parameters:
45
+ - name: date
46
+ in: query
47
+ description: DD-MM-YY | today | tomorrow
48
+ type: string
49
+ format: date
50
+ - name: count
51
+ in: query
52
+ description: Number of lifts to return.
53
+ type: integer
54
+ minimum: 0
55
+ maximum: 1000
56
+ - name: group
57
+ in: query
58
+ description: unique times grouped by date
59
+ type: boolean
60
+ - name: compact
61
+ in: query
62
+ description: unique times grouped by date
63
+ type: boolean
64
+
65
+ responses:
66
+ 200:
67
+ description: successful operation
68
+ schema:
69
+ title: Lifts
70
+ type: array
71
+ items:
72
+ $ref: '#/definitions/Lift'
73
+
74
+ /next_lift:
75
+ get:
76
+ summary: the next lift
77
+ description: Returns the next lift only.
78
+ responses:
79
+ 200:
80
+ description: successful operation
81
+ schema:
82
+ $ref: '#/definitions/Lift'
83
+
84
+ /bascules:
85
+ get:
86
+ summary: bascules position
87
+ description: Current bascules position [ down | moving_up | up | moving_down ]
88
+ responses:
89
+ 200:
90
+ description: successful operation
91
+ schema:
92
+ type: string
93
+ enum:
94
+ - down
95
+ - moving_up
96
+ - up
97
+ - moving_down
98
+
99
+
100
+
101
+ /traffic:
102
+ get:
103
+ summary: traffic on the bridge
104
+ description: Current road/foot traffic on the bridge [ allowed | blocked ]
105
+ responses:
106
+ 200:
107
+ description: successful operation
108
+ schema:
109
+ type: string
110
+ enum:
111
+ - allowed
112
+ - blocked
113
+
114
+
115
+
116
+ definitions:
117
+ Lift:
118
+ type: object
119
+ properties:
120
+ timestamp:
121
+ type: string
122
+ example: 2016-01-01T01:02:03
123
+ vessel:
124
+ type: string
125
+ example: Vessel Name
126
+ direction:
127
+ type: string
128
+ enum:
129
+ - up_river
130
+ - down_river
131
+ Status:
132
+ type: object
133
+ properties:
134
+ time:
135
+ type: string
136
+ description: local time
137
+ example: 2016-01-01 01:02:03
138
+ lifts_count:
139
+ type: number
140
+ description: Number of scheduled lifts
141
+ example: 12
142
+ next_lift:
143
+ type: object
144
+ description: The next lift details
145
+ $ref: '#/definitions/Lift'
146
+ bascules:
147
+ type: string
148
+ description: Current bascules position
149
+ enum:
150
+ - down
151
+ - moving_up
152
+ - up
153
+ - moving_down
154
+ traffic:
155
+ type: string
156
+ description: Current traffic on the bridge
157
+ enum:
158
+ - allowed
159
+ - blocked
160
+ updated:
161
+ type: string
162
+ description: When this info was last updated
163
+ example: 2016-01-01 01:02:03