nextbus 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/.gitignore +22 -0
  2. data/Gemfile +6 -0
  3. data/Gemfile.lock +29 -0
  4. data/LICENSE +20 -0
  5. data/README.rdoc +17 -0
  6. data/Rakefile +27 -0
  7. data/VERSION +1 -0
  8. data/doc/ERM.graffle +1318 -0
  9. data/doc/MBTA_XML_Feed_Trial_Docs_13Nov09-1.pdf +0 -0
  10. data/lib/attr_with_default.rb +51 -0
  11. data/lib/instantiate_with_attrs.rb +12 -0
  12. data/lib/nextbus.rb +10 -0
  13. data/lib/nextbus/agency.rb +18 -0
  14. data/lib/nextbus/client.rb +70 -0
  15. data/lib/nextbus/direction.rb +18 -0
  16. data/lib/nextbus/mash_extensions.rb +12 -0
  17. data/lib/nextbus/prediction.rb +18 -0
  18. data/lib/nextbus/report.rb +10 -0
  19. data/lib/nextbus/route.rb +18 -0
  20. data/lib/nextbus/stop.rb +18 -0
  21. data/lib/nextbus/string_extensions.rb +16 -0
  22. data/lib/nextbus/vehicle.rb +18 -0
  23. data/nextbus.gemspec +106 -0
  24. data/test/fixtures/agency_list.xml +5 -0
  25. data/test/fixtures/error.xml +6 -0
  26. data/test/fixtures/predictions.xml +12 -0
  27. data/test/fixtures/predictions_for_multi_stops.xml +21 -0
  28. data/test/fixtures/route_config.xml +755 -0
  29. data/test/fixtures/route_list.xml +5 -0
  30. data/test/fixtures/vehicle_locations.xml +11 -0
  31. data/test/helper.rb +78 -0
  32. data/test/unit/test_agency.rb +56 -0
  33. data/test/unit/test_attr_with_default.rb +158 -0
  34. data/test/unit/test_client.rb +107 -0
  35. data/test/unit/test_direction.rb +59 -0
  36. data/test/unit/test_instantiate_with_attrs.rb +31 -0
  37. data/test/unit/test_mash_extensions.rb +45 -0
  38. data/test/unit/test_nextbus.rb +9 -0
  39. data/test/unit/test_prediction.rb +47 -0
  40. data/test/unit/test_report.rb +34 -0
  41. data/test/unit/test_route.rb +51 -0
  42. data/test/unit/test_stop.rb +64 -0
  43. data/test/unit/test_string_extensions.rb +33 -0
  44. data/test/unit/test_vehicle.rb +52 -0
  45. data/vendor/cache/columnize-0.3.1.gem +0 -0
  46. data/vendor/cache/crack-0.1.6.gem +0 -0
  47. data/vendor/cache/hashie-0.1.8.gem +0 -0
  48. data/vendor/cache/httparty-0.5.2.gem +0 -0
  49. data/vendor/cache/linecache-0.43.gem +0 -0
  50. data/vendor/cache/mocha-0.9.8.gem +0 -0
  51. data/vendor/cache/rake-0.8.7.gem +0 -0
  52. data/vendor/cache/ruby-debug-0.10.3.gem +0 -0
  53. data/vendor/cache/ruby-debug-base-0.10.3.gem +0 -0
  54. metadata +121 -0
