earth_tools 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +8 -0
- data/Gemfile +3 -0
- data/README.md +136 -0
- data/Rakefile +26 -0
- data/lib/earth_tools/cache.rb +80 -0
- data/lib/earth_tools/configuration.rb +20 -16
- data/lib/earth_tools/exceptions.rb +9 -11
- data/lib/earth_tools/lookup/base.rb +39 -38
- data/lib/earth_tools/lookup/height.rb +2 -2
- data/lib/earth_tools/lookup/sunrise_sunset.rb +4 -1
- data/lib/earth_tools/lookup/time_zone.rb +5 -1
- data/lib/earth_tools/result/base.rb +13 -11
- data/lib/earth_tools/result/height.rb +2 -3
- data/lib/earth_tools/result/sunrise_sunset.rb +3 -4
- data/lib/earth_tools/result/time_zone.rb +5 -4
- data/lib/earth_tools/version.rb +1 -1
- data/lib/earth_tools.rb +18 -24
- data/test/cache_test.rb +47 -0
- data/test/configuration_test.rb +5 -5
- data/test/earth_tools_test.rb +2 -0
- data/test/height_test.rb +1 -2
- data/test/mock_lookup.rb +6 -2
- data/test/sunrise_sunset_test.rb +15 -12
- data/test/time_zone_test.rb +3 -1
- metadata +30 -11
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
# earth_tools
|
2
|
+
[![Gem Version](https://badge.fury.io/rb/earth_tools.png)](http://badge.fury.io/rb/earth_tools) [![Dependency Status](https://gemnasium.com/mckramer/earth_tools.png?travis)](https://gemnasium.com/mckramer/earth_tools) [![Code Climate](https://codeclimate.com/github/mckramer/earth_tools.png)](https://codeclimate.com/github/mckramer/earth_tools) [![Build Status](https://secure.travis-ci.org/mckramer/earth_tools.png?branch=master)](http://travis-ci.org/mckramer/earth_tools)
|
3
|
+
|
4
|
+
`earth_tools` is a wrapper around the wonderful [earthtools.org webservices](http://www.earthtools.org/webservices.htm), which allows you to determine the sea level height, time zone, and surise/sunset times from a set of coordinates.
|
5
|
+
|
6
|
+
## Compatibility
|
7
|
+
|
8
|
+
* Supports Ruby 1.9.2 & 1.9.3
|
9
|
+
|
10
|
+
Will be testing on other versions in the future.
|
11
|
+
|
12
|
+
## Installation instructions
|
13
|
+
|
14
|
+
Add to your Gemfile:
|
15
|
+
|
16
|
+
`gem 'earth_tools'`
|
17
|
+
|
18
|
+
and then bundle your gemfile:
|
19
|
+
|
20
|
+
`bundle install`
|
21
|
+
|
22
|
+
and you are done!
|
23
|
+
|
24
|
+
## API
|
25
|
+
|
26
|
+
The run down of the 3 major functions available
|
27
|
+
|
28
|
+
### Time zone
|
29
|
+
|
30
|
+
# API
|
31
|
+
result = EarthTools.time_zone(latitude, longitude)
|
32
|
+
# Example
|
33
|
+
result = EarthTools.time_zone(40.71417, -74.00639) # New York City
|
34
|
+
result.iso_time # => 2012-06-14 12:56:40 -0500
|
35
|
+
result.utc_offset # => -5
|
36
|
+
result.utc_time # => 2012-06-14 17:56:40 +0000
|
37
|
+
|
38
|
+
### Height above/below sea level
|
39
|
+
|
40
|
+
# API
|
41
|
+
result = EarthTools.height(latitude, longitude)
|
42
|
+
# Example
|
43
|
+
result = EarthTools.height(52.4822, -1.8946) # Birmingham, AL
|
44
|
+
result.meters # => 141
|
45
|
+
result.feet # => 462.6
|
46
|
+
result.height # => 462.6 (when EarthTools::Configuration.units is set to :english units)
|
47
|
+
|
48
|
+
### Sunrise/sunset times
|
49
|
+
|
50
|
+
# API
|
51
|
+
result = EarthTools.sunrise_sunset(latitude, longitude, month, day, timezone, dst)
|
52
|
+
# Example
|
53
|
+
result = EarthTools.sunrise_sunset(40.71417, -74.00639, 12, 4, -5, 0) # New York City, December 4th
|
54
|
+
result.sunrise # => 2012-12-04 07:05:50 -0500
|
55
|
+
result.sunset # => 2012-12-04 16:26:59 -0500
|
56
|
+
|
57
|
+
### Configuration
|
58
|
+
|
59
|
+
# Configure block (set to defaults)
|
60
|
+
EarthTools.configure do |config|
|
61
|
+
config.always_raise = [] # Add any errors that you would like to be custom handled, see "Error handling" section below
|
62
|
+
config.cache = nil # Cache object (see Caching section for what methods are required)
|
63
|
+
config.cache_prefix = "earth_tools:" # Prefix to use for cache keys
|
64
|
+
config.timeout = 3 # Timeout in seconds
|
65
|
+
config.units = :english # Also, can specify :metric
|
66
|
+
end
|
67
|
+
# Static call
|
68
|
+
EarthTools::Configuration.timeout = 5 # Set timeout to 5 seconds
|
69
|
+
|
70
|
+
## Earth Tool API restrictions
|
71
|
+
|
72
|
+
### Limits
|
73
|
+
|
74
|
+
Earth Tools imposes some [usage restrictions](http://www.earthtools.org/webservices.htm#usage) that are duplicated below (these restrictions may not be kept up-to-date, so please check the website):
|
75
|
+
|
76
|
+
1. You *must not* make more than 1 (one) request per second to these webservices.
|
77
|
+
2. You *must* cache results if you believe that you will need to make another identical request within any 24-hour period.
|
78
|
+
3. You *must* delete any cached data when you no longer need it and in any case after 14 days. You should then make a new request for the data in line with the previous two rules. If you wish to keep access to data I am able to license the data for use in this way.
|
79
|
+
|
80
|
+
### Caching
|
81
|
+
|
82
|
+
It is recommended to cache retrieved data when relying on an external service. You can configure a cache store:
|
83
|
+
|
84
|
+
EarthTools::Configuration.cache = Redis.new
|
85
|
+
EarthTools::Configuration.cache_prefix = "..." # Provide a custom cache prefix, defaults to 'earth_tools:'
|
86
|
+
|
87
|
+
The cache store can be any object that supports the following methods:
|
88
|
+
|
89
|
+
<table>
|
90
|
+
<tr>
|
91
|
+
<th>Method</th><th>Description</th><th>Examples</th>
|
92
|
+
</tr>
|
93
|
+
<tr>
|
94
|
+
<td><code>#[](key)</code></td><td>Retrieves a value by with the key</td><td></td>
|
95
|
+
</tr>
|
96
|
+
<tr>
|
97
|
+
<td><code>#[]=(key, value)</code></td><td>Stores a value with the key</td><td></td>
|
98
|
+
</tr>
|
99
|
+
<tr>
|
100
|
+
<td><code>#keys</code></td><td>Retrieves all keys</td><td></td>
|
101
|
+
</tr>
|
102
|
+
<tr>
|
103
|
+
<td>
|
104
|
+
<div><code>#del</code></div>
|
105
|
+
<div>or</div>
|
106
|
+
<div><code>#delete</code></div>
|
107
|
+
</td>
|
108
|
+
<td>Deletes a key</td>
|
109
|
+
<td></td>
|
110
|
+
</tr>
|
111
|
+
</table>
|
112
|
+
|
113
|
+
If you need to expire cached content:
|
114
|
+
|
115
|
+
EarthTools.cache.expire("http://...") # Expire cached result for a URL
|
116
|
+
EarthTools.cache.expire(:all) # Expire all cached results
|
117
|
+
|
118
|
+
There is no need to include the prefix when passing a URL to be expired. Expiring `:all` will only expire keys with the configured prefix (won't kill every entry in your key/value store).
|
119
|
+
|
120
|
+
## Error handling
|
121
|
+
|
122
|
+
By default Earth Tools will rescue any exceptions raised by calls to the webservice and return an empty array (using warn() to inform you of the error). You can override this and implement custom error handling for certain exceptions by using the `:always_raise` option:
|
123
|
+
|
124
|
+
EarthTools::Configuration.always_raise = [SocketError, TimeoutError]
|
125
|
+
|
126
|
+
## Issues & contributing
|
127
|
+
|
128
|
+
No outstanding issues right now. Please post any issues to the [issues queue on github](https://github.com/mckramer/earth_tools/issues).
|
129
|
+
|
130
|
+
## Future
|
131
|
+
|
132
|
+
I would love to see this functionality brought into the `geocoder` gem or similar in the future.
|
133
|
+
|
134
|
+
# License and attributions
|
135
|
+
|
136
|
+
This gem's structure and design borrows heavily from `geocoder`, so thanks to its author. Find me on twitter [@maxckramer](https://twitter.com/maxckramer).
|
data/Rakefile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'bundler'
|
4
|
+
Bundler::GemHelper.install_tasks
|
5
|
+
|
6
|
+
require 'rake/testtask'
|
7
|
+
|
8
|
+
desc 'Default: run unit tests'
|
9
|
+
task :default => :test
|
10
|
+
|
11
|
+
desc 'Run all tests'
|
12
|
+
task :test_all => [:test, :integration_test]
|
13
|
+
|
14
|
+
desc 'Run unit tests'
|
15
|
+
Rake::TestTask.new(:test) do |t|
|
16
|
+
t.libs << 'test'
|
17
|
+
t.pattern = 'test/*_test.rb'
|
18
|
+
t.verbose = true
|
19
|
+
end
|
20
|
+
|
21
|
+
desc 'Run integration tests (require to set proxy in test_helper.rb if needed)'
|
22
|
+
Rake::TestTask.new(:integration_test) do |t|
|
23
|
+
t.libs << 'test'
|
24
|
+
t.pattern = 'test/integration/*_test.rb'
|
25
|
+
t.verbose = true
|
26
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module EarthTools
|
2
|
+
|
3
|
+
##
|
4
|
+
# The Earth Tools cache wrapper.
|
5
|
+
#
|
6
|
+
# Must respond to:
|
7
|
+
# -> [] -- read
|
8
|
+
# -> []= -- write
|
9
|
+
# -> del -- delete
|
10
|
+
# -> keys -- list of keys
|
11
|
+
#
|
12
|
+
class Cache
|
13
|
+
|
14
|
+
##
|
15
|
+
# @constructor
|
16
|
+
def initialize(store, prefix)
|
17
|
+
@store = store
|
18
|
+
@prefix = prefix
|
19
|
+
end
|
20
|
+
|
21
|
+
##
|
22
|
+
# Read from the cache.
|
23
|
+
# @return the object saved in the cache
|
24
|
+
def [](url)
|
25
|
+
@store[key_for(url)]
|
26
|
+
end
|
27
|
+
|
28
|
+
##
|
29
|
+
# Write to the cache.
|
30
|
+
def []=(url, value)
|
31
|
+
@store[key_for(url)] = value
|
32
|
+
end
|
33
|
+
|
34
|
+
##
|
35
|
+
# Delete cache entry for given URL,
|
36
|
+
# or pass <tt>:all</tt> to clear all URLs.
|
37
|
+
def expire(url)
|
38
|
+
if url == :all
|
39
|
+
urls.each{ |u| expire(u) }
|
40
|
+
else
|
41
|
+
@store.send(@store.respond_to?(:del) ? :del : :delete, key_for(url))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
attr_reader :prefix, :store
|
48
|
+
|
49
|
+
##
|
50
|
+
# Cache key for a given URL.
|
51
|
+
# @return [String] the cache key
|
52
|
+
def key_for(url)
|
53
|
+
[@prefix, url].join
|
54
|
+
end
|
55
|
+
|
56
|
+
##
|
57
|
+
# Array of keys with the currently configured prefix
|
58
|
+
# that have non-nil values.
|
59
|
+
# @return [Array] the list of keys
|
60
|
+
def keys
|
61
|
+
@store.keys.select{ |k| k.match /^#{@prefix}/ and cleave(@store[k]) }
|
62
|
+
end
|
63
|
+
|
64
|
+
##
|
65
|
+
# Array of cached URLs.
|
66
|
+
# @return [Array] the list of cached URLs
|
67
|
+
def urls
|
68
|
+
keys.map{ |k| k[/^#{@prefix}(.*)/, 1] }
|
69
|
+
end
|
70
|
+
|
71
|
+
##
|
72
|
+
# Convert empty string to nil.
|
73
|
+
# (Some key/value stores return empty string instead of nil.)
|
74
|
+
# @return [Object, nil] the object or nil
|
75
|
+
def cleave(value)
|
76
|
+
value == "" ? nil : value
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
@@ -1,9 +1,10 @@
|
|
1
1
|
require 'singleton'
|
2
2
|
module EarthTools
|
3
3
|
|
4
|
-
##
|
5
|
-
#
|
6
|
-
#
|
4
|
+
##
|
5
|
+
# This method can be used to change some functional aspects, like
|
6
|
+
# the cache client or the units of calculations.
|
7
|
+
# See {include:Configuration}.
|
7
8
|
def self.configure(&block)
|
8
9
|
if block_given?
|
9
10
|
block.call(Configuration.instance)
|
@@ -12,9 +13,12 @@ module EarthTools
|
|
12
13
|
end
|
13
14
|
end
|
14
15
|
|
16
|
+
##
|
17
|
+
# The singleton that maintains the configuration settings for plugin.
|
15
18
|
class Configuration
|
16
19
|
include Singleton
|
17
20
|
|
21
|
+
# The available options
|
18
22
|
OPTIONS = [
|
19
23
|
:timeout,
|
20
24
|
:proxy,
|
@@ -29,24 +33,24 @@ module EarthTools
|
|
29
33
|
def initialize # :nodoc
|
30
34
|
set_defaults
|
31
35
|
end
|
32
|
-
|
33
|
-
|
36
|
+
|
37
|
+
##
|
38
|
+
# Sets the configuration options to the default values
|
34
39
|
def set_defaults
|
35
|
-
@timeout = 3 #
|
40
|
+
@timeout = 3 # Geocoding service timeout (secs)
|
36
41
|
@proxy = nil # HTTP proxy server (user:pass@host:port)
|
37
|
-
@cache = nil #
|
38
|
-
@cache_prefix = "earth_tools:" #
|
39
|
-
|
40
|
-
#
|
42
|
+
@cache = nil # Cache object (must respond to #[], #[]=, #keys, and #delete)
|
43
|
+
@cache_prefix = "earth_tools:" # Prefix (string) to use for all cache keys
|
44
|
+
|
45
|
+
# Exceptions that should not be rescued by default
|
41
46
|
# (if you want to implement custom error handling);
|
42
47
|
# supports SocketError and Timeout::Error
|
43
48
|
@always_raise = []
|
44
|
-
|
45
|
-
#
|
49
|
+
|
50
|
+
# Calculation options
|
46
51
|
@units = :english # :metric or :english
|
47
52
|
end
|
48
53
|
|
49
|
-
|
50
54
|
# Delegates getters and setters for all configuration settings,
|
51
55
|
# and +set_defaults+ to the singleton instance.
|
52
56
|
instance_eval(OPTIONS.map do |option|
|
@@ -55,15 +59,15 @@ module EarthTools
|
|
55
59
|
def #{o}
|
56
60
|
instance.#{o}
|
57
61
|
end
|
58
|
-
|
62
|
+
|
59
63
|
def #{o}=(value)
|
60
64
|
instance.#{o} = value
|
61
65
|
end
|
62
66
|
EOS
|
63
67
|
end.join("\n\n"))
|
64
|
-
|
68
|
+
|
65
69
|
class << self
|
66
|
-
#
|
70
|
+
# Sets the configuration options to the default values
|
67
71
|
def set_defaults
|
68
72
|
instance.set_defaults
|
69
73
|
end
|
@@ -1,29 +1,27 @@
|
|
1
1
|
module EarthTools
|
2
|
-
|
2
|
+
|
3
|
+
##
|
4
|
+
# Common error, extended by more specific errors.
|
3
5
|
class Error < StandardError
|
4
6
|
end
|
5
|
-
|
7
|
+
|
6
8
|
##
|
7
|
-
#
|
8
|
-
#
|
9
|
+
# Error thrown when there is an issue setting/getting configuration.
|
9
10
|
class ConfigurationError < Error
|
10
11
|
end
|
11
|
-
|
12
|
+
|
12
13
|
##
|
13
|
-
#
|
14
|
-
#
|
14
|
+
# Error thrown when too many requests have been made to EarthTools service in.
|
15
15
|
class OverQueryLimitError < Error
|
16
16
|
end
|
17
17
|
|
18
18
|
##
|
19
|
-
#
|
20
|
-
#
|
19
|
+
# Error thrown when invalid input is provided to the service when querying.
|
21
20
|
class InvalidInputError < Error
|
22
21
|
end
|
23
22
|
|
24
23
|
##
|
25
|
-
#
|
26
|
-
#
|
24
|
+
# Error thrown when unsupported operation is attempted on the service.
|
27
25
|
class UnsupportedOperationError < Error
|
28
26
|
end
|
29
27
|
|
@@ -6,96 +6,97 @@ module EarthTools
|
|
6
6
|
module Lookup
|
7
7
|
|
8
8
|
##
|
9
|
-
#
|
9
|
+
# Base lookup object.
|
10
10
|
#
|
11
11
|
class Base
|
12
12
|
|
13
13
|
##
|
14
14
|
# Query the Earth Tools service and return a EarthTools::Result object.
|
15
|
-
# @return [EarthTools::Result, nil]
|
15
|
+
# @return [EarthTools::Result, nil] the results object
|
16
16
|
def search(*options)
|
17
17
|
begin
|
18
18
|
Timeout::timeout(EarthTools::Configuration.timeout) do
|
19
|
-
|
20
|
-
|
19
|
+
uri = query(options)
|
20
|
+
if cache && results = cache[uri]
|
21
|
+
# Boom, cached
|
22
|
+
else
|
23
|
+
# Not cached, so go get the data
|
24
|
+
raw_results = retrieve(uri)
|
25
|
+
results = parse_xml(raw_results)
|
26
|
+
# Save results to cache, if available
|
27
|
+
cache(uri, results) if cache && results
|
28
|
+
end
|
29
|
+
results
|
21
30
|
end
|
22
31
|
rescue Timeout::Error => err
|
23
32
|
raise_error(err) or warn "Earth Tools API took too long to respond. See EarthTools::Configuration to set the timeout time (currently set to #{EarthTools::Configuration.timeout} seconds)."
|
24
33
|
end
|
25
34
|
end
|
26
|
-
|
35
|
+
|
27
36
|
private
|
28
37
|
|
29
38
|
##
|
30
|
-
# The base URL for the web service endpoint (protocol and server)
|
39
|
+
# The base URL for the web service endpoint (protocol and server).
|
31
40
|
# @return [String] the base URL
|
32
41
|
def base_service_url
|
33
42
|
"http://www.earthtools.org"
|
34
43
|
end
|
35
44
|
|
36
45
|
##
|
37
|
-
#
|
38
|
-
# @return [
|
39
|
-
def
|
40
|
-
|
46
|
+
# Gets the cache.
|
47
|
+
# @return [EarthTools::Cache] the cache
|
48
|
+
def cache
|
49
|
+
EarthTools::Configuration.cache
|
41
50
|
end
|
42
51
|
|
43
52
|
##
|
44
|
-
#
|
45
|
-
#
|
46
|
-
def
|
47
|
-
|
48
|
-
|
49
|
-
parse_xml(XmlSimple.xml_in( raw_results, { 'ForceArray' => false } ))
|
50
|
-
when :json
|
51
|
-
raise UnsupportedOperationError, "JSON is not supported"
|
52
|
-
end
|
53
|
+
# URL to use for querying the geocoding engine.
|
54
|
+
# @return [String] the url
|
55
|
+
def query(*options)
|
56
|
+
base_service_url + query_base + options.join('/')
|
57
|
+
# ([base_service_url, query_base] + options).join('/')
|
53
58
|
end
|
54
59
|
|
55
60
|
##
|
56
|
-
# Handle XML results by validating them
|
57
|
-
#
|
58
|
-
|
61
|
+
# Handle XML results by validating them.
|
62
|
+
# @param raw_xml the xml to parse
|
63
|
+
# @return [Height, SunriseSunset, TimeZone] the result
|
64
|
+
def parse_xml(raw_xml)
|
65
|
+
xml = XmlSimple.xml_in( raw_xml, { 'ForceArray' => false } )
|
59
66
|
if xml['location']
|
60
67
|
result_class.new(xml)
|
61
68
|
else
|
62
69
|
warn "Invalid request."
|
63
|
-
puts xml
|
64
70
|
end
|
65
71
|
end
|
66
72
|
|
67
73
|
##
|
68
|
-
# Handles running query
|
69
|
-
#
|
74
|
+
# Handles running query.
|
75
|
+
# @return [xml] the results
|
70
76
|
def retrieve(query)
|
71
77
|
RestClient.proxy = EarthTools::Configuration.proxy if EarthTools::Configuration.proxy
|
72
|
-
#puts "Trying to connect to endpoint(#{query})"
|
73
|
-
#puts "Using proxy (#{RestClient.proxy})" if RestClient.proxy
|
74
78
|
RestClient.get query
|
75
79
|
end
|
76
80
|
|
77
81
|
##
|
78
82
|
# Raise exception if configuration specifies it should be raised.
|
79
|
-
#
|
80
|
-
#
|
83
|
+
# @return [boolean] false, if exception not raised
|
81
84
|
def raise_error(error, message = nil)
|
82
|
-
if EarthTools::Configuration.always_raise.include?( error.is_a?(Class) ? error : error.class )
|
83
|
-
|
84
|
-
else
|
85
|
-
false
|
86
|
-
end
|
85
|
+
raise error, message if EarthTools::Configuration.always_raise.include?( error.is_a?(Class) ? error : error.class )
|
86
|
+
false
|
87
87
|
end
|
88
88
|
|
89
89
|
##
|
90
|
-
#
|
91
|
-
#
|
90
|
+
# The base of the query for the function.
|
91
|
+
# @return [String] the base of the query
|
92
|
+
# @throws NotImplementedError when child class does not implement method
|
92
93
|
def query_base
|
93
94
|
raise NotImplementedError
|
94
95
|
end
|
95
96
|
|
96
97
|
##
|
97
|
-
#
|
98
|
-
#
|
98
|
+
# The class for the result.
|
99
|
+
# @return [EarthTools::Result::Height, EarthTools::Result::SunriseSunset, EarthTools::Result::TimeZone] the result class
|
99
100
|
def result_class
|
100
101
|
EarthTools::Result.const_get(self.class.to_s.split(":").last)
|
101
102
|
end
|
@@ -2,48 +2,50 @@ module EarthTools
|
|
2
2
|
module Result
|
3
3
|
|
4
4
|
##
|
5
|
-
#
|
5
|
+
# The base result object.
|
6
|
+
#
|
7
|
+
# Contains shared domain fields. Extended by different results.
|
6
8
|
#
|
7
9
|
class Base
|
8
10
|
attr_accessor :data
|
9
|
-
|
11
|
+
|
10
12
|
##
|
11
13
|
# Takes a hash of result data from a parsed Google result document.
|
12
|
-
#
|
14
|
+
# @param [Object] the
|
13
15
|
def initialize(data)
|
14
16
|
@data = data
|
15
17
|
end
|
16
|
-
|
18
|
+
|
17
19
|
##
|
18
20
|
# Get the geographical location
|
19
|
-
# @
|
21
|
+
# @return [Array] a two-element array: [latitude, longitude]
|
20
22
|
def location
|
21
23
|
[latitude, longitude]
|
22
24
|
end
|
23
|
-
|
25
|
+
|
24
26
|
##
|
25
27
|
# Get the latitude
|
26
28
|
# See {http://en.wikipedia.org/wiki/Latitude}.
|
27
|
-
# @
|
29
|
+
# @return [Float] the latitude
|
28
30
|
def latitude
|
29
31
|
@data['latitude'].to_f
|
30
32
|
end
|
31
|
-
|
33
|
+
|
32
34
|
##
|
33
35
|
# Get the longitude
|
34
36
|
# See {http://en.wikipedia.org/wiki/Longitude}.
|
35
|
-
# @
|
37
|
+
# @return the longitude
|
36
38
|
def longitude
|
37
39
|
@data['longitude'].to_f
|
38
40
|
end
|
39
41
|
|
40
42
|
##
|
41
43
|
# The version of the response format
|
42
|
-
# @
|
44
|
+
# @return [Float] the version
|
43
45
|
def version
|
44
46
|
@data['version'].to_f
|
45
47
|
end
|
46
|
-
|
48
|
+
|
47
49
|
end
|
48
50
|
end
|
49
51
|
end
|
@@ -3,17 +3,16 @@ require 'earth_tools/result/base'
|
|
3
3
|
module EarthTools::Result
|
4
4
|
|
5
5
|
##
|
6
|
-
# The sunrise/sunset result
|
7
|
-
#
|
6
|
+
# The sunrise/sunset result.
|
8
7
|
class SunriseSunset < Base
|
9
|
-
|
8
|
+
|
10
9
|
##
|
11
10
|
# The date of the sunrise/sunset data as a hash
|
12
11
|
# @return [Hash] Hash containing year, month, and day as integers
|
13
12
|
def date
|
14
13
|
{ :year => Time.now.year, :month => @data['date']['month'].to_i, :day => @data['date']['day'].to_i }
|
15
14
|
end
|
16
|
-
|
15
|
+
|
17
16
|
##
|
18
17
|
# The sunrise time
|
19
18
|
# @return [Time] the sunrise time
|
@@ -3,10 +3,11 @@ require 'earth_tools/result/base'
|
|
3
3
|
module EarthTools::Result
|
4
4
|
|
5
5
|
##
|
6
|
+
# The Time Zone result.
|
6
7
|
#
|
7
|
-
#
|
8
|
+
# @see http://www.earthtools.org/webservices.htm#timezone
|
8
9
|
class TimeZone < Base
|
9
|
-
|
10
|
+
|
10
11
|
##
|
11
12
|
# Whether or not the {#local_time} and {#iso_time} is currently in DST. If they do, the value of this element will be 'True'. If they do not, the value will be 'False'. If it can't be determined whether or not daylight saving time should be used, the value will be 'Unknown'.
|
12
13
|
# @return [String] 'True' || 'False' || 'Unknown'
|
@@ -42,7 +43,7 @@ module EarthTools::Result
|
|
42
43
|
def local_time
|
43
44
|
iso_time
|
44
45
|
end
|
45
|
-
|
46
|
+
|
46
47
|
##
|
47
48
|
# The number of hours offset from UTC disregarding any correction for daylight saving time
|
48
49
|
# See {http://en.wikipedia.org/wiki/UTC_offset}.
|
@@ -50,7 +51,7 @@ module EarthTools::Result
|
|
50
51
|
def utc_offset
|
51
52
|
@data['offset'].to_i
|
52
53
|
end
|
53
|
-
|
54
|
+
|
54
55
|
##
|
55
56
|
# The nautical suffix for the time zone
|
56
57
|
# See {http://en.wikipedia.org/wiki/Nautical_time}
|
data/lib/earth_tools/version.rb
CHANGED
data/lib/earth_tools.rb
CHANGED
@@ -4,30 +4,24 @@
|
|
4
4
|
#
|
5
5
|
module EarthTools
|
6
6
|
extend self
|
7
|
-
|
7
|
+
|
8
8
|
##
|
9
9
|
# Retrieve for time zone based on latitude and longitude.
|
10
|
-
#
|
11
|
-
# @returns [EarthTools::Result::TimeZone]
|
12
|
-
#
|
10
|
+
# @returns [EarthTools::Result::TimeZone] the time zone result
|
13
11
|
def time_zone(latitude, longitude)
|
14
12
|
get_lookup(:time_zone).search(latitude, longitude) if valid_input?(latitude, longitude)
|
15
13
|
end
|
16
14
|
|
17
15
|
##
|
18
|
-
# Retrieve the sunrise & sunset values
|
19
|
-
#
|
20
|
-
# @returns [EarthTools::Result::SunriseSunset]
|
21
|
-
#
|
16
|
+
# Retrieve the sunrise & sunset values.
|
17
|
+
# @return [EarthTools::Result::SunriseSunset] the sunrise/sunset result
|
22
18
|
def sunrise_sunset(latitude, longitude, month, day, timezone = 99, dst = false)
|
23
19
|
get_lookup(:sunrise_sunset).search(latitude, longitude, day, month, timezone, dst ? 1 : 0)
|
24
20
|
end
|
25
21
|
|
26
22
|
##
|
27
|
-
# Retrieve the land height for a given latitude and longitude
|
28
|
-
#
|
29
|
-
# @returns [EarthTools::Result::Height]
|
30
|
-
#
|
23
|
+
# Retrieve the land height for a given latitude and longitude.
|
24
|
+
# @return [EarthTools::Result::Height] the height result
|
31
25
|
def height(latitude, longitude)
|
32
26
|
get_lookup(:height).search(latitude, longitude)
|
33
27
|
end
|
@@ -36,7 +30,7 @@ module EarthTools
|
|
36
30
|
|
37
31
|
##
|
38
32
|
# Retrieve a lookup object from the store.
|
39
|
-
#
|
33
|
+
# @return [? extends EarthTools::Lookup::Base]
|
40
34
|
def get_lookup(name)
|
41
35
|
@lookups = {} unless defined?(@lookups)
|
42
36
|
@lookups[name] = spawn_lookup(name) unless @lookups.include?(name)
|
@@ -45,7 +39,7 @@ module EarthTools
|
|
45
39
|
|
46
40
|
##
|
47
41
|
# Spawn a lookup of the given name.
|
48
|
-
#
|
42
|
+
# @return [? extends EarthTools::Lookup::Base] new lookup helper
|
49
43
|
def spawn_lookup(name)
|
50
44
|
name = name.to_s
|
51
45
|
require "earth_tools/lookup/#{name}"
|
@@ -54,36 +48,36 @@ module EarthTools
|
|
54
48
|
end
|
55
49
|
|
56
50
|
##
|
57
|
-
# Validates input
|
58
|
-
#
|
51
|
+
# Validates input.
|
52
|
+
# @return [Boolean] true, if coordinates are valid
|
59
53
|
def valid_input?(latitude, longitude)
|
60
54
|
coordinates?(latitude, longitude)
|
61
55
|
end
|
62
56
|
|
63
57
|
##
|
64
|
-
# Validates input
|
65
|
-
#
|
58
|
+
# Validates input.
|
59
|
+
# @return [Boolean] true, if all values are valid
|
66
60
|
def valid_full_input?(latitude, longitude, day, month, timezone, dst)
|
67
61
|
coordinates?(latitude, longitude) && !blank?(day) && !blank(month) && !blank?(timezone) && !blank?(dst)
|
68
62
|
end
|
69
63
|
|
70
64
|
##
|
71
|
-
# Validates a pair of coordinates
|
72
|
-
#
|
65
|
+
# Validates a pair of coordinates.
|
66
|
+
# @return [Boolean] true, if latitute and longitude are (sort of) valid coordinates
|
73
67
|
def coordinates?(latitude, longitude)
|
74
68
|
coordinate?(latitude) && coordinate?(longitude)
|
75
69
|
end
|
76
70
|
|
77
71
|
##
|
78
|
-
# Determine if given value is a coordinate
|
79
|
-
#
|
72
|
+
# Determine if given value is a coordinate.
|
73
|
+
# @return [Boolean] true, if given value is a coordinate
|
80
74
|
def coordinate?(value)
|
81
75
|
!! value.to_f
|
82
76
|
end
|
83
77
|
|
84
78
|
##
|
85
|
-
# Determine if given value is blank
|
86
|
-
#
|
79
|
+
# Determine if given value is blank.
|
80
|
+
# @return [Boolean] true, if value is nil or empty
|
87
81
|
def blank?(value)
|
88
82
|
value.nil? || value.to_s.empty?
|
89
83
|
end
|
data/test/cache_test.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
require 'earth_tools'
|
5
|
+
require 'earth_tools/cache'
|
6
|
+
|
7
|
+
##
|
8
|
+
# Tests for the Cache.
|
9
|
+
class CacheTest < MiniTest::Unit::TestCase
|
10
|
+
|
11
|
+
def setup
|
12
|
+
@raw_cache = {}
|
13
|
+
@cache = EarthTools::Cache.new(@raw_cache, 'earth_tools:')
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_store_and_retrieve_from_cache
|
17
|
+
@cache['test/1'] = :data
|
18
|
+
|
19
|
+
assert_equal :data, @cache['test/1'], 'The cache was not stored properly.'
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_remove_single_url
|
23
|
+
@cache['test/1'] = :data
|
24
|
+
@cache['test/2'] = :data
|
25
|
+
|
26
|
+
@cache.expire('test/1')
|
27
|
+
|
28
|
+
assert_nil @cache['test/1'], 'The cache should have deleted entry.'
|
29
|
+
assert_equal :data, @cache['test/2'], 'The cache should not have deleted entry.'
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_remove_all_prefixed_urls
|
33
|
+
@raw_cache['test'] = :data
|
34
|
+
@raw_cache['other:test'] = :data
|
35
|
+
@cache['test/1'] = :data
|
36
|
+
@cache['test/2'] = :data
|
37
|
+
@cache['test/3'] = :data
|
38
|
+
|
39
|
+
@cache.expire(:all)
|
40
|
+
assert_equal :data, @raw_cache['test'], 'The cache should not affect non-prefixed entries.'
|
41
|
+
assert_equal :data, @raw_cache['other:test'], 'The cache should not affect non-prefixed entries.'
|
42
|
+
assert_nil @cache['test/1'], 'The cache should be empty of prefixed entries.'
|
43
|
+
assert_nil @cache['test/2'], 'The cache should be empty of prefixed entries.'
|
44
|
+
assert_nil @cache['test/3'], 'The cache should be empty of prefixed entries.'
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
data/test/configuration_test.rb
CHANGED
@@ -6,7 +6,7 @@ class ConfigurationTest < MiniTest::Unit::TestCase
|
|
6
6
|
def setup
|
7
7
|
EarthTools::Configuration.set_defaults
|
8
8
|
end
|
9
|
-
|
9
|
+
|
10
10
|
# --- class method configuration ---
|
11
11
|
def test_configurated_by_class_method
|
12
12
|
EarthTools::Configuration.units = :metric
|
@@ -18,7 +18,7 @@ class ConfigurationTest < MiniTest::Unit::TestCase
|
|
18
18
|
refute_equal result.height, result.feet
|
19
19
|
end
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
# --- EarthTools#configure distances configuration ---
|
23
23
|
def test_configuration
|
24
24
|
|
@@ -26,7 +26,7 @@ class ConfigurationTest < MiniTest::Unit::TestCase
|
|
26
26
|
EarthTools.configure do |config|
|
27
27
|
config.units = :metric
|
28
28
|
end
|
29
|
-
|
29
|
+
|
30
30
|
assert_equal EarthTools::Configuration.units, :metric
|
31
31
|
result = EarthTools.height(52.4822, -1.8946)
|
32
32
|
if result.height == 0
|
@@ -34,10 +34,10 @@ class ConfigurationTest < MiniTest::Unit::TestCase
|
|
34
34
|
else
|
35
35
|
refute_equal result.height, result.feet
|
36
36
|
end
|
37
|
-
|
37
|
+
|
38
38
|
# Direct
|
39
39
|
EarthTools.configure.units = :metric
|
40
|
-
|
40
|
+
|
41
41
|
assert_equal EarthTools::Configuration.units, :metric
|
42
42
|
result = EarthTools.height(52.4822, -1.8946)
|
43
43
|
assert_equal result.height, result.meters
|
data/test/earth_tools_test.rb
CHANGED
data/test/height_test.rb
CHANGED
data/test/mock_lookup.rb
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
require 'earth_tools/lookup/base'
|
2
|
-
class EarthTools::Lookup::Base
|
3
2
|
|
3
|
+
##
|
4
|
+
# Mock Lookup class.
|
5
|
+
# Overrides normal lookup base for testing.
|
6
|
+
class EarthTools::Lookup::Base
|
7
|
+
|
4
8
|
private
|
5
9
|
|
6
10
|
def read_fixture(file)
|
@@ -14,5 +18,5 @@ class EarthTools::Lookup::Base
|
|
14
18
|
query = query.split('/')
|
15
19
|
read_fixture "#{ query[3] }_(#{ query[4, query.size-1].join(',') }).xml"
|
16
20
|
end
|
17
|
-
|
21
|
+
|
18
22
|
end
|
data/test/sunrise_sunset_test.rb
CHANGED
@@ -5,8 +5,10 @@ require 'earth_tools'
|
|
5
5
|
require 'earth_tools/result/sunrise_sunset'
|
6
6
|
require 'mock_lookup'
|
7
7
|
|
8
|
+
##
|
9
|
+
# Tests for Sunrise/Sunset lookups.
|
8
10
|
class SunriseSunsetTest < MiniTest::Unit::TestCase
|
9
|
-
|
11
|
+
|
10
12
|
def setup
|
11
13
|
@result = EarthTools.sunrise_sunset(40.71417, -74.00639, 12, 4)
|
12
14
|
end
|
@@ -20,7 +22,7 @@ class SunriseSunsetTest < MiniTest::Unit::TestCase
|
|
20
22
|
end
|
21
23
|
|
22
24
|
def test_sunrise_parsed_correctly
|
23
|
-
assert_equal Time.new(
|
25
|
+
assert_equal Time.new(Time.now.year, 12, 4, 7, 5, 50, '-05:00'), @result.sunrise, 'Sunrise was not parsed correctly'
|
24
26
|
end
|
25
27
|
|
26
28
|
# ---------------------------------------------------------------------------
|
@@ -32,7 +34,7 @@ class SunriseSunsetTest < MiniTest::Unit::TestCase
|
|
32
34
|
end
|
33
35
|
|
34
36
|
def test_sunset_parsed_correctly
|
35
|
-
assert_equal Time.new(
|
37
|
+
assert_equal Time.new(Time.now.year, 12, 4, 16, 26, 59, '-05:00'), @result.sunset, 'Sunset was not parsed correctly'
|
36
38
|
end
|
37
39
|
|
38
40
|
# ---------------------------------------------------------------------------
|
@@ -49,12 +51,13 @@ class SunriseSunsetTest < MiniTest::Unit::TestCase
|
|
49
51
|
end
|
50
52
|
|
51
53
|
def test_twilight_parsed_correctly
|
52
|
-
|
53
|
-
assert_equal Time.new(
|
54
|
-
assert_equal Time.new(
|
55
|
-
assert_equal Time.new(
|
56
|
-
assert_equal Time.new(
|
57
|
-
assert_equal Time.new(
|
54
|
+
year = Time.now.year
|
55
|
+
assert_equal Time.new(year, 12, 4, 5, 27, 39, '-05:00'), @result.twilight(:morning, :astronomical), 'Twilight was not parsed correctly'
|
56
|
+
assert_equal Time.new(year, 12, 4, 6, 35, 6, '-05:00'), @result.twilight(:morning, :civil), 'Twilight was not parsed correctly'
|
57
|
+
assert_equal Time.new(year, 12, 4, 6, 0, 50, '-05:00'), @result.twilight(:morning, :nautical), 'Twilight was not parsed correctly'
|
58
|
+
assert_equal Time.new(year, 12, 4, 18, 5, 10, '-05:00'), @result.twilight(:evening, :astronomical), 'Twilight was not parsed correctly'
|
59
|
+
assert_equal Time.new(year, 12, 4, 16, 57, 43, '-05:00'), @result.twilight(:evening, :civil), 'Twilight was not parsed correctly'
|
60
|
+
assert_equal Time.new(year, 12, 4, 17, 31, 59, '-05:00'), @result.twilight(:evening, :nautical), 'Twilight was not parsed correctly'
|
58
61
|
end
|
59
62
|
|
60
63
|
# ---------------------------------------------------------------------------
|
@@ -73,8 +76,8 @@ class SunriseSunsetTest < MiniTest::Unit::TestCase
|
|
73
76
|
assert @result.morning_astronomical_twilight.is_a?(Time), 'Morning astronomical twilight should be a time'
|
74
77
|
end
|
75
78
|
|
76
|
-
def
|
77
|
-
assert_equal Time.new(
|
79
|
+
def test_morning_astronomical_twilight_parsed_correctly
|
80
|
+
assert_equal Time.new(Time.now.year, 12, 4, 5, 27, 39, '-05:00'), @result.morning_astronomical_twilight, 'Twilight was not parsed correctly'
|
78
81
|
end
|
79
82
|
|
80
83
|
# ---------------------------------------------------------------------------
|
@@ -82,7 +85,7 @@ class SunriseSunsetTest < MiniTest::Unit::TestCase
|
|
82
85
|
# ---------------------------------------------------------------------------
|
83
86
|
|
84
87
|
def test_utc_offset_is_integer
|
85
|
-
|
88
|
+
assert @result.utc_offset.is_a?(Integer), 'UTC offset should be an integer'
|
86
89
|
end
|
87
90
|
|
88
91
|
def test_utc_offset_parsed_correctly
|
data/test/time_zone_test.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: earth_tools
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2013-12-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rest-client
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,15 @@ dependencies:
|
|
21
21
|
version: 1.6.1
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.6.1
|
25
30
|
- !ruby/object:Gem::Dependency
|
26
31
|
name: xml-simple
|
27
|
-
requirement:
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
28
33
|
none: false
|
29
34
|
requirements:
|
30
35
|
- - ~>
|
@@ -32,13 +37,27 @@ dependencies:
|
|
32
37
|
version: 1.1.1
|
33
38
|
type: :runtime
|
34
39
|
prerelease: false
|
35
|
-
version_requirements:
|
36
|
-
|
37
|
-
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 1.1.1
|
46
|
+
description: ! " \n The earth_tools gem is a client library to query the earthtools.org
|
47
|
+
web services.\n Available features include determining time zone, sunset/sunrise
|
48
|
+
times, and height\n above sea level for a given longitute/latitude pair. See
|
49
|
+
the README for more\n information. Also, please follow the web service restrictions
|
50
|
+
for use described \n in the README.\n \n"
|
51
|
+
email: max@maxckramer.com
|
38
52
|
executables: []
|
39
53
|
extensions: []
|
40
54
|
extra_rdoc_files: []
|
41
55
|
files:
|
56
|
+
- README.md
|
57
|
+
- CHANGELOG.md
|
58
|
+
- Gemfile
|
59
|
+
- Rakefile
|
60
|
+
- lib/earth_tools/cache.rb
|
42
61
|
- lib/earth_tools/configuration.rb
|
43
62
|
- lib/earth_tools/exceptions.rb
|
44
63
|
- lib/earth_tools/lookup/base.rb
|
@@ -51,6 +70,7 @@ files:
|
|
51
70
|
- lib/earth_tools/result/time_zone.rb
|
52
71
|
- lib/earth_tools/version.rb
|
53
72
|
- lib/earth_tools.rb
|
73
|
+
- test/cache_test.rb
|
54
74
|
- test/configuration_test.rb
|
55
75
|
- test/earth_tools_test.rb
|
56
76
|
- test/fixtures/height_(52.4822,-1.8946).xml
|
@@ -85,9 +105,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
85
105
|
version: '0'
|
86
106
|
requirements: []
|
87
107
|
rubyforge_project:
|
88
|
-
rubygems_version: 1.
|
108
|
+
rubygems_version: 1.8.23
|
89
109
|
signing_key:
|
90
110
|
specification_version: 3
|
91
|
-
summary: Client library to query the earthtools.org web services
|
111
|
+
summary: Client library to query the earthtools.org web services.
|
92
112
|
test_files: []
|
93
|
-
has_rdoc:
|