octranspo_fetch 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (2) hide show
  1. data/lib/octranspo_fetch.rb +84 -39
  2. metadata +2 -2
@@ -15,13 +15,15 @@ class OCTranspo
15
15
  def initialize(options)
16
16
  @app_id = options[:application_id]
17
17
  @app_key = options[:application_key]
18
- @trips_cache = LruRedux::Cache.new(TRIPS_CACHE_SIZE)
18
+ @next_trips_cache = LruRedux::Cache.new(NEXT_TRIPS_CACHE_SIZE)
19
19
  @route_summary_cache = LruRedux::Cache.new(ROUTE_CACHE_SIZE)
20
20
  @api_calls = 0
21
+ @cache_hits = 0
22
+ @cache_misses = 0
21
23
  end
22
24
 
23
25
  def clear_cache()
24
- @trips_cache.clear()
26
+ @next_trips_cache.clear()
25
27
  @route_summary_cache.clear()
26
28
  end
27
29
 
@@ -30,6 +32,10 @@ class OCTranspo
30
32
  return @api_calls
31
33
  end
32
34
 
35
+ def cache_stats()
36
+ return {hits: @cache_hits, misses: @cache_misses}
37
+ end
38
+
33
39
  # Get a list of routes for a specific stop.
34
40
  #
35
41
  # Returns a {stop, stop_description, routes: [{route, direction_id, direction, heading}]} object.
@@ -37,10 +43,19 @@ class OCTranspo
37
43
  #
38
44
  # Arguments:
39
45
  # stop: (String) The stop number.
46
+ # options[:max_cache_time]: (Integer) Maximum cache age, in seconds. If cached data is
47
+ # available and is newer than this, then the cached value will be returned. Defaults to
48
+ # one day.
40
49
  #
41
- def get_route_summary_for_stop(stop)
50
+ def get_route_summary_for_stop(stop, options={})
51
+ max_cache_time = (options[:max_cache_time] or 60*60*24)
42
52
  cached_result = @route_summary_cache[stop]
43
- if !cached_result.nil? then return cached_result end
53
+ if !cached_result.nil? and ((cached_result[:time] + max_cache_time) > Time.now.to_i)
54
+ @cache_hits += 1
55
+ return cached_result[:route_summary]
56
+ end
57
+
58
+ @cache_misses += 1
44
59
 
45
60
  xresult = fetch "GetRouteSummaryForStop", "stopNo=#{stop}"
46
61
 
@@ -63,7 +78,10 @@ class OCTranspo
63
78
  raise "No routes found"
64
79
  end
65
80
 
66
- @route_summary_cache[stop] = result
81
+ @route_summary_cache[stop] = {
82
+ route_summary: result,
83
+ time: Time.now.to_i
84
+ }
67
85
 
68
86
  return result
69
87
  end
@@ -74,16 +92,33 @@ class OCTranspo
74
92
  # Arguments:
75
93
  # stop: (String) The stop number.
76
94
  # route_no: (String) The route number.
95
+ # options[:max_cache_time]: (Integer) Maximum cache age, in seconds. If cached data is
96
+ # available and is newer than this, then the cached value will be returned. Defaults
97
+ # to five minutes.
77
98
  #
78
- def get_next_trips_for_stop(stop, route_no)
99
+ def get_next_trips_for_stop(stop, route_no, options={})
100
+ max_cache_time = (options[:max_cache_time] or 60*5)
101
+
102
+ # Return result from cache, if available
103
+ cache_key = "#{stop}-#{route_no}"
104
+ cached_result = @next_trips_cache[cache_key]
105
+ if !cached_result.nil? and ((cached_result[:time] + max_cache_time) > Time.now.to_i)
106
+ @cache_hits += 1
107
+ return adjust_cached_trip_times(cached_result[:next_trips])
108
+ end
109
+ @cache_misses += 1
110
+
79
111
  xresult = fetch "GetNextTripsForStop", "stopNo=#{stop}&routeNo=#{route_no}"
80
112
 
81
113
  result = {
82
114
  stop: get_value(xresult, "t:StopNo"),
83
115
  stop_description: get_value(xresult, "t:StopLabel"),
116
+ time: Time.now,
84
117
  routes: []
85
118
  }
86
119
 
120
+ found_data = false
121
+
87
122
  xresult.xpath('t:Route/t:RouteDirection', OCT_NS).each do |route|
88
123
  get_error(route, "Error for route: #{route_no}")
89
124
 
@@ -109,41 +144,27 @@ class OCTranspo
109
144
  })
