enigma_io 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/.rubocop.yml +14 -0
- data/.travis.yml +14 -0
- data/Gemfile +11 -0
- data/LICENSE.txt +22 -0
- data/README.md +64 -0
- data/Rakefile +28 -0
- data/enigma_io.gemspec +30 -0
- data/examples/data.md +94 -0
- data/examples/export.md +35 -0
- data/lib/enigma/client.rb +26 -0
- data/lib/enigma/download.rb +81 -0
- data/lib/enigma/endpoint.rb +108 -0
- data/lib/enigma/endpoints/data.rb +5 -0
- data/lib/enigma/endpoints/export.rb +22 -0
- data/lib/enigma/endpoints/meta.rb +5 -0
- data/lib/enigma/endpoints/stats.rb +5 -0
- data/lib/enigma/response.rb +17 -0
- data/lib/enigma/version.rb +6 -0
- data/lib/enigma.rb +49 -0
- data/test/client_test.rb +32 -0
- data/test/data_test.rb +81 -0
- data/test/endpoint_test.rb +55 -0
- data/test/export_test.rb +67 -0
- data/test/fixtures/download.csv +4 -0
- data/test/fixtures/download.zip +0 -0
- data/test/fixtures/vcr_cassettes/compound_average.yml +121 -0
- data/test/fixtures/vcr_cassettes/data_with_error.yml +38 -0
- data/test/fixtures/vcr_cassettes/export.yml +37 -0
- data/test/fixtures/vcr_cassettes/filtered_data.yml +6727 -0
- data/test/fixtures/vcr_cassettes/limit_data.yml +54 -0
- data/test/fixtures/vcr_cassettes/page_data.yml +53 -0
- data/test/fixtures/vcr_cassettes/selected_data.yml +541 -0
- data/test/fixtures/vcr_cassettes/simple_data.yml +6762 -0
- data/test/fixtures/vcr_cassettes/simple_metadata.yml +75 -0
- data/test/fixtures/vcr_cassettes/simple_stats.yml +48 -0
- data/test/fixtures/vcr_cassettes/sorted_data.yml +6690 -0
- data/test/fixtures/vcr_cassettes/sorted_data_descending.yml +6690 -0
- data/test/meta_test.rb +26 -0
- data/test/response_test.rb +13 -0
- data/test/stats_test.rb +27 -0
- data/test/test_helper.rb +37 -0
- metadata +223 -0
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# This configuration was generated by `rubocop --auto-gen-config`.
|
2
|
+
# The point is for the user to remove these configuration records
|
3
|
+
# one by one as the offences are removed from the code base.
|
4
|
+
|
5
|
+
MethodLength:
|
6
|
+
Max: 20
|
7
|
+
|
8
|
+
TrivialAccessors:
|
9
|
+
Enabled: false
|
10
|
+
|
11
|
+
AllCops:
|
12
|
+
Excludes:
|
13
|
+
- test/**
|
14
|
+
- examples/**
|
data/.travis.yml
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- "1.9.3"
|
4
|
+
- "2.0.0"
|
5
|
+
- "2.1.0"
|
6
|
+
- jruby-19mode # JRuby in 1.9 mode
|
7
|
+
|
8
|
+
# uncomment this line if your project needs to run something other than `rake`:
|
9
|
+
# script: bundle exec rspec spec
|
10
|
+
before_install: gem update --remote bundler
|
11
|
+
|
12
|
+
script:
|
13
|
+
- bundle exec rake test
|
14
|
+
- bundle exec rubocop
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Stephen Pike
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
# Ruby client for the enigma api
|
2
|
+
|
3
|
+
Ruby client for the enigma api located at https://app.enigma.io/api. Supports ruby >= 1.9.3
|
4
|
+
|
5
|
+
Note that you need api key to use their api.
|
6
|
+
|
7
|
+
[![Build Status](https://travis-ci.org/scpike/enigma.png?branch=master)](https://travis-ci.org/scpike/enigma)
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
gem 'enigma'
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install enigma
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
Basic usage is straightforward. There are also more detailed [data api examples](examples/data.md) and [export api examples](examples/export.md) available.
|
26
|
+
|
27
|
+
# Defaults to looking for key in ENV['ENIGMA_KEY']
|
28
|
+
|
29
|
+
client = Enigma::Client.new(key: :secret_key)
|
30
|
+
|
31
|
+
client.meta('us.gov.whitehouse.visitor-list')
|
32
|
+
|
33
|
+
res = client.data('us.gov.whitehouse.visitor-list')
|
34
|
+
|
35
|
+
# get some data
|
36
|
+
|
37
|
+
res.result.map { |r| ... }
|
38
|
+
|
39
|
+
client.stats('us.gov.whitehouse.visitor-list', select: 'type_of_access')
|
40
|
+
|
41
|
+
client.export('us.gov.whitehouse.visitor-list').parse.each do |row|
|
42
|
+
puts row.inspect
|
43
|
+
end
|
44
|
+
|
45
|
+
## Contributing
|
46
|
+
|
47
|
+
1. Fork it
|
48
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
49
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
50
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
51
|
+
5. Create new Pull Request
|
52
|
+
|
53
|
+
### Notes
|
54
|
+
|
55
|
+
You'll need to have rubocop installed.
|
56
|
+
|
57
|
+
gem install rubocop
|
58
|
+
|
59
|
+
Tests are run with `rake`. Check the test coverage (printed when you
|
60
|
+
run rake) as well as the output of rubocop (also run with rake).
|
61
|
+
|
62
|
+
## License
|
63
|
+
|
64
|
+
MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require 'rake/testtask'
|
3
|
+
|
4
|
+
task :gendoc do
|
5
|
+
system "yardoc"
|
6
|
+
system "yard stats --list-undoc"
|
7
|
+
end
|
8
|
+
|
9
|
+
Rake::TestTask.new do |t|
|
10
|
+
t.libs << 'test'
|
11
|
+
t.test_files = FileList['test/**_test.rb']
|
12
|
+
t.verbose = false
|
13
|
+
t.warning = true
|
14
|
+
end
|
15
|
+
|
16
|
+
task :rubocop do |t|
|
17
|
+
sh 'rubocop'
|
18
|
+
end
|
19
|
+
|
20
|
+
task :build => :gendoc do
|
21
|
+
system "gem build enigma_io.gemspec"
|
22
|
+
end
|
23
|
+
|
24
|
+
task :release => :build do
|
25
|
+
system "gem push enigma_io-#{Enigma::VERSION}.gem"
|
26
|
+
end
|
27
|
+
|
28
|
+
task default: [:test, :rubocop]
|
data/enigma_io.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'enigma/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'enigma_io'
|
8
|
+
spec.version = Enigma::VERSION
|
9
|
+
spec.authors = ['Stephen Pike']
|
10
|
+
spec.email = ['steve@scpike.net']
|
11
|
+
spec.description = 'Ruby client for the Enigma API'
|
12
|
+
spec.summary = 'Ruby client for the Enigma API'
|
13
|
+
spec.homepage = 'https://github.com/scpike/enigma'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
17
|
+
spec.test_files = Dir.glob("{test/**/*}")
|
18
|
+
spec.require_paths = ['lib']
|
19
|
+
|
20
|
+
spec.add_development_dependency 'bundler', '~> 1.3'
|
21
|
+
spec.add_development_dependency 'rake'
|
22
|
+
spec.add_development_dependency 'rubocop'
|
23
|
+
spec.add_development_dependency 'yard'
|
24
|
+
|
25
|
+
spec.add_runtime_dependency 'typhoeus'
|
26
|
+
spec.add_runtime_dependency 'hashie'
|
27
|
+
spec.add_runtime_dependency 'rubyzip', '>= 1.0.0'
|
28
|
+
|
29
|
+
spec.required_ruby_version = '>= 1.9.3'
|
30
|
+
end
|
data/examples/data.md
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
# Accessing the Enigma metadata and data apis
|
2
|
+
|
3
|
+
#### Create a client, key should be set in the environment variable 'ENIGMA_KEY'
|
4
|
+
|
5
|
+
client = Enigma::Client.new
|
6
|
+
|
7
|
+
#### Let's look at internet use in the United States
|
8
|
+
|
9
|
+
res = client.meta('us.gov.census.internet.usage11')
|
10
|
+
|
11
|
+
#### The result is a Hashie::Mash object, so you can access it like a hash or like an object
|
12
|
+
|
13
|
+
puts res.keys
|
14
|
+
|
15
|
+
=> ["datapath", "success", "info", "result", "raw"]
|
16
|
+
|
17
|
+
`raw` is the raw repsonse from the Enigma api. Everything else is
|
18
|
+
the contents of the api response
|
19
|
+
|
20
|
+
#### Verify this is a table (something we can get data about):
|
21
|
+
|
22
|
+
res.info.result_type
|
23
|
+
|
24
|
+
=> "table"
|
25
|
+
|
26
|
+
#### Let's see the columns
|
27
|
+
|
28
|
+
res.result.columns.each { |c| puts "#{c.id}: #{c.description}" }
|
29
|
+
|
30
|
+
> region: State Region
|
31
|
+
>
|
32
|
+
> total_over3: Total (thousands)
|
33
|
+
>
|
34
|
+
> number_somewhere: Number of individuals accessing the Internet from some location (thousands). "Some location" means Internet access that occurs either inside or outside the householder's home.
|
35
|
+
>
|
36
|
+
> percent_somewhere: Percent of individuals accessing the Internet from some location. "Some location" means Internet access that occurs either inside or outside the householder's home.
|
37
|
+
>
|
38
|
+
> number_inhome: Individual lives in household with Internet (thousands). At least one member of the individual's household reported using the Internet from home.
|
39
|
+
>
|
40
|
+
> percent_inhome: Percent of individual living in household with Internet. At least one member of the individual's household reported using the Internet from home.
|
41
|
+
>
|
42
|
+
> serialid: Serialid
|
43
|
+
|
44
|
+
#### Now let's get the data
|
45
|
+
|
46
|
+
data = client.data('us.gov.census.internet.usage11')
|
47
|
+
|
48
|
+
data.result.each { |r| puts "#{r.region} #{r.percent_inhome}" }
|
49
|
+
|
50
|
+
> United States 76.50
|
51
|
+
>
|
52
|
+
> Alaska 80.00
|
53
|
+
>
|
54
|
+
> Alabama 69.50
|
55
|
+
>
|
56
|
+
> Arizona 76.00
|
57
|
+
>
|
58
|
+
> Arkansas 68.50
|
59
|
+
>
|
60
|
+
> ...
|
61
|
+
|
62
|
+
#### Let's find the state withthe highest percentage. We also only care about the region and percent_inhome columns
|
63
|
+
|
64
|
+
res = client.data('us.gov.census.internet.usage11',
|
65
|
+
sort: 'percent_inhome',
|
66
|
+
select: [ 'region', 'percent_inhome' ])
|
67
|
+
|
68
|
+
puts res.result.first.to_hash
|
69
|
+
|
70
|
+
=> {"region" =>"New Hampshire", "percent_inhome" =>"87.10"}
|
71
|
+
|
72
|
+
##### What about the lowest?
|
73
|
+
|
74
|
+
puts res.result.last.to_hash
|
75
|
+
|
76
|
+
=> {"region" =>"Mississippi", "percent_inhome" =>"61.40"}
|
77
|
+
|
78
|
+
##### Let's search for Pennsylvania
|
79
|
+
|
80
|
+
res = client.data('us.gov.census.internet.usage11',
|
81
|
+
select: [ 'region', 'percent_inhome' ],
|
82
|
+
search: { region: 'Pennsylvania })
|
83
|
+
|
84
|
+
#### Verify only one result found
|
85
|
+
|
86
|
+
> res.info.total_results
|
87
|
+
|
88
|
+
=> 1
|
89
|
+
|
90
|
+
#### Check it out
|
91
|
+
|
92
|
+
> puts res.first.to_hash
|
93
|
+
|
94
|
+
=> {"region" =>"Pennsylvania", "percent_inhome" =>"75.40"}
|
data/examples/export.md
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# Export API
|
2
|
+
|
3
|
+
#### Start the connection
|
4
|
+
|
5
|
+
client = Enigma::Client.new
|
6
|
+
|
7
|
+
dl = client.export('us.gov.census.internet.usage11')
|
8
|
+
|
9
|
+
#### This did *not* download anything. The api replied with a link to where the file will eventually be
|
10
|
+
|
11
|
+
puts dl.download_url
|
12
|
+
|
13
|
+
> Secret one time url
|
14
|
+
|
15
|
+
#### If you want the actual contents, you can ask the client to poll until the file is available
|
16
|
+
|
17
|
+
dl.get # puts the zipped contents in dl.raw_download
|
18
|
+
|
19
|
+
#### Write the zipped contents to a file
|
20
|
+
|
21
|
+
zipfile = File.open('out.zip', 'wb')
|
22
|
+
|
23
|
+
dl.write zipfile
|
24
|
+
|
25
|
+
#### Write the unzipped contents to a file
|
26
|
+
|
27
|
+
csvfile = File.open('out.csv', 'wb')
|
28
|
+
|
29
|
+
dl.write_csv csvfile
|
30
|
+
|
31
|
+
#### Read the contents of the csv file
|
32
|
+
|
33
|
+
dl.parse.each do |row|
|
34
|
+
puts row.inspect
|
35
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
module Enigma
|
4
|
+
# Connects to the Enigma api at https://app.enigma.io/api.
|
5
|
+
#
|
6
|
+
class Client
|
7
|
+
# Creates a new client connection
|
8
|
+
#
|
9
|
+
# @option opts [String] key - Enigma API key.
|
10
|
+
#
|
11
|
+
# The api key Defaults to the ENIGMA_KEY environment variable
|
12
|
+
#
|
13
|
+
def initialize(opts = {})
|
14
|
+
Enigma.key = ENV['ENIGMA_KEY'] || opts[:key]
|
15
|
+
fail ArgumentError, 'API key is required' unless Enigma.key
|
16
|
+
end
|
17
|
+
|
18
|
+
# Each endpoint becomes a method like `client.meta`
|
19
|
+
#
|
20
|
+
Endpoint.descendants.each do |klass|
|
21
|
+
define_method klass.url_chunk do |*args|
|
22
|
+
klass.new(*args).request
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
module Enigma
|
4
|
+
# Handle polling the url returned by the Export api until we're able
|
5
|
+
# to download it. Will load the zipped contents of the file into
|
6
|
+
# memory, and can then either write it to disk (`write`), write the
|
7
|
+
# unzipped version to disk (`write_csv`) or parse the unzipped
|
8
|
+
# contents using the CSV library as an array of hashes
|
9
|
+
#
|
10
|
+
class Download
|
11
|
+
DELAY = 1 # How long to wait between polling attempts, in seconds
|
12
|
+
attr_accessor :response, :raw_download, :download_contents
|
13
|
+
|
14
|
+
def initialize(res)
|
15
|
+
self.response = res
|
16
|
+
end
|
17
|
+
|
18
|
+
def download_url
|
19
|
+
response.export_url
|
20
|
+
end
|
21
|
+
|
22
|
+
def datapath
|
23
|
+
response.datapath
|
24
|
+
end
|
25
|
+
|
26
|
+
def get
|
27
|
+
@raw_download ||= do_download
|
28
|
+
end
|
29
|
+
|
30
|
+
def write(io)
|
31
|
+
get
|
32
|
+
io.write raw_download
|
33
|
+
end
|
34
|
+
|
35
|
+
def write_tmp
|
36
|
+
tmp = Tempfile.new(datapath)
|
37
|
+
write(tmp)
|
38
|
+
tmp.rewind
|
39
|
+
tmp
|
40
|
+
end
|
41
|
+
|
42
|
+
def unzip
|
43
|
+
@download_contents ||=
|
44
|
+
begin
|
45
|
+
tmp = write_tmp
|
46
|
+
contents = nil
|
47
|
+
Zip::File.open(tmp.path) do |zipfile|
|
48
|
+
contents = zipfile.first.get_input_stream.read
|
49
|
+
end
|
50
|
+
contents
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def write_csv(io)
|
55
|
+
unzip
|
56
|
+
io.write download_contents
|
57
|
+
end
|
58
|
+
|
59
|
+
def parse(opts = {})
|
60
|
+
opts = { headers: true, header_converters: :symbol }
|
61
|
+
CSV.parse(unzip, opts.merge(opts || {}))
|
62
|
+
end
|
63
|
+
|
64
|
+
def do_download
|
65
|
+
Enigma.logger.info "Trying to download #{download_url}"
|
66
|
+
success = false
|
67
|
+
until success
|
68
|
+
req = Typhoeus::Request.new(download_url)
|
69
|
+
req.on_complete do |response|
|
70
|
+
if response.response_code == 404
|
71
|
+
sleep DELAY
|
72
|
+
else
|
73
|
+
success = true
|
74
|
+
return response.body
|
75
|
+
end
|
76
|
+
end
|
77
|
+
req.run
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
module Enigma
|
3
|
+
# Generic Enigma endpoint. Knows how to construct a URL, request it,
|
4
|
+
# and wrap the response in an `Enigma::Response`
|
5
|
+
#
|
6
|
+
# Assumes the api endpoints for its descendants are a lowercase
|
7
|
+
# version of their class names
|
8
|
+
#
|
9
|
+
# Handles some nice conversion of select, search, and where clauses
|
10
|
+
# from ruby hashes to string parameters
|
11
|
+
#
|
12
|
+
class Endpoint
|
13
|
+
attr_accessor :params, :datapath, :url
|
14
|
+
|
15
|
+
def self.descendants
|
16
|
+
ObjectSpace.each_object(Class).select { |klass| klass < self }
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(datapath, opts = {})
|
20
|
+
self.datapath = datapath
|
21
|
+
self.params = opts
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.url_chunk
|
25
|
+
to_s.gsub(/.*::/, '').downcase
|
26
|
+
end
|
27
|
+
|
28
|
+
def params
|
29
|
+
@params[:where] = serialize_where(@params[:where]) if @params[:where]
|
30
|
+
@params[:search] = serialize_search(@params[:search]) if @params[:search]
|
31
|
+
@params[:select] = serialize_select(@params[:select]) if @params[:select]
|
32
|
+
@params
|
33
|
+
end
|
34
|
+
|
35
|
+
# Serialize a where clause. Allows you to pass in a hash and have
|
36
|
+
# it converted to an equality where
|
37
|
+
#
|
38
|
+
# > Filter results with a SQL-style "where" clause. Only applies to
|
39
|
+
# > numerical columns - use the "search" parameter for strings. Valid
|
40
|
+
# > operators are >, < and =. Only one "where" clause per request is
|
41
|
+
# > currently supported.
|
42
|
+
#
|
43
|
+
# @param [String|Hash] where clause to convert
|
44
|
+
# @return [String] parameter ready for the request
|
45
|
+
#
|
46
|
+
def serialize_where(where)
|
47
|
+
if where.is_a? Hash
|
48
|
+
column, value = where.first
|
49
|
+
"#{column}=#{value}"
|
50
|
+
else
|
51
|
+
where
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Serialize a search clause. Allows you to pass in a hash of
|
56
|
+
# one or more fieldName: value pairs
|
57
|
+
#
|
58
|
+
# @param [String|Hash] search clause to convert
|
59
|
+
# @return [String] parameter ready for the request
|
60
|
+
#
|
61
|
+
def serialize_search(search)
|
62
|
+
if search.is_a? Hash
|
63
|
+
search.map do |field, value|
|
64
|
+
value = [value].flatten.join('|')
|
65
|
+
"@#{field} (#{value})"
|
66
|
+
end.join ' '
|
67
|
+
else
|
68
|
+
search
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Serialize a search clause. Allows you to pass in an array of
|
73
|
+
# column names
|
74
|
+
#
|
75
|
+
# @param [String|Array] select clause to convert
|
76
|
+
# @return [String] parameter ready for the request
|
77
|
+
#
|
78
|
+
def serialize_select(select)
|
79
|
+
if select.is_a? Enumerable
|
80
|
+
select.join(',')
|
81
|
+
else
|
82
|
+
select
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Endpoints show up in urls as a lowercase version of their class
|
87
|
+
# names
|
88
|
+
def url_chunk
|
89
|
+
self.class.url_chunk
|
90
|
+
end
|
91
|
+
|
92
|
+
def path
|
93
|
+
[Enigma.api_version, url_chunk, Enigma.key, datapath].join('/')
|
94
|
+
end
|
95
|
+
|
96
|
+
def url
|
97
|
+
URI.join(Enigma.root_url, path).to_s
|
98
|
+
end
|
99
|
+
|
100
|
+
def request
|
101
|
+
Enigma.logger.info "Making request to #{url}"
|
102
|
+
req = Typhoeus::Request.new(url, method: :get, params: params).run
|
103
|
+
Response.parse(req)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
Dir[File.dirname(__FILE__) + '/endpoints/*.rb'].each { |file| require(file) }
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
module Enigma
|
3
|
+
# The export endpoint has no filtering, but it returns a URL where
|
4
|
+
# the download will eventually be available. This client will
|
5
|
+
# default to waiting for the file to be available before continuing
|
6
|
+
#
|
7
|
+
class Export < Endpoint
|
8
|
+
attr_accessor :download, :unzip
|
9
|
+
|
10
|
+
# The API request responds with a URL to poll until it's
|
11
|
+
# ready. Create a new download object with that URL and return it
|
12
|
+
#
|
13
|
+
# @return [Download]
|
14
|
+
#
|
15
|
+
def request
|
16
|
+
req = Typhoeus::Request.new(url, method: :get, params: params).run
|
17
|
+
Enigma.logger.info req.body
|
18
|
+
res = Response.parse(req)
|
19
|
+
Download.new(res)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
module Enigma
|
4
|
+
# We'll wrap the response in a Hashie::Mash subclass so that you can
|
5
|
+
# access the attributes as function calls
|
6
|
+
#
|
7
|
+
# Raw response is kept in the `raw` attribute
|
8
|
+
#
|
9
|
+
class Response
|
10
|
+
def self.parse(res)
|
11
|
+
mash = Hashie::Mash.new(JSON.parse(res.body))
|
12
|
+
mash.raw = res
|
13
|
+
fail mash.message.to_s if mash.info && mash.info.error
|
14
|
+
mash
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/enigma.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'rubygems'
|
3
|
+
|
4
|
+
# Required gems
|
5
|
+
require 'hashie'
|
6
|
+
require 'typhoeus'
|
7
|
+
|
8
|
+
# Core dependencies
|
9
|
+
require 'net/https'
|
10
|
+
require 'uri'
|
11
|
+
require 'json'
|
12
|
+
require 'zip'
|
13
|
+
require 'csv'
|
14
|
+
|
15
|
+
# Library
|
16
|
+
require 'enigma/version'
|
17
|
+
require 'enigma/download'
|
18
|
+
require 'enigma/endpoint'
|
19
|
+
require 'enigma/response'
|
20
|
+
require 'enigma/client'
|
21
|
+
|
22
|
+
# Access to the engima API
|
23
|
+
module Enigma
|
24
|
+
attr_accessor :key, :root_url, :api_version
|
25
|
+
|
26
|
+
def self.root_url
|
27
|
+
'https://api.enigma.io/'
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.api_version
|
31
|
+
'v2'
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.key
|
35
|
+
@key
|
36
|
+
end
|
37
|
+
def self.key=(k)
|
38
|
+
@key = k
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.logger
|
42
|
+
@logger ||=
|
43
|
+
begin
|
44
|
+
logger = Logger.new(STDOUT)
|
45
|
+
logger.level = Logger::INFO
|
46
|
+
logger
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|