ns-yapi 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 9a0c3a195f10cd241f4f0a772e6847aadf53aae8
4
- data.tar.gz: cf8b58cafe9440992ce191c11a2e13fd8216e695
2
+ SHA256:
3
+ metadata.gz: 90e0e28a27c5819ad9ccab28567b1645e19995308e2da96345f0ef7dcb2fdff7
4
+ data.tar.gz: b815628df8eb973f22856ca0d95c9575e2bc02749225bb95685b622bdabe5c9c
5
5
  SHA512:
6
- metadata.gz: 667f33d89852136b0b0ceaafe689d2cd35bbb1988abb4f01601b9f52da84096b3a12300f96d0ddb5a2a54d3548ce65c6856098ca9d23ddba635ff6513b9f8efd
7
- data.tar.gz: d0987af5c7c1449ac9d6e10db022e686af030e55004a11f2084ebd70de32c8fd2356289e7216eeda38c37bd4c38c17dae8644be14f28ab55aa65531cb91cfb87
6
+ metadata.gz: 5da72d6d6be93450f0d7c84aae840719df990e26d9253250bf53fc564431f1127fb606292757fef8ef943650b5eb84834b30ca24222be6cbe5e72f83893676a6
7
+ data.tar.gz: 6bb0a08ac0d239b3fba39e62c094bc207f08c171b9ef3a83e739eefc91078e6fec1ff3045d7dfab95fdda435450f0399b70d102129f88044e288533f90c36b47
data/.rubocop.yml ADDED
@@ -0,0 +1,19 @@
1
+ require: rubocop-performance
2
+
3
+ Style/Documentation:
4
+ Enabled: false
5
+
6
+ Metrics/LineLength:
7
+ Max: 120
8
+ Exclude:
9
+ - spec/**/*
10
+
11
+ Metrics/BlockLength:
12
+ Exclude:
13
+ - spec/**/*
14
+
15
+ Metrics/MethodLength:
16
+ Max: 20
17
+
18
+ Metrics/ClassLength:
19
+ Max: 200
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.1.2
1
+ 2.6.2
data/.travis.yml CHANGED
@@ -1,4 +1,7 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 1.9.3
4
- - 2.2.2
3
+ - 2.6.2
4
+
5
+ script:
6
+ - bundle exec rubocop
7
+ - bundle exec rspec
data/Gemfile CHANGED
@@ -1,15 +1,23 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'http://rubygems.org'
2
4
 
3
- gem "httpclient", "~> 2.3.3"
4
5
  gem 'rake'
6
+ gem 'rest-client'
5
7
 
6
8
  gemspec
7
9
 
8
10
  group :test do
11
+ gem 'coveralls', require: false
12
+ gem 'mocha', require: 'mocha/api'
13
+ gem 'pry'
9
14
  gem 'rspec'
10
15
  gem 'simplecov'
11
- gem "webmock", "~> 1.11.0"
12
- gem "timecop", "~> 0.6.1"
13
- gem 'mocha', require: 'mocha/api'
14
- gem 'coveralls', require: false
16
+ gem 'timecop'
17
+ gem 'webmock'
18
+ end
19
+
20
+ group :development do
21
+ gem 'rubocop', require: false
22
+ gem 'rubocop-performance', require: false
15
23
  end
data/Gemfile.lock CHANGED
@@ -3,69 +3,120 @@ PATH
3
3
  specs:
4
4
  ns-yapi (0.5.0)
5
5
  addressable
6
- httpclient
7
6
  nokogiri
8
7
  nori
8
+ rest-client
9
9
 
10
10
  GEM
11
11
  remote: http://rubygems.org/
12
12
  specs:
