enigma_io 0.0.1
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.
- 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
|
+
[](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
|