citibike 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ coverage
6
+ InstalledFiles
7
+ lib/bundler/man
8
+ pkg
9
+ rdoc
10
+ spec/reports
11
+ spec/cassettes
12
+ test/tmp
13
+ test/version_tmp
14
+ tmp
15
+
16
+ # YARD artifacts
17
+ .yardoc
18
+ _yardoc
19
+ doc/
data/.rspec ADDED
@@ -0,0 +1,4 @@
1
+ --order random
2
+ --fail-fast
3
+ -p
4
+ -c
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in citibike.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,59 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ citibike (0.0.1)
5
+ faraday
6
+ faraday_middleware
7
+ yajl-ruby
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ colorize (0.5.8)
13
+ coveralls (0.6.7)
14
+ colorize
15
+ multi_json (~> 1.3)
16
+ rest-client
17
+ simplecov (>= 0.7)
18
+ thor
19
+ diff-lcs (1.2.4)
20
+ faraday (0.8.7)
21
+ multipart-post (~> 1.1)
22
+ faraday_middleware (0.9.0)
23
+ faraday (>= 0.7.4, < 0.9)
24
+ metaclass (0.0.1)
25
+ mime-types (1.23)
26
+ mocha (0.13.1)
27
+ metaclass (~> 0.0.1)
28
+ multi_json (1.7.7)
29
+ multipart-post (1.2.0)
30
+ rake (10.1.0)
31
+ rest-client (1.6.7)
32
+ mime-types (>= 1.16)
33
+ rspec (2.14.1)
34
+ rspec-core (~> 2.14.0)
35
+ rspec-expectations (~> 2.14.0)
36
+ rspec-mocks (~> 2.14.0)
37
+ rspec-core (2.14.3)
38
+ rspec-expectations (2.14.0)
39
+ diff-lcs (>= 1.1.3, < 2.0)
40
+ rspec-mocks (2.14.1)
41
+ simplecov (0.7.1)
42
+ multi_json (~> 1.0)
43
+ simplecov-html (~> 0.7.1)
44
+ simplecov-html (0.7.1)
45
+ thor (0.18.1)
46
+ vcr (2.3.0)
47
+ yajl-ruby (1.1.0)
48
+
49
+ PLATFORMS
50
+ ruby
51
+
52
+ DEPENDENCIES
53
+ bundler (~> 1.3)
54
+ citibike!
55
+ coveralls
56
+ mocha
57
+ rake
58
+ rspec
59
+ vcr
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 Ethan Langevin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Ethan Langevin
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,121 @@
1
+ # Citibike - Wrapper for the unofficial Citibike NYC API
2
+ [![Build Status](https://secure.travis-ci.org/ejlangev/citibike.png)](http://travis-ci.org/ejlangev/citibike) [![Code Climate](https://codeclimate.com/github/ejlangev/citibike.png)](https://codeclimate.com/github/ejlangev/citibike) [![Coverage Status](https://coveralls.io/repos/ejlangev/citibike/badge.png?branch=master)](https://coveralls.io/r/ejlangev/citibike)
3
+
4
+ A simple gem for interacting with the city bike api. Gives you back
5
+ objects by default with consistently named methods for accessing data.
6
+ Also allows easy geographical searching for nearby stations via
7
+ latitude and longitude and as the crow flies distance.
8
+
9
+ I based it partially on another gem [citibikenyc](https://github.com/edgar/citibikenyc) but wanted to design mine a bit differently/play around with the API from scratch.
10
+
11
+ All contributions are welcome.
12
+
13
+ ## Installation
14
+
15
+ Add this line to your application's Gemfile:
16
+
17
+ gem 'citibike'
18
+
19
+ And then execute:
20
+
21
+ $ bundle
22
+
23
+ Or install it yourself as:
24
+
25
+ $ gem install citibike
26
+
27
+ ## Usage
28
+
29
+ ### Defaults
30
+ ```ruby
31
+ # Here's an example for stations, it also provides data about
32
+ # branches and helmets with more or less equivalent methods
33
+ helmets = Citibike.helmets
34
+ branches = Citibike.branches
35
+
36
+ # Returns a Citibike::Responses::Station object
37
+ stations = Citibike.stations
38
+
39
+ # This object contains the attributes of the response
40
+ stations.success? # True if the response was successful
41
+ stations.last_update # The time this data was last updated
42
+
43
+ # The response object includes Enumerable and proxies
44
+ # unknown methods to an underlying array
45
+ stations.each do |s|
46
+ puts s.class # Citibike::Station
47
+ puts s.latitude # Float
48
+ puts s.longitude # Float
49
+ puts s.available_bikes # Integer
50
+ puts s.available_docks # Integer
51
+ puts s.id # Integer id
52
+ end
53
+
54
+ # It's also simple to find stations within a given distance
55
+ # (in miles) of a LAT/LONG pair (in degrees)
56
+ # nearby will be an array of Citibike::Station objects
57
+ nearby = stations.all_within(LAT, LONG, DISTANCE)
58
+
59
+ # You can do the same thing given an instance of Citibike::Station
60
+ # Note that the results do NOT include station itself
61
+ nearby = stations.all_near(station, DISTANCE)
62
+
63
+ # It's also convenient to look up results by id within the
64
+ # list of stations
65
+ station = stations.find_by_id(ID)
66
+
67
+ # It also supports a variadic version
68
+ stats = stations.find_by_ids(ID1, ID2,...)
69
+
70
+ # Sometimes it might be convenient to a list of stations
71
+ # but still retain the methods of the Citibike::Responses::Station
72
+ # object.
73
+ filtered_stations = stations.clone_with(stations.select(&:active?))
74
+ ```
75
+
76
+ ### Configuration
77
+ If you want to change the default configuration for web requests it
78
+ is simple to do so given that it is using Faraday under the hood.
79
+ Simply create an instance of Citibike::Client and pass a hash of
80
+ your settings overrides.
81
+
82
+ ```ruby
83
+ client = Citibike::Client.new # initializes a default client
84
+
85
+ client.stations # returns Citibike::Responses::Station
86
+ client.helmets # returns Citibike::Responses::Helmet
87
+ client.branches # returns Citibike::Responses::Branch
88
+
89
+ # The simplest option is to unwrap the responses in which case they
90
+ # come back as a simple hash not a custom object
91
+ client = Citibike::Client.new(unwrapped: true)
92
+ client.stations # Instance of Hash
93
+
94
+ # Other configuration options and their default values
95
+ {
96
+ adapter: Faraday.default_adapter,
97
+ headers: {
98
+ 'Accept' => 'application/json; charset=utf-8',
99
+ 'UserAgent' => 'Citibike Gem'
100
+ },
101
+ proxy: nil,
102
+ ssl: {
103
+ verify: false
104
+ },
105
+ debug: false, # Turns on connection logging(currently unused)
106
+ test: false, # True if in a test
107
+ stubs: nil, # Stubs for the test connection
108
+ raw: false, # Don't process the response in any way
109
+ format_path: true, # Append the format to the request path if it lacks one
110
+ format: :json, # Default format
111
+ url: 'http://appservices.citibikenyc.com/'
112
+ }
113
+ ```
114
+
115
+ ## Contributing
116
+
117
+ 1. Fork it
118
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
119
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
120
+ 4. Push to the branch (`git push origin my-new-feature`)
121
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
data/citibike.gemspec ADDED
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'citibike/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "citibike"
8
+ spec.version = Citibike::VERSION
9
+ spec.authors = ["Ethan Langevin"]
10
+ spec.email = ["ejl6266@gmail.com"]
11
+ spec.description = %q{Client for the unofficial Citibike API in NYC}
12
+ spec.summary = %q{Provides an interface for interacting with Citibike NYC data}
13
+ spec.homepage = "http://github.com/ejlangev/citibike"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+ spec.add_development_dependency "coveralls"
25
+ spec.add_development_dependency "vcr"
26
+ spec.add_development_dependency "mocha"
27
+
28
+ spec.add_runtime_dependency "faraday"
29
+ spec.add_runtime_dependency "faraday_middleware"
30
+ spec.add_runtime_dependency "yajl-ruby"
31
+ end
data/lib/citibike.rb ADDED
@@ -0,0 +1,20 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'citibike/version'
4
+ require 'citibike/connection'
5
+ require 'citibike/api'
6
+ require 'citibike/client'
7
+
8
+ # Namespace for methods of accessing real time
9
+ # Citibike data for NYC
10
+ module Citibike
11
+
12
+ def self.method_missing(sym, *args, &block)
13
+ if Citibike::Client.respond_to?(sym)
14
+ return Citibike::Client.send(sym, *args, &block)
15
+ end
16
+
17
+ super
18
+ end
19
+
20
+ end
@@ -0,0 +1,82 @@
1
+ # encoding: UTF-8
2
+
3
+ module Citibike
4
+
5
+ # Base class for shared behavior between
6
+ # all types of objects from this api
7
+ class Api
8
+
9
+ Dir[File.expand_path('../apis/*.rb', __FILE__)].each { |f| require f }
10
+
11
+ # Radius of the earth in miles
12
+ EARTH_RADIUS = 3963.1676
13
+
14
+ attr_reader :internal_object
15
+
16
+ def initialize(data)
17
+ @internal_object = data
18
+ end
19
+
20
+ #
21
+ # Shortcut to access latitude
22
+ #
23
+ # @return [Float] [Object's latitude position]
24
+ def lat
25
+ self['latitude']
26
+ end
27
+
28
+ #
29
+ # Shortcut to access longitude
30
+ #
31
+ # @return [Float] [Object's longitude position]
32
+ def long
33
+ self['longitude']
34
+ end
35
+
36
+ #
37
+ # Returns the distance this object is from the given
38
+ # latitude and longitude. Distance is as the crow flies.
39
+ #
40
+ # @param lat [Float] [A latitude position]
41
+ # @param long [Float] [A longitude position]
42
+ #
43
+ # @return [Float] [Distance from the input postion in miles]
44
+ def distance_from(lat, long)
45
+ dLat = self.degrees_to_radians(lat - self.latitude)
46
+ dLon = self.degrees_to_radians(long - self.longitude)
47
+
48
+ lat1 = self.degrees_to_radians(lat)
49
+ lat2 = self.degrees_to_radians(self.latitude)
50
+
51
+ a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
52
+ Math.sin(dLon / 2) * Math.sin(dLon / 2) *
53
+ Math.cos(lat1) * Math.cos(lat2)
54
+
55
+ c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
56
+
57
+ EARTH_RADIUS * c
58
+ end
59
+
60
+ # Allow access to the hash through the object
61
+ def [](key)
62
+ self.internal_object[key.to_s]
63
+ end
64
+
65
+ # Allow hash keys to be used as methods
66
+ def method_missing(sym, *args, &block)
67
+ if self.internal_object.key?(sym.to_s)
68
+ return self.internal_object[sym.to_s]
69
+ end
70
+
71
+ super
72
+ end
73
+
74
+ protected
75
+
76
+ def degrees_to_radians(deg)
77
+ deg * Math::PI / 180
78
+ end
79
+
80
+ end
81
+
82
+ end
@@ -0,0 +1,11 @@
1
+ module Citibike
2
+
3
+ class Branch < Api
4
+
5
+ def self.path
6
+ return 'v1/branch/list'
7
+ end
8
+
9
+ end
10
+
11
+ end
@@ -0,0 +1,11 @@
1
+ module Citibike
2
+
3
+ class Helmet < Api
4
+
5
+ def self.path
6
+ return 'v1/helmet/list'
7
+ end
8
+
9
+ end
10
+
11
+ end
@@ -0,0 +1,83 @@
1
+ # encoding: UTF-8
2
+
3
+ module Citibike
4
+ # Represents a Station in the Citibike system and
5
+ # holds the data provided from the API itself along with
6
+ # some convenience methods accessing it
7
+ class Station < Api
8
+
9
+ def self.path
10
+ 'data2/stations.php'
11
+ end
12
+ #
13
+ # Returns if this station is active
14
+ #
15
+ # @return [Bool] [Stations is active or not]
16
+ def active?
17
+ self.internal_object['status'] == 'Active'
18
+ end
19
+
20
+ #
21
+ # Returns the number of available bikes at this station
22
+ #
23
+ # @return [Integer] [Number of available bikes]
24
+ def available_bikes
25
+ self['availableBikes']
26
+ end
27
+
28
+ #
29
+ # Returns the number of available docks at this station
30
+ #
31
+ # @return [Integer] [Number of available docks]
32
+ def available_docks
33
+ self['availableDocks']
34
+ end
35
+
36
+ #
37
+ # Returns the address of the station
38
+ #
39
+ # @return [String] [Address of the station]
40
+ def station_address
41
+ self['stationAddress']
42
+ end
43
+
44
+ #
45
+ # Nearby station array of hashes of the form
46
+ # [
47
+ # {
48
+ # :id => Integer,
49
+ # :distance => Float
50
+ # },
51
+ # ...
52
+ # ]
53
+ #
54
+ # @return [Array] [Array of hashes of nearby stations]
55
+ def nearby_stations
56
+ self['nearbyStations']
57
+ end
58
+
59
+ #
60
+ # Returns the ids of nearby stations for
61
+ # easy lookup in a Citibike::Responses::Station object
62
+ #
63
+ # @return [Array] [Array of integer ids]
64
+ def nearby_station_ids
65
+ @nearby_station_ids ||= self.nearby_stations.map { |s| s['id'] }
66
+ end
67
+
68
+ #
69
+ # Returns the distance to a nearby station (in miles?)
70
+ # @param id [Integer] [Id of a nearby station]
71
+ #
72
+ # @return [Float] [Distance to nearby station in miles]
73
+ def distance_to_nearby(id)
74
+ unless self.nearby_station_ids.include?(id.to_i)
75
+ raise "Station #{id} is not a nearby station"
76
+ end
77
+
78
+ sta = self.nearby_stations.find { |s| s['id'].to_i == id.to_i }
79
+ sta['distance']
80
+ end
81
+ end
82
+
83
+ end