13
- addressable (2.3.4)
14
- colorize (0.5.8)
15
- coveralls (0.6.7)
16
- colorize
17
- multi_json (~> 1.3)
18
- rest-client
19
- simplecov (>= 0.7)
20
- thor
21
- crack (0.4.0)
22
- safe_yaml (~> 0.9.0)
23
- diff-lcs (1.2.4)
24
- httpclient (2.3.3)
25
- metaclass (0.0.1)
26
- mime-types (1.23)
27
- mini_portile2 (2.3.0)
28
- mocha (0.14.0)
13
+ addressable (2.6.0)
14
+ public_suffix (>= 2.0.2, < 4.0)
15
+ ast (2.4.0)
16
+ coderay (1.1.2)
17
+ coveralls (0.8.22)
18
+ json (>= 1.8, < 3)
19
+ simplecov (~> 0.16.1)
20
+ term-ansicolor (~> 1.3)
21
+ thor (~> 0.19.4)
22
+ tins (~> 1.6)
23
+ crack (0.4.3)
24
+ safe_yaml (~> 1.0.0)
25
+ diff-lcs (1.3)
26
+ docile (1.3.1)
27
+ domain_name (0.5.20180417)
28
+ unf (>= 0.0.5, < 1.0.0)
29
+ hashdiff (0.3.8)
30
+ http-cookie (1.0.3)
31
+ domain_name (~> 0.5)
32
+ jaro_winkler (1.5.2)
33
+ json (2.2.0)
34
+ metaclass (0.0.4)
35
+ method_source (0.9.2)
36
+ mime-types (3.2.2)
37
+ mime-types-data (~> 3.2015)
38
+ mime-types-data (3.2019.0331)
39
+ mini_portile2 (2.4.0)
40
+ mocha (1.8.0)
29
41
  metaclass (~> 0.0.1)
30
- multi_json (1.7.7)
31
- nokogiri (1.8.1)
32
- mini_portile2 (~> 2.3.0)
42
+ netrc (0.11.0)
43
+ nokogiri (1.10.2)
44
+ mini_portile2 (~> 2.4.0)
33
45
  nori (2.6.0)
34
- rake (10.0.3)
35
- rest-client (1.6.7)
36
- mime-types (>= 1.16)
37
- rspec (2.13.0)
38
- rspec-core (~> 2.13.0)
39
- rspec-expectations (~> 2.13.0)
40
- rspec-mocks (~> 2.13.0)
41
- rspec-core (2.13.1)
42
- rspec-expectations (2.13.0)
43
- diff-lcs (>= 1.1.3, < 2.0)
44
- rspec-mocks (2.13.1)
45
- safe_yaml (0.9.3)
46
- simplecov (0.7.1)
47
- multi_json (~> 1.0)
48
- simplecov-html (~> 0.7.1)
49
- simplecov-html (0.7.1)
50
- thor (0.18.1)
51
- timecop (0.6.1)
52
- webmock (1.11.0)
53
- addressable (>= 2.2.7)
46
+ parallel (1.17.0)
47
+ parser (2.6.2.1)
48
+ ast (~> 2.4.0)
49
+ pry (0.12.2)
50
+ coderay (~> 1.1.0)
51
+ method_source (~> 0.9.0)
52
+ psych (3.1.0)
53
+ public_suffix (3.0.3)
54
+ rainbow (3.0.0)
55
+ rake (12.3.2)
56
+ rest-client (2.0.2)
57
+ http-cookie (>= 1.0.2, < 2.0)
58
+ mime-types (>= 1.16, < 4.0)
59
+ netrc (~> 0.8)
60
+ rspec (3.8.0)
61
+ rspec-core (~> 3.8.0)
62
+ rspec-expectations (~> 3.8.0)
63
+ rspec-mocks (~> 3.8.0)
64
+ rspec-core (3.8.0)
65
+ rspec-support (~> 3.8.0)
66
+ rspec-expectations (3.8.2)
67
+ diff-lcs (>= 1.2.0, < 2.0)
68
+ rspec-support (~> 3.8.0)
69
+ rspec-mocks (3.8.0)
70
+ diff-lcs (>= 1.2.0, < 2.0)
71
+ rspec-support (~> 3.8.0)
72
+ rspec-support (3.8.0)
73
+ rubocop (0.67.2)
74
+ jaro_winkler (~> 1.5.1)
75
+ parallel (~> 1.10)
76
+ parser (>= 2.5, != 2.5.1.1)
77
+ psych (>= 3.1.0)
78
+ rainbow (>= 2.2.2, < 4.0)
79
+ ruby-progressbar (~> 1.7)
80
+ unicode-display_width (>= 1.4.0, < 1.6)
81
+ rubocop-performance (1.1.0)
82
+ rubocop (>= 0.67.0)
83
+ ruby-progressbar (1.10.0)
84
+ safe_yaml (1.0.5)
85
+ simplecov (0.16.1)
86
+ docile (~> 1.1)
87
+ json (>= 1.8, < 3)
88
+ simplecov-html (~> 0.10.0)
89
+ simplecov-html (0.10.2)
90
+ term-ansicolor (1.7.1)
91
+ tins (~> 1.0)
92
+ thor (0.19.4)
93
+ timecop (0.9.1)
94
+ tins (1.20.2)
95
+ unf (0.1.4)
96
+ unf_ext
97
+ unf_ext (0.0.7.5)
98
+ unicode-display_width (1.5.0)
99
+ webmock (3.5.1)
100
+ addressable (>= 2.3.6)
54
101
  crack (>= 0.3.2)
