swissmatch-location 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+
3
+
4
+
5
+ require 'swissmatch/loaderror'
6
+
7
+
8
+
9
+ module SwissMatch
10
+
11
+ # Used to indicate an error while loading the swissmatch data.
12
+ class LoadError < StandardError
13
+
14
+ # Data associated with the error
15
+ attr_reader :data
16
+
17
+ # @param [String] message
18
+ # Same as Exception#initialize, the message of the exception
19
+ # @param [Object] data
20
+ # Arbitrary data
21
+ def initialize(message, data)
22
+ super(message)
23
+ @data = data
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,8 @@
1
+ # encoding: utf-8
2
+
3
+ # Require this file to automatically load swissmatch location data
4
+ # It will try to load from a try the directory in SWISSMATCH_DATA
5
+ # If the env variable SWISSMATCH_DATA is not set, it'll try the data dir in the gem.
6
+
7
+ require 'swissmatch/location'
8
+ SwissMatch.load
@@ -0,0 +1,43 @@
1
+ # encoding: utf-8
2
+ # This file provides a couple of ruby monkey patches.
3
+
4
+ module Enumerable
5
+ unless method_defined?(:last) then
6
+
7
+ # @example
8
+ # ary.last -> obj or nil
9
+ # ary.last(n) -> new_ary
10
+ #
11
+ # @return
12
+ # The last element(s) of self. If the enumerable is empty, the first form returns
13
+ # nil, the second an empty Array.
14
+ # The method is optimized to make use of reverse_each if present.
15
+ def last(n=nil)
16
+ reverse_each_method = method(:reverse_each)
17
+ has_reverse_each = reverse_each_method && reverse_each_method.owner != Enumerable # native reverse_each needed
18
+ if n then
19
+ return_value = []
20
+ if has_reverse_each then
21
+ reverse_each { |val|
22
+ return_value.unshift(val)
23
+ return return_value if return_value.size == n
24
+ }
25
+ else
26
+ each { |val|
27
+ return_value.push(val)
28
+ return_value.shift if return_value.size > n
29
+ }
30
+ end
31
+ else
32
+ if has_reverse_each then
33
+ reverse_each { |value| return value }
34
+ else
35
+ return_value = nil
36
+ each { |value| return_value = value }
37
+ end
38
+ end
39
+
40
+ return_value
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+
3
+ begin
4
+ require 'rubygems/version' # newer rubygems use this
5
+ rescue LoadError
6
+ require 'gem/version' # older rubygems use this
7
+ end
8
+
9
+ module SwissMatch
10
+ module Location
11
+
12
+ # The version of the swissmatch-location gem.
13
+ Version = Gem::Version.new("0.0.1")
14
+ end
15
+ end
@@ -0,0 +1,249 @@
1
+ # encoding: utf-8
2
+
3
+
4
+
5
+ require 'date'
6
+ require 'swissmatch/canton'
7
+ require 'swissmatch/cantons'
8
+ require 'swissmatch/communities'
9
+ require 'swissmatch/community'
10
+ require 'swissmatch/datafiles'
11
+ require 'swissmatch/location/ruby'
12
+ require 'swissmatch/location/version'
13
+ require 'swissmatch/zipcode'
14
+ require 'swissmatch/zipcodes'
15
+
16
+
17
+
18
+ # From SwissMatch::Location
19
+ # Deal with swiss zip codes, cities, communities and cantons.
20
+ #
21
+ # @note
22
+ # All strings passed to SwissMatch are expected to be utf-8. All strings
23
+ # returned by SwissMatch are also in utf-8.
24
+ #
25
+ # @example Load the data
26
+ # require 'swissmatch'
27
+ # SwissMatch.load
28
+ # # alternatively, just require 'swissmatch/location/autoload'
29
+ #
30
+ # @example Get the ONRP for a given zip-code + city
31
+ # require 'swissmatch/location/autoload'
32
+ # SwissMatch.zip_code(8000, 'Zürich').ordering_number # => 4384
33
+ # SwissMatch.zip_code(4384).name # => "Zürich"(de, 0)
34
+ module SwissMatch
35
+
36
+ # This module only contains the version of the swissmatch-location gem
37
+ module Location
38
+ end
39
+
40
+ @data = nil
41
+ class <<self
42
+ # @return [SwissMatch::DataFiles, nil] The data source used
43
+ attr_reader :data
44
+ end
45
+
46
+ # @param [String] name_or_plate
47
+ # The name or license_tag of the canton
48
+ #
49
+ # @return [SwissMatch::Canton]
50
+ # The canton with the given name or license_tag
51
+ def self.canton(name_or_plate)
52
+ @data.cantons[name_or_plate]
53
+ end
54
+
55
+ # @return [SwissMatch::Cantons]
56
+ # All known cantons
57
+ def self.cantons
58
+ @data.cantons
59
+ end
60
+
61
+ # @param [Integer] key
62
+ # The community number of the community
63
+ #
64
+ # @return [SwissMatch::Community]
65
+ # The community with the community number
66
+ def self.community(key)
67
+ @data.communities.by_community_number(key)
68
+ end
69
+
70
+ # @param [String] name
71
+ # The name of the communities
72
+ #
73
+ # @return [SwissMatch::Communities]
74
+ # All communities, or those matching the given name
75
+ def self.communities(name=nil)
76
+ name ? @data.communities.by_name(name) : @data.communities
77
+ end
78
+
79
+ # @param [String, Integer] code_or_name
80
+ # Either the 4 digit zip code as Integer or String, or the city name as a String in
81
+ # utf-8.
82
+ #
83
+ # @return [Array<SwissMatch::ZipCode>]
84
+ # A list of zip codes with the given code or name.
85
+ def self.zip_codes(code_or_name=nil)
86
+ case code_or_name
87
+ when Integer, /\A\d{4}\z/
88
+ @data.zip_codes.by_code(code_or_name.to_i)
89
+ when String
90
+ @data.zip_codes.by_name(code_or_name)
91
+ when nil
92
+ @data.zip_codes
93
+ else
94
+ raise ArgumentError, "Invalid argument, must be a ZipCode#code (Integer or String) or ZipCode#name (String)"
95
+ end
96
+ end
97
+
98
+ # Returns a single zip code. A zip code can be uniquely identified by any of:
99
+ # * Its ordering_number (ONRP, a 4 digit Integer)
100
+ # * Its zip code (4 digit Integer) and add-on (2 digit Integer)
101
+ # * Its zip code (4 digit Integer) and any official name (String)
102
+ # The data can be passed in different ways, e.g. all numbers can be passed either
103
+ # as a String or as an Integer. The identification by zip code and add-on can be done
104
+ # by either using a combined 6 digit number (e.g. 800000 for "8000 Zürich"), or by
105
+ # passing 2 arguments, passing the zip code and the add-on separately.
106
+ #
107
+ # === IMPORTANT
108
+ # You must be aware, that passing a single 4-digit code to SwissMatch::zip_code uses
109
+ # the ONRP, and NOT the zip-code. The 4 digit zip code alone does NOT uniquely identify
110
+ # a zip code.
111
+ #
112
+ #
113
+ # @example Get a zip code by ONRP
114
+ # SwissMatch.zip_code(4384) # => #<SwissMatch::ZipCode:003ff996cf8d3c 8000 Zürich>
115
+ #
116
+ # @example Get a zip code by 4-digit code and add-on
117
+ # SwissMatch.zip_code(8000, 0) # => #<SwissMatch::ZipCode:003ff996cf8d3c 8000 Zürich>
118
+ # SwissMatch.zip_code("8000", "00") # => #<SwissMatch::ZipCode:003ff996cf8d3c 8000 Zürich>
119
+ # SwissMatch.zip_code(800000) # => #<SwissMatch::ZipCode:003ff996cf8d3c 8000 Zürich>
120
+ # SwissMatch.zip_code("800000") # => #<SwissMatch::ZipCode:003ff996cf8d3c 8000 Zürich>
121
+ #
122
+ # @example Get a zip code by 4-digit code and name
123
+ # SwissMatch.zip_code(8000, "Zürich") # => #<SwissMatch::ZipCode:003ff996cf8d3c 8000 Zürich>
124
+ # SwissMatch.zip_code(8000, "Zurigo") # => #<SwissMatch::ZipCode:003ff996cf8d3c 8000 Zürich>
125
+ #
126
+ #
127
+ # @param [String, Integer] code
128
+ # The 4 digit zip code as Integer or String
129
+ # @param [String, Integer] city_or_add_on
130
+ # Either the 2 digit zip-code add-on as string or integer, or the city name as a
131
+ # String in utf-8.
132
+ #
133
+ # @return [SwissMatch::ZipCode]
134
+ # The zip codes with the given code and the given add-on or name.
135
+ def self.zip_code(code, city_or_add_on=nil)
136
+ case city_or_add_on
137
+ when nil
138
+ @data.zip_codes.by_ordering_number(code.to_i)
139
+ when Integer, /\A\d\d\z/
140
+ @data.zip_codes.by_code_and_add_on(code.to_i, city_or_add_on.to_i)
141
+ when String
142
+ @data.zip_codes.by_code_and_name(code.to_i, city_or_add_on)
143
+ else
144
+ raise ArgumentError, "Invalid second argument, must be nil, ZipCode#add_on or ZipCode#name"
145
+ end
146
+ end
147
+
148
+ # @param [String] name
149
+ # The name for which to return matching zip codes
150
+ #
151
+ # @return [Array<SwissMatch::ZipCode>]
152
+ # Zip codes whose name equals the given name
153
+ def self.city(name)
154
+ @data.zip_codes.by_name(name)
155
+ end
156
+
157
+ # @param [String, Integer] code
158
+ # The 4 digit zip code
159
+ # @param [nil, Array<Integer>] only_types
160
+ # An array of zip code types (see ZipCode#type) which the returned zip codes must match.
161
+ # @param [nil, Symbol] locale
162
+ # Return the names in the given locale, defaults to nil/:native (nil and :native are
163
+ # treated the same and will return the native names)
164
+ #
165
+ # @return [Array<String>]
166
+ # A list of unique names matching the parameters (4 digit code, type, locale).
167
+ def self.cities_for_zip_code(code, only_types=nil, locale=nil)
168
+ codes = @data.zip_codes.by_code(code.to_i)
169
+ return [] unless codes
170
+ codes = codes.select { |code| only_types.include?(code.type) } if only_types
171
+ names = case locale
172
+ when :native,nil then codes.map(&:name)
173
+ when :de then codes.map(&:name_de)
174
+ when :fr then codes.map(&:name_fr)
175
+ when :it then codes.map(&:name_it)
176
+ when :rt then codes.map(&:name_rt)
177
+ else raise ArgumentError, "Invalid locale #{locale}"
178
+ end
179
+
180
+ names.uniq
181
+ end
182
+
183
+ # Loads the swissmatch data
184
+ #
185
+ # @param [DataFiles] data_source
186
+ # A valid data-source.
187
+ #
188
+ # @return [self]
189
+ # Returns self
190
+ def self.load(data_source=nil)
191
+ @data = data_source || DataFiles.new
192
+ @data.load!
193
+
194
+ self
195
+ end
196
+
197
+ # @private
198
+ # Used to transliterate city names
199
+ Transliteration1 = {
200
+ "à" => "a",
201
+ "â" => "a",
202
+ "ä" => "a",
203
+ "è" => "e",
204
+ "é" => "e",
205
+ "ê" => "e",
206
+ "ë" => "e",
207
+ "ì" => "i",
208
+ "î" => "i",
209
+ "ï" => "i",
210
+ "ô" => "o",
211
+ "ö" => "o",
212
+ "ù" => "u",
213
+ "ü" => "u",
214
+ }
215
+
216
+ # @private
217
+ # Used to transliterate city names
218
+ Transliteration2 = Transliteration1.merge({
219
+ "ä" => "ae",
220
+ "ö" => "oe",
221
+ "ü" => "ue",
222
+ })
223
+
224
+ # @private
225
+ # Used to transliterate city names
226
+ TransMatch1 = /#{Transliteration1.keys.map { |k| Regexp.escape(k) }.join("|")}/
227
+
228
+ # @private
229
+ # Used to transliterate city names
230
+ TransMatch2 = /#{Transliteration2.keys.map { |k| Regexp.escape(k) }.join("|")}/
231
+
232
+ # @private
233
+ # Used to transliterate city names
234
+ def self.transliterate1(word)
235
+ word.gsub(TransMatch1, Transliteration1).delete("^ A-Za-z").downcase
236
+ end
237
+
238
+ # @private
239
+ # Used to transliterate city names
240
+ def self.transliterate2(word)
241
+ word.gsub(TransMatch2, Transliteration2).delete("^ A-Za-z").downcase
242
+ end
243
+
244
+ # @private
245
+ # Transliterates a string into the unique transliterated (1 & 2) word list
246
+ def self.transliterated_words(string)
247
+ "#{transliterate1(string)} #{transliterate2(string)}".split(" ").uniq
248
+ end
249
+ end
@@ -0,0 +1,47 @@
1
+ # encoding: utf-8
2
+
3
+
4
+
5
+ module SwissMatch
6
+
7
+ # Adds a couple of properties to the String class.
8
+ # These properties are relevant to the naming of Cantons, Communities and
9
+ # ZipCodes. They provide information about the language in which the name is,
10
+ # and which sequence number that name has.
11
+ class Name < ::String
12
+
13
+ # @return [Integer] The sequence number of this name
14
+ attr_reader :sequence_number
15
+
16
+ # @return [Symbol] The language of this name (:de, :fr, :it or :rt)
17
+ attr_reader :language
18
+
19
+ # @param [String] name
20
+ # The name (self)
21
+ # @param [Symbol] language
22
+ # The language this name is in (:de, :fr, :it or :rt)
23
+ # @param [Integer] sequence_number
24
+ # The sequence number of this name
25
+ def initialize(name, language, sequence_number=0)
26
+ @language = language
27
+ @sequence_number = sequence_number
28
+ super(name.to_s)
29
+ end
30
+
31
+ # @return [Hash]
32
+ # All properties of the name as a hash.
33
+ def to_hash
34
+ {
35
+ :name => to_s,
36
+ :language => @language,
37
+ :sequence_number => @sequence_number,
38
+ }
39
+ end
40
+
41
+ # @private
42
+ # @see Object#inspect
43
+ def inspect
44
+ "#{super}(#{@language}, #{@sequence_number})"
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,40 @@
1
+ # As it seems, some zip files delivered by the post are corrupt as per rubyzips standard.
2
+ # This file contains a patch to handle the corruption.
3
+ # As it seems, the zipfiles don't report the "\n" in the zipfile-comment
4
+
5
+ begin
6
+ require 'zip/zip'
7
+ rescue LoadError
8
+ raise "To update the swissmatch datafiles, the rubyzip gem is required, `gem install rubyzip` or add it to your Gemfile."
9
+ end
10
+
11
+ # @private
12
+ # Patching the rubyzip gem
13
+ module Zip
14
+
15
+ # @private
16
+ # Patching the rubyzip gem
17
+ class ZipCentralDirectory
18
+
19
+ # @private
20
+ # Patching the rubyzip gem
21
+ def read_e_o_c_d(io) #:nodoc:
22
+ buf = get_e_o_c_d(io)
23
+ @numberOfThisDisk = ZipEntry::read_zip_short(buf)
24
+ @numberOfDiskWithStartOfCDir = ZipEntry::read_zip_short(buf)
25
+ @totalNumberOfEntriesInCDirOnThisDisk = ZipEntry::read_zip_short(buf)
26
+ @size = ZipEntry::read_zip_short(buf)
27
+ @sizeInBytes = ZipEntry::read_zip_long(buf)
28
+ @cdirOffset = ZipEntry::read_zip_long(buf)
29
+ commentLength = ZipEntry::read_zip_short(buf)
30
+ @comment = buf.read(commentLength)
31
+
32
+ # ORIGINAL
33
+ # raise ZipError, "Zip consistency problem while reading eocd structure" unless buf.size == 0
34
+
35
+ # PATCH, doing it in a similar fashion as Archive::Zip in perl does
36
+ raise ZipError, "Zip consistency problem while reading eocd structure" if @comment.bytesize != commentLength
37
+ # /PATCH
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,303 @@
1
+ # encoding: utf-8
2
+
3
+
4
+
5
+ require 'swissmatch/name'
6
+
7
+
8
+
9
+ module SwissMatch
10
+
11
+ # Represents an area, commonly identified by zip-code and city-name.
12
+ # A unique zip code is determined by any of these:
13
+ # * the postal ordering number
14
+ # * zip code + zip code add on
15
+ # * zip code + name (city)
16
+ class ZipCode
17
+
18
+ # @return [Integer]
19
+ # The postal ordering number, also known as ONRP
20
+ attr_reader :ordering_number # onrp
21
+
22
+ # Described under "PLZ light", as field "PLZ-Typ"
23
+ # * 10 = Domizil- und Fachadressen
24
+ # * 20 = Nur Domiziladressen
25
+ # * 30 = Nur Fach-PLZ
26
+ # * 40 = Firmen-PLZ
27
+ # * 80 = Postinterne PLZ (Angabe Zustellpoststelle auf Bundzetteln oder auf Sackanschriften)
28
+ #
29
+ # @return [Integer]
30
+ # The type of the zip code in a numeric code, one of the values 10, 20, 30, 40 or 80.
31
+ attr_reader :type
32
+
33
+ # Described under "PLZ light", as field "Postleitzahl"
34
+ #
35
+ # @return [Integer]
36
+ # The 4 digit numeric zip code
37
+ attr_reader :code
38
+
39
+ # @return [Integer]
40
+ # The 2 digit numeric code addition, to distinguish zip codes with the same 4 digit code.
41
+ attr_reader :add_on
42
+
43
+ # @return [Integer]
44
+ # The 6 digit numeric zip code and add-on (first 4 digits are the code, last 2
45
+ # digits the add-on).
46
+ attr_reader :full_code
47
+
48
+ # @return [SwissMatch::Canton]
49
+ # The canton this zip code belongs to
50
+ attr_reader :canton
51
+
52
+ # @return [SwissMatch::Name]
53
+ # The official name of this zip code (max. 27 characters)
54
+ attr_reader :name
55
+
56
+ # @return [SwissMatch::Name]
57
+ # The official name of this zip code (max. 18 characters)
58
+ attr_reader :name_short
59
+
60
+ # @return [Symbol]
61
+ # The main language in the area of this zip code. One of :de, :fr, :it or :rt.
62
+ attr_reader :language
63
+
64
+ # @return [SwissMatch::Canton]
65
+ # The second most used language in the area of this zip code. One of :de, :fr, :it or :rt.
66
+ attr_reader :language_alternative
67
+
68
+ # @return [Boolean]
69
+ # Whether this ZipCode instance is included in the MAT[CH]sort sortfile
70
+ attr_reader :sortfile_member
71
+
72
+ # @return [SwissMatch::ZipCode]
73
+ # By which postal office delivery of letters is usually taken care of.
74
+ attr_reader :delivery_by
75
+
76
+ # @return [SwissMatch::Community]
77
+ # The community this zip code belongs to.
78
+ attr_reader :community
79
+
80
+ # @return [Date, nil]
81
+ # The date from which on this zip code starts to be in use
82
+ #
83
+ # @see #in_use?
84
+ attr_reader :valid_from
85
+
86
+ # @return [Date, nil]
87
+ # The date until which on this zip code is in use
88
+ #
89
+ # @see #in_use?
90
+ attr_reader :valid_until
91
+
92
+ def initialize(
93
+ ordering_number,
94
+ type,
95
+ code,
96
+ add_on,
97
+ name,
98
+ names,
99
+ name_short,
100
+ names_short,
101
+ region_names,
102
+ region_names_short,
103
+ canton,
104
+ language,
105
+ language_alternative,
106
+ sortfile_member,
107
+ delivery_by,
108
+ community,
109
+ valid_from,
110
+ valid_until = nil
111
+ )
112
+ @ordering_number = ordering_number
113
+ @type = type
114
+ @code = code
115
+ @add_on = add_on
116
+ @full_code = code*100 + add_on
117
+ @language = language
118
+ @language_alternative = language_alternative
119
+ @name = name.is_a?(Name) ? name : Name.new(name, language)
120
+ @name_short = name_short.is_a?(Name) ? name_short : Name.new(name_short, language)
121
+ @names = (names || [@name]).sort_by(&:sequence_number)
122
+ @names_short = (names_short || [@name_short]).sort_by(&:sequence_number)
123
+ @region_names = region_names
124
+ @region_names_short = region_names_short
125
+ @canton = canton
126
+ @sortfile_member = sortfile_member
127
+ @delivery_by = delivery_by == :self ? self : delivery_by
128
+ @community = community
129
+ @valid_from = valid_from
130
+ @valid_until = valid_until
131
+ end
132
+
133
+ # @return [Array<String>]
134
+ # The name of this zip code in all languages and normalizations (only unique values)
135
+ def transliterated_names
136
+ names.flat_map { |name, ary|
137
+ name = name.to_s # convert from SwissMatch::Name
138
+ [
139
+ SwissMatch.transliterate1(name),
140
+ SwissMatch.transliterate2(name) # TODO: use transliterate gem
141
+ ]
142
+ }.uniq
143
+ end
144
+
145
+ # Since a zip code can - for any given language - have no name, exactly one name,
146
+ # or even multiple names, it is sometimes difficult to write good code to
147
+ # automatically provide well localized addresses. This method helps with that, in that
148
+ # it guarantees a single name, as well chosen as possible.
149
+ # It returns the name for the given language, and with the lowest running number, if
150
+ # no name can be found for the given language, the primary name (@see #name) is
151
+ # returned.
152
+ #
153
+ # @param [Symbol, nil] language
154
+ # One of nil, :de, :fr, :it or :rt
155
+ #
156
+ # @return [SwissMatch::Name]
157
+ # A single name for the zip code, chosen by a 'best fit' algorithm.
158
+ def suggested_name(language=nil)
159
+ (language && @names.find { |name| name.language == language }) || @name
160
+ end
161
+
162
+
163
+ # Since a zip code can - for any given language - have no name, exactly one name,
164
+ # or even multiple names, it is sometimes difficult to write good code to
165
+ # automatically provide well localized addresses. This method helps with that, in that
166
+ # it guarantees a single name, as well chosen as possible.
167
+ # It returns the name for the given language, and with the lowest running number, if
168
+ # no name can be found for the given language, the primary name (@see #name) is
169
+ # returned.
170
+ #
171
+ # @param [Symbol, nil] language
172
+ # One of nil, :de, :fr, :it or :rt
173
+ #
174
+ # @return [SwissMatch::Name]
175
+ # A single short name for the zip code, chosen by a 'best fit' algorithm.
176
+ def suggested_short_name(language=nil)
177
+ (language && @short_name.find { |name| name.language == language }) || @short_name
178
+ end
179
+
180
+ # @param [Symbol, nil] language
181
+ # One of nil, :de, :fr, :it or :rt
182
+ #
183
+ # @return [Array<SwissMatch::Name>]
184
+ # All official names (max. 27 chars) of this zip code.
185
+ def names(language=nil)
186
+ language ? @names.select { |name| name.language == language } : @names
187
+ end
188
+
189
+ # @param [Symbol, nil] language
190
+ # One of nil, :de, :fr, :it or :rt
191
+ #
192
+ # @return [Array<SwissMatch::Name>]
193
+ # All official short names (max. 18 chars) of this zip code.
194
+ def names_short(language=nil)
195
+ language ? @names_short.select { |name| name.language == language } : @names_short
196
+ end
197
+
198
+ # A region name is a name that can be used along a zip code and city, but must not replace
199
+ # the city. For more information, read the section about the PLZ_P2 file, "Bezeichnungstyp"
200
+ # with value "3".
201
+ #
202
+ # @param [Symbol, nil] language
203
+ # One of nil, :de, :fr, :it or :rt
204
+ #
205
+ # @return [Array<SwissMatch::Name>]
206
+ # All official region names (max. 27 chars) of this zip code.
207
+ def region_names(language=nil)
208
+ language ? @region_names.select { |name| name.language == language } : @region_names
209
+ end
210
+
211
+ # A region name is a name that can be used along a zip code and city, but must not replace
212
+ # the city. For more information, read the section about the PLZ_P2 file, "Bezeichnungstyp"
213
+ # with value "3".
214
+ #
215
+ # @param [Symbol, nil] language
216
+ # One of nil, :de, :fr, :it or :rt
217
+ #
218
+ # @return [Array<SwissMatch::Name>]
219
+ # All official short region names (max. 18 chars) of this zip code.
220
+ def region_names_short(language=nil)
221
+ language ? @region_names_short.select { |name| name.language == language } : @region_names_short
222
+ end
223
+
224
+ # Compare two zip codes by their ordering number (ONRP)
225
+ #
226
+ # @return [Integer]
227
+ # Returns -1, 0 or 1.
228
+ def <=>(other)
229
+ @ordering_number <=> other.ordering_number
230
+ end
231
+
232
+ # @param [Date] at
233
+ # The date for which to check the
234
+ #
235
+ # @return [Boolean]
236
+ # Whether the zip code is in active use at the given date.
237
+ def in_use?(at=Date.today)
238
+ if @valid_from then
239
+ if @valid_until then
240
+ at.between?(@valid_from, @valid_until)
241
+ else
242
+ at >= @valid_from
243
+ end
244
+ elsif @valid_until
245
+ at <= @valid_until
246
+ else
247
+ true
248
+ end
249
+ end
250
+
251
+ # @param [Boolean] retain_references
252
+ # If set to false, :delivery_by will be set to the ordering number,
253
+ # :community to the community_number and :canton to the canton's license_tag.
254
+ #
255
+ # @return [Hash]
256
+ # All properties of the zip code as a hash.
257
+ def to_hash(retain_references=false)
258
+ delivery_by = retain_references ? @delivery_by : (@delivery_by && @delivery_by.ordering_number)
259
+ community = retain_references ? @community : (@community && @community.community_number)
260
+ canton = retain_references ? @canton : (@canton && @canton.license_tag)
261
+ {
262
+ :ordering_number => @ordering_number,
263
+ :type => @type,
264
+ :code => @code,
265
+ :add_on => @add_on,
266
+ :name => @name,
267
+ :name_short => @name_short,
268
+ :canton => canton,
269
+ :language => @language,
270
+ :language_alternative => @language_alternative,
271
+ :sortfile_member => @sortfile_member,
272
+ :delivery_by => delivery_by,
273
+ :community => community,
274
+ :valid_from => @valid_from,
275
+ :valid_until => @valid_until,
276
+ }
277
+ end
278
+
279
+ # @private
280
+ # @see Object#hash
281
+ def hash
282
+ [self.class, @ordering_number].hash
283
+ end
284
+
285
+ # @private
286
+ # @see Object#eql?
287
+ def eql?(other)
288
+ self.class == other.class && @ordering_number == other.ordering_number
289
+ end
290
+
291
+ # @return [String]
292
+ # The 4 digit code, followed by the name
293
+ def to_s
294
+ "#{@code} #{@name}"
295
+ end
296
+
297
+ # @return [String]
298
+ # @see Object#inspect
299
+ def inspect
300
+ sprintf "\#<%s:%014x %s>", self.class, object_id, self
301
+ end
302
+ end
303
+ end