locode 0.1.0
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 +7 -0
- data/.gitignore +18 -0
- data/.travis.yml +4 -0
- data/CONTRIBUTERS +2 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +66 -0
- data/Rakefile +18 -0
- data/data/README.md +29 -0
- data/data/locode_data_update.rb +86 -0
- data/data/yaml/dump.yml +984613 -0
- data/lib/locode.rb +119 -0
- data/lib/locode/location.rb +507 -0
- data/lib/locode/version.rb +6 -0
- data/locode.gemspec +26 -0
- data/test/lib/location_test.rb +84 -0
- data/test/lib/version_test.rb +7 -0
- data/test/locode_test.rb +102 -0
- data/test/test_helper.rb +4 -0
- metadata +122 -0
data/lib/locode.rb
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
require 'yaml'
|
|
3
|
+
require 'multi_json'
|
|
4
|
+
|
|
5
|
+
require_relative 'locode/version'
|
|
6
|
+
require_relative 'locode/location'
|
|
7
|
+
|
|
8
|
+
module Locode
|
|
9
|
+
|
|
10
|
+
def self.load_data
|
|
11
|
+
YAML.load(File.read(File.expand_path('../../data/yaml/dump.yml', __FILE__)))
|
|
12
|
+
end
|
|
13
|
+
private_class_method :load_data
|
|
14
|
+
|
|
15
|
+
ALL_LOCATIONS = load_data
|
|
16
|
+
private_constant :ALL_LOCATIONS
|
|
17
|
+
|
|
18
|
+
def self.seaports(limit = ALL_LOCATIONS.size)
|
|
19
|
+
ALL_LOCATIONS.select { |location| location.seaport? }.take(limit)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.rail_terminals(limit = ALL_LOCATIONS.size)
|
|
23
|
+
ALL_LOCATIONS.select { |location| location.rail_terminal? }.take(limit)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.road_terminals(limit = ALL_LOCATIONS.size)
|
|
27
|
+
ALL_LOCATIONS.select { |location| location.road_terminal? }.take(limit)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.airport(limit = ALL_LOCATIONS.size)
|
|
31
|
+
ALL_LOCATIONS.select { |location| location.airport? }.take(limit)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.postal_exchange_offices(limit = ALL_LOCATIONS.size)
|
|
35
|
+
ALL_LOCATIONS.select { |location| location.postal_exchange_office? }.take(limit)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.inland_clearance_depots(limit = ALL_LOCATIONS.size)
|
|
39
|
+
ALL_LOCATIONS.select { |location| location.inland_clearance_depot? }.take(limit)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# the spec says these are currently just oil platforms
|
|
43
|
+
def self.fixed_transport_functions(limit = ALL_LOCATIONS.size)
|
|
44
|
+
ALL_LOCATIONS.select { |location| location.fixed_transport_functions? }.take(limit)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def self.border_crossing_functions(limit = ALL_LOCATIONS.size)
|
|
48
|
+
ALL_LOCATIONS.select { |location| location.border_crossing? }.take(limit)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Public: Find Locations that partially match the Search String.
|
|
52
|
+
# This means you can search by just the country code or a
|
|
53
|
+
# whole LOCODE.
|
|
54
|
+
#
|
|
55
|
+
# search_string - The string that will be used in the LOCODE search.
|
|
56
|
+
#
|
|
57
|
+
# Examples
|
|
58
|
+
#
|
|
59
|
+
# Locode.find_by_locode('US')
|
|
60
|
+
# #=> [<Locode::Location: 'US NYC'>,
|
|
61
|
+
# <Locode::Location: 'US LAX'>, ... ]
|
|
62
|
+
#
|
|
63
|
+
# Locode.find_by_locode('DE HAM')
|
|
64
|
+
# #=> [<Locode::Location: 'DE HAM'>]
|
|
65
|
+
#
|
|
66
|
+
# Locode.find_by_locode('foobar')
|
|
67
|
+
# #=> []
|
|
68
|
+
#
|
|
69
|
+
# Returns an Array of Location
|
|
70
|
+
def self.find_by_locode(search_string)
|
|
71
|
+
return [] unless search_string && search_string.is_a?(String)
|
|
72
|
+
ALL_LOCATIONS.select { |location| location.locode.start_with?(search_string.strip.upcase) }
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Public: Find Locations whose full name or full name without diacritics
|
|
76
|
+
# matches the search string
|
|
77
|
+
#
|
|
78
|
+
# search_string - The string that will be used in the LOCODE search.
|
|
79
|
+
#
|
|
80
|
+
# Examples
|
|
81
|
+
#
|
|
82
|
+
# Locode.find_by_name('Göteborg')
|
|
83
|
+
# #=> [<Locode::Location: 'SE GOT'>]
|
|
84
|
+
#
|
|
85
|
+
# Locode.find_by_name('Gothenburg')
|
|
86
|
+
# #=> [<Locode::Location: 'SE GOT'>]
|
|
87
|
+
#
|
|
88
|
+
# Returns an Array of Location because the name might not be unique
|
|
89
|
+
def self.find_by_name(search_string)
|
|
90
|
+
return [] unless search_string && search_string.is_a?(String)
|
|
91
|
+
ALL_LOCATIONS.select do |location|
|
|
92
|
+
names = []
|
|
93
|
+
names << location.full_name if location.full_name
|
|
94
|
+
names << location.full_name_without_diacritics if location.full_name_without_diacritics
|
|
95
|
+
names += location.alternative_full_names
|
|
96
|
+
names += location.alternative_full_names_without_diacritics
|
|
97
|
+
names.map { |name| name.downcase }.any? { |name| name.start_with?(search_string.strip.downcase) }
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Public: Find locations for a specific country with a specific function
|
|
102
|
+
#
|
|
103
|
+
# country_code - ISO 3166 alpha-2 Country Code String to filter locations by country
|
|
104
|
+
# function - Integer or :B that specifies the function of the location
|
|
105
|
+
# limit - Integer to specify how many locations you want
|
|
106
|
+
#
|
|
107
|
+
# Examples
|
|
108
|
+
#
|
|
109
|
+
# Locode.find_by_country_and_function('BE', 1)
|
|
110
|
+
# #=> [<Locode::Location: 'BE ANR'>, ..]
|
|
111
|
+
#
|
|
112
|
+
# Returns an Array of Locations that satisfy the above conditions
|
|
113
|
+
def self.find_by_country_and_function(country_code, function, limit = ALL_LOCATIONS.size)
|
|
114
|
+
return [] unless country_code.to_s =~ /^[A-Z]{2}$/
|
|
115
|
+
return [] unless function.to_s =~ /^[1-7]{1}|:B{1}$/
|
|
116
|
+
|
|
117
|
+
ALL_LOCATIONS.select { |location| location.country_code == country_code && location.function_classifier.include?(function) }.take(limit)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
@@ -0,0 +1,507 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module Locode
|
|
4
|
+
class Location
|
|
5
|
+
# Public: Initializes a new Location
|
|
6
|
+
#
|
|
7
|
+
# location_attributes - A Hash of the following structure
|
|
8
|
+
# {
|
|
9
|
+
# country_code: String | Symbol
|
|
10
|
+
# city_code: String | Symbol
|
|
11
|
+
# full_name: String
|
|
12
|
+
# full_name_without_diacritics: String
|
|
13
|
+
# subdivision: String | Symbol
|
|
14
|
+
# function_classifier: String | Array
|
|
15
|
+
# status: String | Symbol
|
|
16
|
+
# date: String
|
|
17
|
+
# iata_code: String | nil
|
|
18
|
+
# coordinates: String | nil
|
|
19
|
+
# }
|
|
20
|
+
#
|
|
21
|
+
# Examples
|
|
22
|
+
#
|
|
23
|
+
# Locode::Location.new
|
|
24
|
+
# #=> <Locode::Location: invalid location>
|
|
25
|
+
#
|
|
26
|
+
# location_attributes = {
|
|
27
|
+
# country_code: 'US',
|
|
28
|
+
# city_code: 'NYC',
|
|
29
|
+
# full_name: 'New York',
|
|
30
|
+
# full_name_without_diacritics: 'New York',
|
|
31
|
+
# subdivision: 'NY',
|
|
32
|
+
# function_classifier: '12345---',
|
|
33
|
+
# status: 'AI',
|
|
34
|
+
# date: '0401',
|
|
35
|
+
# iata_code: '',
|
|
36
|
+
# coordinates: '4042N 07400W'
|
|
37
|
+
# }
|
|
38
|
+
#
|
|
39
|
+
# Locode::Location.new(location_attributes)
|
|
40
|
+
# #=> <Locode::Location: 'US NYC'>
|
|
41
|
+
#
|
|
42
|
+
#
|
|
43
|
+
def initialize(location_attributes)
|
|
44
|
+
location_attributes.each do |k,v|
|
|
45
|
+
begin
|
|
46
|
+
send("#{k}=", v) if !v.nil?
|
|
47
|
+
rescue NoMethodError
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Internal: Once we are done parsing the csv files we no longer want to allow
|
|
53
|
+
# changes to the alternative_full_names or
|
|
54
|
+
# alternative_full_names_without_diacritics.
|
|
55
|
+
#
|
|
56
|
+
# Returns nothing
|
|
57
|
+
def parsing_completed
|
|
58
|
+
private :alternative_full_names=,
|
|
59
|
+
:alternative_full_names_without_diacritics=,
|
|
60
|
+
:parsing_completed
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Public: UN/LOCODE
|
|
64
|
+
#
|
|
65
|
+
# Examples
|
|
66
|
+
#
|
|
67
|
+
# Locode.find_by_locode('US NYC').first.locode
|
|
68
|
+
# #=> 'US NYC'
|
|
69
|
+
#
|
|
70
|
+
# Returns a String that represents the UN/LOCODE
|
|
71
|
+
def locode
|
|
72
|
+
"#{country_code.to_s} #{city_code.to_s}".strip
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Public: ISO 3166 alpha-2 Country Code
|
|
76
|
+
#
|
|
77
|
+
# Examples
|
|
78
|
+
#
|
|
79
|
+
# Locode.find_by_locode('US NYC').first.country_code
|
|
80
|
+
# #=> 'US'
|
|
81
|
+
#
|
|
82
|
+
# Returns a String containing the country code or an empty string.
|
|
83
|
+
def country_code
|
|
84
|
+
@country_code.to_s
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Public: Three letter code for a place
|
|
88
|
+
#
|
|
89
|
+
# Examples
|
|
90
|
+
#
|
|
91
|
+
# Locode.find_by_locode('US NYC').first.city_code
|
|
92
|
+
# #=> 'NYC'
|
|
93
|
+
#
|
|
94
|
+
# Returns a String containing the city code or an empty string.
|
|
95
|
+
def city_code
|
|
96
|
+
@city_code.to_s
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Public: The name of a location
|
|
100
|
+
#
|
|
101
|
+
# Examples
|
|
102
|
+
#
|
|
103
|
+
# Locode.find_by_locode('SE GOT').first.full_name
|
|
104
|
+
# #=> 'Göteborg'
|
|
105
|
+
#
|
|
106
|
+
# Returns a String containing the full name of the location or an
|
|
107
|
+
# empty string.
|
|
108
|
+
def full_name
|
|
109
|
+
@full_name
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Public: The alternative names of a location
|
|
113
|
+
#
|
|
114
|
+
# Examples
|
|
115
|
+
#
|
|
116
|
+
# Locode.find_by_locode('SE GOT').first.alternative_full_names
|
|
117
|
+
# #=> ['Gothenburg']
|
|
118
|
+
#
|
|
119
|
+
# Returns an Array of Strings containing the alternative full names of the
|
|
120
|
+
# location or an empty array.
|
|
121
|
+
def alternative_full_names
|
|
122
|
+
@alternative_full_names ||= []
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Internal: adds the alternative full name of a location to the list of alternatives
|
|
126
|
+
# This might not be coherent with the normal semantics of a setter
|
|
127
|
+
# but I think it is ok since it is just a private method.
|
|
128
|
+
#
|
|
129
|
+
# Returns nothing
|
|
130
|
+
def alternative_full_names=(alternative_full_name)
|
|
131
|
+
if alternative_full_name && alternative_full_name.is_a?(String)
|
|
132
|
+
@alternative_full_names ||= []
|
|
133
|
+
@alternative_full_names << alternative_full_name.strip
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Public: The name of the location, but all non-latin-base characters
|
|
138
|
+
# are converted.
|
|
139
|
+
#
|
|
140
|
+
# Examples
|
|
141
|
+
#
|
|
142
|
+
# Locode.find_by_locode('SE GOT').first.full_name_without_diacritics
|
|
143
|
+
# #=> 'Goteborg'
|
|
144
|
+
#
|
|
145
|
+
# Returns a String which contains the the full name without
|
|
146
|
+
# diacritics or an empty string
|
|
147
|
+
def full_name_without_diacritics
|
|
148
|
+
@full_name_without_diacritics
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Public: The alternative names of the location, but all non-latin-base
|
|
152
|
+
# characters are converted.
|
|
153
|
+
#
|
|
154
|
+
# Examples
|
|
155
|
+
#
|
|
156
|
+
# Locode.find_by_locode('SE GOT').first.alternative_full_names_without_diacritics
|
|
157
|
+
# #=> ['Gothenburg']
|
|
158
|
+
#
|
|
159
|
+
# Returns an Array of Strings containing the alternative full names without
|
|
160
|
+
# diacritics of the location or an empty array.
|
|
161
|
+
def alternative_full_names_without_diacritics
|
|
162
|
+
@alternative_full_names_without_diacritics ||= []
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Internal: adds the alternative full name without diacritics of the location
|
|
166
|
+
# to the list of alternatives.
|
|
167
|
+
# This might not be coherent with the normal semantics of a setter
|
|
168
|
+
# but I think it is ok since it is just a private method.
|
|
169
|
+
#
|
|
170
|
+
# Returns nothing
|
|
171
|
+
def alternative_full_names_without_diacritics=(alternative_full_name_without_diacritics)
|
|
172
|
+
if alternative_full_name_without_diacritics && alternative_full_name_without_diacritics.is_a?(String)
|
|
173
|
+
@alternative_full_names_without_diacritics ||= []
|
|
174
|
+
@alternative_full_names_without_diacritics << alternative_full_name_without_diacritics.strip
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Public: The ISO 1 to 3 character alphabetic and/or numeric code for the
|
|
179
|
+
# administrative division (state, province, department, etc.)
|
|
180
|
+
# of the country, as included in ISO 3166-2/1998. Only the
|
|
181
|
+
# latter part of the complete ISO 3166-2 code element (after
|
|
182
|
+
# the hyphen) is shown.
|
|
183
|
+
#
|
|
184
|
+
# Examples
|
|
185
|
+
#
|
|
186
|
+
# Locode.find_by_locode('US NYC').first.subdivision
|
|
187
|
+
# #=> 'NY'
|
|
188
|
+
#
|
|
189
|
+
# Locode.find_by_locode('SE GOT').first.subdivision
|
|
190
|
+
# #=> 'O'
|
|
191
|
+
#
|
|
192
|
+
# Returns a String representing the subdivision or an empty string
|
|
193
|
+
def subdivision
|
|
194
|
+
@subdivision
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Public: contains a 1-digit function classifier code for the location
|
|
198
|
+
#
|
|
199
|
+
# Examples
|
|
200
|
+
#
|
|
201
|
+
# Locode.find_by_locode('US NYC').first.function_classifier
|
|
202
|
+
# #=> [1, 2, 3, 4, 5]
|
|
203
|
+
#
|
|
204
|
+
# Returns an Array containing Integer or :B with the following
|
|
205
|
+
# meanings:
|
|
206
|
+
# 1 = seaport, any port with the possibility of transport via water
|
|
207
|
+
# 2 = rail terminal
|
|
208
|
+
# 3 = road terminal
|
|
209
|
+
# 4 = airport
|
|
210
|
+
# 5 = postal exchange office
|
|
211
|
+
# 6 = Inland Clearance Depot – ICD or "Dry Port"
|
|
212
|
+
# 7 = reserved for fixed transport functions (e.g. oil platform)
|
|
213
|
+
# :B = border crossing
|
|
214
|
+
def function_classifier
|
|
215
|
+
@function_classifier
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# Public: Indicates the status of the entry by a 2-character code
|
|
219
|
+
#
|
|
220
|
+
# Examples
|
|
221
|
+
#
|
|
222
|
+
# Locode.find_by_locode('US NYC').first.status
|
|
223
|
+
# #=> :AI
|
|
224
|
+
#
|
|
225
|
+
# Returns a Symbol with the following meaning or nil
|
|
226
|
+
# :AA = Approved by competent national government agency
|
|
227
|
+
# :AC = Approved by Customs Authority
|
|
228
|
+
# :AF = Approved by national facilitation body
|
|
229
|
+
# :AI = Code adopted by international organisation (IATA or ECLAC)
|
|
230
|
+
# :AM = Approved by the UN/LOCODE Maintenance Agency
|
|
231
|
+
# :AS = Approved by national standardisation body
|
|
232
|
+
# :AQ = Entry approved, functions not verified
|
|
233
|
+
# :RL = Recognised location - Existence and representation of location name
|
|
234
|
+
# confirmed by check against nominated gazetteer or other
|
|
235
|
+
# reference work
|
|
236
|
+
# :RN = Request from credible national sources for locations in their own country
|
|
237
|
+
# :RQ = Request under consideration
|
|
238
|
+
# :RR = Request rejected
|
|
239
|
+
# :QQ = Original entry not verified since date indicated
|
|
240
|
+
# :UR = Entry included on user's request; not officially approved
|
|
241
|
+
# :XX = Entry that will be removed from the next issue of UN/LOCODE
|
|
242
|
+
#
|
|
243
|
+
def status
|
|
244
|
+
@status
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# Public: The date the location was added or updated
|
|
248
|
+
#
|
|
249
|
+
# Examples
|
|
250
|
+
#
|
|
251
|
+
# Locode.find_by_locode('US NYC').first.date
|
|
252
|
+
# #=> '0401'
|
|
253
|
+
#
|
|
254
|
+
# Returns a String containing the date the location was added to the
|
|
255
|
+
# list of LOCODEs. The meaning of the date values is the following:
|
|
256
|
+
# '0207' equals July 2002, '9501' equals January 1995
|
|
257
|
+
def date
|
|
258
|
+
@date
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
# Public: The IATA code for the location if different from the second
|
|
262
|
+
# part of the UN/LOCODE. Else the second part of the UN/LOCODE.
|
|
263
|
+
#
|
|
264
|
+
# Examples
|
|
265
|
+
#
|
|
266
|
+
# Locode.find_by_locode('SE GOT').first.iata_code
|
|
267
|
+
# #=> 'XWL'
|
|
268
|
+
#
|
|
269
|
+
# Locode.find_by_locode('US NYC').first.iata_code
|
|
270
|
+
# #=> 'NYC'
|
|
271
|
+
#
|
|
272
|
+
# Returns a String which is the IATA code if it is different from
|
|
273
|
+
# the city code of the LOCODE. Else it returns the city
|
|
274
|
+
# code.
|
|
275
|
+
#
|
|
276
|
+
def iata_code
|
|
277
|
+
@iata_code
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
# Public: The coordinates of a location.
|
|
281
|
+
#
|
|
282
|
+
# Examples
|
|
283
|
+
#
|
|
284
|
+
# Locode.find_by_locode('SE GOT').first.coordinates
|
|
285
|
+
# #=> nil
|
|
286
|
+
#
|
|
287
|
+
# Locode.find_by_locode('SE GO2').first.coordinates
|
|
288
|
+
# #=> '5742N 01156E'
|
|
289
|
+
#
|
|
290
|
+
# Returns nil if no coordinates are associated with the Location
|
|
291
|
+
# otherwise it returns a String with the coordinates which
|
|
292
|
+
# represents these with two numbers and letters for the cardinal
|
|
293
|
+
# directions. The first followed by either N or S, the second by
|
|
294
|
+
# either E or W.
|
|
295
|
+
def coordinates
|
|
296
|
+
@coordinates
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
# Public: The String representation of the Location
|
|
300
|
+
#
|
|
301
|
+
# Examples
|
|
302
|
+
#
|
|
303
|
+
# Locode.find_by_locode('US NYC').first.to_s
|
|
304
|
+
# #=> <Locode::Location: 'US NYC'>
|
|
305
|
+
#
|
|
306
|
+
# Returns a String that represents the Location
|
|
307
|
+
def to_s
|
|
308
|
+
"<Locode::Location: '#{locode}'>"
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
# Public: The Hash representation of the Location
|
|
312
|
+
#
|
|
313
|
+
# Examples
|
|
314
|
+
#
|
|
315
|
+
# Locode.find_by_locode('BE ANR').first.to_h
|
|
316
|
+
# #=> {:country_code=>"BE", ... }
|
|
317
|
+
#
|
|
318
|
+
# Returns a Hash that represents the Location
|
|
319
|
+
def to_h
|
|
320
|
+
{
|
|
321
|
+
country_code: country_code,
|
|
322
|
+
city_code: city_code,
|
|
323
|
+
full_name: full_name,
|
|
324
|
+
full_name_without_diacritics: full_name_without_diacritics,
|
|
325
|
+
subdivision: subdivision,
|
|
326
|
+
function_classifier: function_classifier,
|
|
327
|
+
status: status,
|
|
328
|
+
date: date,
|
|
329
|
+
iata_code: iata_code,
|
|
330
|
+
coordinates: coordinates
|
|
331
|
+
}
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
# Public: The JSON representation of the Location
|
|
335
|
+
#
|
|
336
|
+
# Examples
|
|
337
|
+
#
|
|
338
|
+
# Locode.find_by_locode('US NYC').first.to_json
|
|
339
|
+
# #=> {"country_code":"US","city_code":"NYC", ...}
|
|
340
|
+
#
|
|
341
|
+
# Returns a JSON that represents the Location
|
|
342
|
+
def to_json
|
|
343
|
+
MultiJson.dump(self.to_h)
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
# Public: To check whether the Locations attributes are all
|
|
347
|
+
# initialized correctly.
|
|
348
|
+
#
|
|
349
|
+
# Examples
|
|
350
|
+
#
|
|
351
|
+
# Locode.find_by_locode('US NYC').first.valid?
|
|
352
|
+
# #=> true
|
|
353
|
+
#
|
|
354
|
+
# Locode::Location.new.valid?
|
|
355
|
+
# #=> false
|
|
356
|
+
#
|
|
357
|
+
# Returns true or false
|
|
358
|
+
def valid?
|
|
359
|
+
country_code && country_code.size == 2 && city_code && city_code.size == 3
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
private
|
|
363
|
+
|
|
364
|
+
# Internal: sets the ISO 3166 alpha-2 Country Code
|
|
365
|
+
#
|
|
366
|
+
# Returns nothing
|
|
367
|
+
def country_code=(country_code)
|
|
368
|
+
if country_code
|
|
369
|
+
if country_code.is_a?(String)
|
|
370
|
+
@country_code = country_code.strip.upcase.to_sym
|
|
371
|
+
elsif country_code.is_a?(Symbol)
|
|
372
|
+
@country_code = country_code.upcase
|
|
373
|
+
end
|
|
374
|
+
end
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
# Internal: sets the three letter code for a place
|
|
378
|
+
#
|
|
379
|
+
# Returns nothing
|
|
380
|
+
def city_code=(city_code)
|
|
381
|
+
if city_code
|
|
382
|
+
if city_code.is_a?(String)
|
|
383
|
+
@city_code = city_code.strip.upcase.to_sym
|
|
384
|
+
elsif city_code.is_a?(Symbol)
|
|
385
|
+
@city_code = city_code.upcase
|
|
386
|
+
end
|
|
387
|
+
end
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
# Internal: sets the name of a location
|
|
391
|
+
#
|
|
392
|
+
# Returns nothing
|
|
393
|
+
def full_name=(full_name)
|
|
394
|
+
if full_name && full_name.is_a?(String)
|
|
395
|
+
@full_name = full_name.strip
|
|
396
|
+
end
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
# Internal: sets the full name without diacritics of the location
|
|
400
|
+
#
|
|
401
|
+
# Returns nothing
|
|
402
|
+
def full_name_without_diacritics=(full_name_without_diacritics)
|
|
403
|
+
if full_name_without_diacritics && full_name_without_diacritics.is_a?(String)
|
|
404
|
+
@full_name_without_diacritics = full_name_without_diacritics.strip
|
|
405
|
+
end
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
# Internal: sets the ISO 1 to 3 character alphabetic and/or numeric code
|
|
409
|
+
#
|
|
410
|
+
# Returns nothing
|
|
411
|
+
def subdivision=(subdivision)
|
|
412
|
+
if subdivision && subdivision.is_a?(String)
|
|
413
|
+
@subdivision = subdivision.strip
|
|
414
|
+
end
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
# Internal: sets the 1-digit function classifier code for the location
|
|
418
|
+
#
|
|
419
|
+
# Returns nothing
|
|
420
|
+
def function_classifier=(function_classifier)
|
|
421
|
+
if function_classifier
|
|
422
|
+
if function_classifier.is_a?(String)
|
|
423
|
+
@function_classifier = function_classifier.strip.chars.select do |char|
|
|
424
|
+
char.to_i.between?(1, 7) || char.upcase.to_s == "B"
|
|
425
|
+
end.map do |char|
|
|
426
|
+
if char.to_i.between?(1, 7)
|
|
427
|
+
char.to_i
|
|
428
|
+
else
|
|
429
|
+
char.upcase.to_sym
|
|
430
|
+
end
|
|
431
|
+
end
|
|
432
|
+
elsif function_classifier.is_a?(Array)
|
|
433
|
+
@function_classifier = function_classifier.flatten
|
|
434
|
+
end
|
|
435
|
+
end
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
# Internal: sets the status indicator of the entry
|
|
439
|
+
#
|
|
440
|
+
# Returns nothing
|
|
441
|
+
def status=(status)
|
|
442
|
+
if status && (status.is_a?(String) || status.is_a?(Symbol))
|
|
443
|
+
@status = status.upcase.to_sym
|
|
444
|
+
end
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
# Internal: sets the date the location was added or updated
|
|
448
|
+
#
|
|
449
|
+
# Returns nothing
|
|
450
|
+
def date=(date)
|
|
451
|
+
if date && date.is_a?(String)
|
|
452
|
+
@date = date.strip
|
|
453
|
+
end
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
# Internal: sets the IATA code for the location
|
|
457
|
+
#
|
|
458
|
+
# Returns nothing
|
|
459
|
+
def iata_code=(iata_code)
|
|
460
|
+
if iata_code && iata_code.is_a?(String)
|
|
461
|
+
@iata_code = iata_code.strip
|
|
462
|
+
end
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
# Internal: sets the coordinates of a location.
|
|
466
|
+
#
|
|
467
|
+
# Returns nothing
|
|
468
|
+
def coordinates=(coordinates)
|
|
469
|
+
if coordinates && coordinates.is_a?(String)
|
|
470
|
+
@coordinates = coordinates.strip
|
|
471
|
+
end
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
# Internal: Used to link name of a status and the status number
|
|
475
|
+
def self.functions_name_identifier
|
|
476
|
+
{
|
|
477
|
+
seaport: 1,
|
|
478
|
+
rail_terminal: 2,
|
|
479
|
+
road_terminal: 3,
|
|
480
|
+
airport: 4,
|
|
481
|
+
postal_exchange_office: 5,
|
|
482
|
+
inland_clearance_depot: 6,
|
|
483
|
+
fixed_transport_functions: 7,
|
|
484
|
+
border_crossing: :B
|
|
485
|
+
}
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
# Dynamically defines the following predicates:
|
|
489
|
+
#
|
|
490
|
+
# seaport?
|
|
491
|
+
# rail_terminal?
|
|
492
|
+
# road_terminal?
|
|
493
|
+
# airport?
|
|
494
|
+
# postal_exchange_office?
|
|
495
|
+
# inland_clearance_depot?
|
|
496
|
+
# fixed_transport_functions?
|
|
497
|
+
# border_crossing?
|
|
498
|
+
#
|
|
499
|
+
# Each returns: true | false
|
|
500
|
+
#
|
|
501
|
+
functions_name_identifier.each do |key, value|
|
|
502
|
+
define_method "#{key}?" do
|
|
503
|
+
function_classifier.include?(value)
|
|
504
|
+
end
|
|
505
|
+
end
|
|
506
|
+
end
|
|
507
|
+
end
|