102
+ hashdiff
55
103
 
56
104
  PLATFORMS
57
105
  ruby
58
106
 
59
107
  DEPENDENCIES
60
108
  coveralls
61
- httpclient (~> 2.3.3)
62
109
  mocha
63
110
  ns-yapi!
111
+ pry
64
112
  rake
113
+ rest-client
65
114
  rspec
115
+ rubocop
116
+ rubocop-performance
66
117
  simplecov
67
- timecop (~> 0.6.1)
68
- webmock (~> 1.11.0)
118
+ timecop
119
+ webmock
69
120
 
70
121
  BUNDLED WITH
71
- 1.16.0
122
+ 1.17.2
@@ -1,13 +1,15 @@
1
- class PricesUrl
1
+ # frozen_string_literal: true
2
2
 
3
+ class PricesUrl
3
4
  def initialize(url)
4
- raise InvalidURL, "You must give an url, ie http://www.ns.nl/api" unless url
5
+ raise InvalidURL, 'You must give an url, ie http://www.ns.nl/api' unless url
6
+
5
7
  @url = url
6
8
  end
7
9
 
8
- def url (opts = {date: nil, from: "", to: ""})
9
- opts[:date] = opts[:date].strftime("%d%m%Y") if opts[:date]
10
- uri = URI.escape(opts.collect{|k,v| "#{k}=#{v}"}.join('&'))
10
+ def url(opts = { date: nil, from: '', to: '' })
11
+ opts[:date] = opts[:date].strftime('%d%m%Y') if opts[:date]
12
+ uri = Addressable::URI.escape(opts.collect { |k, v| "#{k}=#{v}" }.join('&'))
11
13
  "#{@url}?#{uri}"
12
14
  end
13
15
 
data/lib/ns_client.rb CHANGED
@@ -1,191 +1,219 @@
1
- #encoding: utf-8
1
+ # frozen_string_literal: true
2
+
2
3
  require 'pathname'
3
4
  require 'time'
4
5
  require 'nori'
5
6
  require 'nokogiri'
6
- require 'httpclient'
7
- require "addressable/uri"
7
+ require 'rest-client'
8
+ require 'addressable/uri'
8
9
 
9
10
  this = Pathname.new(__FILE__).realpath
10
- lib_path = File.expand_path("..", this)
11
- $:.unshift(lib_path)
12
-
13
- $ROOT = File.expand_path("../", lib_path)
11
+ lib_path = File.expand_path('..', this)
12
+ $LOAD_PATH.unshift(lib_path)
14
13
 
15
14
  Dir.glob(File.join(lib_path, '/**/*.rb')).each do |file|
16
15
  require file
17
16
  end
18
17
 
19
18
  module NSYapi
20
-
21
19
  class Configuration
22
- attr_accessor :username, :password
20
+ attr_accessor :username, :password, :configuration
23
21
  end
24
22
 
25
23
  def self.configure(configuration = NSYapi::Configuration.new)
26
24
  yield configuration if block_given?
27
- @@configuration = configuration
25
+ @configuration = configuration
28
26
  end
29
27
 
30
28
  def self.configuration # :nodoc:
31
- @@configuration ||= NSYapi::Configuration.new
29
+ @configuration ||= NSYapi::Configuration.new
32
30
  end
33
31
 
34
32
  def self.client
35
- @client_instance = NSClient.new(configuration.username, configuration.password) unless @client_instance
33
+ @client_instance ||= NSClient.new(configuration.username, configuration.password)
36
34
  @client_instance
37
35
  end
38
-
39
36
  end
40
37
 
41
38
  class NSClient
