tenka 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: []