110
145
  end
111
146
 
112
- cache_key = "#{stop}-#{route_obj[:route]}-#{route_obj[:direction]}"
113
- if route_obj[:trips].length == 0
114
- # Sometimes OC Transpo doesn't return any data. When this happens, fetch data from the cache.
115
- trips = @trips_cache[cache_key]
116
- if !trips.nil?
117
- time_delta = Time.now.to_i - trips[:time]
118
- route_obj[:request_processing_time] += time_delta
119
- route_obj[:trips] = deep_copy(trips[:trips])
120
- route_obj[:cached] = true
121
- route_obj[:trips].each do |trip|
122
- trip[:adjusted_schedule_time] -= (time_delta.to_f / 60).round
123
- if trip[:adjustment_age] > 0
124
- trip[:adjustment_age] += time_delta.to_f / 60
125
- end
126
- end
127
-
128
- # Filter out results with negative arrival times, since they've probably
129
- # already gone by.
130
- route_obj[:trips].select! { |trip| trip[:adjusted_schedule_time] >= 0 }
131
-
132
- else
133
- # No data in the cache... Hrm...
134
- end
135
-
136
- else
137
- # Cache the trips for later
138
- @trips_cache[cache_key] = {
139
- time: Time.now.to_i,
140
- trips: route_obj[:trips]
141
- }
147
+ if route_obj[:trips].length != 0
148
+ # Assume that if any trips are filled in, then all the trips will be filled in?
149
+ # Is this a safe assumption?
150
+ found_data = true
142
151
  end
143
152
 
144
153
  result[:routes].push route_obj
145
154
  end
146
155
 
156
+ # Sometimes OC Transpo doesn't return any data for a route, even though it should. When
157
+ # this happens, if we have cached data, we use that, even if it's slightly stale.
158
+ if !found_data and !cached_result.nil?
159
+ # Use the cached data, even if it's stale
160
+ result = adjust_cached_trip_times(cached_result[:next_trips])
161
+ else
162
+ @next_trips_cache[cache_key] = {
163
+ next_trips: result,
164
+ time: Time.now.to_i
165
+ }
166
+ end
167
+
147
168
 
148
169
  return result
149
170
  end
@@ -199,14 +220,13 @@ class OCTranspo
199
220
 
200
221
  BASE_URL = "https://api.octranspo1.com/v1.1"
201
222
  OCT_NS = {'oct' => 'http://octranspo.com', 't' => 'http://tempuri.org/'}
202
- TRIPS_CACHE_SIZE = 100
223
+ NEXT_TRIPS_CACHE_SIZE = 100
203
224
  ROUTE_CACHE_SIZE = 100
204
225
 
205
226
  # Fetch and parse some data from the OC-Transpo API. Returns a nokogiri object for
206
227
  # the Result within the XML document.
207
228
  def fetch(resource, params)
208
229
  @api_calls = (@api_calls + 1)
209
-
210
230
  response = RestClient.post("#{BASE_URL}/#{resource}",
211
231
  "appID=#{@app_id}&apiKey=#{@app_key}&#{params}")
212
232
 
@@ -259,5 +279,30 @@ class OCTranspo
259
279
  def deep_copy(o)
260
280
  Marshal.load(Marshal.dump(o))
261
281
  end
262
- end
263
282
 
283
+ # When returning cached trips, we need to adjust the `:adjustment_age` and
284
+ # `:adjusted_schedule_time` of each entry to reflect how long the object has been
285
+ # sitting in the cache.
286
+ def adjust_cached_trip_times(cached_routes)
287
+
288
+ cached_routes = deep_copy cached_routes
289
+ cached_routes[:cached] = true
290
+
291
+ time_delta = Time.now.to_i - cached_routes[:time].to_i
292
+ cached_routes[:routes].each do |route_obj|
293
+ route_obj[:trips].each do |trip|
294
+ trip[:adjusted_schedule_time] -= (time_delta.to_f / 60).round
295
+ if trip[:adjustment_age] > 0
296
+ trip[:adjustment_age] += time_delta.to_f / 60
297
+ end
298
+ end
299
+
300
+ # Filter out results with negative arrival times, since they've probably
301
+ # already gone by.
302
+ route_obj[:trips].select! { |trip| trip[:adjusted_schedule_time] >= 0 }
303
+ end
304
+
305
+ return cached_routes
306
+ end
307
+
308
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: octranspo_fetch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-09-21 00:00:00.000000000 Z
12
+ date: 2013-09-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: nokogiri