42
-
43
- attr_accessor :last_received_raw_xml, :last_received_corrected_xml
39
+ attr_accessor :last_received_raw_xml, :last_received_corrected_xml, :username, :password
44
40
 
45
41
  def initialize(username, password)
46
- @client = HTTPClient.new
47
- @client.set_auth("http://webservices.ns.nl", username, password)
48
- @prices_url = PricesUrl.new("http://webservices.ns.nl/ns-api-prijzen-v3")
49
- @last_received_raw_xml = ""
50
- @last_received_corrected_xml = ""
42
+ @username = username
43
+ @password = password
44
+ @prices_url = PricesUrl.new('https://webservices.ns.nl/ns-api-prijzen-v3')
45
+ @last_received_raw_xml = ''
46
+ @last_received_corrected_xml = ''
51
47
  end
52
48
 
53
49
  def stations
54
- parse_stations(get_xml("http://webservices.ns.nl/ns-api-stations-v2"))
50
+ parse_stations(get_xml('https://webservices.ns.nl/ns-api-stations-v2'))
55
51
  end
56
52
 
57
53
  def stations_short
58
- parse_stations_as_map(get_xml("http://webservices.ns.nl/ns-api-stations-v2"))
54
+ parse_stations_as_map(get_xml('https://webservices.ns.nl/ns-api-stations-v2'))
59
55
  end
60
56
 
61
- def disruptions (query = nil)
57
+ def disruptions(query = nil)
62
58
  response_xml = get_xml(disruption_url(query))
63
59
  raise_error_when_response_is_error(response_xml)
64
60
  parse_disruptions(response_xml)
65
61
  end
66
62
 
67
- def prices (opts = {from: nil, to: nil, via: nil, date: nil})
68
- raise MissingParameter, "from and to station is required" if (opts[:from] == nil && opts[:to] == nil)
69
- raise MissingParameter, "from station is required" unless opts[:from]
70
- raise MissingParameter, "to station is required" unless opts[:to]
71
- raise SameDestinationError,
72
- "from (#{opts[:from]}) and to (#{opts[:to]}) parameters should not be equal" if opts[:from] == opts[:to]
63
+ def prices(opts = { from: nil, to: nil, via: nil, date: nil })
64
+ validate_prices(opts)
73
65
  response_xml = get_xml(@prices_url.url(opts))
74
66
  raise_error_when_response_is_error(response_xml)
75
67
  parse_prices(response_xml)
76
68
  end
77
69
 
70
+ def validate_price_parameters(opts)
71
+ raise MissingParameter, 'from and to station is required' if opts[:from].nil? && opts[:to].nil?
72
+ raise MissingParameter, 'from station is required' unless opts[:from]
73
+ raise MissingParameter, 'to station is required' unless opts[:to]
74
+ end
75
+
76
+ def validate_prices(opts)
77
+ validate_price_parameters(opts)
78
+
79
+ return unless opts[:from] == opts[:to]
80
+
81
+ raise SameDestinationError,
82
+ "from (#{opts[:from]}) and to (#{opts[:to]}) parameters should not be equal"
83
+ end
84
+
78
85
  def parse_prices(response_xml)
79
86
  prices_response = PricesResponse.new
80
- (response_xml/'/VervoerderKeuzes/VervoerderKeuze').each do |transporter|
81
- prices_response.tariff_units = (transporter/'./Tariefeenheden').text.to_i
82
-
83
- (transporter/'ReisType').each do |travel_type|
84
- prices = []
85
-
86
- (travel_type/'ReisKlasse').each do |travel_class|
87
- (travel_class/'Korting/Kortingsprijs').each do |price_element|
88
- product_price = ProductPrice.new
89
- product_price.discount = price_element.attr("name")
90
- product_price.train_class = travel_class.attr("klasse")
91
- product_price.amount = price_element.attr("prijs").gsub(",", ".").to_f
92
- prices << product_price
93
- end
94
- end
87
+ (response_xml / '/VervoerderKeuzes/VervoerderKeuze').each do |transporter|
88
+ prices_response.tariff_units = (transporter / './Tariefeenheden').text.to_i
89
+
90
+ (transporter / 'ReisType').each do |travel_type|
91
+ prices = parse_travel_type(travel_type)
95
92
 
96
93
  name = travel_type.attr('name')
