reittiopas2 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: dd7af7e068a579e9245cfb7ca79121e9a4310fdd
4
+ data.tar.gz: 5445ce3e8a5846c31ac8ccad61c557eb3375594c
5
+ SHA512:
6
+ metadata.gz: 9377560e0fa31558ccc6f733a4c0de539b47d86686d7adb62240c833bf49a67a5959270518c2d1bbae3495a13a2d3a225acb7b9b60c78d16b052c0bd7da91a5b
7
+ data.tar.gz: 7fc721b52e394ae44299c1e32a9fab26a157dfe139fd97f421adac22be4d68378494288748f2281becf3733627593a35626f76f7fed7ddd6787b276303cd244d
data/.gitignore ADDED
@@ -0,0 +1,20 @@
1
+ *.swp
2
+ *.swo
3
+
4
+ *.gem
5
+ *.rbc
6
+ .bundle
7
+ .config
8
+ .yardoc
9
+ Gemfile.lock
10
+ InstalledFiles
11
+ _yardoc
12
+ coverage
13
+ doc/
14
+ lib/bundler/man
15
+ pkg
16
+ rdoc
17
+ spec/reports
18
+ test/tmp
19
+ test/version_tmp
20
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,7 @@
1
+ guard 'rspec', :version => 2 do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
4
+ watch(%r{^lib/reittiopas2/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
5
+ watch('spec/spec_helper.rb') { "spec" }
6
+ end
7
+
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Iivari Äikäs, Jussi Virtanen
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,32 @@
1
+ # Reittiopas2
2
+
3
+ Barebones gem wrapping
4
+ [reittiopas v2 API](http://developer.reittiopas.fi/pages/fi/reittiopas-api.php),
5
+ providing simple route searching and geocoding.
6
+
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ gem 'reittiopas2'
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install reittiopas2
21
+
22
+ ## Usage
23
+
24
+ Sadly, you have to dive into docstrings for documentation.
25
+
26
+ ## Contributing
27
+
28
+ 1. Fork it
29
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
30
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
31
+ 4. Push to the branch (`git push origin my-new-feature`)
32
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require "rspec/core/rake_task"
4
+
5
+ RSpec::Core::RakeTask.new('spec')
6
+ task :default => :spec
@@ -0,0 +1,84 @@
1
+ class Reittiopas2
2
+
3
+ # Handles connectting to Reittiopas API and transforms query hashes into form
4
+ # that Reittiopas API can digest them. This class is also responsible for
5
+ # parsing response into Ruby readable form.
6
+ #
7
+ # @api private
8
+ class Connection
9
+
10
+ def initialize(username, password)
11
+ @base_url = "http://api.reittiopas.fi/hsl/prod/"
12
+ @base_query = {"user" => username, "pass" => password}
13
+ end
14
+
15
+
16
+ # Forms proper query from credentials + given query and sends it to API
17
+ # endpoint. Also parses response into Ruby readable form.
18
+ #
19
+ # In case of errors, be those from API endpoint or from internal processes,
20
+ # return value will be hash with one field 'error' which contains error
21
+ # message.
22
+ #
23
+ # In fatal error situations Exception will be thrown, only exception for this
24
+ # rule is JSON::ParserException which is caught and returned as value of
25
+ # 'error' field.
26
+ #
27
+ # @param [Hash] query the query which is to be sent to API endpoint
28
+ # @return [Hash] the whatever data was requested from API, or Hash containing
29
+ # key 'error' containing error message.
30
+ def perform_query(query={})
31
+ query = fix_arrays(query)
32
+ uri = Addressable::URI.parse(@base_url)
33
+ uri.query_values = @base_query.merge(query)
34
+
35
+ res = Net::HTTP.get_response(uri)
36
+
37
+ if res.code == '500'
38
+ # In case of errors, code 500 is returned and there is explanation in
39
+ # response body as HTML.
40
+ {'error' => res.body}
41
+ elsif res.body.nil? or res.body.empty?
42
+ {'error' => "Response body was empty!"}
43
+ else
44
+ JSON.load res.body.to_s
45
+ end
46
+ rescue JSON::ParserError => ex
47
+ {'error' => ex.class}
48
+ end
49
+
50
+
51
+ private
52
+
53
+
54
+ # Reittiopas API wants arrays as values seperated with pipes. This method
55
+ # checks query for arrays and converts them to API friendly versions.
56
+ #
57
+ # @param [Hash] query the query to be checked for arrays to convert.
58
+ # @return [Hash] hash where arrays are converted to pipe presentation.
59
+ def fix_arrays(query)
60
+ retval = {}
61
+ query.each do |key, val|
62
+ if val.is_a? Array
63
+ retval[key] = array_to_query_form(val)
64
+ else
65
+ retval[key] = val
66
+ end
67
+ end
68
+ retval
69
+ end
70
+
71
+
72
+ def array_to_query_form(arr)
73
+ raise(ArgumentError, "arr is not Array") unless arr.is_a? Array
74
+
75
+ retval = ""
76
+ arr.each do |val|
77
+ retval << val << "|"
78
+ end
79
+
80
+ retval[0..-2] # Remove last pipe
81
+ end
82
+ end
83
+
84
+ end
@@ -0,0 +1,123 @@
1
+ class Reittiopas2
2
+
3
+ # Module containing public API to perform geocoding and reverse geocoding
4
+ # operations.
5
+ #
6
+ # Location info returned from geocode methods is following form:
7
+ # @example geocode location info
8
+ # [
9
+ # {
10
+ # 'locType' => 'stop',
11
+ # 'locTypeId' => '10',
12
+ # 'name' => 'Elielinaukio',
13
+ # 'matchedName' => 'Elielinaukio',
14
+ # 'lang' => 'fi',
15
+ # 'city' => 'Helsinki',
16
+ # 'coords' => '2552335,6673660',
17
+ # 'details' => {
18
+ # 'address' => 'Elielinaukio',
19
+ # 'code' => '1020128',
20
+ # 'shortCode' => '2020',
21
+ # 'lines' => [
22
+ # '1015A 1:Länsiterminaali',
23
+ # '1015A 2:Elielinaukio'
24
+ # ]
25
+ # }
26
+ # }
27
+ # ]
28
+ #
29
+ #
30
+ # @example reverse_geocode location info
31
+ # [
32
+ # {
33
+ # 'locType' => 'address',
34
+ # 'locTypeId' => '900',
35
+ # 'name' => 'Purotie 8, Helsinki',
36
+ # 'matchedName' => nil,
37
+ # 'lang' => 'fi',
38
+ # 'city' => 'Helsinki',
39
+ # 'coords' => '2552335,6673660',
40
+ # 'distance' => '38.4187454245971',
41
+ # 'details' => {
42
+ # 'address' => nil,
43
+ # 'houseNumber' => '8',
44
+ # }
45
+ # }
46
+ # ]
47
+ #
48
+ module Geocoding
49
+
50
+ # Search for information about place.
51
+ #
52
+ # @param [String] place_name the name of place to be searched. This can be
53
+ # street name, point of interest, bus stop, etc.
54
+ # @param [Hash] opts the optional parameters which can be used to alter search
55
+ # results. Any invalid keys are removed from query parameters, but values
56
+ # are never checked.
57
+ #
58
+ # @option opts [String] 'cities' ('all') List of cities which are accepted.
59
+ # @option opts [String] 'loc_types' ('all') List of location types which are
60
+ # accepted.
61
+ # @option opts [Integer] 'disable_error_correction' (0) Disable levenshtein
62
+ # error correction. 1 = Error correction NOT in use, 0 = error correction in
63
+ # use.
64
+ # @option opts [Integer] 'disable_unique_stop_names' (1) Disable unique stop
65
+ # names in the result. 1 = all stops are shown in the result, 0 = only one
66
+ # stop is included in the result for stops with same name.
67
+ #
68
+ # @return [Array<Hash>] array containing location hashes matched given query.
69
+ # @see #reverse_geocode
70
+ def geocode(place_name, opts={})
71
+ clean = geocode_remove_invalid_keys(opts)
72
+ query = {'request' =>'geocode', 'key' => place_name}.merge(clean)
73
+ @connection.perform_query(query)
74
+ end
75
+
76
+
77
+ # Do reverse geocode search to find information about given coordinates.
78
+ #
79
+ # @param [String] coords the coordinate pair "<x_coordinate>,<y_coordinate>"
80
+ # @param [Hash] opts the optional parameters which can be used to alter query
81
+ # results. Any invalid keys are removed from query parameters, but values
82
+ # are never checked.
83
+ #
84
+ # @option opts [Integer] 'limit' (1) Limit for the number of locations
85
+ # returned.
86
+ # @option opts [Integer] 'radius' (1000) Radius of the search in meters. Range
87
+ # 1-1000.
88
+ # @option opts [String] 'result_contains' ('address') Limit the search to
89
+ # given location types. Possible values are: address, stop, poi
90
+ #
91
+ # @return [Array<Hash>] array containing location hashes matching given
92
+ # coordinates.
93
+ # @see #geocode
94
+ def reverse_geocode(coords, opts={})
95
+ clean = reverse_geocode_remove_invalid_keys(opts)
96
+ query = {'request' => 'reverse_geocode', 'coordinate' => coords}.merge(clean)
97
+ @connection.perform_query(query)
98
+ end
99
+
100
+
101
+ private
102
+
103
+
104
+ def geocode_remove_invalid_keys(query)
105
+ whitelist = [ 'key', 'cities', 'loc_types', 'disable_error_correction',
106
+ 'disable_unique_stop_names' ]
107
+
108
+ query.select do |key, val|
109
+ whitelist.include? key
110
+ end
111
+ end
112
+
113
+ def reverse_geocode_remove_invalid_keys(query)
114
+ whitelist = [ 'coordinate', 'limit', 'radius', 'result_contains' ]
115
+
116
+ query.select do |key, val|
117
+ whitelist.include? key
118
+ end
119
+ end
120
+ end
121
+
122
+
123
+ end
@@ -0,0 +1,145 @@
1
+
2
+ class Reittiopas2
3
+
4
+ # Routing module contains all needed functionality to query route information.
5
+ module Routing
6
+
7
+ # Query the route between two coordinate points. In routing it is recommended
8
+ # to use street addresses (their coordinates) as it is difficult for the end
9
+ # user to know where exactly a stop is located. There might be several stops
10
+ # with the same name that are located far away from each other (for example
11
+ # the stop 'Sturenkatu' in Helsinki).
12
+ #
13
+ # @param [String, Hash] from Coordinates where route begins.
14
+ # @param [String, Hash] to Coordinates where route ends.
15
+ # @param [Hash] opts Optional parameters to query.
16
+ # @return [Hash] the route between given points.
17
+ #
18
+ # @option opts [String] via
19
+ # @option opts [Number] date (Current date) YYYYMMDD
20
+ # @option opts [Number] time (Current time) HHMM
21
+ # @option opts [String] timetype ('departure') Time of the request is either
22
+ # * 'departure'
23
+ # * 'arrival'
24
+ # @option opts [Number] via_time Minimum time spent at a via_point in
25
+ # minutes.
26
+ # @option opts [String] zone (No restriction) Ticket zone, possible values
27
+ # are:
28
+ # * 'helsinki',
29
+ # * 'espoo',
30
+ # * 'vantaa',
31
+ # * 'whole' (Helsinki, Espoo, Kauniainen, Vantaa, Kirkkonummi and Kerava.
32
+ # Whole region, excluding non HSL areas),
33
+ # * 'region' (Helsinki, Espoo, Kauniainen and Vantaa region)
34
+ # @option opts [String, Array<String>] transport_types ('all') Transport
35
+ # types included in the request. Possible types are:
36
+ # * 'all',
37
+ # * 'bus',
38
+ # * 'train',
39
+ # * 'metro',
40
+ # * 'tram',
41
+ # * 'service',
42
+ # * 'U-line',
43
+ # * 'ferry',
44
+ # * 'walk' (ONLY walking)
45
+ # @option opts [String] optimize ('default') Routing profile. Possible
46
+ # values:
47
+ # * 'default' = (wait cost=1.0, walk cost=1.2, change cost=6),
48
+ # * 'fastest' = (wait cost=1.0, walk cost=1.0, change cost=0),
49
+ # * 'least_transfers' = (wait cost=1.0, walk cost=1.5, change cost=20),
50
+ # * 'least_walking' = (wait cost=1.0, walk cost=5.0, change cost=6)
51
+ # @option opts [Number] change_margin (3) The minimum number of minutes
52
+ # between changes. Range 0-10.
53
+ # @option opts [Number] change_cost Penalty for a change, user is rather
54
+ # N minutes later at the destination than changes to another vehicle. Range
55
+ # 1-99. This parameter overrides the setting from optimize.
56
+ # @option opts [Number] wait_cost With this parameter you can weigh wait
57
+ # time cost when calculating fastest route. Range 0.1 – 10.0. This
58
+ # parameter overrides optimize parameter.
59
+ # @option opts [Number] walk_speed (70) Walking speed. m/min, range 1-500.
60
+ # @option opts [String] detail ('normal') Detail level of the response.
61
+ # Possible values:
62
+ # * 'limited' = only legs are returned,
63
+ # * 'normal' = intermiediate stops are returned,
64
+ # * 'full' = route share coordinates are returned
65
+ # @option opts [Number] show (3) Number of routes in the response.
66
+ # @option opts [Number] mode_cost_|transport_type_id| Mode costs for different
67
+ # transport types. These settings override previous values set with
68
+ # transport_types-parameter. Values may range from 0.1 to 10 or it can be -1
69
+ # when the transport type is excluded from routing.
70
+ #
71
+ # transport_type_id in the parameter name may be any of the following:
72
+ # 1. Helsinki internal bus lines
73
+ # 2. trams
74
+ # 3. Espoo internal bus lines
75
+ # 4. Vantaa internal bus lines
76
+ # 5. regional bus lines
77
+ # 6. metro
78
+ # 7. ferry
79
+ # 8. U-lines
80
+ # 12. commuter trains
81
+ # 21. Helsinki service lines
82
+ # 22. Helsinki night buses
83
+ # 23. Espoo service lines
84
+ # 24. Vantaa service lines
85
+ # 25. region night buses
86
+ # 36. Kirkkonummi internal bus lines
87
+ # 39. Kerava internal bus lines
88
+ def route(from, to, opts={})
89
+ if from.class != to.class
90
+ return {'error' => 'ArgumentError: from.class != to.class'}
91
+ end
92
+
93
+ if from.is_a? String
94
+ query = route_query_from_strings(from, to)
95
+ elsif from.is_a? Hash and from['coordinate'] and to['coordinate']
96
+ query = route_query_from_locations(from, to)
97
+ else
98
+ return {'error' => 'ArgumentError: locations were not acceptable types'}
99
+ end
100
+
101
+ clean_opts = route_remove_invalid_keys(opts)
102
+ @connection.perform_query(clean_opts.merge(query))
103
+ end
104
+
105
+
106
+ private
107
+
108
+
109
+ def route_query_from_strings(from, to)
110
+ {
111
+ 'request' => 'route',
112
+ 'from' => from,
113
+ 'to' => to
114
+ }
115
+ end
116
+
117
+ def route_query_from_locations(from, to)
118
+ {
119
+ 'request' => 'route',
120
+ 'from' => from['coordinate'],
121
+ 'to' => to['coordinate']
122
+ }
123
+ end
124
+
125
+ def route_remove_invalid_keys(opts)
126
+ keys = ['via', 'date', 'time', 'timetype', 'via_time', 'zone',
127
+ 'transport_types', 'optimize', 'change_margin', 'change_cost',
128
+ 'wait_cost', 'walk_cost', 'walk_speed', 'detail', 'show']
129
+ mode_costs = ['mode_cost_1', 'mode_cost_2', 'mode_cost_3', 'mode_cost_4',
130
+ 'mode_cost_5', 'mode_cost_6', 'mode_cost_7', 'mode_cost_7',
131
+ 'mode_cost_8', 'mode_cost_12', 'mode_cost_21', 'mode_cost_22',
132
+ 'mode_cost_23', 'mode_cost_24', 'mode_cost_25', 'mode_cost_36',
133
+ 'mode_cost_39']
134
+
135
+ whitelist = []
136
+ whitelist.concat keys
137
+ whitelist.concat mode_costs
138
+
139
+ opts.select do |key, val|
140
+ whitelist.include? key
141
+ end
142
+ end
143
+ end
144
+
145
+ end
@@ -0,0 +1,3 @@
1
+ class Reittiopas2
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,19 @@
1
+ require "net/http"
2
+ require "addressable/uri"
3
+ require "json"
4
+
5
+ require "reittiopas2/version"
6
+
7
+ require "reittiopas2/connection"
8
+ require "reittiopas2/geocoding"
9
+ require "reittiopas2/routing"
10
+
11
+ class Reittiopas2
12
+ include Geocoding
13
+ include Routing
14
+
15
+ def initialize(username, password)
16
+ @connection = Reittiopas2::Connection.new(username, password)
17
+ end
18
+ end
19
+
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/reittiopas2/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Iivari Äikäs", "Jussi Virtanen"]
6
+ gem.email = ["iivari.aikas@gmail.com"]
7
+ gem.description = %q{Reittiopas version 2 API library.}
8
+ gem.summary = %q{Gem providing easy access to reittiopas version 2 API.}
9
+ gem.homepage = "https://github.com/orva/reittiopas2"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "reittiopas2"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Reittiopas2::VERSION
17
+
18
+ gem.add_dependency "json", "~> 1.8"
19
+ gem.add_dependency "addressable", "~> 2.3"
20
+
21
+ gem.add_development_dependency "rspec", "~> 2.14"
22
+ gem.add_development_dependency "webmock", "~> 1.15"
23
+ gem.add_development_dependency "guard-rspec", "~> 4.0"
24
+ gem.add_development_dependency "simplecov", "~> 0.7"
25
+ end
@@ -0,0 +1,84 @@
1
+ require "spec_helper"
2
+
3
+ describe Reittiopas2::Connection do
4
+ before :each do
5
+ @user = "username"
6
+ @pwd = "password"
7
+ @conn = Reittiopas2::Connection.new @user, @pwd
8
+
9
+ @base_url = "http://api.reittiopas.fi/hsl/prod/"
10
+ @query = {"user" => @user, "pass" => @pwd}
11
+ end
12
+
13
+
14
+
15
+ describe "query forming" do
16
+ it "should put pipes into arrays" do
17
+ q = @query.merge({ "array" => ["one", "two", "three"] })
18
+ expected_q = @query.merge({ "array" => "one|two|three"})
19
+
20
+ stub_request(:get, @base_url).
21
+ with(:query => expected_q)
22
+
23
+ @conn.perform_query(q)
24
+ a_request(:get, @base_url).with(:query => expected_q).should have_been_made
25
+ end
26
+
27
+
28
+ it "should form query only with credentials in case no query given." do
29
+ stub_request(:get, @base_url).
30
+ with(:query => @query)
31
+
32
+ @conn.perform_query
33
+ a_request(:get, @base_url).with(:query => @query).should have_been_made
34
+ end
35
+ end
36
+
37
+
38
+ describe "perform_query return value" do
39
+ it "should be hash filled with data in case of succesful query." do
40
+ body = '{"max":5000,"used":44}'
41
+ query = @query.merge({ "request" => "stats" })
42
+
43
+ stub_request(:get, @base_url).
44
+ with(:query => query).
45
+ to_return(:body => body)
46
+
47
+ ret = @conn.perform_query(query)
48
+ ret.should == {"max" => 5000, "used" => 44}
49
+ end
50
+
51
+
52
+ it "should have response body in error field in case of 500 code" do
53
+ body = "Hello! Error!"
54
+
55
+ stub_request(:get, @base_url).
56
+ with(:query => @query).
57
+ to_return(:status => 500, :body => body)
58
+
59
+ ret = @conn.perform_query
60
+ ret.should == {'error' => body}
61
+ end
62
+
63
+
64
+ it "should error field in case of JSON parse error" do
65
+ body = "well this is not valid JSON"
66
+
67
+ stub_request(:get, @base_url).
68
+ with(:query => @query).
69
+ to_return(:body => body)
70
+
71
+ ret = @conn.perform_query
72
+ ret['error'].should_not be_nil
73
+ end
74
+
75
+ it "should have error field in case of empty response body" do
76
+ stub_request(:get, @base_url).
77
+ with(:query => @query).
78
+ to_return(:body => '')
79
+
80
+ ret = @conn.perform_query
81
+ ret.should == {'error' => 'Response body was empty!'}
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,2 @@
1
+ [{"locType":"stop","locTypeId":10,"name":"Elielinaukio","matchedName":"Elielinaukio","lang":"fi","city":"Helsinki","coords":"2552335,6673660","details":{"address":"Elielinaukio","code":"1020128","shortCode":"2020","changeCost":0.6,"lines":["1015A 1:L\u00e4nsiterminaali","1015A 2:Elielinaukio","1018N 1:Elielinaukio","1039N 1:Konala-Malminkartano","1040 2:Elielinaukio","1042 2:Elielinaukio","1043 2:Elielinaukio","1063 2:Elielinaukio","1069 2:Elielinaukio","1102N 1:Kannelm\u00e4ki"],"transport_type_id":1}},{"locType":"stop","locTypeId":10,"name":"Elielinaukio","matchedName":"Elielinaukio","lang":"fi","city":"Helsinki","coords":"2552355,6673705","details":{"address":"Laituri 21","code":"1020129","shortCode":"2021","changeCost":0.3,"lines":["1040 1:Pohjois-Haagan asema","1102N 1:Kannelm\u00e4ki"],"transport_type_id":1}},{"locType":"stop","locTypeId":10,"name":"Elielinaukio","matchedName":"Elielinaukio","lang":"fi","city":"Helsinki","coords":"2552310,6673680","details":{"address":"Elielinaukio","code":"1020130","shortCode":"","changeCost":0.6,"lines":["2210N 1:Espoon keskus-Eliel","2220N 1:Jupperi-Eliel"],"transport_type_id":1}},{"locType":"stop","locTypeId":10,"name":"Elielinaukio","matchedName":"Elielinaukio","lang":"fi","city":"Helsinki","coords":"2552355,6673712","details":{"address":"Laituri 22","code":"1020131","shortCode":"2022","changeCost":0.3,"lines":["1043 1:Hakuninmaa"],"transport_type_id":1}},{"locType":"stop","locTypeId":10,"name":"Elielinaukio","matchedName":"Elielinaukio","lang":"fi","city":"Helsinki","coords":"2552355,6673733","details":{"address":"Laituri 25","code":"1020132","shortCode":"2025","changeCost":0.6,"lines":["1042 1:Kannelm\u00e4ki"],"transport_type_id":1}},{"locType":"stop","locTypeId":10,"name":"Elielinaukio","matchedName":"Elielinaukio","lang":"fi","city":"Helsinki","coords":"2552355,6673719","details":{"address":"Laituri 23","code":"1020133","shortCode":"2023","changeCost":0.3,"lines":["1018N 1:Elielinaukio","1063 1:Palohein\u00e4"],"transport_type_id":1}},{"locType":"stop","locTypeId":10,"name":"Elielinaukio","matchedName":"Elielinaukio","lang":"fi","city":"Helsinki","coords":"2552355,6673726","details":{"address":"Laituri 24","code":"1020135","shortCode":"2024","changeCost":0.3,"lines":["1039N 1:Konala-Malminkartano","1069 1:Malmi"],"transport_type_id":1}},{"locType":"stop","locTypeId":10,"name":"Elielinaukio","matchedName":"Elielinaukio","lang":"fi","city":"Helsinki","coords":"2552335,6673662","details":{"address":"Elielinaukio","code":"1020228","shortCode":"2020","changeCost":0.6,"lines":["2194 2:Elielinaukio, l. 36","2194A 2:Elielinaukio, l. 36","2195 2:Elielinaukio, l. 36","2195N 2:Elielinaukio, l. 36","2206 2:Elielinaukio, l. 35","2206A 2:Elielinaukio, l. 35","2212 2:Elielinaukio, l. 35","2213 2:Elielinaukio, l. 35","2213N 2:Elielinaukio, l. 35","2231 2:Elielinaukio, l. 34","2231K 2:Elielinaukio, l. 34","2231N 2:Elielinaukio, l. 34","2247 2:Elielinaukio, l. 34","2247A 2:Elielinaukio, l. 34","2248 2:Elielinaukio, l. 34","2248A 2:Elielinaukio, l. 34","2270 2:Elielinaukio, l. 34","2270A 2:Elielinaukio, l. 34","2270N 2:Elielinaukio, l. 34","2315 2:Elielinaukio, l. 26","2321 2:Elielinaukio, l. 26","2324 2:Elielinaukio, l. 27","2324K 2:Elielinaukio, l. 27","2324N 2:Elielinaukio, l. 27","2345 2:Elielinaukio, l. 27","2345N 2:Elielinaukio, l. 27","4300N 2:Elielinaukio, l. 28","4360 2:Elielinaukio, l. 29","4360K 2:Elielinaukio, l. 29","4361 2:Elielinaukio, l. 29","4362 2:Elielinaukio, l. 29","4362N 2:Elielinaukio, l. 29","4362T 2:Elielinaukio, l. 29","4363 2:Elielinaukio, l. 28","4363A 2:Elielinaukio, l. 28","4364 2:Elielinaukio, l. 29","4415 2:Elielinaukio, l. 31","4451 2:Elielinaukio, l. 31","4452 2:Elielinaukio, l. 32","4452K 2:Elielinaukio, l. 32","4453 2:Elielinaukio, l. 33","4453Z 2:Elielinaukio, l. 33","4474 2:Elielinaukio,l.31","4474A 2:Elielinaukio,l.31","4474AK2:Elielinaukio,l.31","4474K 2:Elielinaukio,l.31","4474V 2:Elielinaukio,l.31","4474VK2:Elielinaukio,l.31"],"transport_type_id":1}},{"locType":"stop","locTypeId":10,"name":"Elielinaukio","matchedName":"Elielinaukio","lang":"fi","city":"Helsinki","coords":"2552355,6673740","details":{"address":"Laituri 26","code":"1020239","shortCode":"2026","changeCost":0.3,"lines":["2315 1:Vanhakartano","2321 1:Koskelo"],"transport_type_id":1}},{"locType":"stop","locTypeId":10,"name":"Elielinaukio","matchedName":"Elielinaukio","lang":"fi","city":"Helsinki","coords":"2552355,6673747","details":{"address":"Laituri 27","code":"1020241","shortCode":"2027","changeCost":0.3,"lines":["2324 1:Kalaj\u00e4rvi","2324K 1:Kalaj\u00e4rvi","2324N 1:Kalaj\u00e4rvi","2345 1:Rinnekoti","2345N 1:Rinnekoti"],"transport_type_id":1}},{"locType":"stop","locTypeId":10,"name":"Elielinaukio","matchedName":"Elielinaukio","lang":"fi","city":"Helsinki","coords":"2552355,6673754","details":{"address":"Laituri 28","code":"1020243","shortCode":"2028","changeCost":0.3,"lines":["4300N 1:H\u00e4meenkyl\u00e4","4363 1:H\u00e4meenkyl\u00e4","4363A 1:Askisto"],"transport_type_id":1}},{"locType":"stop","locTypeId":10,"name":"Elielinaukio","matchedName":"Elielinaukio","lang":"fi","city":"Helsinki","coords":"2552355,6673761","details":{"address":"Laituri 29","code":"1020245","shortCode":"2029","changeCost":0.3,"lines":["4360 1:Petikko","4360K 1:Petikko","4361 1:Petikko","4362 1:Petikko","4362N 1:Askisto","4362T 1:Petikko","4364 1:Askisto"],"transport_type_id":1}},{"locType":"stop","locTypeId":10,"name":"Elielinaukio","matchedName":"Elielinaukio","lang":"fi","city":"Helsinki","coords":"2552305,6673698","details":{"address":"Laituri 31","code":"1020249","shortCode":"2031","changeCost":0.3,"lines":["4415 1:Lentoasema, lait. 4","4451 1:Lentoasema, lait. 4","4474 1:Katriinan sairaala","4474A 1:Reuna","4474AK1:Reuna","4474K 1:Katriinan sairaala","4474V 1:Koivup\u00e4\u00e4","4474VK1:Koivup\u00e4\u00e4"],"transport_type_id":1}},{"locType":"stop","locTypeId":10,"name":"Elielinaukio","matchedName":"Elielinaukio","lang":"fi","city":"Helsinki","coords":"2552304,6673705","details":{"address":"Laituri 32","code":"1020251","shortCode":"2032","changeCost":0.3,"lines":["4400N 1:Pakkala","4452 1:Myyrm\u00e4en as, lait. 7","4452K 1:Myyrm\u00e4en as, lait. 7"],"transport_type_id":1}},{"locType":"stop","locTypeId":10,"name":"Elielinaukio","matchedName":"Elielinaukio","lang":"fi","city":"Helsinki","coords":"2552303,6673712","details":{"address":"Laituri 33","code":"1020253","shortCode":"2033","changeCost":0.3,"lines":["4453 1:Martinlaakso, lait.3","4453Z 1:Martinlaakso, lait.3"],"transport_type_id":1}},{"locType":"stop","locTypeId":10,"name":"Elielinaukio","matchedName":"Elielinaukio","lang":"fi","city":"Helsinki","coords":"2552302,6673719","details":{"address":"Laituri 34","code":"1020255","shortCode":"2034","changeCost":0.3,"lines":["2210N 1:Espoon keskus-Eliel","2220N 1:Jupperi-Eliel","2231 1:H\u00e4mevaara","2231K 1:H\u00e4mevaara","2231N 1:J\u00e4rvenper\u00e4","2247 1:L\u00e4hderanta","2247A 1:Jupperi","2248 1:L\u00e4hderanta","2248A 1:Jupperi","2270 1:Tuomarila","2270A 1:Kuurinniitty","2270N 1:Kuurinniitty"],"transport_type_id":1}},{"locType":"stop","locTypeId":10,"name":"Elielinaukio","matchedName":"Elielinaukio","lang":"fi","city":"Helsinki","coords":"2552301,6673726","details":{"address":"Laituri 35","code":"1020257","shortCode":"2035","changeCost":0.3,"lines":["2206 1:Karamalmi","2206A 1:Karamalmi","2212 1:Kauniainen","2213 1:Kauklahti","2213N 1:Kauklahti"],"transport_type_id":1}},{"locType":"stop","locTypeId":10,"name":"Elielinaukio","matchedName":"Elielinaukio","lang":"fi","city":"Helsinki","coords":"2552300,6673733","details":{"address":"Laituri 36","code":"1020259","shortCode":"2036","changeCost":0.3,"lines":["2194 1:Tapiola","2194A 1:Orion","2195 1:Latokaski","2195N 1:Latokaski"],"transport_type_id":1}}]
2
+
@@ -0,0 +1,2 @@
1
+ [{"locType":"address","locTypeId":"900","name":"Purotie 8, Helsinki","matchedName":null,"lang":"fi","city":"Helsinki","coords":"2548220,6678498","distance":"38.4187454245971","details":{"address":null,"houseNumber":"8"}}]
2
+
@@ -0,0 +1,136 @@
1
+ require "spec_helper"
2
+
3
+ describe Reittiopas2::Geocoding do
4
+
5
+ before :each do
6
+ user = "username"
7
+ pwd = "password"
8
+ @reitti = Reittiopas2.new(user, pwd)
9
+
10
+ @base_query = {'user' => user, 'pass' => pwd}
11
+ @base_url = "http://api.reittiopas.fi/hsl/prod/"
12
+ end
13
+
14
+
15
+ describe "geocoding" do
16
+ before :each do
17
+ @city = 'Helsinki'
18
+ @query = @base_query.merge('request' => 'geocode', 'key' => @city)
19
+ end
20
+
21
+
22
+ it "should perform request to API geocode endpoint." do
23
+ stub_request(:get, @base_url).
24
+ with(:query => @query)
25
+
26
+ @reitti.geocode(@city)
27
+
28
+ a_request(:get, @base_url).
29
+ with(:query => @query).should have_been_made.once
30
+ end
31
+
32
+
33
+ it "should send only valid keys to API" do
34
+ valid_opts = {
35
+ 'cities' => ["helsinki", "espoo"],
36
+ 'loc_types' => ['stop', 'address'],
37
+ 'disable_error_correction' => 0,
38
+ 'disable_unique_stop_names' => 0
39
+ }
40
+
41
+ opts_query_form = {
42
+ "cities" => "helsinki|espoo",
43
+ "loc_types" => "stop|address",
44
+ "disable_error_correction" => 0,
45
+ "disable_unique_stop_names" => 0
46
+ }
47
+
48
+
49
+ query = @query.merge(opts_query_form)
50
+ opts = valid_opts.merge('not_valid_key' => 'or value')
51
+
52
+ stub_request(:get, @base_url).
53
+ with(:query => query)
54
+
55
+ @reitti.geocode(@city, opts)
56
+
57
+ a_request(:get, @base_url).
58
+ with(:query => query).should have_been_made.once
59
+ end
60
+
61
+
62
+ it "should return hash of location hashes if query is success" do
63
+ opts = {'loc_types' => 'stop'}
64
+
65
+ json = File.new('spec/data/geocode.json')
66
+ stub_request(:get, @base_url).
67
+ with(:query => @query.merge(opts)).
68
+ to_return(:body => json)
69
+
70
+ locs = @reitti.geocode("Helsinki", opts)
71
+ locs.should be_kind_of(Array)
72
+ locs[0].should be_kind_of(Hash)
73
+ end
74
+ end
75
+
76
+
77
+ describe "reverse geocoding" do
78
+ before :each do
79
+ @coords = '2548196,6678528'
80
+ @query = @base_query.merge('request' => 'reverse_geocode',
81
+ 'coordinate' => @coords)
82
+ end
83
+
84
+
85
+ it "should perform request to API reverse_geocode endpoint." do
86
+ stub_request(:get, @base_url).
87
+ with(:query => @query)
88
+
89
+ @reitti.reverse_geocode(@coords)
90
+
91
+ a_request(:get, @base_url).
92
+ with(:query => @query).should have_been_made.once
93
+ end
94
+
95
+
96
+ it "should send only valid keys to API" do
97
+ valid_opts = {
98
+ 'coordinate' => '2552335,6673660',
99
+ 'limit' => 1,
100
+ 'radius' => 1000,
101
+ 'result_contains' => 'stop'
102
+ }
103
+
104
+ opts_query_form = {
105
+ "coordinate" => "2552335,6673660",
106
+ "limit" => 1,
107
+ "radius" => 1000,
108
+ "result_contains" => "stop"
109
+ }
110
+
111
+
112
+ opts = valid_opts.merge('not_valid_key' => 'or value')
113
+ query = @query.merge(opts_query_form)
114
+
115
+ stub_request(:get, @base_url).
116
+ with(:query => query)
117
+
118
+ @reitti.reverse_geocode(@coords, opts)
119
+
120
+ a_request(:get, @base_url).
121
+ with(:query => query).should have_been_made.once
122
+ end
123
+
124
+
125
+ it "should return hash of location hashes if query is success" do
126
+ json = File.new('spec/data/reverse_geocode.json')
127
+ stub_request(:get, @base_url).
128
+ with(:query => @query).
129
+ to_return(:body => json)
130
+
131
+ locs = @reitti.reverse_geocode(@coords)
132
+ locs.should be_kind_of(Array)
133
+ locs[0].should be_kind_of(Hash)
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,83 @@
1
+ require "spec_helper"
2
+
3
+ describe Reittiopas2::Routing do
4
+ before :each do
5
+ user = "username"
6
+ pass = "password"
7
+ @reitti = Reittiopas2.new(user, pass)
8
+
9
+ @base_query = {'user' => user, 'pass' => pass}
10
+ @base_url = "http://api.reittiopas.fi/hsl/prod/"
11
+ end
12
+
13
+ describe "route fetching" do
14
+ before :each do
15
+ @from = '2546445,6675512'
16
+ @to = '2549445,6675513'
17
+ @query = @base_query.merge('request' => 'route',
18
+ 'from' => @from,
19
+ 'to' => @to)
20
+ end
21
+
22
+ it "should accept coordinates as strings." do
23
+ stub_request(:get, @base_url).
24
+ with(:query => @query)
25
+
26
+ @reitti.route(@from, @to)
27
+ a_request(:get, @base_url).
28
+ with(:query => @query).should have_been_made.once
29
+ end
30
+
31
+ it "should accept location hashes as coordinates." do
32
+ from_hash = {'coordinate' => @from}
33
+ to_hash = {'coordinate' => @to}
34
+
35
+ stub_request(:get, @base_url).
36
+ with(:query => @query)
37
+
38
+ @reitti.route(from_hash, to_hash)
39
+ a_request(:get, @base_url).
40
+ with(:query => @query).should have_been_made.once
41
+ end
42
+
43
+ it "should reject hashes without coordinates." do
44
+ from_hash = {'fake' => @from}
45
+ to_hash = {'fake' => @to}
46
+
47
+ ret = @reitti.route(from_hash, to_hash)
48
+ ret['error'].should == 'ArgumentError: locations were not acceptable types'
49
+ end
50
+
51
+ it "should reject coordinates which are different types." do
52
+ from_hash = {'fake' => @from}
53
+ ret = @reitti.route(from_hash, @to)
54
+ ret['error'].should == 'ArgumentError: from.class != to.class'
55
+ end
56
+
57
+ # This test works because we are not checking values, only keys
58
+ it "should reject keys that are not valid query parameters" do
59
+ keys = ['via', 'date', 'time', 'timetype', 'via_time', 'zone',
60
+ 'transport_types', 'optimize', 'change_margin', 'change_cost',
61
+ 'wait_cost', 'walk_cost', 'walk_speed', 'detail', 'show']
62
+ mode_costs = ['mode_cost_1', 'mode_cost_2', 'mode_cost_3', 'mode_cost_4',
63
+ 'mode_cost_5', 'mode_cost_6', 'mode_cost_7', 'mode_cost_7',
64
+ 'mode_cost_8', 'mode_cost_12', 'mode_cost_21', 'mode_cost_22',
65
+ 'mode_cost_23', 'mode_cost_24', 'mode_cost_25', 'mode_cost_36',
66
+ 'mode_cost_39']
67
+
68
+ opts = {}
69
+ keys.each { |key| opts[key] = "_" }
70
+ mode_costs.each { |key| opts[key] = "_" }
71
+
72
+ query = @query.merge(opts)
73
+ stub_request(:get, @base_url).
74
+ with(:query => query)
75
+
76
+ invalid = query.merge('not valid' => 'at all')
77
+ @reitti.route(@from, @to, invalid)
78
+
79
+ a_request(:get, @base_url).
80
+ with(:query => query).should have_been_made.once
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,15 @@
1
+ require "rubygems"
2
+ require "bundler/setup"
3
+ require "webmock/rspec"
4
+
5
+ require "simplecov"
6
+ SimpleCov.start
7
+
8
+ require "reittiopas2"
9
+
10
+
11
+ RSpec.configure do |config|
12
+ config.treat_symbols_as_metadata_keys_with_true_values = true
13
+ config.run_all_when_everything_filtered = true
14
+ config.filter_run :focus
15
+ end
metadata ADDED
@@ -0,0 +1,153 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: reittiopas2
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Iivari Äikäs
8
+ - Jussi Virtanen
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-10-28 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: json
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ~>
19
+ - !ruby/object:Gem::Version
20
+ version: '1.8'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ~>
26
+ - !ruby/object:Gem::Version
27
+ version: '1.8'
28
+ - !ruby/object:Gem::Dependency
29
+ name: addressable
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ~>
33
+ - !ruby/object:Gem::Version
34
+ version: '2.3'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ~>
40
+ - !ruby/object:Gem::Version
41
+ version: '2.3'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rspec
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ~>
47
+ - !ruby/object:Gem::Version
48
+ version: '2.14'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ~>
54
+ - !ruby/object:Gem::Version
55
+ version: '2.14'
56
+ - !ruby/object:Gem::Dependency
57
+ name: webmock
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ~>
61
+ - !ruby/object:Gem::Version
62
+ version: '1.15'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: '1.15'
70
+ - !ruby/object:Gem::Dependency
71
+ name: guard-rspec
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ~>
75
+ - !ruby/object:Gem::Version
76
+ version: '4.0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ~>
82
+ - !ruby/object:Gem::Version
83
+ version: '4.0'
84
+ - !ruby/object:Gem::Dependency
85
+ name: simplecov
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ~>
89
+ - !ruby/object:Gem::Version
90
+ version: '0.7'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ~>
96
+ - !ruby/object:Gem::Version
97
+ version: '0.7'
98
+ description: Reittiopas version 2 API library.
99
+ email:
100
+ - iivari.aikas@gmail.com
101
+ executables: []
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - .gitignore
106
+ - .rspec
107
+ - Gemfile
108
+ - Guardfile
109
+ - LICENSE
110
+ - README.md
111
+ - Rakefile
112
+ - lib/reittiopas2.rb
113
+ - lib/reittiopas2/connection.rb
114
+ - lib/reittiopas2/geocoding.rb
115
+ - lib/reittiopas2/routing.rb
116
+ - lib/reittiopas2/version.rb
117
+ - reittiopas2.gemspec
118
+ - spec/connection_spec.rb
119
+ - spec/data/geocode.json
120
+ - spec/data/reverse_geocode.json
121
+ - spec/geocoding_spec.rb
122
+ - spec/routing_spec.rb
123
+ - spec/spec_helper.rb
124
+ homepage: https://github.com/orva/reittiopas2
125
+ licenses: []
126
+ metadata: {}
127
+ post_install_message:
128
+ rdoc_options: []
129
+ require_paths:
130
+ - lib
131
+ required_ruby_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - '>='
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ required_rubygems_version: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - '>='
139
+ - !ruby/object:Gem::Version
140
+ version: '0'
141
+ requirements: []
142
+ rubyforge_project:
143
+ rubygems_version: 2.0.3
144
+ signing_key:
145
+ specification_version: 4
146
+ summary: Gem providing easy access to reittiopas version 2 API.
147
+ test_files:
148
+ - spec/connection_spec.rb
149
+ - spec/data/geocode.json
150
+ - spec/data/reverse_geocode.json
151
+ - spec/geocoding_spec.rb
152
+ - spec/routing_spec.rb
153
+ - spec/spec_helper.rb