timezone 0.6.0 → 0.99.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +99 -1
- data/.travis.yml +6 -3
- data/CHANGES.markdown +10 -0
- data/Gemfile +1 -1
- data/README.markdown +129 -89
- data/Rakefile +7 -4
- data/benchmark.rb +13 -13
- data/lib/timezone/active_support.rb +147 -134
- data/lib/timezone/configure.rb +131 -110
- data/lib/timezone/deprecate.rb +32 -0
- data/lib/timezone/error.rb +16 -5
- data/lib/timezone/loader.rb +19 -16
- data/lib/timezone/lookup/basic.rb +24 -2
- data/lib/timezone/lookup/geonames.rb +9 -5
- data/lib/timezone/lookup/google.rb +24 -15
- data/lib/timezone/lookup/test.rb +8 -8
- data/lib/timezone/lookup.rb +68 -0
- data/lib/timezone/net_http_client.rb +23 -9
- data/lib/timezone/nil_zone.rb +32 -0
- data/lib/timezone/parser.rb +10 -5
- data/lib/timezone/version.rb +2 -1
- data/lib/timezone/zone.rb +230 -99
- data/lib/timezone.rb +75 -1
- data/test/basic_lookup_test.rb +2 -2
- data/test/geonames_lookup_test.rb +13 -6
- data/test/google_lookup_test.rb +34 -24
- data/test/http_test_client.rb +7 -6
- data/test/test_lookup_test.rb +3 -1
- data/test/test_timezone.rb +59 -0
- data/test/timezone/lookup/test_geonames.rb +59 -0
- data/test/timezone/lookup/test_google.rb +94 -0
- data/test/timezone/lookup/test_test.rb +24 -0
- data/test/timezone/test_deprecate.rb +20 -0
- data/test/timezone/test_loader.rb +32 -0
- data/test/timezone/test_lookup.rb +53 -0
- data/test/timezone/test_nil_zone.rb +26 -0
- data/test/timezone/test_zone.rb +49 -0
- data/test/timezone_test.rb +64 -63
- data/timezone.gemspec +16 -15
- metadata +39 -38
- data/.rubocop_todo.yml +0 -235
data/lib/timezone/configure.rb
CHANGED
@@ -1,235 +1,256 @@
|
|
1
1
|
require 'timezone/net_http_client'
|
2
2
|
require 'timezone/lookup'
|
3
|
+
require 'timezone/deprecate'
|
3
4
|
|
4
5
|
module Timezone
|
5
|
-
#
|
6
|
-
#
|
7
|
-
# You'll want to sign up for a geonames username at
|
8
|
-
# {http://www.geonames.org/login Geonames}. Use the username to
|
9
|
-
# configure your application for latitude and longitude based
|
10
|
-
# timezone searches.
|
11
|
-
# Alternatively, you'll want to sign up for a Google api key at
|
12
|
-
# {https://code.google.com/apis/console/ Google}. Use the api key to
|
13
|
-
# configure your application for latitude and longitude based
|
14
|
-
# timezone searches.
|
15
|
-
#
|
16
|
-
# If you aren't going to initialize timezone objects based on lat,lng
|
17
|
-
# then this configuration is not necessary.
|
18
|
-
#
|
19
|
-
# @example
|
20
|
-
# Timezone::Configure.begin do |c|
|
21
|
-
# c.geonames_url = 'api.geonames.org'
|
22
|
-
# c.username = 'foo-bar'
|
23
|
-
# c.google_api_key = 'abc123'
|
24
|
-
# end
|
6
|
+
# @deprecated `Timezone::Configure` will be removed in the release
|
7
|
+
# of the `timezone gem. Use `Timezone::Lookup::config` instead.
|
25
8
|
#
|
9
|
+
# The old way to configure this gem.
|
10
|
+
# rubocop:disable Style/ClassVars
|
26
11
|
class Configure
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
12
|
+
DEPRECATE = '[DEPRECATED] `Timezone::Configure` will be removed ' \
|
13
|
+
'in the release of the `timezone gem. Use `Timezone::Lookup` ' \
|
14
|
+
'instead.'.freeze
|
15
|
+
|
16
|
+
# @deprecated `Timezone::Configure` will be removed in the release
|
17
|
+
# of the `timezone gem. Use `Timezone::Lookup instead.
|
31
18
|
def self.google_api_key
|
32
|
-
|
19
|
+
@@google_api_key ||= nil
|
33
20
|
end
|
34
21
|
|
35
|
-
#
|
36
|
-
#
|
37
|
-
# @param [String] api_key
|
38
|
-
# the Google API key
|
22
|
+
# @deprecated `Timezone::Configure` will be removed in the release
|
23
|
+
# of the `timezone gem. Use `Timezone::Lookup instead.
|
39
24
|
def self.google_api_key=(api_key)
|
40
|
-
|
25
|
+
@@google_api_key = api_key
|
41
26
|
end
|
42
27
|
|
43
|
-
#
|
44
|
-
#
|
45
|
-
# @return [String]
|
46
|
-
# the Google Client ('abc123')
|
28
|
+
# @deprecated `Timezone::Configure` will be removed in the release
|
29
|
+
# of the `timezone gem. Use `Timezone::Lookup instead.
|
47
30
|
def self.google_client_id
|
48
|
-
|
31
|
+
@@google_client_id ||= nil
|
49
32
|
end
|
50
33
|
|
51
|
-
#
|
52
|
-
#
|
53
|
-
# @param [String] client
|
54
|
-
# the Google Client
|
34
|
+
# @deprecated `Timezone::Configure` will be removed in the release
|
35
|
+
# of the `timezone gem. Use `Timezone::Lookup instead.
|
55
36
|
def self.google_client_id=(client)
|
56
|
-
|
37
|
+
@@google_client_id = client
|
57
38
|
end
|
58
39
|
|
59
|
-
#
|
60
|
-
#
|
61
|
-
# @return [Boolean]
|
40
|
+
# @deprecated `Timezone::Configure` will be removed in the release
|
41
|
+
# of the `timezone gem. Use `Timezone::Lookup instead.
|
62
42
|
def self.use_google?
|
63
|
-
|
43
|
+
!google_api_key.nil?
|
64
44
|
end
|
65
45
|
|
66
|
-
#
|
67
|
-
#
|
68
|
-
# @return [Boolean]
|
46
|
+
# @deprecated `Timezone::Configure` will be removed in the release
|
47
|
+
# of the `timezone gem. Use `Timezone::Lookup instead.
|
69
48
|
def self.use_google_enterprise?
|
70
|
-
use_google? &&
|
49
|
+
use_google? && !google_client_id.nil?
|
71
50
|
end
|
72
51
|
|
52
|
+
# @deprecated `Timezone::Configure` will be removed in the release
|
53
|
+
# of the `timezone gem. Use `Timezone::Lookup instead.
|
73
54
|
def self.lookup=(lookup)
|
74
55
|
@lookup = lookup && lookup.new(self)
|
75
56
|
end
|
76
57
|
|
58
|
+
# @deprecated `Timezone::Configure` will be removed in the release
|
59
|
+
# of the `timezone gem. Use `Timezone::Lookup instead.
|
77
60
|
def self.lookup
|
78
61
|
return @lookup if @lookup
|
79
62
|
|
80
63
|
use_google? ? google_lookup : geonames_lookup
|
81
64
|
end
|
82
65
|
|
66
|
+
# Responsible for mapping old configuration options to new
|
67
|
+
# configuration style for forwards-compatability with the
|
68
|
+
# updated Google lookup.
|
69
|
+
class GoogleConfigMapper
|
70
|
+
def initialize(config)
|
71
|
+
@config = config
|
72
|
+
end
|
73
|
+
|
74
|
+
def protocol; @config.protocol; end
|
75
|
+
|
76
|
+
def url; @config.url; end
|
77
|
+
|
78
|
+
def http_client; @config.http_client; end
|
79
|
+
|
80
|
+
def api_key; @config.google_api_key; end
|
81
|
+
|
82
|
+
def client_id; @config.google_client_id; end
|
83
|
+
|
84
|
+
def request_handler; nil; end
|
85
|
+
end
|
86
|
+
|
87
|
+
private_constant :GoogleConfigMapper
|
88
|
+
|
89
|
+
# @deprecated `Timezone::Configure` will be removed in the release
|
90
|
+
# of the `timezone gem. Use `Timezone::Lookup instead.
|
83
91
|
def self.google_lookup
|
84
|
-
@google_lookup ||=
|
92
|
+
@google_lookup ||=
|
93
|
+
Timezone::Lookup::Google.new(GoogleConfigMapper.new(self))
|
94
|
+
end
|
95
|
+
|
96
|
+
# Responsible for mapping old configuration options to new
|
97
|
+
# configuration style for forwards-compatability with the
|
98
|
+
# updated Geonames lookup.
|
99
|
+
class GeonamesConfigMapper
|
100
|
+
def initialize(config)
|
101
|
+
@config = config
|
102
|
+
end
|
103
|
+
|
104
|
+
def protocol; @config.protocol; end
|
105
|
+
|
106
|
+
def url; @config.url; end
|
107
|
+
|
108
|
+
def username; @config.username; end
|
109
|
+
|
110
|
+
def http_client; @config.http_client; end
|
111
|
+
|
112
|
+
def request_handler; nil; end
|
85
113
|
end
|
86
114
|
|
115
|
+
private_constant :GeonamesConfigMapper
|
116
|
+
|
117
|
+
# @deprecated `Timezone::Configure` will be removed in the release
|
118
|
+
# of the `timezone gem. Use `Timezone::Lookup instead.
|
87
119
|
def self.geonames_lookup
|
88
|
-
@geonames_lookup ||=
|
120
|
+
@geonames_lookup ||=
|
121
|
+
Timezone::Lookup::Geonames.new(GeonamesConfigMapper.new(self))
|
89
122
|
end
|
90
123
|
|
91
|
-
#
|
92
|
-
#
|
93
|
-
# @return [String]
|
94
|
-
# the Geonames API URL ('api.geonames.org')
|
124
|
+
# @deprecated `Timezone::Configure` will be removed in the release
|
125
|
+
# of the `timezone gem. Use `Timezone::Lookup instead.
|
95
126
|
def self.geonames_url
|
96
127
|
@@geonames_url ||= 'api.geonames.org'
|
97
128
|
end
|
98
129
|
|
99
|
-
#
|
100
|
-
#
|
101
|
-
# @param [String] url
|
102
|
-
# the Geonames API URL
|
130
|
+
# @deprecated `Timezone::Configure` will be removed in the release
|
131
|
+
# of the `timezone gem. Use `Timezone::Lookup instead.
|
103
132
|
def self.geonames_url=(url)
|
104
133
|
@@geonames_url = url
|
105
134
|
end
|
106
135
|
|
107
|
-
|
108
|
-
|
136
|
+
# @deprecated `Timezone::Configure` will be removed in the release
|
137
|
+
# of the `timezone gem. Use `Timezone::Lookup instead.
|
138
|
+
def self.url=(url)
|
139
|
+
self.geonames_url = url
|
109
140
|
end
|
110
141
|
|
111
|
-
#
|
112
|
-
#
|
113
|
-
# @return [String]
|
114
|
-
# the Google API URL ('maps.googleapis.com')
|
142
|
+
# @deprecated `Timezone::Configure` will be removed in the release
|
143
|
+
# of the `timezone gem. Use `Timezone::Lookup instead.
|
115
144
|
def self.google_url
|
116
145
|
@@google_url ||= 'maps.googleapis.com'
|
117
146
|
end
|
118
147
|
|
119
|
-
#
|
120
|
-
#
|
121
|
-
# @param [String] url
|
122
|
-
# the Google API URL
|
148
|
+
# @deprecated `Timezone::Configure` will be removed in the release
|
149
|
+
# of the `timezone gem. Use `Timezone::Lookup instead.
|
123
150
|
def self.google_url=(url)
|
124
151
|
@@google_url = url
|
125
152
|
end
|
126
153
|
|
127
|
-
#
|
128
|
-
#
|
129
|
-
# @return [String]
|
130
|
-
# the Google or Geonames API URL
|
154
|
+
# @deprecated `Timezone::Configure` will be removed in the release
|
155
|
+
# of the `timezone gem. Use `Timezone::Lookup instead.
|
131
156
|
def self.url
|
132
157
|
use_google? ? google_url : geonames_url
|
133
158
|
end
|
134
159
|
|
135
|
-
#
|
136
|
-
#
|
137
|
-
# @param [String] protocol
|
138
|
-
# the Geonames API HTTP procotol
|
160
|
+
# @deprecated `Timezone::Configure` will be removed in the release
|
161
|
+
# of the `timezone gem. Use `Timezone::Lookup instead.
|
139
162
|
def self.geonames_protocol=(protocol)
|
140
163
|
@@geonames_protocol = protocol
|
141
164
|
end
|
142
165
|
|
143
|
-
#
|
144
|
-
#
|
145
|
-
# @return [String]
|
146
|
-
# the Geonames API HTTP protocol ('http')
|
166
|
+
# @deprecated `Timezone::Configure` will be removed in the release
|
167
|
+
# of the `timezone gem. Use `Timezone::Lookup instead.
|
147
168
|
def self.geonames_protocol
|
148
169
|
@@geonames_protocol ||= 'http'
|
149
170
|
end
|
150
171
|
|
151
|
-
#
|
152
|
-
#
|
153
|
-
# @param [String] protocol
|
154
|
-
# the Google API HTTP procotol
|
172
|
+
# @deprecated `Timezone::Configure` will be removed in the release
|
173
|
+
# of the `timezone gem. Use `Timezone::Lookup instead.
|
155
174
|
def self.google_protocol=(protocol)
|
156
175
|
@@google_protocol = protocol
|
157
176
|
end
|
158
177
|
|
159
|
-
#
|
160
|
-
#
|
161
|
-
# @return [String]
|
162
|
-
# the Google API HTTP protocol ('https')
|
178
|
+
# @deprecated `Timezone::Configure` will be removed in the release
|
179
|
+
# of the `timezone gem. Use `Timezone::Lookup instead.
|
163
180
|
def self.google_protocol
|
164
181
|
@@google_protocol ||= 'https'
|
165
182
|
end
|
166
183
|
|
167
|
-
#
|
168
|
-
#
|
169
|
-
# @return [String]
|
170
|
-
# the Google or Geonames API protocol
|
184
|
+
# @deprecated `Timezone::Configure` will be removed in the release
|
185
|
+
# of the `timezone gem. Use `Timezone::Lookup instead.
|
171
186
|
def self.protocol
|
172
187
|
use_google? ? google_protocol : geonames_protocol
|
173
188
|
end
|
174
189
|
|
175
|
-
#
|
176
|
-
#
|
177
|
-
# @return [Object]
|
178
|
-
# the HTTP client ({Timezone::NetHTTPClient Timezone::NetHTTPClient})
|
190
|
+
# @deprecated `Timezone::Configure` will be removed in the release
|
191
|
+
# of the `timezone gem. Use `Timezone::Lookup instead.
|
179
192
|
def self.http_client
|
180
193
|
@@http_client ||= Timezone::NetHTTPClient
|
181
194
|
end
|
182
195
|
|
183
|
-
#
|
184
|
-
#
|
185
|
-
# @param [Object] client
|
186
|
-
# the HTTP client that handles requests to Geonames and Google
|
187
|
-
#
|
196
|
+
# @deprecated `Timezone::Configure` will be removed in the release
|
197
|
+
# of the `timezone gem. Use `Timezone::Lookup instead.
|
188
198
|
def self.http_client=(client)
|
189
199
|
@@http_client = client
|
190
200
|
end
|
191
201
|
|
192
|
-
#
|
193
|
-
#
|
194
|
-
# @return [String]
|
195
|
-
# the Geonames API username ('foo-bar')
|
202
|
+
# @deprecated `Timezone::Configure` will be removed in the release
|
203
|
+
# of the `timezone gem. Use `Timezone::Lookup instead.
|
196
204
|
def self.username
|
197
205
|
@@username ||= nil
|
198
206
|
end
|
199
207
|
|
200
|
-
#
|
201
|
-
#
|
202
|
-
# @param [String] username
|
203
|
-
# the Geonames API username
|
208
|
+
# @deprecated `Timezone::Configure` will be removed in the release
|
209
|
+
# of the `timezone gem. Use `Timezone::Lookup instead.
|
204
210
|
def self.username=(username)
|
205
211
|
@@username = username
|
206
212
|
end
|
207
213
|
|
214
|
+
# @deprecated `Timezone::Configure` will be removed in the release
|
215
|
+
# of the `timezone gem. Use `Timezone::Lookup instead.
|
208
216
|
def self.begin
|
217
|
+
Deprecate.call(self, :begin, DEPRECATE)
|
209
218
|
yield self
|
210
219
|
end
|
211
220
|
|
212
|
-
|
221
|
+
# @deprecated `Timezone::Configure` will be removed in the release
|
222
|
+
# of the `timezone gem. Use `Timezone::Lookup instead.
|
223
|
+
def self.replace(what, with = {})
|
213
224
|
replacements # instantiate @@replacements
|
214
225
|
@@replacements[what] = with[:with]
|
215
226
|
end
|
216
227
|
|
228
|
+
# @deprecated `Timezone::Configure` will be removed in the release
|
229
|
+
# of the `timezone gem. Use `Timezone::Lookup instead.
|
217
230
|
def self.replacements
|
218
231
|
@@replacements ||= {}
|
219
232
|
end
|
220
233
|
|
234
|
+
# @deprecated `Timezone::Configure` will be removed in the release
|
235
|
+
# of the `timezone gem. Use `Timezone::Lookup instead.
|
221
236
|
def self.default_for_list
|
222
237
|
@@default_list ||= nil
|
223
238
|
end
|
224
239
|
|
240
|
+
# @deprecated `Timezone::Configure` will be removed in the release
|
241
|
+
# of the `timezone gem. Use `Timezone::Lookup instead.
|
225
242
|
def self.default_for_list=(*list)
|
226
243
|
@@default_list = list.flatten!
|
227
244
|
end
|
228
245
|
|
246
|
+
# @deprecated `Timezone::Configure` will be removed in the release
|
247
|
+
# of the `timezone gem. Use `Timezone::Lookup instead.
|
229
248
|
def self.order_list_by
|
230
249
|
@@order_by ||= :utc_offset
|
231
250
|
end
|
232
251
|
|
252
|
+
# @deprecated `Timezone::Configure` will be removed in the release
|
253
|
+
# of the `timezone gem. Use `Timezone::Lookup instead.
|
233
254
|
def self.order_list_by=(order)
|
234
255
|
@@order_by = order
|
235
256
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Timezone
|
2
|
+
# This class provides a way to set a custom hook for deprecations.
|
3
|
+
module Deprecate
|
4
|
+
class << self
|
5
|
+
# Set the custom deprecation callback. By default this
|
6
|
+
# issues a deprecation warning.
|
7
|
+
#
|
8
|
+
# @param callback [#call] the custom callback
|
9
|
+
#
|
10
|
+
# @example Send a message to StatsD
|
11
|
+
# Timezone::Deprecate.callback = lambda do |klass, method, _|
|
12
|
+
# StatsD.increment(sanitize(klass, method))
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# @example Send a message to a custom logger
|
16
|
+
# Timezone::Deprecate.callback = lambda do |klass, method, msg|
|
17
|
+
# MyLogger.log("[#{klass} : #{method}] #{msg}")
|
18
|
+
# end
|
19
|
+
attr_writer :callback
|
20
|
+
|
21
|
+
# @!visibility private
|
22
|
+
def callback
|
23
|
+
@callback || -> (_, _, message) { warn(message) }
|
24
|
+
end
|
25
|
+
|
26
|
+
# @!visibility private
|
27
|
+
def call(klass, method, message)
|
28
|
+
callback && callback.call(klass, method, message)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/timezone/error.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
module Timezone
|
2
|
-
# Error messages that can be raised by this gem. To catch any
|
2
|
+
# Error messages that can be raised by this gem. To catch any
|
3
|
+
# related error message, use Error::Base.
|
3
4
|
#
|
4
5
|
# begin
|
5
6
|
# ...
|
@@ -7,13 +8,23 @@ module Timezone
|
|
7
8
|
# puts "Timezone Error: #{e.message}"
|
8
9
|
# end
|
9
10
|
module Error
|
11
|
+
# Top-level error. All other timezone errors subclass this one.
|
10
12
|
class Base < StandardError; end
|
13
|
+
# Indicates an invalid timezone name.
|
11
14
|
class InvalidZone < Base; end
|
15
|
+
# @deprecated this class will be removed in the next release of the gem.
|
12
16
|
class NilZone < Base; end
|
13
|
-
|
14
|
-
class
|
17
|
+
# Indicates a lookup failure.
|
18
|
+
class Lookup < Base; end
|
19
|
+
# Indicates an error during lookup using the geonames API.
|
20
|
+
class GeoNames < Lookup; end
|
21
|
+
# Indicates an error during lookup using the google API.
|
22
|
+
class Google < Lookup; end
|
23
|
+
# @deprecated this class will be removed in the next release of the gem.
|
15
24
|
class ParseTime < Base; end
|
16
|
-
|
17
|
-
class
|
25
|
+
# Indicates a missing stub during a test lookup.
|
26
|
+
class Test < Lookup; end
|
27
|
+
# Indicates an invalid configuration.
|
28
|
+
class InvalidConfig < Base; end
|
18
29
|
end
|
19
30
|
end
|
data/lib/timezone/loader.rb
CHANGED
@@ -1,27 +1,34 @@
|
|
1
1
|
require 'timezone/error'
|
2
2
|
|
3
|
-
module Timezone
|
3
|
+
module Timezone # rubocop:disable Style/Documentation
|
4
|
+
# Responsible for loading and parsing timezone data from files.
|
4
5
|
module Loader
|
5
|
-
ZONE_FILE_PATH = File.expand_path(File.dirname(__FILE__)+'/../../data')
|
6
|
+
ZONE_FILE_PATH = File.expand_path(File.dirname(__FILE__) + '/../../data')
|
6
7
|
SOURCE_BIT = 0
|
7
8
|
|
8
9
|
class << self
|
9
|
-
def load(
|
10
|
+
def load(name)
|
11
|
+
raise ::Timezone::Error::InvalidZone unless valid?(name)
|
12
|
+
|
10
13
|
@rules ||= {}
|
11
|
-
@rules[
|
14
|
+
@rules[name] ||= parse_zone_data(get_zone_data(name))
|
12
15
|
end
|
13
16
|
|
14
17
|
def names
|
15
|
-
|
18
|
+
@names ||= parse_zone_names
|
19
|
+
end
|
20
|
+
|
21
|
+
def valid?(name)
|
22
|
+
names.include?(name)
|
16
23
|
end
|
17
24
|
|
18
25
|
private
|
19
26
|
|
20
27
|
def parse_zone_names
|
21
|
-
files = Dir[File.join(ZONE_FILE_PATH,
|
28
|
+
files = Dir[File.join(ZONE_FILE_PATH, '**/*')].map do |file|
|
22
29
|
next if File.directory?(file)
|
23
30
|
|
24
|
-
file.
|
31
|
+
file.sub("#{ZONE_FILE_PATH}/", '')
|
25
32
|
end
|
26
33
|
|
27
34
|
files.compact
|
@@ -35,7 +42,7 @@ module Timezone
|
|
35
42
|
source = source.to_i
|
36
43
|
dst = dst == '1'
|
37
44
|
offset = offset.to_i
|
38
|
-
source = rules.last[SOURCE_BIT]+source if rules.last
|
45
|
+
source = rules.last[SOURCE_BIT] + source if rules.last
|
39
46
|
rules << [source, name, dst, offset]
|
40
47
|
end
|
41
48
|
|
@@ -43,15 +50,11 @@ module Timezone
|
|
43
50
|
end
|
44
51
|
|
45
52
|
# Retrieve the data from a particular time zone
|
46
|
-
def get_zone_data(
|
47
|
-
|
48
|
-
|
49
|
-
if !File.exists?(file)
|
50
|
-
raise Timezone::Error::InvalidZone, "'#{zone}' is not a valid zone."
|
51
|
-
end
|
52
|
-
|
53
|
-
File.read(file)
|
53
|
+
def get_zone_data(name)
|
54
|
+
File.read(File.join(ZONE_FILE_PATH, name))
|
54
55
|
end
|
55
56
|
end
|
56
57
|
end
|
58
|
+
|
59
|
+
private_constant :Loader
|
57
60
|
end
|
@@ -2,9 +2,13 @@ require 'timezone/error'
|
|
2
2
|
|
3
3
|
module Timezone
|
4
4
|
module Lookup
|
5
|
+
# @abstract Subclass and override {#lookup} to implement
|
6
|
+
# a custom Lookup class.
|
5
7
|
class Basic
|
6
8
|
attr_reader :config
|
7
9
|
|
10
|
+
# @param config [#protocol, #url, #request_handler] a configuration
|
11
|
+
# object
|
8
12
|
def initialize(config)
|
9
13
|
if config.protocol.nil?
|
10
14
|
raise(::Timezone::Error::InvalidConfig, 'missing protocol')
|
@@ -17,11 +21,29 @@ module Timezone
|
|
17
21
|
@config = config
|
18
22
|
end
|
19
23
|
|
24
|
+
# Returns an instance of the request handler.
|
25
|
+
#
|
26
|
+
# @return [#get] an instance of a request handler
|
20
27
|
def client
|
21
|
-
|
28
|
+
# TODO: Remove http_client once on 1.0.0
|
29
|
+
@client ||=
|
30
|
+
if !config.request_handler.nil?
|
31
|
+
config.request_handler.new(config)
|
32
|
+
else
|
33
|
+
config.http_client.new(config.protocol, config.url)
|
34
|
+
end
|
22
35
|
end
|
23
36
|
|
24
|
-
|
37
|
+
# Returns a timezone name for a given lat, long pair.
|
38
|
+
#
|
39
|
+
# @param lat [Double] latitude coordinate
|
40
|
+
# @param long [Double] longitude coordinate
|
41
|
+
# @return [String] the timezone name
|
42
|
+
# @return [nil] if the lat, long pair does not resolve to an
|
43
|
+
# actual timezone
|
44
|
+
# @raise [Timezone::Error::Base] if an error occurred while
|
45
|
+
# while performing the lookup
|
46
|
+
def lookup(_lat, _long)
|
25
47
|
raise NoMethodError, 'lookup is not implemented'
|
26
48
|
end
|
27
49
|
end
|
@@ -5,17 +5,21 @@ require 'uri'
|
|
5
5
|
|
6
6
|
module Timezone
|
7
7
|
module Lookup
|
8
|
+
# @!visibility private
|
8
9
|
class Geonames < ::Timezone::Lookup::Basic
|
9
10
|
def initialize(config)
|
10
11
|
if config.username.nil?
|
11
|
-
raise(::Timezone::Error::InvalidConfig, 'missing username')
|
12
|
+
raise(::Timezone::Error::InvalidConfig, 'missing username'.freeze)
|
12
13
|
end
|
13
14
|
|
15
|
+
config.protocol ||= 'http'.freeze
|
16
|
+
config.url ||= 'api.geonames.org'.freeze
|
17
|
+
|
14
18
|
super
|
15
19
|
end
|
16
20
|
|
17
|
-
def lookup(lat,
|
18
|
-
response = client.get(url(lat,
|
21
|
+
def lookup(lat, long)
|
22
|
+
response = client.get(url(lat, long))
|
19
23
|
|
20
24
|
return unless response.code =~ /^2\d\d$/
|
21
25
|
|
@@ -32,10 +36,10 @@ module Timezone
|
|
32
36
|
|
33
37
|
private
|
34
38
|
|
35
|
-
def url(lat,
|
39
|
+
def url(lat, long)
|
36
40
|
query = URI.encode_www_form(
|
37
41
|
'lat' => lat,
|
38
|
-
'lng' =>
|
42
|
+
'lng' => long,
|
39
43
|
'username' => config.username)
|
40
44
|
"/timezoneJSON?#{query}"
|
41
45
|
end
|
@@ -8,53 +8,62 @@ require 'cgi'
|
|
8
8
|
|
9
9
|
module Timezone
|
10
10
|
module Lookup
|
11
|
+
# @!visibility private
|
11
12
|
class Google < ::Timezone::Lookup::Basic
|
12
13
|
def initialize(config)
|
13
|
-
if config.
|
14
|
-
raise(::Timezone::Error::InvalidConfig, 'missing api key')
|
14
|
+
if config.api_key.nil?
|
15
|
+
raise(::Timezone::Error::InvalidConfig, 'missing api key'.freeze)
|
15
16
|
end
|
17
|
+
|
18
|
+
config.protocol ||= 'https'.freeze
|
19
|
+
config.url ||= 'maps.googleapis.com'.freeze
|
20
|
+
|
16
21
|
super
|
17
22
|
end
|
18
23
|
|
19
|
-
def lookup(lat,
|
20
|
-
response = client.get(url(lat,
|
24
|
+
def lookup(lat, long)
|
25
|
+
response = client.get(url(lat, long))
|
21
26
|
|
22
|
-
if response.code == '403'
|
23
|
-
raise(Timezone::Error::Google, '403 Forbidden')
|
27
|
+
if response.code == '403'.freeze
|
28
|
+
raise(Timezone::Error::Google, '403 Forbidden'.freeze)
|
24
29
|
end
|
25
30
|
|
26
31
|
return unless response.code =~ /^2\d\d$/
|
27
32
|
data = JSON.parse(response.body)
|
28
33
|
|
29
|
-
if data['status'] != 'OK'
|
34
|
+
if data['status'.freeze] != 'OK'.freeze
|
30
35
|
raise(Timezone::Error::Google, data['errorMessage'])
|
31
36
|
end
|
32
37
|
|
33
|
-
data['timeZoneId']
|
38
|
+
data['timeZoneId'.freeze]
|
34
39
|
rescue => e
|
35
40
|
raise(Timezone::Error::Google, e.message)
|
36
41
|
end
|
37
42
|
|
38
43
|
private
|
39
44
|
|
45
|
+
def use_google_enterprise?
|
46
|
+
!config.client_id.nil?
|
47
|
+
end
|
48
|
+
|
40
49
|
def authorize(url)
|
41
|
-
if
|
42
|
-
url += "&client=#{CGI.escape(config.
|
50
|
+
if use_google_enterprise?
|
51
|
+
url += "&client=#{CGI.escape(config.client_id)}"
|
43
52
|
|
44
53
|
sha1 = OpenSSL::Digest.new('sha1')
|
45
|
-
binary_key = Base64.decode64(config.
|
54
|
+
binary_key = Base64.decode64(config.api_key.tr('-_', '+/'))
|
46
55
|
binary_signature = OpenSSL::HMAC.digest(sha1, binary_key, url)
|
47
|
-
signature = Base64.encode64(binary_signature).tr('+/','-_').strip
|
56
|
+
signature = Base64.encode64(binary_signature).tr('+/', '-_').strip
|
48
57
|
|
49
58
|
url + "&signature=#{signature}"
|
50
59
|
else
|
51
|
-
url + "&key=#{config.
|
60
|
+
url + "&key=#{config.api_key}"
|
52
61
|
end
|
53
62
|
end
|
54
63
|
|
55
|
-
def url(lat,
|
64
|
+
def url(lat, long)
|
56
65
|
query = URI.encode_www_form(
|
57
|
-
'location' => "#{lat},#{
|
66
|
+
'location' => "#{lat},#{long}",
|
58
67
|
'timestamp' => Time.now.to_i)
|
59
68
|
|
60
69
|
authorize("/maps/api/timezone/json?#{query}")
|