97
94
  prices_response.products[name] = prices
98
95
  end
99
-
100
96
  end
101
97
  prices_response
102
98
  end
103
99
 
100
+ def parse_travel_type(travel_type)
101
+ prices = []
102
+
103
+ (travel_type / 'ReisKlasse').each do |travel_class|
104
+ (travel_class / 'Korting/Kortingsprijs').each do |price_element|
105
+ product_price = ProductPrice.new
106
+ product_price.discount = price_element.attr('name')
107
+ product_price.train_class = travel_class.attr('klasse')
108
+ product_price.amount = price_element.attr('prijs').tr(',', '.').to_f
109
+ prices << product_price
110
+ end
111
+ end
112
+
113
+ prices
114
+ end
115
+
104
116
  def parse_stations(response_xml)
105
117
  result = []
106
- (response_xml/'/Stations/Station').each do |station|
107
- s = Station.new
108
- s.code = (station/'./Code').text
109
- s.type = (station/'./Type').text
110
- s.country = (station/'./Land').text
111
- s.short_name = (station/'./Namen/Kort').text
112
- s.name = (station/'./Namen/Middel').text
113
- s.long_name = (station/'./Namen/Lang').text
114
- s.lat = (station/'./Lat').text
115
- s.long = (station/'./Lon').text
116
- s.uiccode = (station/'./UICCode').text
117
- result << s
118
+ (response_xml / '/Stations/Station').each do |station|
119
+ result << parse_station(station)
118
120
  end
119
121
  result
120
122
  end
121
123
 
124
+ def parse_station(station)
125
+ s = Station.new
126
+ s.code = parse_station_field(station, './Code')
127
+ s.type = parse_station_field(station, './Type')
128
+ s.country = parse_station_field(station, './Land')
129
+ s.short_name = parse_station_field(station, './Namen/Kort')
130
+ s.name = parse_station_field(station, './Namen/Middel')
131
+ s.long_name = parse_station_field(station, './Namen/Lang')
132
+ s.lat = parse_station_field(station, './Lat')
133
+ s.long = parse_station_field(station, './Lon')
134
+ s.uiccode = parse_station_field(station, './UICCode')
135
+ s
136
+ end
137
+
138
+ def parse_station_field(station, field)
139
+ (station / field).text
140
+ end
141
+
122
142
  def parse_stations_as_map(response_xml)
123
143
  result = {}
124
- (response_xml/'/Stations/Station').each do |station|
125
- code = (station/'./Code').text
126
- name = (station/'./Namen/Middel').text
127
- country = (station/'./Land').text
144
+ (response_xml / '/Stations/Station').each do |station|
145
+ code = (station / './Code').text
146
+ name = (station / './Namen/Middel').text
147
+ country = (station / './Land').text
128
148
  result[code] = [name, country]
129
149
  end
130
150
  result
131
151
  end
132
152
 
133
153
  def parse_disruptions(response_xml)
134
- result = {planned: [], unplanned: []}
135
- (response_xml/'/Storingen').each do |disruption|
136
- (disruption/'Ongepland/Storing').each do |unplanned|
137
- unplanned_disruption = UnplannedDisruption.new
138
- unplanned_disruption.id = (unplanned/'./id').text
139
- unplanned_disruption.trip = (unplanned/'./Traject').text
140
- unplanned_disruption.reason = (unplanned/'./Reden').text
141
- unplanned_disruption.message = (unplanned/'./Bericht').text
142
- unplanned_disruption.datetime_string = (unplanned/'./Datum').text
143
- unplanned_disruption.cause = (unplanned/'./Oorzaak').text
144
- result[:unplanned] << unplanned_disruption
154
+ result = { planned: [], unplanned: [] }
155
+ (response_xml / '/Storingen').each do |disruption|
156
+ (disruption / 'Ongepland/Storing').each do |unplanned|
157
+ result[:unplanned] << parse_unplanned_disruption(unplanned)
145
158
  end
146
159
 
147
- (disruption/'Gepland/Storing').each do |planned|
148
- planned_disruption = PlannedDisruption.new
149
- planned_disruption.id = (planned/'./id').text
150
- planned_disruption.trip = (planned/'./Traject').text
151
- planned_disruption.reason = (planned/'./Reden').text
152
- planned_disruption.advice = (planned/'./Advies').text
153
- planned_disruption.message = (planned/'./Bericht').text
154
- planned_disruption.cause = (planned/'./Oorzaak').text
155
- result[:planned] << planned_disruption
160
+ (disruption / 'Gepland/Storing').each do |planned|
161
+ result[:planned] << parse_planned_disruption(planned)
156
162
  end
