knuverse-knufactor 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.
Files changed (37) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +14 -0
  5. data/.travis.yml +14 -0
  6. data/CONTRIBUTING.md +16 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE +22 -0
  9. data/README.md +125 -0
  10. data/Rakefile +10 -0
  11. data/bin/console +14 -0
  12. data/bin/setup +8 -0
  13. data/knuverse-knufactor.gemspec +42 -0
  14. data/lib/core_extensions/string/transformations.rb +28 -0
  15. data/lib/knuverse/knufactor.rb +42 -0
  16. data/lib/knuverse/knufactor/api_client.rb +30 -0
  17. data/lib/knuverse/knufactor/api_client_base.rb +112 -0
  18. data/lib/knuverse/knufactor/api_exception.rb +7 -0
  19. data/lib/knuverse/knufactor/exceptions/api_client_not_configured.rb +9 -0
  20. data/lib/knuverse/knufactor/exceptions/immutable_modification.rb +9 -0
  21. data/lib/knuverse/knufactor/exceptions/invalid_arguments.rb +9 -0
  22. data/lib/knuverse/knufactor/exceptions/invalid_options.rb +9 -0
  23. data/lib/knuverse/knufactor/exceptions/invalid_property.rb +9 -0
  24. data/lib/knuverse/knufactor/exceptions/invalid_where_query.rb +9 -0
  25. data/lib/knuverse/knufactor/exceptions/missing_path.rb +9 -0
  26. data/lib/knuverse/knufactor/exceptions/new_instance_with_id.rb +9 -0
  27. data/lib/knuverse/knufactor/helpers/api_client.rb +36 -0
  28. data/lib/knuverse/knufactor/helpers/resource.rb +112 -0
  29. data/lib/knuverse/knufactor/helpers/resource_class.rb +49 -0
  30. data/lib/knuverse/knufactor/resource.rb +207 -0
  31. data/lib/knuverse/knufactor/resource_collection.rb +189 -0
  32. data/lib/knuverse/knufactor/resources/client.rb +44 -0
  33. data/lib/knuverse/knufactor/simple_api_client.rb +20 -0
  34. data/lib/knuverse/knufactor/validations/api_client.rb +42 -0
  35. data/lib/knuverse/knufactor/validations/resource.rb +17 -0
  36. data/lib/knuverse/knufactor/version.rb +10 -0
  37. metadata +220 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 224e08737aef33c81546244ee7b5c61ee4dee62d
