caltrain 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ module DataParser
2
+ class << self
3
+ def data
4
+ @data ||= {}
5
+ end
6
+
7
+ def parse(file_path)
8
+ data[file_path] ||= File.read(file_path).
9
+ split(/[\n\r]+/)[1..-1].
10
+ map { |line| line.gsub('"', '').
11
+ gsub(/\s+/, ' ').
12
+ split(/,+/) }
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,26 @@
1
+ module Printing
2
+ def print_trip(trip, time, options={})
3
+ start = options[:starting_at]
4
+ output = options[:output] || $stdout
5
+
6
+ output << train_info(trip, time) << "\n"
7
+ output << " " << stop_info(trip, start) << "\n"
8
+ end
9
+
10
+ def train_info(trip, time)
11
+ "#{time} - Train #{trip.train_no} (#{trip.type})"
12
+ end
13
+
14
+ def stop_info(trip, start=trip.stops.first)
15
+ string = trip.stops.join(' -> ')
16
+ "*#{string[(string =~ /\b#{start}\b/ || 0)..-1]}"
17
+ end
18
+
19
+ def pretty_hash(hash)
20
+ max_field_width = hash.values.map(&:size).max
21
+
22
+ hash.each do |key, value|
23
+ puts "%#{max_field_width}s #{key}" % value
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,93 @@
1
+ module Schedule
2
+ class << self
3
+ include Printing
4
+
5
+ def times_path
6
+ "#{Caltrain.base_dir}/data/google_transit/stop_times.txt"
7
+ end
8
+
9
+ def data
10
+ @data ||= DataParser.parse(times_path)
11
+ end
12
+
13
+ def list(loc, dir, output=$stdout)
14
+ trips = trips_with_times(loc, dir).select { |_, time| time > now }
15
+ options = {:output => output, :starting_at => loc}
16
+
17
+ trips.each { |trip, time| print_trip(trip, time, options) }
18
+ end
19
+
20
+ def next(loc, dir, output=$stdout)
21
+ trip = trips_with_times(loc, dir).find { |_, time| time > now }
22
+ options = {:output => output, :starting_at => loc}
23
+
24
+ print_trip(trip.first, trip.last, options)
25
+ end
26
+
27
+ def trips_with_times(loc, dir)
28
+ @trips_with_times ||= trips_for_today(dir).map do |trip|
29
+ if time = trip.time_at_location(loc)
30
+ [trip, time]
31
+ end
32
+ end.compact.sort_by_nth(1)
33
+ end
34
+
35
+ def trips_for_today(dir)
36
+ if weekend?
37
+ Trip.weekend(dir)
38
+ elsif saturday?
39
+ Trip.saturday_only(dir)
40
+ else
41
+ Trip.weekday(dir)
42
+ end
43
+ end
44
+
45
+ def now
46
+ Time.now.strftime('%H:%M:%S')
47
+ end
48
+
49
+ def weekend?
50
+ saturday? || Time.now.sunday?
51
+ end
52
+
53
+ def saturday?
54
+ Time.now.saturday?
55
+ end
56
+
57
+ def abbrevs
58
+ @abbrevs ||= {
59
+ :gil => "Gilroy",
60
+ :smt => "San Martin",
61
+ :mrg => "Morgan Hill",
62
+ :bhl => "Blossom Hill",
63
+ :cap => "Capitol",
64
+ :tam => "Tamien",
65
+ :sj => "San Jose",
66
+ :clp => "College Park",
67
+ :sc => "Santa Clara",
68
+ :law => "Lawrence",
69
+ :sv => "Sunnyvale",
70
+ :mv => "Mountain View",
71
+ :sa => "San Antonio",
72
+ :cal => "California Ave",
73
+ :pa => "Palo Alto",
74
+ :men => "Menlo Park",
75
+ :ath => "Atherton",
76
+ :rc => "Redwood City",
77
+ :scl => "San Carlos",
78
+ :bel => "Belmont",
79
+ :hil => "Hillsdale",
80
+ :hay => "Hayward Park",
81
+ :sm => "San Mateo",
82
+ :brl => "Burlingame",
83
+ :bdw => "Broadway",
84
+ :mil => "Millbrae",
85
+ :sb => "San Bruno",
86
+ :ssf => "So. San Francisco",
87
+ :bsh => "Bayshore",
88
+ :tt => "22nd Street",
89
+ :sf => "San Francisco"
90
+ }.freeze
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,86 @@
1
+ class Trip
2
+ attr_reader :trip_id, :route_id, :service_id, :headsign, :direction, :shape_id
3
+
4
+ class << self
5
+ def trips_path
6
+ "#{Caltrain.base_dir}/data/google_transit/trips.txt"
7
+ end
8
+
9
+ def data
10
+ DataParser.parse(trips_path)
11
+ end
12
+
13
+ def new_from_data(data)
14
+ new(*data)
15
+ end
16
+
17
+ def trips(dir=nil)
18
+ @trips ||= []
19
+ [:north, :south].include?(dir) ? @trips.select { |trip| trip.direction == dir } : @trips
20
+ end
21
+
22
+ def weekend(dir=nil)
23
+ trips(dir).select { |trip| trip.service_id =~ /^WE/ }
24
+ end
25
+
26
+ def weekday(dir=nil)
27
+ trips(dir).select { |trip| trip.service_id =~ /^WD/ }
28
+ end
29
+
30
+ def saturday_only(dir=nil)
31
+ trips(dir).select { |trip| trip.service_id =~ /^ST/ }
32
+ end
33
+
34
+ def today(dir=nil)
35
+ if Schedule.saturday?
36
+ weekend(dir) + saturday_only(dir)
37
+ elsif Schedule.weekend?
38
+ weekend(dir)
39
+ else
40
+ weekday(dir)
41
+ end
42
+ end
43
+
44
+ def <<(trip)
45
+ @trips << trip unless trips.map(&:trip_id).include?(trip.trip_id)
46
+ end
47
+ end
48
+
49
+ def initialize(*data)
50
+ @trip_id = data[0]
51
+ @route_id = data[1]
52
+ @service_id = data[2]
53
+ @headsign = data[3]
54
+ @direction = data[4] == '0' ? :north : :south
55
+ @shape_id = data[5]
56
+
57
+ Trip << self
58
+ end
59
+
60
+ def train_no
61
+ @trip_id =~ /^(\d+)_.+$/; $1.to_i
62
+ end
63
+
64
+ def type
65
+ @route_id =~ /^ct_(\w+)_\d+/; $1
66
+ end
67
+
68
+ def time_data
69
+ @time_data ||= Schedule.data.select { |row| row[0] == @trip_id }
70
+ end
71
+
72
+ def times
73
+ @times ||= time_data.map_nth(1)
74
+ end
75
+
76
+ def time_at_location(loc)
77
+ return nil if loc == stops.last
78
+ loc_str = Schedule.abbrevs[loc]
79
+ time_data.find { |row| row[3] =~ /^#{loc_str}/ }[1] rescue nil
80
+ end
81
+
82
+ def stops(dir=@direction)
83
+ @stops ||= Schedule.abbrevs.select { |_, v| time_data.map_nth(3).any? { |d| d =~ /^#{v}/ } }.keys
84
+ dir == :north ? @stops : @stops.reverse
85
+ end
86
+ end
data/lib/caltrain.rb CHANGED
@@ -1,134 +1,56 @@
1
- class Caltrain
2
- class << self
3
- def base_dir
4
- File.expand_path('..', File.dirname(__FILE__))
5
- end
6
-
7
- def times_path
8
- "#{base_dir}/data/google_transit/stop_times.txt"
9
- end
10
-
11
- def trips_path
12
- "#{base_dir}/data/google_transit/trips.txt"
13
- end
14
-
15
- def upcoming_departures(loc, dir)
16
- times(loc, dir).select { |time| time > now }.sort
17
- end
18
-
19
- def next_departure(loc, dir)
20
- times(loc, dir).find { |time| time > now }
21
- end
22
-
23
- def times(loc, dir)
24
- @times ||= times_for_location(loc).select { |line| trip_ids_for_direction(dir).include?(line[0]) }.map {|i| i[1]}.sort
25
- end
26
-
27
- def trip_ids_for_direction(dir)
28
- @trip_ids_for_direction ||= trips_for_time_of_week.select {|trip| trip[4] == (dir == :north ? '0' : '1') }.map(&:first)
29
- end
30
-
31
- def trips_for_time_of_week
32
- @trips_for_time_of_week ||= all_trips.select { |trip| trip[2] =~ (weekend? ? /^WE/ : /^WD/) }
33
- end
34
-
35
- def times_for_location(loc)
36
- @times_for_location ||= all_times.select { |arr| arr[3] =~ /^#{abbrevs[loc]}/ }
37
- end
1
+ require 'core_ext'
38
2
 
39
- def all_times
40
- @all_times ||= File.read(times_path).split(/[\n\r]+/)[1..-1].map { |line| line.gsub('"', '').split(/,+/) }
41
- end
42
-
43
- def all_trips
44
- @all_trips ||= File.read(trips_path).split(/[\n\r]+/)[1..-1].map { |line| line.gsub('"', '').split(/,+/) }.sort
45
- end
46
-
47
- def weekend?
48
- @weekend ||= (Time.now.saturday? || Time.now.sunday?)
49
- end
3
+ module Caltrain
4
+ require 'caltrain/data_parser'
5
+ require 'caltrain/trip'
6
+ require 'caltrain/printing'
7
+ require 'caltrain/schedule'
50
8
 
51
- def now
52
- @now ||= Time.now.strftime('%H:%M:%S')
53
- end
54
-
55
- def abbrevs
56
- {
57
- :tt => "22nd Street",
58
- :ath => "Atherton",
59
- :bsh => "Bayshore",
60
- :bel => "Belmont",
61
- :bhl => "Blossom Hill",
62
- :bdw => "Broadway",
63
- :brl => "Burlingame",
64
- :cal => "California Ave",
65
- :cap => "Capitol",
66
- :clp => "College Park",
67
- :gil => "Gilroy",
68
- :hay => "Hayward Park",
69
- :hil => "Hillsdale",
70
- :law => "Lawrence",
71
- :men => "Menlo Park",
72
- :mil => "Millbrae",
73
- :mrg => "Morgan Hill",
74
- :mv => "Mountain View",
75
- :pa => "Palo Alto",
76
- :rc => "Redwood City",
77
- :sa => "San Antonio",
78
- :sb => "San Bruno",
79
- :scl => "San Carlos",
80
- :sf => "San Francisco",
81
- :sj => "San Jose",
82
- :smt => "San Martin",
83
- :sm => "San Mateo",
84
- :sc => "Santa Clara",
85
- :ssf => "So. San Francisco",
86
- :sv => "Sunnyvale",
87
- :tam => "Tamien"
88
- }
89
- end
90
-
91
- def pretty_abbrevs
92
- max_field_width = abbrevs.values.map(&:size).max
9
+ class << self
10
+ include Printing
93
11
 
94
- abbrevs.values.zip(abbrevs.keys).each do |name, abr|
95
- puts "%#{max_field_width}s #{abr}" % name
96
- end
12
+ def base_dir
13
+ File.expand_path('..', File.dirname(__FILE__))
97
14
  end
98
15
 
99
16
  def usage
100
17
  puts "Usage:"
101
- puts " caltrain <action> <location> <direction>"
102
- puts " action => [ next | list ]"
18
+ puts " caltrain <location> <direction> [ list | next ]"
103
19
  puts ""
104
20
  puts "Abbreviations:"
105
- pretty_abbrevs
21
+ pretty_hash(Schedule.abbrevs)
106
22
  exit(1)
107
23
  end
108
24
 
109
- def actions
110
- { :list => :upcoming_departures, :next => :next_departure }
111
- end
112
-
113
25
  def clean!
114
- instance_variables.each { |i| instance_variable_set(i, nil) }
26
+ [self, Trip].each do |klass|
27
+ klass.instance_variables.each { |i| klass.instance_variable_set(i, nil) }
28
+ end
115
29
  true
116
30
  end
117
31
 
118
32
  def run!(args)
119
- begin
120
- act, loc, dir = *args
121
-
122
- if dir =~ /^n/
123
- puts method(actions[act.to_sym]).call(loc.to_sym, :north)
124
- elsif dir =~ /^s/
125
- puts method(actions[act.to_sym]).call(loc.to_sym, :south)
126
- else
127
- raise
128
- end
129
- rescue
130
- usage
33
+ loc, dir, act = *args
34
+ loc = loc.to_sym
35
+ act = :next unless act
36
+
37
+ populate_trips!
38
+
39
+ if dir =~ /^n/i
40
+ Schedule.method(act).call(loc, :north)
41
+ elsif dir =~ /^s/i
42
+ Schedule.method(act).call(loc, :south)
43
+ else
44
+ raise
131
45
  end
46
+ rescue
47
+ usage
48
+ end
49
+
50
+ private
51
+
52
+ def populate_trips!
53
+ DataParser.parse(Trip.trips_path).each { |args| Trip.new_from_data(args) }
132
54
  end
133
55
  end
134
56
  end
data/lib/core_ext.rb ADDED
@@ -0,0 +1,9 @@
1
+ class Array
2
+ def map_nth(n)
3
+ map { |elem| elem[n] } rescue self
4
+ end
5
+
6
+ def sort_by_nth(n)
7
+ sort_by { |elem| elem[n] } rescue self
8
+ end
9
+ end
@@ -1,99 +1,5 @@
1
- require 'minitest/autorun'
2
- require 'minitest/pride'
3
- require 'mocha'
4
-
5
- require File.expand_path('../lib/caltrain', File.dirname(__FILE__))
6
-
7
1
  describe Caltrain do
8
- before { Caltrain.stubs(:weekend?).returns(false) }
9
-
10
- after { Caltrain.clean! }
11
-
12
2
  it 'has a base directory' do
13
3
  Caltrain.base_dir.must_equal(File.expand_path('..', File.dirname(__FILE__)))
14
4
  end
15
-
16
- describe 'trips' do
17
- it 'should parse trips.txt depending on the day of the week' do
18
- Caltrain.all_trips.first.must_equal(['101_20110701','ct_local_20110701','WD_20110701','San Francisco (Train 101)','0','cal_sj_sf'])
19
- end
20
- end
21
-
22
- describe 'times' do
23
- it 'should parse stop_times.txt correctly' do
24
- line = ['280_20110701','18:50:00','18:50:00','Mountain View Caltrain','10']
25
- Caltrain.all_times.must_include(line)
26
- end
27
-
28
- it 'should return times for a location' do
29
- ['16:58:00', '08:13:00', '19:44:00'].each do |time|
30
- Caltrain.times_for_location(:sv).map {|i| i[1]}.must_include(time)
31
- end
32
- end
33
- end
34
-
35
- describe '#next_departure' do
36
- it 'should return the next departure for north' do
37
- Caltrain.stubs(:now).returns('08:05:12')
38
- Caltrain.next_departure(:sv, :north).must_equal('08:13:00')
39
- end
40
-
41
- it 'should return the next departure for south' do
42
- Caltrain.stubs(:now).returns('18:50:00')
43
- Caltrain.next_departure(:sf, :south).must_equal('18:56:00')
44
- end
45
- end
46
-
47
- describe '#upcoming_departures' do
48
- it 'should return the upcoming trips for north' do
49
- Caltrain.stubs(:now).returns('08:05:12')
50
- upcoming1 = Caltrain.upcoming_departures(:sv, :north)
51
- upcoming1.must_include('08:18:00')
52
- upcoming1.wont_include('09:55:00')
53
- upcoming1.wont_include('09:14:00')
54
- end
55
-
56
- it 'should return upcoming trips for south' do
57
- Caltrain.stubs(:now).returns('18:30:10')
58
- upcoming2 = Caltrain.upcoming_departures(:sf, :south)
59
- upcoming2.must_include('18:33:00')
60
- upcoming2.wont_include('18:44:00')
61
- upcoming2.wont_include('18:59:00')
62
- end
63
- end
64
-
65
- describe 'weekend versus weekday' do
66
- it 'should pick weekday times' do
67
- Caltrain.stubs(:now).returns('09:10:10')
68
-
69
- Caltrain.next_departure(:mv, :north).must_equal('09:29:00')
70
-
71
- upcoming = Caltrain.upcoming_departures(:mv, :north)
72
- upcoming.must_include('16:37:00')
73
- upcoming.wont_include('09:19:00')
74
- end
75
-
76
- it 'should pick weekend times' do
77
- Caltrain.stubs(:weekend?).returns(true)
78
- Caltrain.stubs(:now).returns('07:46:15')
79
-
80
- Caltrain.next_departure(:sj, :north).must_equal('08:00:00')
81
-
82
- upcoming = Caltrain.upcoming_departures(:sj, :south)
83
- upcoming.must_include('12:00:00')
84
- upcoming.wont_include('07:50:00')
85
- end
86
- end
87
-
88
- describe 'excluding last stops' do
89
- it 'should not include times for last sf stop' do
90
- Caltrain.stubs(:now).returns('18:58:30')
91
- Caltrain.next_departure(:sf, :south).must_equal('19:30:00')
92
- end
93
-
94
- it 'should not include times for last sj stop' do
95
- Caltrain.stubs(:now).returns('08:12:10')
96
- Caltrain.next_departure(:sj, :north).must_equal('08:22:00')
97
- end
98
- end
99
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: caltrain
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.7
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -13,7 +13,7 @@ date: 2012-03-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: mocha
16
- requirement: &2151812960 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,7 +21,12 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *2151812960
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
25
30
  description: A command line tool to easily figure out when the next caltrain leaves.
26
31
  email: alexgenco@gmail.com
27
32
  executables:
@@ -29,17 +34,14 @@ executables:
29
34
  extensions: []
30
35
  extra_rdoc_files: []
31
36
  files:
37
+ - lib/caltrain/data_parser.rb
38
+ - lib/caltrain/printing.rb
39
+ - lib/caltrain/schedule.rb
40
+ - lib/caltrain/trip.rb
32
41
  - lib/caltrain.rb
42
+ - lib/core_ext.rb
33
43
  - bin/caltrain
34
- - data/google_transit/agency.txt
35
- - data/google_transit/calendar.txt
36
- - data/google_transit/calendar_dates.txt
37
- - data/google_transit/fare_attributes.txt
38
- - data/google_transit/fare_rules.txt
39
- - data/google_transit/routes.txt
40
- - data/google_transit/shapes.txt
41
44
  - data/google_transit/stop_times.txt
42
- - data/google_transit/stops.txt
43
45
  - data/google_transit/trips.txt
44
46
  - spec/caltrain_spec.rb
45
47
  homepage: http://github.com/alexgenco/caltrain
@@ -62,7 +64,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
62
64
  version: '0'
63
65
  requirements: []
64
66
  rubyforge_project:
65
- rubygems_version: 1.8.17
67
+ rubygems_version: 1.8.24
66
68
  signing_key:
67
69
  specification_version: 3
68
70
  summary: Get the next caltrain
@@ -1,2 +0,0 @@
1
- agency_id,agency_name,agency_url,agency_timezone,agency_phone,agency_lang
2
- "caltrain-ca-us","Caltrain","http://www.caltrain.com","America/Los_Angeles","800-660-4287","en"
@@ -1,5 +0,0 @@
1
- service_id,monday,tuesday,wednesday,thursday,friday,saturday,sunday,start_date,end_date
2
- "ST_20110701","0","0","0","0","0","1","0","20110701","20120219"
3
- "WD_20110701","1","1","1","1","1","0","0","20110701","20160701"
4
- "WE_20110701","0","0","0","0","0","1","1","20110701","20160701"
5
- "ST_20120220","0","0","0","0","0","1","0","20120220","20120220"
@@ -1,6 +0,0 @@
1
- service_id,date,exception_type
2
- "SN_20110701","20111226","1"
3
- "WD_20110701","20111226","2"
4
- "WD_20110701","20120102","2"
5
- "SN_20110701","20120102","1"
6
-
@@ -1,13 +0,0 @@
1
- fare_id,price,currency_type,payment_method,transfers,transfer_duration
2
- "OW_1_20110701",2.7500,"USD","1",,
3
- "OW_2_20110701",4.7500,"USD","1",,
4
- "OW_3_20110701",6.7500,"USD","1",,
5
- "OW_4_20110701",8.7500,"USD","1",,
6
- "OW_5_20110701",10.7500,"USD","1",,
7
- "OW_6_20110701",12.7500,"USD","1",,
8
- "OW_1_20120220",2.7500,"USD","1",,
9
- "OW_2_20120220",4.7500,"USD","1",,
10
- "OW_3_20120220",6.7500,"USD","1",,
11
- "OW_4_20120220",8.7500,"USD","1",,
12
- "OW_5_20120220",10.7500,"USD","1",,
13
- "OW_6_20120220",12.7500,"USD","1",,