mode-sdk 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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a2fe9f487c0cc04dd360dd4e4439a6d7a9876d95
4
+ data.tar.gz: 1d23400061905abb0ecec7a45831e3b39f89a5d6
5
+ SHA512:
6
+ metadata.gz: 054cd8e9d9f0c36963b85c7678812d8b60e8baadd8de87e4ccf75b4b5b928e720ea3276ecb1632b3278779852615ef1bf1dbc26035b367310f11604888e02914
7
+ data.tar.gz: 2f6481aef7a2385992b57841016d8adab0bf5175f8c786325a2b5977a844ff787121a311d1bc329ac5a5d01f8f7b27e3aa80c56ca4475de09c02011e2061c252
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
@@ -0,0 +1 @@
1
+ --markup=markdown
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in mode-sdk.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Mode Analytics
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.
@@ -0,0 +1,109 @@
1
+ # Mode Ruby SDK
2
+
3
+ [![Code Climate](https://codeclimate.com/repos/53ea7f57e30ba007c500a24a/badges/44f08215be76ea780d56/gpa.svg)](https://codeclimate.com/repos/53ea7f57e30ba007c500a24a/feed)
4
+
5
+ This SDK provides a wrapper for the [Mode Analytics](https://modeanalytics.com)
6
+ API.
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ gem 'mode-sdk'
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install mode-sdk
21
+
22
+ ## Usage
23
+
24
+ ### Setup
25
+
26
+ ```ruby
27
+ Mode::Sdk.configure do |config|
28
+ config.token = 'token'
29
+ config.secret = 'secret'
30
+ end
31
+ ```
32
+
33
+ You can manage your Mode API tokens here:
34
+ [https://modeanalytics.com/settings/access_tokens]
35
+ (https://modeanalytics.com/settings/access_tokens)
36
+
37
+ ### 1. Upload CSV to Mode
38
+
39
+ Before importing a table, you will need to upload the raw CSV (with no header)
40
+ using the `Upload` class. The first argument can be either a string or an
41
+ IO-like object.
42
+
43
+ ```ruby
44
+ csv = File.open('sf_film_locations.csv', 'r') { |f| f.read }
45
+
46
+ upload = Mode::Sdk::Upload.new(csv)
47
+ upload.create
48
+ upload.token # => '5b68f4b6a3c6' save this token for step 2
49
+ ```
50
+
51
+ Full Upload API documentation:
52
+ [http://developer.modeanalytics.com/#page:uploads,header:uploads-upload]
53
+ (http://developer.modeanalytics.com/#page:uploads,header:uploads-upload)
54
+
55
+ ### 2. Import the table
56
+
57
+ Next, initialize a `Table` instance with a name:
58
+
59
+ ```ruby
60
+ table = Mode::Sdk::Table.new('sf_film_locations')
61
+ table.exists? # => true, false
62
+ ```
63
+
64
+ Assign a column schema (required) and description (optional):
65
+
66
+ ```ruby
67
+ table.columns = [
68
+ { name: 'movie_title', type: 'string' },
69
+ { name: 'release_year', type: 'integer' },
70
+ { name: 'location', type: 'string' }
71
+ ]
72
+
73
+ table.description = 'Famous film locations in San Francisco'
74
+ ```
75
+
76
+ Then use the upload token from Step 1 to create or replace the table:
77
+
78
+ ```ruby
79
+ table.upload_token = '5b68f4b6a3c6'
80
+
81
+ table.create
82
+ table.replace
83
+ ```
84
+
85
+ ### 3. Check the status of your import
86
+
87
+ The response from `Table#create` or `Table#replace` will indicate the status of
88
+ your import, including:
89
+
90
+ * `enqueued`
91
+ * `running`
92
+ * `succeeded`
93
+ * `failed`
94
+
95
+ You can poll this response until your import has completed.
96
+
97
+ ## Examples
98
+
99
+ A collection of examples can be found in the
100
+ [mode/mode-ruby-examples](https://github.com/mode/mode-ruby-examples)
101
+ repository.
102
+
103
+ ## Contributing
104
+
105
+ 1. Fork it
106
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
107
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
108
+ 4. Push to the branch (`git push origin my-new-feature`)
109
+ 5. Create new Pull Request
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
@@ -0,0 +1,70 @@
1
+ # encoding: utf-8
2
+ #
3
+ # Mode Analytics namespace
4
+ #
5
+ module Mode
6
+ # Official [Mode Analytics](https://modeanalytics.com) Ruby SDK
7
+ #
8
+ module Sdk
9
+ class << self
10
+ # Set Mode configuration values
11
+ #
12
+ # @yield [Mode::Sdk::Configuration] the configuration instance
13
+ #
14
+ # @example
15
+ # Mode.configure do |config|
16
+ # config.token = "token"
17
+ # config.secret = "secret"
18
+ # end
19
+ #
20
+ def configure
21
+ yield config
22
+ end
23
+
24
+ # The Mode configuration instance
25
+ #
26
+ # @return [Mode::Sdk::Configuration] the configuration instance
27
+ #
28
+ def config
29
+ @config ||= Mode::Sdk::Configuration.new
30
+ end
31
+
32
+ # The Mode API representation of the authenticated account
33
+ #
34
+ # @return [Hash] the account representation
35
+ #
36
+ def account
37
+ @account ||= Mode::Sdk::Client.account
38
+ end
39
+
40
+ # The username of the authenticated account
41
+ #
42
+ # @return [String] the username
43
+ #
44
+ def username
45
+ account.fetch('username')
46
+ end
47
+
48
+ # Un-memoize everything
49
+ #
50
+ def reset
51
+ %w(config account).each do |name|
52
+ next unless instance_variable_defined?(:"@#{name}")
53
+
54
+ remove_instance_variable(:"@#{name}")
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ require 'mode/sdk/client'
62
+ require 'mode/sdk/column'
63
+ require 'mode/sdk/column_set'
64
+ require 'mode/sdk/configuration'
65
+ require 'mode/sdk/hash_util'
66
+ require 'mode/sdk/table'
67
+ require 'mode/sdk/table_import'
68
+ require 'mode/sdk/upload'
69
+ require 'mode/sdk/version'
70
+ require 'mode/sdk/warehouse_util'
@@ -0,0 +1,87 @@
1
+ # encoding: utf-8
2
+ #
3
+ require 'cgi'
4
+ require 'net/http'
5
+ require 'uri'
6
+
7
+ module Mode
8
+ module Sdk
9
+ # The Client class provides an interface for common Mode API requests
10
+ #
11
+ class Client
12
+ class << self
13
+ # Make an HTTP HEAD request with the Mode API
14
+ #
15
+ # @param path [String] the request path
16
+ # @param options [optional, Hash] hash of
17
+ # {Mode::Sdk::Client::Request#initialize request options}
18
+ #
19
+ # @return [Mode::Sdk::Client::Response] the response
20
+ #
21
+ def head(path, options = {})
22
+ request(Net::HTTP::Head.new(path), nil, options)
23
+ end
24
+
25
+ # Make an HTTP GET request with the Mode API
26
+ #
27
+ # @param path [String] the request path
28
+ # @param options [optional, Hash] hash of
29
+ # {Mode::Sdk::Client::Request#initialize request options}
30
+ #
31
+ # @return [Mode::Sdk::Client::Response] the response
32
+ #
33
+ def get(path, options = {})
34
+ request(Net::HTTP::Get.new(path), nil, options)
35
+ end
36
+
37
+ # Make an HTTP POST request with the Mode API
38
+ #
39
+ # The body param may be either:
40
+ #
41
+ # * a String containing raw CSV, or
42
+ # * an IO-like object containing the raw CSV for streaming requests
43
+ #
44
+ # @param path [String] the request path
45
+ # @param body [String, #read] the request body
46
+ # @param options [optional, Hash] hash of
47
+ # {Mode::Sdk::Client::Request#initialize request options}
48
+ #
49
+ # @return [Mode::Sdk::Client::Response] the response
50
+ #
51
+ def post(path, body, options = {})
52
+ request(Net::HTTP::Post.new(path), body, options)
53
+ end
54
+
55
+ # Make an HTTP PUT request with the Mode API
56
+ #
57
+ # @param path [String] the request path
58
+ # @param body [String, #read] the request body
59
+ # @param options [optional, Hash] hash of
60
+ # {Mode::Sdk::Client::Request#initialize request options}
61
+ #
62
+ # @return [Mode::Sdk::Client::Response] the response
63
+ #
64
+ def put(path, body, options = {})
65
+ request(Net::HTTP::Put.new(path), body, options)
66
+ end
67
+
68
+ # Get the Mode API representation of the authenticated account
69
+ #
70
+ # @return [Mode::Sdk::Client::Response] the response
71
+ #
72
+ def account
73
+ get('/api/account', expect: [200]).body
74
+ end
75
+
76
+ private
77
+
78
+ def request(request, body, options = {})
79
+ Mode::Sdk::Client::Request.new(request, body, options).response
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ require 'mode/sdk/client/request'
87
+ require 'mode/sdk/client/response'
@@ -0,0 +1,133 @@
1
+ # encoding: utf-8
2
+ #
3
+ require 'mode/sdk/version'
4
+
5
+ module Mode
6
+ module Sdk
7
+ class Client
8
+ # The Request class wraps a Net::HTTPRequest object for the Mode API
9
+ #
10
+ class Request
11
+ # Construct a new Request instance
12
+ #
13
+ # @param http_request [Net::HTTPRequest] the http request
14
+ # @param body [String, #read] the request body
15
+ # @param options [optional, Hash] hash of options
16
+ #
17
+ # @option options [Array<Integer>] :expect an array of expected
18
+ # response codes
19
+ # @option options [String] :content_type the content type of the
20
+ # request
21
+ # @option options [Integer] :content_length the content length of the
22
+ # request
23
+ #
24
+ # @return [Mode::Sdk::Client::Request] the instance
25
+ #
26
+ def initialize(http_request, body = nil, options = {})
27
+ @http_request = http_request
28
+ @body = body
29
+ @options = options
30
+ end
31
+
32
+ # Result of the HTTP request
33
+ #
34
+ # @return [Mode::Sdk::Client::Response] the response
35
+ #
36
+ def response
37
+ http_response = self.class.http.request(build_http_request)
38
+
39
+ response = Mode::Sdk::Client::Response.new(
40
+ http_response, expected_codes)
41
+
42
+ response.tap(&:validate!)
43
+ end
44
+
45
+ private
46
+
47
+ DEFAULT_HOST = 'https://modeanalytics.com'
48
+ DEFAULT_CONTENT_TYPE = 'application/json'
49
+ USER_AGENT = "mode-sdk/#{Mode::Sdk::VERSION}"
50
+
51
+ attr_reader :http_request, :body, :options
52
+
53
+ def build_http_request
54
+ http_request.tap do
55
+ assign_basic_auth
56
+ assign_headers
57
+ assign_content_meta
58
+ assign_body
59
+ end
60
+ end
61
+
62
+ def assign_basic_auth
63
+ http_request.basic_auth(*basic_auth)
64
+ end
65
+
66
+ def assign_headers
67
+ http_request['accept'] = DEFAULT_CONTENT_TYPE
68
+ http_request['user-agent'] = USER_AGENT
69
+ end
70
+
71
+ def assign_content_meta
72
+ http_request.content_type = content_type
73
+ http_request.content_length = content_length if content_length
74
+ end
75
+
76
+ def assign_body
77
+ case body
78
+ when nil
79
+ # no-op
80
+ when String
81
+ http_request.body = body
82
+ else
83
+ http_request.body_stream = body
84
+ end
85
+ end
86
+
87
+ def expected_codes
88
+ options.fetch(:expect, [])
89
+ end
90
+
91
+ def content_type
92
+ options.fetch(:content_type, DEFAULT_CONTENT_TYPE)
93
+ end
94
+
95
+ def content_length
96
+ options.fetch(:content_length, nil)
97
+ end
98
+
99
+ def basic_auth
100
+ [Mode::Sdk.config.token, Mode::Sdk.config.secret]
101
+ end
102
+
103
+ class << self
104
+ # The Net::HTTP instance used to make requests
105
+ #
106
+ # @return [Net::HTTP] the HTTP instance
107
+ #
108
+ def http
109
+ return @http if defined?(@http)
110
+
111
+ @http = Net::HTTP.new(uri.host, uri.port)
112
+ @http.use_ssl = host =~ /\Ahttps/
113
+ @http
114
+ end
115
+
116
+ # The Mode API host to use for requests
117
+ #
118
+ # @return [String] the API host
119
+ #
120
+ def host
121
+ ENV['MODE_HOST'] || Mode::Sdk.config.host || DEFAULT_HOST
122
+ end
123
+
124
+ private
125
+
126
+ def uri
127
+ @uri ||= URI(host)
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end