157
163
  end
158
164
  result
159
165
  end
160
166
 
167
+ def parse_unplanned_disruption(disruption)
168
+ result = UnplannedDisruption.new
169
+ result.id = (disruption / './id').text
170
+ result.trip = (disruption / './Traject').text
171
+ result.reason = (disruption / './Reden').text
172
+ result.message = (disruption / './Bericht').text
173
+ result.datetime_string = (disruption / './Datum').text
174
+ result.cause = (disruption / './Oorzaak').text
175
+ result
176
+ end
177
+
178
+ def parse_planned_disruption(disruption)
179
+ result = PlannedDisruption.new
180
+ result.id = (disruption / './id').text
181
+ result.trip = (disruption / './Traject').text
182
+ result.reason = (disruption / './Reden').text
183
+ result.advice = (disruption / './Advies').text
184
+ result.message = (disruption / './Bericht').text
185
+ result.cause = (disruption / './Oorzaak').text
186
+ result
187
+ end
188
+
161
189
  def raise_error_when_response_is_error(xdoc)
162
- (xdoc/'/error').each do |error|
163
- message = (error/'./message').text
190
+ (xdoc / '/error').each do |error|
191
+ message = (error / './message').text
164
192
  raise InvalidStationNameError, message
165
193
  end
166
194
  end
167
195
 
168
196
  def get_xml(url)
169
- response = @client.get url
170
- @last_received_raw_xml = response.content
197
+ response = RestClient::Request.new(url: url, user: username, password: password, method: :get).execute
198
+ @last_received_raw_xml = response.body
171
199
  @last_received_corrected_xml = remove_unwanted_whitespace(@last_received_raw_xml)
172
200
  begin
173
201
  Nokogiri.XML(@last_received_corrected_xml) do |config|
174
202
  config.options = Nokogiri::XML::ParseOptions::STRICT
175
203
  end
176
204
  rescue Nokogiri::XML::SyntaxError => e
177
- raise UnparseableXMLError.new e
205
+ raise UnparseableXMLError, e
178
206
  end
179
-
180
207
  end
181
208
 
182
- def remove_unwanted_whitespace content
183
- content.gsub /<\s*(\/?)\s*?([a-zA-Z0-9]*)\s*([a-zA-Z0-9]*)\s*>/, '<\1\2\3>'
209
+ def remove_unwanted_whitespace(content)
210
+ content.gsub %r{<\s*(/?)\s*?([a-zA-Z0-9]*)\s*([a-zA-Z0-9]*)\s*>}, '<\1\2\3>'
184
211
  end
185
212
 
186
213
  def disruption_url(query)
187
- return "http://webservices.ns.nl/ns-api-storingen?station=#{query}" if query
188
- "http://webservices.ns.nl/ns-api-storingen?actual=true"
214
+ return "https://webservices.ns.nl/ns-api-storingen?station=#{query}" if query
215
+
216
+ 'https://webservices.ns.nl/ns-api-storingen?actual=true'
189
217
  end
190
218
 
191
219
  class PricesResponse
@@ -197,20 +225,19 @@ class NSClient
197
225
  end
198
226
 
199
227
  def enkele_reis
200
- products["Enkele reis"]
228
+ products['Enkele reis']
201
229
  end
202
230
 
203
231
  def dagretour
204
- products["Retour"]
232
+ products['Retour']
205
233
  end
206
-
207
234
  end
208
235
 
209
236
  class ProductPrice
210
237
  attr_accessor :discount, :train_class, :amount
211
238
  DISCOUNT_MAP ||= {
212
- "20% korting" => "reductie_20",
213
- "40% korting" => "reductie_40"
239
+ '20% korting' => 'reductie_20',
240
+ '40% korting' => 'reductie_40'
214
241
  }.freeze
215
242
 
216
243
  def type
@@ -241,5 +268,4 @@ class NSClient
241
268
 
242
269
  class SameDestinationError < StandardError
243
270
  end
244
-
245
271
  end