octranspo 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. data/.gitignore +3 -0
  2. data/Gemfile +2 -0
  3. data/Gemfile.lock +26 -0
  4. data/Rakefile +8 -0
  5. data/lib/octranspo.rb +20 -0
  6. data/lib/octranspo/headsign.rb +2539 -0
  7. data/lib/octranspo/headsigns.yml +40565 -0
  8. data/lib/octranspo/landmarks.rb +37 -0
  9. data/lib/octranspo/lingo.rb +125 -0
  10. data/lib/octranspo/lingo/headsign_messages.yml +104 -0
  11. data/lib/octranspo/lingo/headsign_signatures.yml +3 -0
  12. data/lib/octranspo/lingo/landmarks.yml +205 -0
  13. data/lib/octranspo/lingo/roadways.yml +1957 -0
  14. data/lib/octranspo/mobile_resource_methods.rb +98 -0
  15. data/lib/octranspo/mobile_route_data.rb +85 -0
  16. data/lib/octranspo/mobile_route_schedule.rb +118 -0
  17. data/lib/octranspo/mobile_stop_schedule.rb +137 -0
  18. data/lib/octranspo/remote_resource_methods.rb +89 -0
  19. data/lib/octranspo/route.rb +31 -0
  20. data/lib/octranspo/routes.yml +507 -0
  21. data/lib/octranspo/service_date.rb +17 -0
  22. data/lib/octranspo/stations.yml +77 -0
  23. data/lib/octranspo/stop.rb +69 -0
  24. data/lib/octranspo/stop_resource.rb +46 -0
  25. data/lib/octranspo/stops.yml +30696 -0
  26. data/lib/octranspo/version.rb +3 -0
  27. data/octranspo.gemspec +22 -0
  28. data/test/fixtures/mobile_route_data/20090323/route_123_index_0.html +457 -0
  29. data/test/fixtures/mobile_route_data/20090323/route_123_index_1.html +99 -0
  30. data/test/fixtures/mobile_route_data/20090323/route_1_index_0.html +700 -0
  31. data/test/fixtures/mobile_route_data/20090323/route_1_index_1.html +691 -0
  32. data/test/fixtures/mobile_route_data/20090323/route_2_index_0.html +682 -0
  33. data/test/fixtures/mobile_route_data/20090323/route_2_index_1.html +646 -0
  34. data/test/fixtures/mobile_route_data/20100323/route_123_index_0.html +469 -0
  35. data/test/fixtures/mobile_route_data/20100323/route_123_index_1.html +105 -0
  36. data/test/fixtures/mobile_route_data/20100323/route_1_index_0.html +703 -0
  37. data/test/fixtures/mobile_route_data/20100323/route_1_index_1.html +694 -0
  38. data/test/fixtures/mobile_route_data/20100323/route_2_index_0.html +685 -0
  39. data/test/fixtures/mobile_route_data/20100323/route_2_index_1.html +649 -0
  40. data/test/fixtures/mobile_route_data/20100323/route_3_index_0.html +649 -0
  41. data/test/fixtures/mobile_route_data/20100323/route_3_index_1.html +721 -0
  42. data/test/fixtures/mobile_route_schedule/20090323/route_18_direction_2_location_25.html +1038 -0
  43. data/test/fixtures/mobile_route_schedule/20090323/route_1_direction_1_location_0.html +1127 -0
  44. data/test/fixtures/mobile_route_schedule/20090323/route_2_direction_2_location_11.html +1430 -0
  45. data/test/fixtures/mobile_route_schedule/20090323/route_2_direction__location_11.html +104 -0
  46. data/test/fixtures/mobile_route_schedule/20100323/route_1_direction_1_location_0.html +1180 -0
  47. data/test/fixtures/mobile_route_schedule/20100323/route_2_direction_2_location_11.html +1554 -0
  48. data/test/fixtures/mobile_route_schedule/20100323/route_3_direction_1_location_0.html +1082 -0
  49. data/test/fixtures/mobile_route_schedule/20100323/route_3_direction_1_location_1.html +1082 -0
  50. data/test/fixtures/mobile_stop_schedule/20090323/location_1987.yml +198 -0
  51. data/test/fixtures/mobile_stop_schedule/20090323/location_1987_route_index_0.html +1283 -0
  52. data/test/fixtures/mobile_stop_schedule/20090323/location_1987_route_index_1.html +194 -0
  53. data/test/fixtures/mobile_stop_schedule/20090323/location_1987_route_index_2.html +116 -0
  54. data/test/fixtures/mobile_stop_schedule/20090323/location_3058_route_index_12.html +149 -0
  55. data/test/fixtures/mobile_stop_schedule/20090323/location_8789_route_index_0.html +1002 -0
  56. data/test/fixtures/mobile_stop_schedule/20100323/location_1987.yml +209 -0
  57. data/test/fixtures/mobile_stop_schedule/20100323/location_1987_route_index_0.html +1393 -0
  58. data/test/fixtures/mobile_stop_schedule/20100323/location_1987_route_index_1.html +206 -0
  59. data/test/fixtures/mobile_stop_schedule/20100323/location_1987_route_index_2.html +123 -0
  60. data/test/fixtures/mobile_stop_schedule/20100323/location_3058_route_index_12.html +278 -0
  61. data/test/fixtures/mobile_stop_schedule/20100323/location_7558_route_index_0.html +979 -0
  62. data/test/fixtures/mobile_stop_schedule/20100323/location_8789_route_index_0.html +1059 -0
  63. data/test/fixtures/stop_resource/1987.html +126 -0
  64. data/test/test_helper.rb +15 -0
  65. data/test/unit/headsign_test.rb +34 -0
  66. data/test/unit/lingo_test.rb +139 -0
  67. data/test/unit/mobile_resource_methods_test.rb +25 -0
  68. data/test/unit/mobile_route_data_test.rb +124 -0
  69. data/test/unit/mobile_route_schedule_test.rb +210 -0
  70. data/test/unit/mobile_stop_schedule_test.rb +153 -0
  71. data/test/unit/service_date_test.rb +27 -0
  72. data/test/unit/stop_resource_test.rb +26 -0
  73. metadata +251 -0
