muni 0.0.1

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.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in muni.gemspec
4
+ gemspec
@@ -0,0 +1 @@
1
+ Hello
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- mode: ruby -*-
3
+
4
+ require 'muni'
5
+ require 'thor'
6
+
7
+ class MUNI < Thor
8
+ desc "list", "Lists all routes"
9
+ def list
10
+ begin
11
+ Muni::Route.find(:all).each do |route|
12
+ say_status route.tag, route.title, :green
13
+ end
14
+ rescue Muni::NextBusError => e
15
+ say_status "ERROR", e.message, :red
16
+ end
17
+ end
18
+
19
+ desc "show [ROUTE_TAG] [DIRECTION]", "Shows a specifc route by name"
20
+ method_options %w( verbose -v ) => :boolean
21
+ def show(tag, direction = nil)
22
+ begin
23
+ route = Muni::Route.find(tag)
24
+ say_status route.tag, route.title, :green
25
+ dirs = direction ? [route.send(direction.downcase.to_sym)] : route.directions
26
+ dirs.each do |direction|
27
+ say_status direction.id, direction.name, :yellow
28
+ if options.verbose
29
+ print_table direction.stops.collect{|stop| [stop.tag, stop.title]}, {:ident => 8}
30
+ end
31
+ end
32
+ rescue Muni::NextBusError => e
33
+ say_status "ERROR", e.message, :red
34
+ end
35
+ end
36
+
37
+ desc "predict [ROUTE] [DIRECTION] [STOP]", "Retrieve predictions for a route at a specific stop"
38
+ def predict(route, direction, *stop)
39
+ # Join the remaining arguments so that predict can be called without
40
+ # having to quote the stop name.
41
+ stop = stop.join(' ')
42
+
43
+ # Get the bus route information
44
+ # TODO Only do this if necessary
45
+ bus = Muni::Route.find(route)
46
+
47
+ # Look up the stop information unless a numerica stop tag is specified
48
+ stop = bus.send(direction.downcase.to_sym).stop_at(stop).tag unless stop =~ /[1-9][0-9]+/
49
+
50
+ # Look up the direction information if using "inbound" or "outbound" instead
51
+ # of a direction tag.
52
+ direction = bus.send(direction.downcase.to_sym).id if direction =~ /(inbound|outbound)/i
53
+
54
+ # Create the stop object from the route, direction, and tag.
55
+ stop = Muni::Stop.new({:route_tag => route, :direction => direction, :tag => stop})
56
+
57
+ # Retrieve the predictions
58
+ begin
59
+ print_table stop.predictions.collect{|time| [time.vehicle, time.pretty_time]}, {:ident => 8}
60
+ rescue Muni::NextBusError => e
61
+ say_status "ERROR", e.message, :red
62
+ end
63
+ end
64
+
65
+ end
66
+
67
+ MUNI.start
@@ -0,0 +1,7 @@
1
+ require 'muni/base'
2
+ require 'muni/route'
3
+ require "muni/version"
4
+
5
+ module Muni
6
+
7
+ end
@@ -0,0 +1,27 @@
1
+ require 'mash'
2
+ module Muni
3
+ class NextBusError < StandardError; end
4
+ class Base < Mash
5
+ class << self
6
+ private
7
+ def fetch(command, options = nil)
8
+ url = build_url(command, options)
9
+ xml = Net::HTTP.get(URI.parse(url))
10
+ doc = XmlSimple.xml_in(xml)
11
+ fail NextBusError, doc['Error'].first['content'].gsub(/\n/,'') if doc['Error']
12
+ doc
13
+ end
14
+
15
+ def build_url(command, options = nil)
16
+ url = "http://webservices.nextbus.com/service/publicXMLFeed?command=%s&a=sf-muni" % command
17
+ if options
18
+ options.each { |key,value|
19
+ url << "&#{key}=#{value}"
20
+ }
21
+ end
22
+ url
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,12 @@
1
+ require 'amatch'
2
+
3
+ module Muni
4
+ class Direction < Base
5
+ def stop_at(place)
6
+ pattern = Amatch::Sellers.new(place)
7
+ stops.sort_by{ |stop|
8
+ pattern.match(stop.title)
9
+ }.first
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,20 @@
1
+ require 'action_view'
2
+
3
+ module Muni
4
+ class Prediction < Base
5
+ include ActionView::Helpers::DateHelper
6
+ def time
7
+ Time.at((epochTime.to_i / 1000.0).to_i)
8
+ end
9
+
10
+ def pretty_time
11
+ if time > Time.now
12
+ distance_of_time_in_words_to_now(time)
13
+ elsif time < Time.now
14
+ time_ago_in_words(time)
15
+ else
16
+ "Arriving"
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,73 @@
1
+ require 'net/http'
2
+ require 'xmlsimple'
3
+
4
+ require 'muni/stop'
5
+ require 'muni/direction'
6
+
7
+ module Muni
8
+ class Route < Base
9
+ def outbound
10
+ directions.select{|dir| dir.name =~ /outbound/i}.first
11
+ end
12
+
13
+ def inbound
14
+ directions.select{|dir| dir.name =~ /inbound/i}.first
15
+ end
16
+
17
+ class << self
18
+ def find(tag)
19
+ if tag == :all
20
+ find_all
21
+ else
22
+ find_by_tag(tag)
23
+ end
24
+ end
25
+
26
+ private
27
+ def find_all
28
+ document = fetch(:routeList)
29
+ document['route'].collect do |el|
30
+ Route.new(el)
31
+ end
32
+ end
33
+
34
+ def find_by_tag(tag)
35
+ document = fetch(:routeConfig, {:r => tag})
36
+ route = Route.new({:tag => document['route'].first['tag'], :title => document['route'].first['title']})
37
+
38
+ stops = {}
39
+
40
+ document['route'].first['stop'].each do |stop|
41
+ st = Stop.new({
42
+ :tag => stop['tag'],
43
+ :title => stop['title'],
44
+ :lat => stop['lat'],
45
+ :lon => stop['lat'],
46
+ :stopId => stop['lat'],
47
+ })
48
+ stops[st[:tag]] = st
49
+ end
50
+
51
+ directions = []
52
+ route.directions = document['route'].first['direction'].collect do |direction|
53
+ direction_stops = direction['stop'].collect do |stop|
54
+ stops[stop['tag']]
55
+ end
56
+
57
+ direction_stops.each do |stop|
58
+ stop.route_tag = route.tag
59
+ stop.direction = direction['tag']
60
+ end
61
+
62
+ Direction.new({
63
+ :id => direction['tag'],
64
+ :name => direction['title'],
65
+ :stops => direction_stops
66
+ })
67
+ end
68
+
69
+ route
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,13 @@
1
+ require 'muni/base'
2
+ require 'muni/prediction'
3
+
4
+ module Muni
5
+ class Stop < Base
6
+ def predictions
7
+ document = Stop.send(:fetch, :predictions, {:r => route_tag, :d => direction, :s => tag})
8
+ document['predictions'].first['direction'].first['prediction'].collect do |pred|
9
+ Prediction.new(pred)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,3 @@
1
+ module Muni
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "muni/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "muni"
7
+ s.version = Muni::VERSION
8
+ s.authors = ["Jeff Remer"]
9
+ s.email = ["jeff@threestarchina.com"]
10
+ s.homepage = "http://jeffremer.com/muni"
11
+ s.summary = %q{NextBus San Francisco Muni API Client}
12
+ s.description = %q{A simple NextBus API client for retrieving San Francisco Muni bus route and stop prediction information}
13
+
14
+ s.rubyforge_project = "muni"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_dependency "mash"
22
+ s.add_dependency "xml-simple"
23
+ s.add_dependency "actionpack"
24
+ s.add_dependency "amatch"
25
+ # For the executable
26
+ s.add_dependency "thor"
27
+ s.add_development_dependency "rspec", "~> 2.0.0.beta.22"
28
+ end
@@ -0,0 +1,12 @@
1
+ require 'muni/base'
2
+ require 'muni/route'
3
+
4
+ describe "Muni::Route" do
5
+ it "fetches all available routes" do
6
+ Muni::Route.find(:all)
7
+ end
8
+
9
+ it "fetches routes by tag" do
10
+ Muni::Route.find(21)
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ require 'muni/base'
2
+ require 'muni/route'
3
+
4
+ describe "Muni::Stop" do
5
+ it "#times fetches predictions" do
6
+ route = Muni::Route.find(21)
7
+ direction = route.directions.first
8
+ stop = direction.stops.first
9
+ times = stop.predictions
10
+ times.each do |time|
11
+ p time.pretty_time
12
+ end
13
+ end
14
+ end
metadata ADDED
@@ -0,0 +1,132 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: muni
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jeff Remer
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-05-31 00:00:00.000000000 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: mash
17
+ requirement: &2152729760 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: *2152729760
26
+ - !ruby/object:Gem::Dependency
27
+ name: xml-simple
28
+ requirement: &2152729320 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: *2152729320
37
+ - !ruby/object:Gem::Dependency
38
+ name: actionpack
39
+ requirement: &2152728900 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ type: :runtime
46
+ prerelease: false
47
+ version_requirements: *2152728900
48
+ - !ruby/object:Gem::Dependency
49
+ name: amatch
50
+ requirement: &2152728460 !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ type: :runtime
57
+ prerelease: false
58
+ version_requirements: *2152728460
59
+ - !ruby/object:Gem::Dependency
60
+ name: thor
61
+ requirement: &2152728000 !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ type: :runtime
68
+ prerelease: false
69
+ version_requirements: *2152728000
70
+ - !ruby/object:Gem::Dependency
71
+ name: rspec
72
+ requirement: &2152727420 !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: 2.0.0.beta.22
78
+ type: :development
79
+ prerelease: false
80
+ version_requirements: *2152727420
81
+ description: A simple NextBus API client for retrieving San Francisco Muni bus route
82
+ and stop prediction information
83
+ email:
84
+ - jeff@threestarchina.com
85
+ executables:
86
+ - muni
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - .gitignore
91
+ - Gemfile
92
+ - README.md
93
+ - Rakefile
94
+ - bin/muni
95
+ - lib/muni.rb
96
+ - lib/muni/base.rb
97
+ - lib/muni/direction.rb
98
+ - lib/muni/prediction.rb
99
+ - lib/muni/route.rb
100
+ - lib/muni/stop.rb
101
+ - lib/muni/version.rb
102
+ - muni.gemspec
103
+ - spec/route_spec.rb
104
+ - spec/stop_spec.rb
105
+ has_rdoc: true
106
+ homepage: http://jeffremer.com/muni
107
+ licenses: []
108
+ post_install_message:
109
+ rdoc_options: []
110
+ require_paths:
111
+ - lib
112
+ required_ruby_version: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ required_rubygems_version: !ruby/object:Gem::Requirement
119
+ none: false
120
+ requirements:
121
+ - - ! '>='
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ requirements: []
125
+ rubyforge_project: muni
126
+ rubygems_version: 1.6.2
127
+ signing_key:
128
+ specification_version: 3
129
+ summary: NextBus San Francisco Muni API Client
130
+ test_files:
131
+ - spec/route_spec.rb
132
+ - spec/stop_spec.rb