box-com 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a22a998fddabe194a554b6f6a6bdc196754f147f
4
+ data.tar.gz: 609f5df6720094f297bb2b0f7ec9296508221e76
5
+ SHA512:
6
+ metadata.gz: e80c6bab9290a0b62d35a95b80ac945eada2b5e78d5a1a0967fa8eb49e4a410cc160caeb1254cf0dc4d052ae4a8d20b749db79c17c64800eb42d79007ed63f90
7
+ data.tar.gz: 10b6b0f5c7079ae0e4553c5deb48c082beb7395b89137f8a00740ff518e4a1c8eb62e15d5e39f176751890fabab24db6eae3a45a37ad3b144abf05a78b66317d
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in box.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 David Michael
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,29 @@
1
+ # Box
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'box'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install box
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it ( http://github.com/<my-github-username>/box/fork )
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,64 @@
1
+
2
+ module Box
3
+ class Authorization
4
+
5
+ def self.authorize(config = {})
6
+ username, password = config[:username], config[:password]
7
+ raise "Unable to get auth tokens without username and password" unless username && password
8
+
9
+ require 'mechanize'
10
+
11
+ puts '... attempting to authorize with username and password'
12
+ client_id, client_secret = config[:client_id], config[:client_secret]
13
+
14
+
15
+ agent = Mechanize::new
16
+ session = Session.new(config)
17
+
18
+ # Get the authorization URL from Box by specifying redirect URL
19
+ # as the arbitrary but working Chase bank home page - this must match the address at Box
20
+ # authorize_url = box_session.authorize_url('https://anywhere.airdye.com/oauth2callback')
21
+ authorize_url = session.authorize_url(redirect_uri: 'https://www.chase.com')
22
+
23
+ # process the first login screen
24
+ login_page = agent.get(authorize_url)
25
+
26
+ # get the login form where you enter the username and password
27
+ login_form = login_page.form_with(name: 'login_form')
28
+ login_form.login = username
29
+ login_form.password = password
30
+
31
+ # submit the form and get the allow/deny page back
32
+ allow_page = agent.submit(login_form)
33
+
34
+ # find the form that allows consent
35
+ consent_form = allow_page.form_with(name: 'consent_form')
36
+
37
+ # now find the button that submits the allow page with consent
38
+ accept_button = consent_form.button_with(name: 'consent_accept')
39
+
40
+ # Submit the form to cause the redirection with authentication code
41
+ redirpage = agent.submit(consent_form, accept_button)
42
+
43
+ # Use the CGI module to get a hash of the variables (stuff after ?)
44
+ # and then the authentication code is embedded in [" and "] so
45
+ # strip those
46
+ code_query = CGI::parse(redirpage.uri.query)['code'].to_s
47
+ code = code_query[2,code_query.length-4]
48
+
49
+ # get the box access token using the authentication code
50
+ session.aquire_access_token(code)
51
+
52
+ # print the tokens to show we have them
53
+ p session.access_token
54
+ p session.refresh_token
55
+
56
+ Box::Session.on_token_refresh.call(session.access_token, session.refresh_token)
57
+
58
+ # Create a new Box client based on the authenticated session
59
+ # ap Box.client.root.items
60
+
61
+ return session.oauth2_access_token
62
+ end
63
+ end
64
+ end
data/lib/box/client.rb ADDED
@@ -0,0 +1,168 @@
1
+
2
+
3
+
4
+ module Box
5
+ class Client
6
+ VERSION = '2.0'
7
+ attr_accessor :session
8
+
9
+ def initialize(session)
10
+ @session = session
11
+ end
12
+
13
+ def root
14
+ Folder.new(self, id: 0)
15
+ end
16
+
17
+ def walk(root, &block)
18
+ root.items.each do |item|
19
+ if item.folder?
20
+ walk(item, &block)
21
+ elsif item.file?
22
+ yield item
23
+ else
24
+ puts "Unknown item type #{item.id}:#{item.type}"
25
+ end
26
+ end
27
+ end
28
+
29
+ # Starting at the "root" of Box which is always "All Files", make successive requests untl you have reached the
30
+ # final path in the input
31
+ def folder(path) # /path/to/folder
32
+ path = Pathname(path).each_filename.to_a
33
+ folder = root
34
+ path.each do |name|
35
+ folder = folder.folders.select {|folder| folder.name == name}.first
36
+ return nil unless folder
37
+ end
38
+ folder
39
+ end
40
+
41
+ def search(query, options = {})
42
+ params = options.merge({query: query})
43
+
44
+ response = get('search', params)
45
+ parse_items(response.body)
46
+ end
47
+
48
+ # Process the Box "items" returned from a request
49
+ def parse_items(results)
50
+ return [] if results['entries'].empty?
51
+ results['entries'].reduce([]) do |entries, entry|
52
+ entries << case entry['type']
53
+ when 'file' then Box::File.new(self, entry)
54
+ when 'folder' then Box::Folder.new(self, entry)
55
+ end
56
+ entries
57
+ end
58
+ end
59
+
60
+ def connection
61
+ conn = Faraday.new(Box::API_URL) do |builder|
62
+ builder.request :json
63
+ builder.request :multipart
64
+ # builder.response :logger
65
+ builder.response :json, :content_type => /\bjson$/
66
+ # What a joke. This must be LAST to avoid encoding errors in JSON request body
67
+ builder.adapter :net_http
68
+ end
69
+
70
+ conn.headers['Authorization'] = "Bearer #{@session.access_token}"
71
+ conn
72
+ end
73
+
74
+ def make_uri(path)
75
+
76
+ end
77
+
78
+ def get(path, params = {}, retries = 0)
79
+ request('GET', path, params)
80
+ end
81
+
82
+ def post(path, params = {}, retries = 0)
83
+ request('POST', path, params)
84
+ end
85
+
86
+ def put(path, params = {}, retries = 0)
87
+ request('PUT', path, params)
88
+ end
89
+
90
+ def delete(path, params = {}, retries = 0)
91
+ request('DELETE', path, params)
92
+ end
93
+
94
+ def upload(params = {}, retries = 0)
95
+ # Basic parameters
96
+ local_path, content_type, file_name, parent_id = params[:local_path], params[:content_type], params[:file_name], params[:parent_id]
97
+ # If there is a file_id, it means we want to replace it.
98
+ uri = if params[:box_id]
99
+ "https://upload.box.com/api/2.0/files/#{params[:box_id]}/content"
100
+ else
101
+ 'https://upload.box.com/api/2.0/files/content'
102
+ end
103
+
104
+ puts "[Box.com] POST #{uri}"
105
+
106
+ # Construct the payload
107
+ payload = {
108
+ filename: Faraday::UploadIO.new(local_path, content_type, file_name),
109
+ parent_id: parent_id
110
+ }
111
+
112
+ response = connection.post(uri, payload) do |request|
113
+ request.headers['Content-Type'] = 'multipart/form-data'
114
+ end
115
+
116
+ case response.status
117
+ when 401
118
+ try_token_refresh!
119
+ return upload(params, retries + 1) if retries == 0
120
+ else
121
+ return handle_response(response)
122
+ end
123
+ end
124
+
125
+ # Generic HTTP request method with retries for bad authorization
126
+ def request(method, path, params = {}, retries = 0)
127
+ uri = Addressable::URI.parse(::File.join(VERSION, path))
128
+ if method == 'GET'
129
+ uri.query_values = params.reduce({}){|hash, (k,v)| hash[k] = v.to_s; hash}
130
+ # params = {}
131
+ end
132
+
133
+ puts "[Box.com] #{method} #{::File.join(Box::API_URL, uri.to_s)}"
134
+ response = connection.send(method.downcase, uri.to_s, params)
135
+
136
+ case response.status
137
+ when 401
138
+ try_token_refresh!
139
+ return request(method, path, params, retries + 1) if retries == 0
140
+ else
141
+ return handle_response(response)
142
+ end
143
+ # TODO: We need to retry connection failures - or at least catch them
144
+ # rescue Faraday::ConnectionFailed => e
145
+ end
146
+
147
+ def try_token_refresh!
148
+ @session.refresh_token!
149
+ rescue OAuth2::Error => e
150
+ raise "Sorry, could not refresh tokens: #{e.message}"
151
+ end
152
+
153
+ def handle_response(response)
154
+ case response.status
155
+ when 400
156
+ raise Box::MalformedAuthHeaders, response.headers
157
+ when 404
158
+ raise Box::ResourceNotFound, JSON.dump(response.body)
159
+ when 409
160
+ raise Box::NameConflict, JSON.dump(response.body)
161
+ when 500
162
+ ap response.body
163
+ end
164
+ return response
165
+ end
166
+
167
+ end
168
+ end
@@ -0,0 +1,16 @@
1
+ module Box
2
+ class BoxError < StandardError
3
+ end
4
+
5
+ class ArgumentError < BoxError
6
+ end
7
+
8
+ class NameConflict < BoxError
9
+ end
10
+
11
+ class ResourceNotFound < BoxError
12
+ end
13
+
14
+ class MalformedAuthHeaders < BoxError
15
+ end
16
+ end
data/lib/box/file.rb ADDED
@@ -0,0 +1,61 @@
1
+ module Box
2
+ class File < Item
3
+ def_delegators :@metadata, :sha1, :name, :size, :etag, :parent
4
+
5
+ def self.download_uri(id)
6
+ response = Box.client.get("/files/#{id}/content")
7
+ uri = nil
8
+ uri = response.headers['location'] if response.status == 302
9
+ uri
10
+ end
11
+
12
+ def size
13
+ @metadata['size']
14
+ end
15
+
16
+ def path
17
+ "/" + path_names.join('/')
18
+ end
19
+
20
+ def path_with_file
21
+ ::File.join(path, name)
22
+ end
23
+
24
+ def paths
25
+ @metadata['path_collection']['entries']
26
+ end
27
+
28
+ def path_names
29
+ paths.map {|path| path['name']}
30
+ end
31
+
32
+ def self.delete_existing(id)
33
+ Box.client.delete("files/#{id}")
34
+ end
35
+
36
+
37
+ # Ruby is such a pain in the ass with it's loosy goosy type
38
+ # @return [Box::File] The newly created file on Box
39
+ def copy_to(folder, options = {})
40
+ raise Box::ArgumentError, 'folder must be a Box::Folder' unless folder.is_a?(Box::Folder)
41
+ raise Box::ArgumentError, 'options must be a Hash' unless options.is_a?(Hash)
42
+
43
+ folder_id = folder.id
44
+
45
+ params = {parent: {id: folder_id}, name: options[:name]}
46
+ # This response is a Box file object
47
+ response = @client.post("files/#{id}/copy", params)
48
+ Box::File.new(@client, response.body)
49
+ end
50
+
51
+ # Since this is just an update, this method is idempotent always returning a file
52
+ def move_to(folder, options = {})
53
+ folder_id = (folder.is_a?(Box::Folder)) ? folder.id : folder
54
+
55
+ response = @client.put("files/#{id}", parent:{id: folder_id})
56
+ Box::File.new(@client, response.body)
57
+ end
58
+
59
+
60
+ end
61
+ end
data/lib/box/folder.rb ADDED
@@ -0,0 +1,65 @@
1
+ module Box
2
+ class Folder < Item
3
+ LIMIT = 1000
4
+ def_delegators :@metadata, :name
5
+
6
+ def load_info!
7
+ @client.get("/folders/#{id}")
8
+ end
9
+
10
+ # Check to see if an item of the same name in the folder
11
+ def has_item?(name)
12
+ items.find {|item| item.name == name}
13
+ end
14
+
15
+ def subfolder(folder_name)
16
+ folders = items.select {|item| item.folder? and item.name == folder_name}
17
+ return nil if folders.empty?
18
+ folders.first
19
+ end
20
+
21
+ def find_or_create_subfolder(folder_name)
22
+ folder = subfolder(folder_name)
23
+ return folder unless folder.nil?
24
+
25
+ puts "[Box.com] Creating subfolder in #{self.name} for #{folder_name}"
26
+ response = @client.post('folders', {name: folder_name, parent:{id: self.id}})
27
+
28
+ if response.status == 201 # created
29
+ folder = Box::Folder.new(@client, response.body)
30
+ puts "[Box.com] Created folder for #{folder_name} in #{name} as #{folder.id}"
31
+ folder
32
+ else
33
+ puts "[Box.com] Error creating folder, #{response.body}"
34
+ nil
35
+ end
36
+
37
+ end
38
+
39
+ # Warning: This gets on all files for a directory with no limit by recursively calling itself until it reaches
40
+ # the limit
41
+ def items(params = {}, collection = [])
42
+ # Format params defaults
43
+ params = {fields: 'sha1,name,path_collection,size', limit: LIMIT, offset: 0}.merge(params)
44
+ # Add expected fields and limit
45
+ response = @client.get("/folders/#{id}/items", params)
46
+ ap response
47
+ # Add the results to the total collection
48
+ collection.push *@client.parse_items(response.body)
49
+
50
+ total_count = response.body['total_count']
51
+ offset = (LIMIT * (params[:offset] + 1))
52
+
53
+ if total_count > offset
54
+ puts "[Box.com] Recursively calling for items in folder #{name} - #{LIMIT}, #{offset}, #{total_count}"
55
+ return self.items({offset: offset}, collection)
56
+ end
57
+
58
+ collection
59
+ end
60
+
61
+ def folders
62
+ items.select {|item| item.type == 'folder' }
63
+ end
64
+ end
65
+ end
data/lib/box/item.rb ADDED
@@ -0,0 +1,40 @@
1
+ require 'forwardable'
2
+
3
+ module Box
4
+ class Item
5
+ attr_accessor :client, :metadata
6
+ extend Forwardable
7
+ extend Memoist
8
+
9
+ def self.type
10
+ self.name.demodulize.downcase
11
+ end
12
+
13
+ def initialize(*args)
14
+ if args.size == 1
15
+ @client, @metadata = Box.client, Hashie::Mash.new(args[0])
16
+ else
17
+ @client, @metadata = args[0], Hashie::Mash.new(args[1])
18
+ end
19
+
20
+ end
21
+
22
+ def folder?
23
+ type == 'folder'
24
+ end
25
+
26
+ def file?
27
+ type == 'file'
28
+ end
29
+
30
+ def self.find(id)
31
+ response = Box.client.get("#{type.pluralize}/#{id}")
32
+ self.new(Box.client, response.body)
33
+ rescue Box::ResourceNotFound => e
34
+ nil
35
+ end
36
+
37
+
38
+ def_delegators :@metadata, :id, :type
39
+ end
40
+ end
@@ -0,0 +1,72 @@
1
+ module Box
2
+ class Session
3
+ extend Memoist
4
+ attr_accessor :client_id, :client_secret, :access_token, :refresh_token, :on_token_refresh, :oauth2_access_token
5
+ attr_accessor :config
6
+
7
+ class << self
8
+ attr_accessor :on_token_refresh
9
+ @on_token_refresh = -> (access_token, refresh_token) {}
10
+ end
11
+
12
+ OAUTH2_URLS = {
13
+ site: 'https://www.box.com',
14
+ authorize_url: '/api/oauth2/authorize',
15
+ token_url: '/api/oauth2/token'
16
+ }
17
+
18
+ def initialize(config = {})
19
+ @config = config
20
+ # We must have at least these variables
21
+ @client_id = config[:client_id]
22
+ @client_secret = config[:client_secret]
23
+ @access_token = config[:access_token]
24
+ @refresh_token = config[:refresh_token]
25
+
26
+ if @access_token
27
+ @oauth2_access_token = OAuth2::AccessToken.new(oauth2_client, @access_token, {refresh_token: @refresh_token})
28
+ end
29
+ end
30
+
31
+ def oauth2_client
32
+ OAuth2::Client.new(@client_id, @client_secret, OAUTH2_URLS.dup)
33
+ end
34
+ memoize :oauth2_client
35
+
36
+ # {redirect_uri: value}
37
+ def authorize_url(options = {})
38
+ oauth2_client.auth_code.authorize_url(options)
39
+ end
40
+
41
+ # @return [OAuth2::AccessToken]
42
+ def aquire_access_token(code)
43
+ @oauth2_access_token = oauth2_client.auth_code.get_token(code)
44
+ set_tokens!
45
+ @oauth2_access_token
46
+ end
47
+
48
+ def set_tokens!
49
+ @access_token = @oauth2_access_token.token
50
+ @refresh_token = @oauth2_access_token.refresh_token
51
+ end
52
+
53
+ def refresh_token!
54
+ @oauth2_access_token = @oauth2_access_token.refresh!
55
+ set_tokens!
56
+ Box::Session.on_token_refresh.call(@oauth2_access_token.token, @oauth2_access_token.refresh_token)
57
+ @oauth2_access_token
58
+ rescue OAuth2::Error => e
59
+ if e.code == 'invalid_client' || ((e.code == 'invalid_grant') && (e.description == 'Refresh token has expired' || e.description == 'Invalid refresh token'))
60
+ raise e if @config[:disable_auth]
61
+ puts "Error authenticating Box -> #{e.message}"
62
+ puts 'Attempting to reauthorize and get new tokens'
63
+ @oauth2_access_token = Box::Authorization.authorize(config)
64
+ set_tokens!
65
+ return @oauth2_access_token
66
+ else
67
+ raise e
68
+ end
69
+ end
70
+
71
+ end
72
+ end
@@ -0,0 +1,3 @@
1
+ module Box
2
+ VERSION = "0.0.7"
3
+ end
data/lib/box.rb ADDED
@@ -0,0 +1,61 @@
1
+ require 'memoist'
2
+ require 'hashie'
3
+ require 'faraday'
4
+ require 'faraday_middleware'
5
+ require 'addressable/uri'
6
+ require 'oauth2'
7
+
8
+ $LOAD_PATH << File.dirname(__FILE__)
9
+ require 'box/version'
10
+
11
+ module Box
12
+ API_URL = 'https://api.box.com'
13
+ UPLOAD_URL = 'https://upload.box.com/api/2.0'
14
+ ISO_8601_TEST = Regexp.new(/^[0-9]{4}-[0-9]{2}-[0-9]{2}T/)
15
+
16
+ class << self
17
+ extend Memoist
18
+
19
+ def client(config = {})
20
+ # Accounts for both string and Symbol keyed hashes.
21
+ # This is basically stringify_keys, just less efficient
22
+ config = Hashie::Mash.new(config)
23
+ # You can either pass in the config, or set it from the environment variables
24
+ config = {
25
+ access_token: config['access_token'] || ENV['BOX_ACCESS_TOKEN'],
26
+ refresh_token: config['refresh_token'] || ENV['BOX_REFRESH_TOKEN'],
27
+ client_id: config['client_id'] || ENV['BOX_CLIENT_ID'],
28
+ client_secret: config['client_secret'] || ENV['BOX_CLIENT_SECRET'],
29
+ username: config['username'] || ENV['BOX_USERNAME'],
30
+ password: config['password'] || ENV['BOX_PASSWORD']
31
+ }
32
+
33
+ # Box::Authorization.authorize client_id, client_secret
34
+ session = create_session(config)
35
+ Box::Client.new(session)
36
+ end
37
+ # memoize :client
38
+
39
+ def create_session(config = {})
40
+ Box::Session.new config
41
+ end
42
+
43
+ end
44
+ end
45
+
46
+ require 'box/exceptions'
47
+ require 'box/client'
48
+ require 'box/session'
49
+ require 'box/authorization'
50
+ require 'box/item'
51
+ require 'box/folder'
52
+ require 'box/file'
53
+
54
+ Box::Session.on_token_refresh = -> (access_token,refresh_token) {
55
+ puts 'Box::Session.on_token_refresh called with'
56
+ puts access_token
57
+ puts refresh_token
58
+ }
59
+
60
+
61
+
metadata ADDED
@@ -0,0 +1,183 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: box-com
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.7
5
+ platform: ruby
6
+ authors:
7
+ - David Michael
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-06-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: memoist
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '='
46
+ - !ruby/object:Gem::Version
47
+ version: 0.9.3
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '='
53
+ - !ruby/object:Gem::Version
54
+ version: 0.9.3
55
+ - !ruby/object:Gem::Dependency
56
+ name: mechanize
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 2.7.2
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '='
67
+ - !ruby/object:Gem::Version
68
+ version: 2.7.2
69
+ - !ruby/object:Gem::Dependency
70
+ name: faraday
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.9.0
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.9.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: faraday_middleware
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '='
88
+ - !ruby/object:Gem::Version
89
+ version: 0.9.1
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '='
95
+ - !ruby/object:Gem::Version
96
+ version: 0.9.1
97
+ - !ruby/object:Gem::Dependency
98
+ name: addressable
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "<="
102
+ - !ruby/object:Gem::Version
103
+ version: 2.2.4
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "<="
109
+ - !ruby/object:Gem::Version
110
+ version: 2.2.4
111
+ - !ruby/object:Gem::Dependency
112
+ name: hashie
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: oauth2
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: 0.9.3
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: 0.9.3
139
+ description: Write a longer description. Optional.
140
+ email:
141
+ - david.michael@giantmachines.com
142
+ executables: []
143
+ extensions: []
144
+ extra_rdoc_files: []
145
+ files:
146
+ - Gemfile
147
+ - LICENSE.txt
148
+ - README.md
149
+ - Rakefile
150
+ - lib/box.rb
151
+ - lib/box/authorization.rb
152
+ - lib/box/client.rb
153
+ - lib/box/exceptions.rb
154
+ - lib/box/file.rb
155
+ - lib/box/folder.rb
156
+ - lib/box/item.rb
157
+ - lib/box/session.rb
158
+ - lib/box/version.rb
159
+ homepage: ''
160
+ licenses:
161
+ - MIT
162
+ metadata: {}
163
+ post_install_message:
164
+ rdoc_options: []
165
+ require_paths:
166
+ - lib
167
+ required_ruby_version: !ruby/object:Gem::Requirement
168
+ requirements:
169
+ - - ">="
170
+ - !ruby/object:Gem::Version
171
+ version: '0'
172
+ required_rubygems_version: !ruby/object:Gem::Requirement
173
+ requirements:
174
+ - - ">="
175
+ - !ruby/object:Gem::Version
176
+ version: '0'
177
+ requirements: []
178
+ rubyforge_project:
179
+ rubygems_version: 2.2.2
180
+ signing_key:
181
+ specification_version: 4
182
+ summary: Write a short summary. Required.
183
+ test_files: []