tenka 1.0.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.
Files changed (6) hide show
  1. checksums.yaml +7 -0
  2. data/README +24 -0
  3. data/Rakefile +33 -0
  4. data/doc/TODO +0 -0
  5. data/lib/tenka_client.rb +176 -0
  6. metadata +66 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 724361046f8874506dff4a9eaaabc8ff9fcb28fa
4
+ data.tar.gz: 52735cb687afdaf5a5ef8cf5c183511c70f0abab
5
+ SHA512:
6
+ metadata.gz: c083c3566b6a72c9e01bbc3e62c6f939c9d129de9bc1cc2bac859cd86b4f4efbcad4556672fbbfe4fa63934474579de87a78580ff0773fca7d2a3b1c3f82ee4c
7
+ data.tar.gz: 601e02c7fb7ef93f3a4614548d32f933535839b7c5ce6314e1093951fd3b3476168fdcf168928a6e8fa7f26f3b7c891a6df085a65e4031c2b4f5418a8a4c67d5
data/README ADDED
@@ -0,0 +1,24 @@
1
+ I'll document this when I damn well please, thank you very much.
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+ (Seriously, though...TODO.)
@@ -0,0 +1,33 @@
1
+ require 'rubygems/package_task'
2
+ require 'rdoc/task'
3
+
4
+ $: << "#{File.dirname(__FILE__)}/lib"
5
+
6
+ spec = eval File.read(Dir['*.gemspec'][0])
7
+
8
+ Rake::RDocTask.new(:doc) { |t|
9
+ t.main = 'README'
10
+ t.rdoc_files.include 'lib/**/*.rb', 'doc/*', 'bin/*', 'ext/**/*.c',
11
+ 'ext/**/*.rb'
12
+ t.options << '-S' << '-N'
13
+ t.rdoc_dir = 'doc/rdoc'
14
+ }
15
+
16
+ Gem::PackageTask.new(spec) { |pkg|
17
+ pkg.need_tar_bz2 = true
18
+ }
19
+ desc "Cleans out the packaged files."
20
+ task(:clean) {
21
+ FileUtils.rm_rf 'pkg'
22
+ }
23
+
24
+ desc "Builds and installs the gem for #{spec.name}"
25
+ task(:install => :package) {
26
+ g = "pkg/#{spec.name}-#{spec.version}.gem"
27
+ system "gem install -l #{g}"
28
+ }
29
+
30
+ desc "Runs IRB, automatically require()ing #{spec.name}."
31
+ task(:irb) {
32
+ exec "irb -Ilib -r#{spec.name}"
33
+ }
File without changes
@@ -0,0 +1,176 @@
1
+ # Copyright © 2017-2018 Rekka Labs (https://rekka.io/)
2
+ # See LICENSE for licensing information.
3
+
4
+ %w(
5
+ json
6
+
7
+ uri cgi net/http
8
+ set
9
+ ).each &method(:require)
10
+
11
+ module Tenka
12
+ # The main class for the client.
13
+ # TODO: Provide usage examples here.
14
+ class Client
15
+ # The defaults when initializing a client.
16
+ # `host` specifies the hostname to connect to. You probably won't
17
+ # need to change this.
18
+ # `ssl` turns HTTPS on or off. If you are using an API key, you
19
+ # probably want to leave it on.
20
+ # `port` specifies the port to connect to.
21
+ DefaultOpts = {
22
+ host: 'api.tenka.io',
23
+ port: 443,
24
+ ssl: true,
25
+ api_key: nil,
26
+ }.freeze
27
+ DefaultOpts.each_key { |k| define_method(k) { opts[k] } }
28
+
29
+ # For endpoints that require units to be specified, this is the
30
+ # list of units.
31
+ Units = Set.new %w(
32
+ mile
33
+ km
34
+ )
35
+
36
+ attr_accessor :opts, :last_resp,
37
+ :last_error,
38
+ :rate_limit_calls_left,
39
+ :api_tokens_left
40
+
41
+ # Creates a new client. To simplify the API, the client carries
42
+ # information in the form of state, and thus is not thread-safe;
43
+ # you should instantiate one client per thread that accesses Tenka.
44
+ def initialize opts = {}
45
+ self.opts = DefaultOpts.merge(opts)
46
+
47
+ # If they don't specify a port but they do turn off SSL, we set
48
+ # the port to 80.
49
+ if !opts.has_key?(port) && !ssl
50
+ self.opts[:port] = 80
51
+ end
52
+ end
53
+
54
+ def last_error
55
+ return nil if !last_resp || last_resp.code[0] == '2'
56
+ h = begin
57
+ JSON.parse(last_resp.body)
58
+ rescue JSON::ParserError => e
59
+ # TODO: Might wanna dependency-inject a logger for cases
60
+ # like this.
61
+ return nil
62
+ end
63
+
64
+ h
65
+ end
66
+
67
+ # The individual endpoints follow:
68
+
69
+ # Reverse-geocode a latitude/longitude pair.
70
+ def containing_lat_long lat, long
71
+ ok, body = get('/containing/lat-long', lat: lat, long: long)
72
+ ok && body
73
+ end
74
+ alias_method :containing_lat_lon, :containing_lat_long
75
+
76
+ # Reverse-geocode a ZIP code centroid. (Tenka only supports US ZIPs
77
+ # for now, but this is not enforced client-side.)
78
+ #
79
+ # Note that ZIP codes often describe very odd shapes; they are based
80
+ # on postal service routes rather than geographical boundaries.
81
+ # As a result, the centroid may lie in a different city than any
82
+ # single point within the actual boundaries of the ZIP code. (ZIP
83
+ # centroids are popular because of their granularity, but they should
84
+ # be used with caution.)
85
+ #
86
+ # ZIP codes can start with leading zeroes. It is advised to
87
+ # use a string to represent a ZIP code rather than an integer.
88
+ def containing_zip zip
89
+ ok, body = get('/containing/zip', zip: zip)
90
+ ok && body
91
+ end
92
+
93
+ # Returns a list of ZIP codes whose centroids are within a given
94
+ # radius of another ZIP code's centroid. (See the remarks about
95
+ # centroids in #containing_zip.)
96
+ def nearby_zip zip, radius, units = 'mile'
97
+ unless Units.include?(units)
98
+ raise ArgumentError, "Invalid unit #{units}. Must be one "\
99
+ "of #{Units.to_a.join(', ')}."
100
+ end
101
+ ok, body = get('/nearby/zip',
102
+ zip: zip, radius: radius, units: units)
103
+ ok && body['zips']
104
+ end
105
+
106
+ # Returns the number of calls your API token has left, as well as
107
+ # how many calls you can make before you hit the rate limit for the
108
+ # server.
109
+ #
110
+ # This information is automatically gathered (whether your API key
111
+ # is valid or not) on every request, so you can also just check
112
+ # Tenka::Client#rate_limit_calls_left and Tenka::Client#api_tokens_left.
113
+ #
114
+ # Making a request against this endpoint does not reduce the number
115
+ # of API tokens you have left. (It does count towards the rate limit.)
116
+ #
117
+ # A 404 from this endpoint indicates that your API key is invalid.
118
+ def calls_left
119
+ ok, body = get('/tokens-remaining')
120
+ ok && {
121
+ rate_limit: rate_limit_calls_left,
122
+ api_limit: api_tokens_left,
123
+ }
124
+ end
125
+
126
+ private
127
+
128
+ def get path, query = nil
129
+ req_headers = {}
130
+ if api_key
131
+ req_headers['API-Token'] = api_key
132
+ end
133
+ path = path + h2query(query) if query
134
+ http_req Net::HTTP::Get.new(path, req_headers)
135
+ end
136
+
137
+ # Returns [true/false, parsed_body, response_object].
138
+ def http_req req, body = nil
139
+ resp = Net::HTTP.start(host, port, use_ssl: ssl) { |h|
140
+ h.request req, body
141
+ }
142
+ body = JSON.parse(resp.body)
143
+ self.last_resp = resp
144
+ update_counters!
145
+
146
+ # Error detection is easy for Tenka.
147
+ [resp.code[0] == '2', body]
148
+ end
149
+
150
+ # Check rate limits and API tokens remaining in the headers that came
151
+ # back;
152
+ def update_counters!
153
+ if l = last_resp['rate-limit-calls-left']
154
+ self.rate_limit_calls_left = l.to_i
155
+ end
156
+
157
+ if l = last_resp['api-token-calls-remaining']
158
+ self.api_tokens_left = l.to_i
159
+ end
160
+ end
161
+
162
+ def server_uri
163
+ # Under normal circumstances, `host` shouldn't change (just start
164
+ # a new client).
165
+ @_server_uri ||= URI("http#{ssl ? 's' : ''}://#{host}/")
166
+ end
167
+
168
+ def h2query h
169
+ '?' <<
170
+ h.map { |k,v|
171
+ (CGI.escape(k.to_s) << '=' << CGI.escape(v.to_s)).
172
+ gsub('+', '%20')
173
+ }.join('&')
174
+ end
175
+ end
176
+ end
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tenka
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Pete Elmore
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-01-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: json
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: |
28
+ Tenka is a REST API that lets you do GIS operations. It makes it easy
29
+ to add intelligence about the earth to your applications.
30
+ email:
31
+ - pete@tenka.io
32
+ executables: []
33
+ extensions: []
34
+ extra_rdoc_files:
35
+ - doc/TODO
36
+ - README
37
+ files:
38
+ - README
39
+ - Rakefile
40
+ - doc/TODO
41
+ - lib/tenka_client.rb
42
+ homepage: https://github.com/tenka/tenka-client-ruby
43
+ licenses:
44
+ - MIT
45
+ metadata: {}
46
+ post_install_message:
47
+ rdoc_options: []
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 2.2.0
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ requirements: []
61
+ rubyforge_project:
62
+ rubygems_version: 2.6.11
63
+ signing_key:
64
+ specification_version: 4
65
+ summary: A client for Tenka's REST API.
66
+ test_files: []