@@ -0,0 +1,51 @@
1
+ # http://snippets.dzone.com/posts/show/4382
2
+
3
+ module AttrWithDefault
4
+
5
+ module ClassMethods
6
+
7
+ def attr_accessor(*args)
8
+ attrs, attrs_with_defaults = split_for_last_hash(args)
9
+ attrs_with_defaults.each do |name, default|
10
+ attr_reader_with_default name, default
11
+ attr_writer name
12
+ end
13
+
14
+ super(*attrs)
15
+ end
16
+
17
+ def attr_reader(*args)
18
+ attrs, attrs_with_defaults = split_for_last_hash(args)
19
+ attrs_with_defaults.each do |name, default|
20
+ attr_reader_with_default name, default
21
+ end
22
+
23
+ super(*attrs)
24
+ end
25
+
26
+ private
27
+
28
+ def attr_reader_with_default(name, default)
29
+ define_method(name) do
30
+ unless instance_variable_defined?("@#{name}")
31
+ instance_variable_set "@#{name}", default
32
+ end
33
+ instance_variable_get "@#{name}"
34
+ end
35
+ end
36
+
37
+ def split_for_last_hash(args)
38
+ if args.last.is_a?(Hash)
39
+ [args[0...-1], args.last]
40
+ else
41
+ [args, {}]
42
+ end
43
+ end
44
+
45
+ end
46
+
47
+ def self.included(other_module)
48
+ other_module.extend ClassMethods
49
+ end
50
+
51
+ end
@@ -0,0 +1,12 @@
1
+ module InstantiateWithAttrs
2
+
3
+ def initialize(attrs={})
4
+ super()
5
+ attrs.each do |name, value|
6
+ setter = "#{name.to_s}=".to_sym
7
+ self.send(setter, value) if self.respond_to?(setter)
8
+ end
9
+ self
10
+ end
11
+
12
+ end
@@ -0,0 +1,10 @@
1
+ require 'httparty'
2
+ require 'hashie'
3
+
4
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
5
+ require 'instantiate_with_attrs'
6
+ require 'attr_with_default'
7
+
8
+ dir = File.join(File.dirname(__FILE__), 'nextbus')
9
+ $LOAD_PATH.unshift(dir)
10
+ Dir[File.join(dir, "*.rb")].each {|file| require File.basename(file) }
@@ -0,0 +1,18 @@
1
+ module Nextbus
2
+ class Agency
3
+
4
+ include InstantiateWithAttrs
5
+ include AttrWithDefault
6
+
7
+ attr_accessor :tag, :title, :short_title, :region_title, :routes => [], :vehicles => []
8
+
9
+ def self.all
10
+ Nextbus.client.agencies.map{|agency| new(agency) }
11
+ end
12
+
13
+ def self.find(id)
14
+ all.detect{|agency| agency.tag == id }
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,70 @@
1
+ require 'singleton'
2
+ module Nextbus
3
+
4
+ def self.client
5
+ Nextbus::Client.instance
6
+ end
7
+
8
+ class Client
9
+
10
+ include Singleton
11
+ include HTTParty
12
+
13
+ base_uri "http://webservices.nextbus.com/service/publicXMLFeed?command="
14
+
15
+ def agencies
16
+ response = self.class.do_request("agencyList", {})
17
+ response.agency
18
+ end
19
+
20
+ def routes(agency_id)
21
+ response = self.class.do_request("routeList", {"a" => agency_id})
22
+ response.route
23
+ end
24
+
25
+ def route(agency_id, route_id)
26
+ response = self.class.do_request("routeConfig", {"a" => agency_id, "r" => route_id})
27
+ response.route
28
+ end
29
+
30
+ def predictions(agency_id, route_id, stop_id)
31
+ response = self.class.do_request("predictions", {"a" => agency_id, "r" => route_id, "s" => stop_id})
32
+ response.predictions.direction.prediction
33
+ end
34
+
35
+ def vehicles(agency_id, route_id, time=Time.now)
36
+ response = self.class.do_request("vehicleLocations", {"a" => agency_id, "r" => route_id, "t" => time.to_i.to_s})
37
+ response.vehicle
38
+ end
39
+
40
+ def directions(agency_id, route_id)
41
+ response = self.class.do_request("routeConfig", {"a" => agency_id, "r" => route_id})
42
+ response.route.direction
43
+ end
44
+
45
+ def stops(agency_id, route_id, direction_id)
46
+ response = self.class.do_request("routeConfig", {"a" => agency_id, "r" => route_id})
47
+ response.route.direction.detect{|direction| direction.tag == direction_id }.stop
48
+ end
49
+
50
+ protected
51
+
52
+ def self.do_request(resource, params)
53
+ uri = build_uri(resource, params)
54
+ response = get(uri)
55
+ mash_response_body(response)
56
+ end
57
+
58
+ def self.build_uri(resource, params)
59
+ params = params.to_a
60
+ params.map!{|pair| pair.join('=') }
61
+ params.unshift(resource)
62
+ params.join('&')
63
+ end
64
+
65
+ def self.mash_response_body(response)
66
+ Hashie::Mash.new(response["body"])
67
+ end
68
+
69
+ end
70
+ end
@@ -0,0 +1,18 @@
1
+ module Nextbus
2
+ class Direction
3
+
4
+ include InstantiateWithAttrs
5
+ include AttrWithDefault
6
+
7
+ attr_accessor :tag, :title, :name, :route, :reports => [], :stops => []
8
+
9
+ def self.all(agency_id, route_id)
10
+ Nextbus.client.directions(agency_id, route_id).map{|direction| new(direction) }
11
+ end
12
+
13
+ def self.find(agency_id, route_id, id)
14
+ all(agency_id, route_id).detect{|direction| direction.tag == id }
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,12 @@
1
+ module Hashie
2
+
3
+ class Mash < Hashie::Hash
4
+
5
+ protected
6
+
7
+ def convert_key(key) #:nodoc:
8
+ key.to_s.underscore
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,18 @@
1
+ module Nextbus
2
+ class Prediction
3
+
4
+ include InstantiateWithAttrs
5
+ include AttrWithDefault
6
+
7
+ attr_accessor :time, :departure, :stop
8
+
9
+ def epoch_time=(nanosecs)
10
+ self.time = Time.at(nanosecs.to_i/1000)
11
+ end
12
+
13
+ def self.all(agency_id, route_id, stop_id)
14
+ Nextbus.client.predictions(agency_id, route_id, stop_id).map{|prediction| new(prediction) }
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,10 @@
1
+ module Nextbus
2
+ class Report
3
+
4
+ include InstantiateWithAttrs
5
+ include AttrWithDefault
6
+
7
+ attr_accessor :lat, :lon, :heading, :time, :vehicle, :direction
8
+
9
+ end
10
+ end
@@ -0,0 +1,18 @@
1
+ module Nextbus
2
+ class Route
3
+
4
+ include InstantiateWithAttrs
5
+ include AttrWithDefault
6
+
7
+ attr_accessor :tag, :title, :agency, :directions => []
8
+
9
+ def self.all(agency_id)
10
+ Nextbus.client.routes(agency_id).map{|agency| new(agency) }
11
+ end
12
+
13
+ def self.find(agency_id, id)
14
+ new Nextbus.client.route(agency_id, id)
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ module Nextbus
2
+ class Stop
3
+
4
+ include InstantiateWithAttrs
5
+ include AttrWithDefault
6
+
7
+ attr_accessor :tag, :title, :lat, :lon, :id, :direction, :predictions => []
8
+
9
+ def self.all(agency_id, route_id, direction_id)
10
+ Nextbus.client.stops(agency_id, route_id, direction_id).map{|stop| new(stop) }
11
+ end
12
+
13
+ def self.find(agency_id, route_id, direction_id, id)
14
+ all(agency_id, route_id, direction_id).detect{|stop| stop.tag == id }
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,16 @@
1
+ class String
2
+
3
+ def underscore
4
+ self.class.underscore(self)
5
+ end
6
+
7
+ def self.underscore(camel_cased_word)
8
+ camel_cased_word.to_s.gsub(/::/, '/').
9
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
10
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
11
+ tr(" ", "_").
12
+ tr("-", "_").
13
+ downcase
14
+ end
15
+
16
+ end
@@ -0,0 +1,18 @@
1
+ module Nextbus
2
+ class Vehicle
3
+
4
+ include InstantiateWithAttrs
5
+ include AttrWithDefault
6
+
7
+ attr_accessor :id, :agency, :route, :reports => []
8
+
9
+ def self.all(agency_id, route_id)
10
+ Nextbus.client.vehicles(agency_id, route_id).map{|vehicle| new(vehicle) }
11
+ end
12
+
13
+ def self.find(agency_id, route_id, id)
14
+ all(agency_id, route_id).detect{|vehicle| vehicle.id == id }
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,106 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{nextbus}
8
+ s.version = "0.0.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Greg Sterndale"]
12
+ s.date = %q{2010-02-23}
13
+ s.description = %q{NextBus API client}
14
+ s.email = %q{gsterndale@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".gitignore",
21
+ "Gemfile",
22
+ "Gemfile.lock",
23
+ "LICENSE",
24
+ "README.rdoc",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "doc/ERM.graffle",
28
+ "doc/MBTA_XML_Feed_Trial_Docs_13Nov09-1.pdf",
29
+ "lib/attr_with_default.rb",
30
+ "lib/instantiate_with_attrs.rb",
31
+ "lib/nextbus.rb",
32
+ "lib/nextbus/agency.rb",
33
+ "lib/nextbus/client.rb",
34
+ "lib/nextbus/direction.rb",
35
+ "lib/nextbus/mash_extensions.rb",
36
+ "lib/nextbus/prediction.rb",
37
+ "lib/nextbus/report.rb",
38
+ "lib/nextbus/route.rb",
39
+ "lib/nextbus/stop.rb",
40
+ "lib/nextbus/string_extensions.rb",
41
+ "lib/nextbus/vehicle.rb",
42
+ "nextbus.gemspec",
43
+ "test/fixtures/agency_list.xml",
44
+ "test/fixtures/error.xml",
45
+ "test/fixtures/predictions.xml",
46
+ "test/fixtures/predictions_for_multi_stops.xml",
47
+ "test/fixtures/route_config.xml",
48
+ "test/fixtures/route_list.xml",
49
+ "test/fixtures/vehicle_locations.xml",
50
+ "test/helper.rb",
51
+ "test/unit/test_agency.rb",
52
+ "test/unit/test_attr_with_default.rb",
53
+ "test/unit/test_client.rb",
54
+ "test/unit/test_direction.rb",
55
+ "test/unit/test_instantiate_with_attrs.rb",
56
+ "test/unit/test_mash_extensions.rb",
57
+ "test/unit/test_nextbus.rb",
58
+ "test/unit/test_prediction.rb",
59
+ "test/unit/test_report.rb",
60
+ "test/unit/test_route.rb",
61
+ "test/unit/test_stop.rb",
62
+ "test/unit/test_string_extensions.rb",
63
+ "test/unit/test_vehicle.rb",
64
+ "vendor/cache/columnize-0.3.1.gem",
65
+ "vendor/cache/crack-0.1.6.gem",
66
+ "vendor/cache/hashie-0.1.8.gem",
67
+ "vendor/cache/httparty-0.5.2.gem",
68
+ "vendor/cache/linecache-0.43.gem",
69
+ "vendor/cache/mocha-0.9.8.gem",
70
+ "vendor/cache/rake-0.8.7.gem",
71
+ "vendor/cache/ruby-debug-0.10.3.gem",
72
+ "vendor/cache/ruby-debug-base-0.10.3.gem"
73
+ ]
74
+ s.homepage = %q{http://github.com/neweryankee/nextbus}
75
+ s.rdoc_options = ["--charset=UTF-8"]
76
+ s.require_paths = ["lib"]
77
+ s.rubygems_version = %q{1.3.5}
78
+ s.summary = %q{NextBus API client}
79
+ s.test_files = [
80
+ "test/helper.rb",
81
+ "test/unit/test_agency.rb",
82
+ "test/unit/test_attr_with_default.rb",
83
+ "test/unit/test_client.rb",
84
+ "test/unit/test_direction.rb",
85
+ "test/unit/test_instantiate_with_attrs.rb",
86
+ "test/unit/test_mash_extensions.rb",
87
+ "test/unit/test_nextbus.rb",
88
+ "test/unit/test_prediction.rb",
89
+ "test/unit/test_report.rb",
90
+ "test/unit/test_route.rb",
91
+ "test/unit/test_stop.rb",
92
+ "test/unit/test_string_extensions.rb",
93
+ "test/unit/test_vehicle.rb"
94
+ ]
95
+
96
+ if s.respond_to? :specification_version then
97
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
98
+ s.specification_version = 3
99
+
100
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
101
+ else
102
+ end
103
+ else
104
+ end
105
+ end
106
+
@@ -0,0 +1,5 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <body copyright="All data copyright agencies listed below and NextBus Inc 2010.">
3
+ <agency tag="mbta" title="MBTA" regionTitle="Massachusetts"/>
4
+ <agency tag="sf-muni" title="San Francisco Muni" shortTitle="SF Muni" regionTitle="California-Northern"/>
5
+ </body>
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <body copyright="All data copyright MBTA 2010.">
3
+ <Error shouldRetry="false">
4
+ Exception occurred in XMLFeed, status=[command=vehicleLocations,agency=mbta] message: For input string: ""
5
+ </Error>
6
+ </body>
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <body copyright="All data copyright MBTA 2010.">
3
+ <predictions agencyTitle="MBTA" routeTitle="39" routeTag="39" stopTitle="105 S Huntington (Stop 22365)">
4
+ <direction title="Outbound">
5
+ <prediction seconds="745" minutes="12" epochTime="1266681828070" isDeparture="false" dirTag="out" vehicle="1041" block="S393_6"/>
6
+ <prediction seconds="1299" minutes="21" epochTime="1266681828075" isDeparture="false" dirTag="out" vehicle="1022" block="S393_7"/>
7
+ <prediction seconds="1929" minutes="32" epochTime="1266683011947" isDeparture="false" dirTag="out" vehicle="1027" block="S393_1"/>
8
+ <prediction seconds="2529" minutes="42" epochTime="1266683611947" isDeparture="false" dirTag="out" vehicle="1040" block="S393_2"/>
9
+ <prediction seconds="3129" minutes="52" epochTime="1266684211947" isDeparture="false" dirTag="out" vehicle="1037" block="S393_8"/>
10
+ </direction>
11
+ </predictions>
12
+ </body>