@@ -0,0 +1,98 @@
1
+ require "hpricot"
2
+ require "iconv"
3
+
4
+ module OCTranspo
5
+ module MobileResourceMethods
6
+ include RemoteResourceMethods
7
+
8
+ def site
9
+ "http://www.octranspo.com/mobileweb"
10
+ end
11
+
12
+ def date_stamp (date)
13
+ date.strftime("%Y%m%d")
14
+ end
15
+
16
+ def default_options
17
+ {:index => 0, :date => Date.current}
18
+ end
19
+
20
+ protected
21
+
22
+ # Return a Hpricot DOM representing source_code.
23
+ def interpret (source_code)
24
+ Hpricot(convert_to_utf_8(source_code), :fixup_tags => true)
25
+ end
26
+
27
+ # Return a copy of source_code converted to UTF-8.
28
+ def convert_to_utf_8 (source_code)
29
+ Iconv.new('UTF-8', 'ISO-8859-1').iconv(source_code).sub('charset=iso-8859-1', 'charset=utf-8')
30
+ end
31
+
32
+ # TODO: Use Lingo instead of local regexps.
33
+ def extract_route_and_direction_from (text)
34
+ if matches = /OTrn (.+)/.match(text)
35
+ {:route => "O-Train", :direction => matches[1].strip.mb_chars.upcase}
36
+ elsif matches = /^SHTL SCOTIA BANK PLACE BANQUE SCOTIA/.match(text)
37
+ {:route => nil, :direction => "SHUTTLE TO SCOTIA BANK PLACE BANQUE SCOTIA"}
38
+ elsif matches = /(\d+) (.+)/.match(text)
39
+ {:route => matches[1], :direction => matches[2].strip.mb_chars.upcase}
40
+ else
41
+ raise "Failed to extract route and direction: #{text}"
42
+ end
43
+ end
44
+
45
+ # TODO: Use Lingo instead of local regexps.
46
+ def extract_location_from (text)
47
+ if matches = /(.+) ([1-9][A-E]) +\((\d\d\d\d)\)/.match(text)
48
+ {:station => matches[1], :platform => matches[2], :number => matches[3], :name => "#{matches[1]} #{matches[2]}"}
49
+ elsif matches = /(.+) \((\d\d\d\d)\)/.match(text)
50
+ {:name => matches[1], :number => matches[2]}
51
+ else
52
+ {:name => text.strip}
53
+ end
54
+ end
55
+
56
+ def extract_time_from (text, schedule)
57
+ match = /(\d+):(\d+) (AM|PM)/.match(text)
58
+ if match
59
+ hour = match[1].to_i
60
+ minute = match[2].to_i
61
+ meridian = match[3]
62
+
63
+ if meridian == "AM"
64
+ if hour < 4
65
+ hour = hour + 24
66
+ end
67
+ else
68
+ unless hour == 12
69
+ hour = hour + 12
70
+ end
71
+ end
72
+
73
+ seconds_after_midnight = hour.hours.seconds + minute.minutes.seconds
74
+ return Time.zone.parse(schedule[:date].to_s) + seconds_after_midnight
75
+ end
76
+ end
77
+
78
+ def extract_departure_from (text, schedule)
79
+ flags = extract_flags_from(text)
80
+ destination_flag = schedule[:destinations].keys.detect { |key| flags.delete(key) }
81
+ departure = {}
82
+ departure[:time] = extract_time_from(text, schedule)
83
+ departure[:destination] = schedule[:destinations][destination_flag]
84
+ if flags.any?
85
+ departure[:notes] = flags.map { |key| schedule[:legend][key] }
86
+ end
87
+ departure
88
+ end
89
+
90
+ def extract_flags_from (text)
91
+ if flag_data = /\[(.+)\]/.match(text)
92
+ flag_data[1].split("")
93
+ else
94
+ []
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,85 @@
1
+ module OCTranspo
2
+ module MobileRouteData
3
+
4
+ include MobileResourceMethods
5
+ extend self
6
+
7
+ # Provides an interface for retreiving basic information about an OC Transpo transit route. Data is
8
+ # sourced from http://octranspo.com/mobileweb/jnot/post.routelist.schedules.oci
9
+ #
10
+ # = Usage
11
+ #
12
+ # OCTranspo::MobileRouteData.find returns an array of direction hashes for a single route. Most routes
13
+ # operate in two directions so you will usually receive an array with two items in it. For example:
14
+ #
15
+ # # Find data for OCTranspo bus route 2 on today’s date
16
+ # route_data = OCTranspo::MobileRouteData.find(2)
17
+ #
18
+ # # Bus route 2 has two directions
19
+ # route_data.size # => 2
20
+ #
21
+ # # 2 BAYSHORE is the first direction
22
+ # route_data[0][:route] # => "2"
23
+ # route_data[0][:direction] # => "BAYSHORE"
24
+ # route_data[0][:direction_index] # => "2"
25
+ # route_data[0][:stops] # => [{:name => "RIDEAU 4A", :number => "3009"}, ...]
26
+ #
27
+ # # 2 DOWNTOWN is the opposite direction
28
+ # route_data[1][:route] # => "2"
29
+ # route_data[1][:direction] # => "DOWNTOWN / CENTRE-VILLE"
30
+ # route_data[1][:direction_index] # => "1"
31
+ # route_data[1][:stops] # => [{:name => "BAYSHORE 4B", :number => "3050"}, ...]
32
+ #
33
+ # = Notes
34
+ #
35
+ # The direction_index attributes returned by OCTranspo::MobileRouteData methods are always the values
36
+ # "1" or "2". These values correspond to the direction_index argument in OCTranspo::MobileRouteSchedule.
37
+ # Sometimes the first direction has the value "1", sometimes it has the value "2". When a route only has
38
+ # one direction it usually has the value "1". All in all very confusing.
39
+
40
+ # Returns an array of direction hashes or nil if the route was not found.
41
+ def find (route_name_or_number, options={})
42
+ options[:route] = route_name_or_number
43
+ directions = find_all_directions(options)
44
+ if directions.any?
45
+ return directions
46
+ end
47
+ end
48
+
49
+ def find_all_directions (options={})
50
+ directions = []
51
+ 0.upto(1) do |index|
52
+ directions << find_one_direction(options.merge(:index => index))
53
+ end
54
+ directions.compact
55
+ end
56
+
57
+ def find_one_direction (options={})
58
+ decode(read(options))
59
+ end
60
+
61
+ def url_for (options={})
62
+ options = default_options.merge(options)
63
+ url = "#{site}/jnot/post.routelist.schedules.oci?"
64
+ url << "rangeIndex=5&day=#{date_stamp(options[:date])}"
65
+ url << "&route=#{options[:route]}&routeIndex=#{options[:index]}"
66
+ end
67
+
68
+ def path_for (options={})
69
+ options = default_options.merge(options)
70
+ "#{date_stamp(options[:date])}/route_#{options[:route]}_index_#{options[:index]}.html"
71
+ end
72
+
73
+ def decode (source_code)
74
+ document = interpret(source_code)
75
+ if table_of_stops = document.at("#SchedulesStopListStopsTable")
76
+ direction = extract_route_and_direction_from(table_of_stops.search(:td)[1].inner_text)
77
+ direction[:direction_index] = document.at("#direction")[:value].sub('Direction', '')
78
+ direction[:stops] = table_of_stops.search("td.checkbox").map do |cell|
79
+ extract_location_from(cell.inner_text.strip)
80
+ end
81
+ return direction
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,118 @@
1
+ module OCTranspo
2
+ module MobileRouteSchedule
3
+
4
+ include MobileResourceMethods
5
+ extend self
6
+
7
+ # Provides an interface for retreiving OC Transpo timetables for a specific route, direction and location_index.
8
+ # This API is somewhat less convenient than OCTranpo::MobileStopSchedule.
9
+ #
10
+ # The data returned by this module is sourced from:
11
+ # http://www.octranspo.com/mobileweb/jnot/post.stoplist.schedules.oci
12
+ #
13
+ # == Usage
14
+ #
15
+ # # Find the schedule for the 85 going to HURDMAN from Bayshore Station (the first stop on the route)
16
+ # schedule = OCTranspo::MobileRouteSchedule.find(:route => 85, :direction => "HURDMAN", :location_index => 0)
17
+ # schedule[:route] # => "85"
18
+ # schedule[:direction] # => "HURDMAN"
19
+ # schedule[:location][:name] # => "BAYSHORE 4B"
20
+ # schedule[:location][:number] # => "3050"
21
+ # schedule[:date] # => Date.current
22
+ # schedule[:departures] # => [{:time => Time.zone.parse('04:12'), :destination => "HURDMAN"}, ...]
23
+ #
24
+ # # Find the schedule for the 85 going to HURDMAN from Bronson Ave. & Somerset St. (53rd stop on the route)
25
+ # schedule = OCTranspo::MobileRouteSchedule.find(:route => 85, :direction => "HURDMAN", :location_index => 53)
26
+ # schedule[:route] # => "85"
27
+ # schedule[:direction] # => "HURDMAN"
28
+ # schedule[:location][:name] # => "BRONSON / SOMERSET W-O"
29
+ # schedule[:location][:number] # => "6625"
30
+ # schedule[:date] # => Date.current
31
+ # schedule[:departures] # => [{:time => Time.zone.parse('04:42'), :destination => "HURDMAN"}, ...]
32
+
33
+ # Return a Hpricot DOM representing source_code.
34
+ def interpret (source_code)
35
+ Hpricot(convert_to_utf_8(source_code))
36
+ end
37
+
38
+ def find (options={})
39
+ options[:date] = if options[:date]
40
+ options[:date].to_date
41
+ else
42
+ Date.current
43
+ end
44
+ decode(read(options), options[:date])
45
+ end
46
+
47
+ def find_one (options={})
48
+ options[:date] = if options[:date]
49
+ options[:date].to_date
50
+ else
51
+ Date.current
52
+ end
53
+ decode(read(options), options[:date])
54
+ end
55
+
56
+ def decode (source_code, date)
57
+ document = interpret(source_code)
58
+
59
+ route_and_direction_text = document.at("#SchedulesTimesSelectedTable").search(:tr)[2].search(:td)[1].inner_text
60
+ location_text = document.at("#SchedulesTimesListTable td.header").inner_text
61
+ departure_texts = Array(document.search('#SchedulesTimesListTable td'))[1..-1].map do |cell| cell.inner_text.strip end
62
+
63
+ schedule = {:date => date, :legend => {}}
64
+ schedule.merge! extract_route_and_direction_from(route_and_direction_text)
65
+ schedule[:destinations] = Hash.new(schedule[:direction])
66
+
67
+ Array(document.search("#SchedulesTimesNotesTable td.notes")).in_groups_of(2) do |flag_cell, value_cell|
68
+ flag = flag_cell.inner_text.sub('[', '').sub(']', '').strip
69
+ text = value_cell.inner_text.strip
70
+ if text.include?("Destination")
71
+ schedule[:destinations][flag] = text.sub("Destination ", "")
72
+ else
73
+ schedule[:legend][flag] = text
74
+ end
75
+ end
76
+
77
+ schedule[:location] = extract_location_from(location_text)
78
+ schedule[:departures] = departure_texts.inject([]) do |departures, text|
79
+ departures << extract_departure_from(text, schedule)
80
+ end
81
+
82
+ schedule[:headsigns] = schedule[:departures].map do |d| d[:destination] end.uniq
83
+
84
+ return schedule
85
+ end
86
+
87
+ def default_options
88
+ {:direction_index => 1, :location_index => 0, :date => Date.current}
89
+ end
90
+
91
+ def url_for (options={})
92
+ options = default_options.merge(convert_options(options))
93
+ url = "#{site}/jnot/post.stoplist.schedules.oci?"
94
+ url << "rangeIndex=5&day=#{date_stamp(options[:date])}"
95
+ url << "&route=#{options[:route]}"
96
+ url << "&direction=Direction#{options[:direction_index]}"
97
+ url << "&check#{options[:location_index]}=on"
98
+ end
99
+
100
+ def path_for (options={})
101
+ options = default_options.merge(convert_options(options))
102
+ "#{date_stamp(options[:date])}/route_#{options[:route]}_direction_#{options[:direction_index]}_location_#{options[:location_index]}.html"
103
+ end
104
+
105
+ def convert_options (options={})
106
+ if options[:direction]
107
+ route_data = MobileRouteData.find(options[:route], :date => options[:date])
108
+ direction_data = route_data.detect do |dataset|
109
+ dataset[:direction] === options[:direction]
110
+ end
111
+ if direction_data
112
+ options[:direction_index] = direction_data[:direction_index]
113
+ end
114
+ end
115
+ return options
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,137 @@
1
+ module OCTranspo
2
+ module MobileStopSchedule
3
+
4
+ include MobileResourceMethods
5
+ extend self
6
+
7
+ # Provides an interface for retreiving OCTranspo timetables for a specific route and location. This API
8
+ # is more convenient than OCTranpo::MobileStopSchedule but it doesn’t work for stops without numbers.
9
+ #
10
+ # The data returned by this module is sourced from:
11
+ # http:/octranspo.com/mobileweb/jnot/post.routelist.stoptimes.oci
12
+ #
13
+ # == Usage
14
+ #
15
+ # # Find the schedule for the 85 going to HURDMAN at Bayshore Station (the first stop on the route)
16
+ # schedule = OCTranspo::MobileStopSchedule.find(:route => 85, :direction => "HURDMAN", :location => "3050")
17
+ # schedule[:route] # => "85"
18
+ # schedule[:direction] # => "HURDMAN"
19
+ # schedule[:location][:name] # => "BAYSHORE 4B"
20
+ # schedule[:location][:number] # => "3050"
21
+ # schedule[:date] # => Date.current
22
+ # schedule[:departures] # => [{:time => Time.zone.parse('04:12'), :destination => "HURDMAN"}, ...]
23
+ #
24
+ # # Find the schedule for the 85 going to HURDMAN at Bronson Ave. & Somerset St.
25
+ # schedule = OCTranspo::MobileStopSchedule.find(:route => 85, :direction => "HURDMAN", :location => "6625")
26
+ # schedule[:route] # => "85"
27
+ # schedule[:direction] # => "HURDMAN"
28
+ # schedule[:location][:name] # => "BRONSON / SOMERSET W-O"
29
+ # schedule[:location][:number] # => "6625"
30
+ # schedule[:date] # => Date.current
31
+ # schedule[:departures] # => [{:time => Time.zone.parse('04:42'), :destination => "HURDMAN"}, ...]
32
+
33
+ def find (options={})
34
+ route = "#{options.delete(:route)}"
35
+ direction = "#{options.delete(:direction)}"
36
+ find_all(options).detect do |schedule|
37
+ (schedule[:route] == route) && (schedule[:direction] == direction)
38
+ end
39
+ end
40
+
41
+ def find_all (options={})
42
+ options[:date] ||= default_options[:date]
43
+ if File.exist? index_path_for(options)
44
+ localize YAML.load(open(index_path_for(options)))
45
+ else
46
+ all_routes = []
47
+ while schedule = find_one(options.merge(:route_index => all_routes.size))
48
+ all_routes << schedule
49
+ end
50
+ open index_path_for(options), 'w' do |f|
51
+ YAML.dump all_routes, f
52
+ end
53
+
54
+ find_all(options)
55
+ end
56
+ end
57
+
58
+ def find_one (options={})
59
+ source_code = read(options)
60
+ decode(source_code)
61
+ end
62
+
63
+ def localize (schedules)
64
+ schedules.each do |schedule|
65
+ schedule[:departures].each do |departure|
66
+ departure[:time] = departure[:time].in_time_zone
67
+ end
68
+ end
69
+ end
70
+
71
+ def decode (source_code)
72
+ document = interpret(source_code)
73
+ unless document.at('.InfoBeanError')
74
+ schedule = {:legend => {}}
75
+
76
+ header_rows = document.search("#StopTimesTimesSelectedTable > tr")
77
+
78
+ date_text = header_rows[0].search(:td)[1].inner_text
79
+ schedule.merge! :date => Date.parse(date_text)
80
+
81
+ location_text = header_rows[2].search(:td)[1].inner_text
82
+ schedule.merge! :location => extract_location_from(location_text)
83
+
84
+ route_and_direction_text = header_rows[6].search(:td)[1].inner_text
85
+ schedule.merge! extract_route_and_direction_from(route_and_direction_text)
86
+
87
+ schedule[:destinations] = Hash.new(schedule[:direction])
88
+ header_rows.slice(8, header_rows.size).each do |row|
89
+ cells = row.search(:td)
90
+ flag = /\[(.)\]/.match(cells[0].inner_text)[1]
91
+ text = cells[1].inner_text
92
+ if text.include?("Destination")
93
+ schedule[:destinations][flag] = text.sub("Destination ", "")
94
+ else
95
+ schedule[:legend][flag] = text
96
+ end
97
+ end
98
+
99
+ departure_texts = document.search("#StopTimesTimesListTable0 td").map {|cell| cell.inner_text.strip }
100
+ schedule[:departures] = departure_texts.inject([]) do |departures, text|
101
+ departures << extract_departure_from(text, schedule)
102
+ end
103
+
104
+ schedule[:headsigns] = schedule[:departures].map { |d| d[:destination] }.uniq
105
+
106
+ return schedule
107
+ end
108
+ end
109
+
110
+ def default_options
111
+ {:route_index => 0, :date => Date.current}
112
+ end
113
+
114
+ def url_for (options={})
115
+ options = default_options.merge(options)
116
+ url = "#{site}/jnot/post.routelist.stoptimes.oci?"
117
+ url << "rangeIndex=5&day=#{date_stamp(options[:date])}"
118
+ url << "&stop=#{options[:location]}"
119
+ url << "&check#{options[:route_index]}=on"
120
+ end
121
+
122
+ def path_for (options={})
123
+ options = default_options.merge(options)
124
+ "#{date_stamp(options[:date])}/location_#{options[:location]}_route_index_#{options[:route_index]}.html"
125
+ end
126
+
127
+ def index_path_for (options={})
128
+ options = default_options.merge(options)
129
+ case @source
130
+ when :fixtures
131
+ "#{fixtures_path}/#{date_stamp(options[:date])}/location_#{options[:location]}.yml"
132
+ else
133
+ "#{cache_path}/#{date_stamp(options[:date])}/location_#{options[:location]}.yml"
134
+ end
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,89 @@
1
+ require "open-uri"
2
+
3
+ module OCTranspo
4
+ module RemoteResourceMethods
5
+
6
+ def read (options={})
7
+ # puts url_for(options)
8
+ case @source
9
+ when :fixtures
10
+ read_from_fixture(options)
11
+ else
12
+ read_from_cache(options) || cache(options)
13
+ end
14
+ end
15
+
16
+ def read_from_remote (options)
17
+ url = url_for(options)
18
+ puts "Opening #{url}"
19
+ open(url).read
20
+ end
21
+
22
+ def read_from_fixture (options)
23
+ address = fixture_path_for(options)
24
+ create_fixture(options) unless File.exist?(address)
25
+ open(address).read
26
+ end
27
+
28
+ def read_from_cache (options)
29
+ address = cache_path_for(options)
30
+ if File.exist?(address)
31
+ open(address).read
32
+ end
33
+ end
34
+
35
+ def reset!
36
+ @source = nil
37
+ end
38
+
39
+ def use_fixtures!
40
+ @source = :fixtures
41
+ end
42
+
43
+ def site; end
44
+
45
+ def url_for (options={}); end
46
+
47
+ def path_for (options={}); end
48
+
49
+ def fixture_path_for (options={});
50
+ "#{fixtures_path}/#{path_for(options)}"
51
+ end
52
+
53
+ def cache_path_for (options={});
54
+ "#{cache_path}/#{path_for(options)}"
55
+ end
56
+
57
+ def fixtures_path
58
+ "test/fixtures/#{self.name.demodulize.underscore}"
59
+ end
60
+
61
+ def cache_path
62
+ "cache/octranspo/#{self.name.demodulize.underscore}"
63
+ end
64
+
65
+ def create_fixture (options={})
66
+ address = fixture_path_for(options)
67
+ puts "Creating fixture #{address}"
68
+ system "mkdir -p #{File.dirname(address)}"
69
+ File.open(address, "w") do |f|
70
+ f.write read_from_remote(options)
71
+ end
72
+ read_from_fixture(options)
73
+ end
74
+
75
+ def cache (options={})
76
+ address = cache_path_for(options)
77
+ system "mkdir -p #{File.dirname(address)}"
78
+ File.open(address, "w") do |f|
79
+ f.write read_from_remote(options)
80
+ end
81
+ puts "Cached #{address}"
82
+ read_from_cache(options)
83
+ end
84
+
85
+ def logger
86
+ RAILS_DEFAULT_LOGGER
87
+ end
88
+ end
89
+ end