4
+ data.tar.gz: abd0db60daee18081303470062a4f120b5300e0c
5
+ SHA512:
6
+ metadata.gz: bbc6a2bdc236889d73413e8ebc55b0de64404c457eb522c028f217000f7de000d82088af673bf998a2fe08569864002d7cc43f81838cfcec234fee366bf260a2
7
+ data.tar.gz: 5c7d09db089ebf5517c98a3c97a145e134c627f8ca551954318cbb231d6e78cb2358330835c28b66c09d51a508180c370ae448443ebc3851183380f5cb14d76f
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ # Ignore Mac stuff
11
+ .DS_Store
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.rubocop.yml ADDED
@@ -0,0 +1,14 @@
1
+ Metrics/LineLength:
2
+ Max: 100
3
+
4
+ Metrics/ClassLength:
5
+ Max: 150
6
+
7
+ Metrics/AbcSize:
8
+ Max: 22
9
+
10
+ Metrics/MethodLength:
11
+ Max: 20
12
+
13
+ Metrics/CyclomaticComplexity:
14
+ Max: 10
data/.travis.yml ADDED
@@ -0,0 +1,14 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.1.10
5
+ - 2.2.5
6
+ - 2.3.1
7
+ cache: bundler
8
+ deploy:
9
+ provider: rubygems
10
+ api_key:
11
+ secure: A9iVmtSsR9iJsjGMXfKHNVuZn13QVdnnRAmAwXQvCSXmDaZQyHLejYnAGQ3WjZc2XbLxSn/787MUTJC2yxG9pJy+Nnyr6L9ZtoBD3F5/q+iqklJXIXtuVV/wOcknG+m71kEIIntZv+g2hVjHo/CfAbm10ycqlJDw/MMoPGxkwXHkj9qGEft6jXygzGEDGxnmqyBRAHZ/BI0vjojdTLM8yO/0RFF1X2cs55YRGyicmE8V/ZYwL5cOVfwCqYNtVp4Pce4GxWfX+Dp1PceI4jnX9hq/kMa+0mTkLWPdMQLYVjyIaZ7eso8RQb9yVPdIGZKp2vQqN1t+s/EHgmiriv/tHluvLnIWjHIwYFuX0Bd9wttp7Sb1K7HhqP0T/Ymny41KLr4Nnx3PqIcm9dz57x6zvq1bIykVlG3sZNthVPKAwbX6lj6X/HCyYaDr65EwFPDWsOxt78zJdbZuV0pdg4rEJaF8MHKy3lOfwm52AfIC4iMsnshhSylDETjwG7P556ZHzutODB3JPSKwmJriGihVBiG5V5TpdrPD9oZPs6rg8XWZs9bdhroIk9FcfE5MZJ0UDUdcV3dpcdJI109G3SR2rxSHaozfeaggsBa4QMD9gPP+Tx2DiD4eOntxo3zhoylISeKIJptLEOqz+vPEWcj/J25+iXbotvlFB1khibVWn0c=
12
+ gem: knuverse-knufactor
13
+ on:
14
+ tags: true
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,16 @@
1
+ ## Welcome!
2
+
3
+ We're so glad you're thinking about contributing to a KnuEdge open source project! If you're unsure about anything, just ask -- or submit the issue or pull request anyway. The worst that can happen is you'll be politely asked to change something. We love all friendly contributions.
4
+
5
+ We want to ensure a welcoming environment for all of our projects. Our IT staff encourage ethical contribution to our open source projects and all contributors should do the same.
6
+
7
+ We encourage you to read this project's CONTRIBUTING policy (you are here), its [LICENSE](LICENSE), and its [README](README.md).
8
+
9
+ If you have any questions just [shoot us an email](mailto:opensource@knuedge.com).
10
+
11
+ ## Licensing
12
+
13
+ This project and all code contained within it are released under the [MIT License](https://opensource.org/licenses/MIT).
14
+
15
+ All contributions to this project will be released under the [MIT License](https://opensource.org/licenses/MIT). By submitting a pull request, you are agreeing to comply
16
+ with this license and for any contributions to be released under it.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in knuverse-knufactor.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2016 KnuEdge
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,125 @@
1
+ The KnuVerse Knufactor SDK for Ruby
2
+ ====================
3
+
4
+ About
5
+ ----
6
+ This project is a Ruby SDK that allows developers to create apps that use Knuverse's Knufactor Cloud APIs.
7
+
8
+ Documentation for the API can be found [here](https://cloud.knuverse.com/docs/)
9
+
10
+ Note that this SDK isn't a direct port of the [Python SDK](https://github.com/KnuVerse/knuverse-sdk-python), though it is based heavily on the work done there. This SDK uses a Object-Oriented pattern, which is likely more familiar to Ruby developers than a collection of functions may be.
11
+
12
+ Building and Installing
13
+ ----
14
+ Building the gem requires a modern Ruby:
15
+
16
+ # highly recommend using RVM here, and Ruby 2.x or above
17
+ gem build knuverse-knufactor.gemspec
18
+ # install what you just built
19
+ gem install knuverse-knufactor-*.gem
20
+
21
+ That said, recent releases should be available on [rubygems.org](https://rubygems.org/) so building is probably not necessary.
22
+
23
+ Just add the following to your Gemfile:
24
+
25
+ gem 'knuverse/knufactor'
26
+
27
+ Then run:
28
+
29
+ bundle install
30
+
31
+ If you're not using [bundler](http://bundler.io/) for some reason (shame on you!), you can manually install like so:
32
+
33
+ gem install knuverse-knufactor
34
+
35
+ If you see a message like this:
36
+
37
+ Thanks for installing the KnuVerse Knufactor Ruby SDK!
38
+
39
+ You should be all set!
40
+
41
+ Usage
42
+ ------
43
+ Check back, because this file will be updated with a lot more usage examples.
44
+
45
+ For a typical custom ruby application, you'll need to do something like the following to get started:
46
+
47
+ ```ruby
48
+ require 'knuverse/knufactor'
49
+
50
+ # configure the API client singleton
51
+ KnuVerse::Knufactor::APIClient.configure(
52
+ apikey: 'b1b71d68cffea1d43257fff9deadbeef',
53
+ secret: '34d04e5f05a194444e9c26358a94eaf2'
54
+ )
55
+ ```
56
+
57
+ From here, the SDK will automatically rely on this configuration singleton unless you tell it otherwise. Here are some examples of interacting with the API:
58
+
59
+ ```ruby
60
+ # pull some data
61
+ KnuVerse::Knufactor::APIClient.about_service
62
+ # => {"company"=>"KnuVerse", "version"=>"1.4.0", "multi_tenant"=>true, "service"=>"audiopin", "name"=>"KnuVerse"}
63
+
64
+ # Make working with resources easier
65
+ include KnuVerse::Knufactor::Resources
66
+
67
+ # Find some clients
68
+ clients = Client.all
69
+
70
+ # Take a look at the last client
71
+ client = clients.last
72
+ client.name
73
+ # => "user75"
74
+
75
+ # Make a change
76
+ client.bypass_limit = 10
77
+
78
+ # Now persist it
79
+ client.save
80
+
81
+ # Verify the change worked
82
+ client.reload
83
+ client.bypass_limit
84
+ # => 10
85
+
86
+ # Find some specific clients... all locked out clients
87
+ locked_clients = Client.where(:password_lock?, true)
88
+ # => #<KnuVerse::Knufactor::ResourceCollection:...
89
+
90
+ # Find clients that are either enrolled or installed
91
+ happy_clients = Client.where(:state, 'enrolled').or(:state, 'installed')
92
+
93
+ # Find clients with a name that starts with "z"
94
+ z_clients = Client.where(:name, /^z/i)
95
+ ```
96
+
97
+ For some more complex situations, you may want a specialized API Client (interact with it as an instance, or have a thread-local or context-specific client):
98
+
99
+ ```ruby
100
+ # interact with the singleton as a client instance for simplicity
101
+ api_client = KnuVerse::Knufactor::APIClient.instance
102
+
103
+ # In complex, multi-user systems, using a singleton class might not work.
104
+ # For this reason, there is a regular class version of the API client that is not tied to the singleton
105
+ local_api_client1 = KnuVerse::Knufactor::SimpleAPIClient.new(
106
+ apikey: 'b1b71d68cffea1d43257fff9deadbeef', secret: '57838344acf7f5876226ede247c5881a'
107
+ )
108
+ local_api_client2 = KnuVerse::Knufactor::SimpleAPIClient.new(
109
+ apikey: '33371d68cffea1d43257fff9deadf00d', secret: 'e7d1c88825dc96a05bc38c39cca4a1ca'
110
+ )
111
+ local_api_client1 == local_api_client2
112
+ # => false
113
+
114
+ # Perform an operation with a specific instance of the API Client
115
+ Client.all(api_client: local_api_client1)
116
+ # All finder methods work this way, allowing the 'api_client' option to be passed
117
+ Client.where(:name, 'bobby', api_client: local_api_client2)
118
+
119
+ ```
120
+
121
+ License
122
+ -------
123
+ This project and all code contained within it are released under the [MIT License](https://opensource.org/licenses/MIT). As stated in [CONTRIBUTING](CONTRIBUTING.md):
124
+
125
+ > All contributions to this project will be released under the [MIT License](https://opensource.org/licenses/MIT). By submitting a pull request, you are agreeing to comply with this license and for any contributions to be released under it.
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+ require 'rubocop/rake_task'
4
+ require 'yard'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+ RuboCop::RakeTask.new(:rubocop)
8
+ YARD::Rake::YardocTask.new
9
+
10
+ task default: [:spec, :rubocop, :yard]
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'knuverse/knufactor'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require 'irb'
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,42 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'knuverse/knufactor/version'
5
+
6
+ # rubocop:disable Metrics/BlockLength
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'knuverse-knufactor'
9
+ spec.version = KnuVerse::Knufactor::VERSION
10
+ spec.date = Time.now.strftime('%Y-%m-%d')
11
+ spec.authors = ['Jonathan Gnagy']
12
+ spec.email = ['jgnagy@knuedge.com']
13
+
14
+ spec.summary = 'KnuVerse Knufactor Ruby SDK'
15
+ spec.homepage = 'https://github.com/KnuVerse/knuverse-knufactor-sdk-ruby'
16
+ spec.license = 'MIT'
17
+
18
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
19
+ f.match(%r{^(test|spec|features)/})
20
+ end
21
+ spec.bindir = 'exe'
22
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
+ spec.require_paths = ['lib']
24
+
25
+ spec.metadata['yard.run'] = 'yri'
26
+
27
+ spec.required_ruby_version = '~> 2.0'
28
+ spec.post_install_message = 'Thanks for installing the KnuVerse Knufactor Ruby SDK!'
29
+ spec.platform = Gem::Platform::RUBY
30
+
31
+ spec.add_runtime_dependency 'linguistics', '~> 2.1'
32
+ spec.add_runtime_dependency 'rest-client', '~> 2.0'
33
+ spec.add_runtime_dependency 'will_paginate', '~> 3.1'
34
+
35
+ spec.add_development_dependency 'bundler', '~> 1.12'
36
+ spec.add_development_dependency 'rake', '~> 10.0'
37
+ spec.add_development_dependency 'rspec', '~> 3.1'
38
+ spec.add_development_dependency 'rubocop', '~> 0.35'
39
+ spec.add_development_dependency 'yard', '~> 0.8'
40
+ spec.add_development_dependency 'simplecov'
41
+ spec.add_development_dependency 'travis', '~> 1.8'
42
+ end
@@ -0,0 +1,28 @@
1
+ module CoreExtensions
2
+ module String
3
+ # Monkey-patches for String to add some simple missing transformations
4
+ module Transformations
5
+ # Convert CamelCase to underscored_text
6
+ # @return [String]
7
+ def to_underscore
8
+ gsub(/::/, '/')
9
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
10
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
11
+ .tr('-', '_')
12
+ .downcase
13
+ end
14
+
15
+ # Convert underscored_text to CamelCase
16
+ # @return [String]
17
+ def to_camel
18
+ split('_').map(&:capitalize).join
19
+ end
20
+
21
+ # Attempt to guess a more human-like view of a string
22
+ # @return [String]
23
+ def humanize
24
+ gsub(/_id$/, '').tr('_', ' ').capitalize
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,42 @@
1
+ # Standard Library Requirements
2
+ require 'json'
3
+ require 'singleton'
4
+
5
+ # External Library Requirements
6
+ require 'linguistics'
7
+ Linguistics.use(:en)
8
+ require 'rest-client'
9
+ require 'will_paginate'
10
+ require 'will_paginate/array'
11
+
12
+ # Internal Requirements
13
+ require 'core_extensions/string/transformations'
14
+ String.include CoreExtensions::String::Transformations
15
+
16
+ require 'knuverse/knufactor/version'
17
+ require 'knuverse/knufactor/api_exception'
18
+ require 'knuverse/knufactor/exceptions/api_client_not_configured'
19
+ require 'knuverse/knufactor/exceptions/immutable_modification'
20
+ require 'knuverse/knufactor/exceptions/invalid_arguments'
21
+ require 'knuverse/knufactor/exceptions/invalid_options'
22
+ require 'knuverse/knufactor/exceptions/invalid_property'
23
+ require 'knuverse/knufactor/exceptions/invalid_where_query'
24
+ require 'knuverse/knufactor/exceptions/missing_path'
25
+ require 'knuverse/knufactor/exceptions/new_instance_with_id'
26
+ require 'knuverse/knufactor/helpers/api_client'
27
+ require 'knuverse/knufactor/validations/api_client'
28
+ require 'knuverse/knufactor/api_client_base'
29
+ require 'knuverse/knufactor/api_client'
30
+ require 'knuverse/knufactor/simple_api_client'
31
+ require 'knuverse/knufactor/resource_collection'
32
+ require 'knuverse/knufactor/helpers/resource'
33
+ require 'knuverse/knufactor/helpers/resource_class'
34
+ require 'knuverse/knufactor/validations/resource'
35
+ require 'knuverse/knufactor/resource'
36
+ require 'knuverse/knufactor/resources/client'
37
+
38
+ module KnuVerse
39
+ # The SDK for interacting with the KnuVerse Knufactor Cloud API
40
+ module Knufactor
41
+ end
42
+ end
@@ -0,0 +1,30 @@
1
+ module KnuVerse
2
+ module Knufactor
3
+ # The API Client Singleton class
4
+ class APIClient < APIClientBase
5
+ include Singleton
6
+
7
+ def self.configure(opts = {})
8
+ instance.configure(opts)
9
+ end
10
+
11
+ def self.about_service
12
+ instance.about_service
13
+ end
14
+
15
+ def configure(opts = {})
16
+ # validations
17
+ validate_opts(opts)
18
+
19
+ # Set up the client's state
20
+ @server = opts[:server] || 'https://cloud.knuverse.com'
21
+ @apikey = opts[:apikey]
22
+ @secret = opts[:secret]
23
+ @base_uri = opts[:base_uri] || '/api/v1/'
24
+ @last_auth = nil
25
+ @auth_token = nil
26
+ @configured = true
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,112 @@
1
+ module KnuVerse
2
+ module Knufactor
3
+ # The API Client Base class
4
+ class APIClientBase
5
+ attr_reader :server, :base_uri
6
+ attr_accessor :apikey, :secret, :account
7
+
8
+ include Validations::APIClient
9
+ include Helpers::APIClient
10
+
11
+ def about_service
12
+ get 'about'
13
+ end
14
+
15
+ def api_uri
16
+ @api_uri ||= URI.parse(server)
17
+ @api_uri.path = base_uri
18
+ @api_uri
19
+ end
20
+
21
+ def authenticated?
22
+ raise Exceptions::APIClientNotConfigured unless configured?
23
+ @auth_token && @last_auth && ((Time.now.utc - @last_auth) < (60 * 14)) ? true : false
24
+ end
25
+
26
+ def configured?
27
+ @configured ? true : false
28
+ end
29
+
30
+ def json_headers
31
+ base = { content_type: :json, accept: :json }
32
+ authenticated? ? base.merge(authorization: auth_header) : base
33
+ end
34
+
35
+ def refresh_auth_bearer
36
+ auth_data = {
37
+ key_id: @apikey,
38
+ secret: @secret
39
+ }
40
+ resp = post('auth', auth_data)
41
+ resp['jwt']
42
+ end
43
+
44
+ def refresh_auth
45
+ @auth_token = refresh_auth_bearer
46
+ @last_auth = Time.now.utc
47
+ true
48
+ end
49
+
50
+ def get(uri, data = nil)
51
+ refresh_auth unless authenticated?
52
+
53
+ client_action do |client|
54
+ if data
55
+ JSON.parse client[uri].get(json_headers.merge(params: data))
56
+ else
57
+ JSON.parse client[uri].get(json_headers)
58
+ end
59
+ end
60
+ end
61
+
62
+ def post(uri, data = nil)
63
+ refresh_auth unless authenticated? || uri == 'auth'
64
+
65
+ client_action do |client|
66
+ if data
67
+ JSON.parse client[uri].post(json_escape(data.to_json), json_headers)
68
+ else
69
+ JSON.parse client[uri].post(nil, json_headers)
70
+ end
71
+ end
72
+ end
73
+
74
+ def patch(uri, data)
75
+ refresh_auth unless authenticated?
76
+
77
+ client_action do |client|
78
+ response = client[uri].patch(json_escape(data.to_json), json_headers)
79
+ if response && !response.empty?
80
+ JSON.parse(response)
81
+ else
82
+ true
83
+ end
84
+ end
85
+ end
86
+
87
+ def put(uri, data)
88
+ refresh_auth unless authenticated?
89
+
90
+ client_action do |client|
91
+ client[uri].put(json_escape(data.to_json), json_headers)
92
+ end
93
+ end
94
+
95
+ private
96
+
97
+ def auth_header
98
+ authenticated? ? "Bearer #{@auth_token}" : nil
99
+ end
100
+
101
+ def client_action
102
+ raise Exceptions::APIClientNotConfigured unless configured?
103
+ yield connection
104
+ end
105
+
106
+ # Don't bother creating a connection until we need one
107
+ def connection
108
+ @connection ||= RestClient::Resource.new(api_uri.to_s)
109
+ end
110
+ end
111
+ end
112
+ end