google_storage 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/Gemfile +4 -0
- data/README.textile +110 -0
- data/Rakefile +3 -0
- data/examples/examples.rb +60 -0
- data/google_storage.gemspec +19 -0
- data/lib/generators/google_storage/install/install_generator.rb +18 -0
- data/lib/generators/templates/google_storage.rake +27 -0
- data/lib/generators/templates/google_storage.yml +48 -0
- data/lib/google_storage/bucket.rb +76 -0
- data/lib/google_storage/client.rb +111 -0
- data/lib/google_storage/object.rb +107 -0
- data/lib/google_storage/request.rb +81 -0
- data/lib/google_storage/token.rb +31 -0
- data/lib/google_storage/version.rb +3 -0
- data/lib/google_storage.rb +9 -0
- metadata +76 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.textile
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
h1. Google Storage using API v2
|
2
|
+
|
3
|
+
I wrote this gem because the only google storage gems I could find already written were either written a while ago and only set up to use the old
|
4
|
+
version of Google's API or they were dependent on Gems that don't run properly on... dare I say it... windows.. :) Apologies, I'm without choice
|
5
|
+
at the moment, but anyways, this gem should run fine on all platforms and I've tried to keep dependencies to a minimum. Let me know if you find
|
6
|
+
any bugs though ok and feel free to contribute.
|
7
|
+
|
8
|
+
Google Storage is still a little experimental at the moment and the responses back from google differ quite a bit from each other.
|
9
|
+
I've tried to parse all of these different responses and turn them into a fairly common type of response object though.
|
10
|
+
|
11
|
+
h2. Setup Guide
|
12
|
+
|
13
|
+
There's a couple of steps to follow in order to setup this gem. The new Google Storage APIv2 uses OAuth 2.0 to authenticate requests, you can read up
|
14
|
+
more on the nitty gritty if you like here: "Google's OAuth2 Guide":http://code.google.com/apis/accounts/docs/OAuth2.html
|
15
|
+
|
16
|
+
I'll try and add client side support also once this gem is up and running properly, but for the moment it's only setup to support server side web
|
17
|
+
applications.
|
18
|
+
|
19
|
+
But it's also possible to run this gem from the command line if you want.
|
20
|
+
|
21
|
+
|
22
|
+
h2. Install the google_storage Gem
|
23
|
+
|
24
|
+
Include the google_storage gem in your Gemfile and install config files
|
25
|
+
<pre>gem "google_storage"
|
26
|
+
bundle install
|
27
|
+
</pre>
|
28
|
+
Then in your Rails application directory run:
|
29
|
+
<pre>rails generate google_storage:install
|
30
|
+
</pre>
|
31
|
+
This will generate some rake tasks a google_storage.yml file in your config directory.
|
32
|
+
<pre>/Application/config/google_storage.yml
|
33
|
+
/Application/lib/tasks/google_storage.task
|
34
|
+
</pre>
|
35
|
+
|
36
|
+
|
37
|
+
|
38
|
+
h2. Setup your Google Project Client ID's
|
39
|
+
|
40
|
+
Visit "Google Storage":http://code.google.com/apis/storage/ and select activate Google Storage.
|
41
|
+
If you haven't already got a project set up, it'll prompt you to create one.
|
42
|
+
|
43
|
+
When you have access to your Google APIs Console, you need to enable Google Storage. When you select enable, you'll be shown terms and conditions
|
44
|
+
and you'll then need to setup billing. They have full pricing details there for you to check out as well but I think it's pretty reasonable..
|
45
|
+
|
46
|
+
Create a client ID for your project
|
47
|
+
On the left menu select "API Access" and then "Create an OAuth 2.0 client ID"
|
48
|
+
Enter your project's name and brand information.
|
49
|
+
|
50
|
+
Select Application type = Web Application and enter your site's information. If you're using localhost and running
|
51
|
+
locally make sure you include the port number you're using as well.
|
52
|
+
|
53
|
+
|
54
|
+
h2. Enter your Client ID details into your google_storage.yml
|
55
|
+
|
56
|
+
This is the example google_storage.yml file that is copied into your config directory of your rails application.
|
57
|
+
|
58
|
+
Follow the steps to obtain a refresh token from google.
|
59
|
+
|
60
|
+
<pre>
|
61
|
+
#Replace the following example ids with your own
|
62
|
+
|
63
|
+
google_config:
|
64
|
+
x-goog-project-id: 825628431245
|
65
|
+
|
66
|
+
#Client ID for web applications
|
67
|
+
web_applications:
|
68
|
+
client_id: 825628431245.apps.googleusercontent.com
|
69
|
+
client_secret: lpNYX5SPFDB6N-40lyMIPlQn
|
70
|
+
redirect_uris: 'http://localhost:3000/example'
|
71
|
+
js_origins: 'http://localhost:3000'
|
72
|
+
|
73
|
+
#refresh_token: replace this line with a refresh token from google, read below on how to get one
|
74
|
+
|
75
|
+
#You need to acquire a refresh token from google so that the google_storage gem
|
76
|
+
#can acquire access tokens whenever it needs to make new requests
|
77
|
+
|
78
|
+
# 1. Make sure you've signed up for Google Storage and filled in the above client ID details
|
79
|
+
# for your web application first
|
80
|
+
#
|
81
|
+
# 2. Depending on how much access you want to grant to your application run
|
82
|
+
# ONE of the following from your applications root directory. If you intend to be able to create and
|
83
|
+
# destroy objects and buckets and also be able to set permissions then use full_control
|
84
|
+
#
|
85
|
+
# rake gs:grant:read_only
|
86
|
+
# rake gs:grant:read_write
|
87
|
+
# rake gs:grant:full_control
|
88
|
+
#
|
89
|
+
# 3. Step 2 will generate a URL for you. Copy and paste the URL into a browser and you should be prompted
|
90
|
+
# by google to authorize the request by logging into your browser using the google email account you setup
|
91
|
+
# your google storage account with
|
92
|
+
#
|
93
|
+
# 4. When you 'allow access' you'll be redirected back to the redirect URL you setup in your client ID
|
94
|
+
# Your redirect URL should now include an authorization code. It'll look something like this:
|
95
|
+
# http://localhost:3000/example?code=4/WvlklnjtybhRJpaKpmDYrzIhAzyx
|
96
|
+
#
|
97
|
+
# 5. Copy that code from your URL and run the following rake task from your application directory
|
98
|
+
#
|
99
|
+
# rake gs:refresh_token['paste_your_auth_code_here']
|
100
|
+
# Example: rake gs:refresh_token['4/WvlklnjtybhRJpaKpmDYrzIhAzyx']
|
101
|
+
#
|
102
|
+
# 6. If everything worked you should see something that looks like the following:
|
103
|
+
#
|
104
|
+
# refresh_token: 1/x4X-U57snRMkLIWWYHWLCXPbfcnyGsdfx04sWAiG_1k
|
105
|
+
#
|
106
|
+
# 7. Copy and paste the refresh_token into this file. Your application should now be able to make calls to your
|
107
|
+
# Google Storage API
|
108
|
+
</pre>
|
109
|
+
|
110
|
+
Once you've acquired your refresh_token you can now make calls to the API. Refer to the examples dir on how to use.
|
data/Rakefile
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
### Configuration ###
|
2
|
+
#
|
3
|
+
# require "google_storage"
|
4
|
+
#
|
5
|
+
# The following will look for google_storage.yml in your rails config directory
|
6
|
+
# gs_client = GoogleStorage::Client.new
|
7
|
+
#
|
8
|
+
# Otherwise you can pass in the path to the google_storage.yml
|
9
|
+
# gs_client = GoogleStorage::Client.new(:config_yml => 'C:/example_path/google_storage.yml')
|
10
|
+
#
|
11
|
+
### Service Requests ###
|
12
|
+
#
|
13
|
+
# GET Service
|
14
|
+
# gs_client.list_buckets
|
15
|
+
#
|
16
|
+
### Bucket Requests ###
|
17
|
+
#
|
18
|
+
# GET Access Control List for Bucket
|
19
|
+
# gs_client.list_acls_for_bucket('bucket_name')
|
20
|
+
# gs_client.bucket_acls('bucket_name')
|
21
|
+
#
|
22
|
+
# PUT Bucket
|
23
|
+
# gs_client.create_bucket('bucket_name') <-- private bucket
|
24
|
+
# gs_client.create_bucket('bucket_name', :x_goog_acl => 'public-read') <-- public bucket
|
25
|
+
#
|
26
|
+
# GET Bucket
|
27
|
+
# gs_client.get_bucket('bucket_name')
|
28
|
+
# gs_client.bucket_contents('bucket_name')
|
29
|
+
#
|
30
|
+
# DELETE Bucket
|
31
|
+
# gs_client.delete_bucket('bucket_name')
|
32
|
+
#
|
33
|
+
### Object Requests ###
|
34
|
+
#
|
35
|
+
# GET Access Control List for Object
|
36
|
+
# gs_client.list_acls_for_object('bucket_name', 'object_name')
|
37
|
+
# gs_client.object_acls('bucket_name', 'object_name')
|
38
|
+
#
|
39
|
+
# GET Object
|
40
|
+
# gs_client.get_object('bucket_name', 'object_name')
|
41
|
+
# gs_client.get_object('bucket_name', 'object_name', :write_to_file => '/tmp/example/file.jpg')
|
42
|
+
#
|
43
|
+
# POST Object
|
44
|
+
# !!!! NOT ADDING POST METHOD AT THIS STAGE AS REQUIRES USE OF API V1 USING LEGACY ACCESS KEY
|
45
|
+
# !!!! USE PUT METHOD INSTEAD TO UPLOAD FILES
|
46
|
+
#
|
47
|
+
# PUT Object
|
48
|
+
# gs_client.put_object('bucket_name', 'object_name', :data => File.read('/tmp/example/file.jpg'), :x_goog_acl => 'public-read')
|
49
|
+
# gs_client.put_object('bucket_name', 'object_name', :path_to_file => '/tmp/example/file.jpg')
|
50
|
+
#
|
51
|
+
# HEAD Object
|
52
|
+
# gs_client.head_object('bucket_name', 'object_name')
|
53
|
+
#
|
54
|
+
# DELETE Object
|
55
|
+
# gs_client.delete_object('bucket_name', 'object_name')
|
56
|
+
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
$:.push File.expand_path("../lib", __FILE__)
|
2
|
+
require "google_storage/version"
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "google_storage"
|
6
|
+
s.version = GoogleStorage::VERSION
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.authors = ["Lucas Hills"]
|
9
|
+
s.email = ["lucas@lucashills.com"]
|
10
|
+
s.homepage = "https://github.com/2potatocakes/google_storage"
|
11
|
+
s.summary = "Google Storage for Developers is a RESTful service for storing and accessing your data on Google's infrastructure"
|
12
|
+
s.description = "A Ruby client library for using the new Google Storage API v2 using OAuth2.0"
|
13
|
+
s.files = `git ls-files`.split("\n")
|
14
|
+
s.require_paths = ["lib"]
|
15
|
+
|
16
|
+
s.add_dependency("crack", "~> 0.1.1")
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
s.extra_rdoc_files = ["README.textile"]
|
19
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module GoogleStorage
|
4
|
+
# Copies a google_storage.yml file into your applications config directory and a rake file
|
5
|
+
# into your lib/tasks directory
|
6
|
+
|
7
|
+
class InstallGenerator < Rails::Generators::Base
|
8
|
+
desc "Copies a google_storage.yml file into your application"
|
9
|
+
source_root File.expand_path('../../../templates', __FILE__)
|
10
|
+
|
11
|
+
"Copies a google_storage.yml file into your applications config directory and a rake file into lib/tasks"
|
12
|
+
def generate_layout
|
13
|
+
copy_file 'google_storage.yml', 'config/google_storage.yml'
|
14
|
+
copy_file 'google_storage.rake', 'lib/tasks/google_storage.rake'
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'google_storage'
|
2
|
+
|
3
|
+
namespace :gs do
|
4
|
+
client = GoogleStorage::Client.new
|
5
|
+
|
6
|
+
namespace :grant do
|
7
|
+
desc "Google Storage URL to grant 'read only' access to your storage account"
|
8
|
+
task :read_only do
|
9
|
+
puts client.authorization_url(:read_only)
|
10
|
+
end
|
11
|
+
|
12
|
+
desc "Google Storage URL to grant 'read/write' access to your storage account"
|
13
|
+
task :read_write do
|
14
|
+
puts client.authorization_url(:read_write)
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "Google Storage URL to grant 'read only' access to your storage account"
|
18
|
+
task :full_control do
|
19
|
+
puts client.authorization_url(:full_control)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
desc "Acquire Google Storage refresh token"
|
24
|
+
task :refresh_token, :auth_code do |t, args|
|
25
|
+
puts "refresh_token: #{client.acquire_refresh_token(args[:auth_code])}"
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
#Replace the following example ids with your own
|
2
|
+
|
3
|
+
google_config:
|
4
|
+
x-goog-project-id: 825628431245
|
5
|
+
|
6
|
+
#Client ID for web applications
|
7
|
+
web_applications:
|
8
|
+
client_id: 825628431245.apps.googleusercontent.com
|
9
|
+
client_secret: lpNYX5SPFDB6N-40lyMIPlQn
|
10
|
+
redirect_uris: 'http://localhost:3000/example'
|
11
|
+
js_origins: 'http://localhost:3000'
|
12
|
+
|
13
|
+
#refresh_token: replace this line with a refresh token from google, read below on how to get one
|
14
|
+
|
15
|
+
#You need to acquire a refresh token from google so that the google_storage gem
|
16
|
+
#can acquire access tokens whenever it needs to make new requests
|
17
|
+
|
18
|
+
# 1. Make sure you've signed up for Google Storage and filled in the above client ID details
|
19
|
+
# for your web application first
|
20
|
+
#
|
21
|
+
# 2. Depending on how much access you want to grant to your application run
|
22
|
+
# ONE of the following from your applications root directory. If you intend to be able to create and
|
23
|
+
# destroy objects and buckets and also be able to set permissions then use full_control
|
24
|
+
#
|
25
|
+
# rake gs:grant:read_only
|
26
|
+
# rake gs:grant:read_write
|
27
|
+
# rake gs:grant:full_control
|
28
|
+
#
|
29
|
+
# 3. Step 2 will generate a URL for you. Copy and paste the URL into a browser and you should be prompted
|
30
|
+
# by google to authorize the request by logging into your browser using the google email account you setup
|
31
|
+
# your google storage account with
|
32
|
+
#
|
33
|
+
# 4. When you 'allow access' you'll be redirected back to the redirect URL you setup in your client ID
|
34
|
+
# Your redirect URL should now include an authorization code. It'll look something like this:
|
35
|
+
# http://localhost:3000/example?code=4/WvlklnjtybhRJpaKpmDYrzIhAzyx
|
36
|
+
#
|
37
|
+
# 5. Copy that code from your URL and run the following rake task from your application directory
|
38
|
+
#
|
39
|
+
# rake gs:refresh_token['paste_your_auth_code_here']
|
40
|
+
# Example: rake gs:refresh_token['4/WvlklnjtybhRJpaKpmDYrzIhAzyx']
|
41
|
+
#
|
42
|
+
# 6. If everything worked you should see something that looks like the following:
|
43
|
+
#
|
44
|
+
# refresh_token: 1/x4X-U57snRMkLIWWYHWLCXPbfcnyGsdfx04sWAiG_1k
|
45
|
+
#
|
46
|
+
# 7. Copy and paste the refresh_token into this file. Your application should now be able to make calls to your
|
47
|
+
# Google Storage API
|
48
|
+
|
@@ -0,0 +1,76 @@
|
|
1
|
+
|
2
|
+
module GoogleStorage
|
3
|
+
class Client
|
4
|
+
|
5
|
+
def list_buckets(options={})
|
6
|
+
options[:send_goog_project_id] = true
|
7
|
+
resp = get(nil, '/', options)
|
8
|
+
resp_obj = Crack::XML.parse(resp.body)
|
9
|
+
if resp_obj["ListAllMyBucketsResult"]
|
10
|
+
resp_obj[:success] = true
|
11
|
+
resp_obj[:buckets] = resp_obj["ListAllMyBucketsResult"]["Buckets"]["Bucket"]
|
12
|
+
resp_obj[:raw] = Crack::XML.parse(resp.body)
|
13
|
+
resp_obj.each_key {|key| resp_obj.delete(key) unless key == :success || key == :buckets || key == :raw }
|
14
|
+
end
|
15
|
+
return resp_obj
|
16
|
+
end
|
17
|
+
|
18
|
+
def list_acls_for_bucket(bucket, options={})
|
19
|
+
resp = get(bucket, '/?acl', options)
|
20
|
+
resp_obj = Crack::XML.parse(resp.body)
|
21
|
+
if resp_obj["AccessControlList"]
|
22
|
+
resp_obj[:success] = true
|
23
|
+
resp_obj[:bucket_name] = bucket
|
24
|
+
resp_obj[:acl] = resp_obj["AccessControlList"]
|
25
|
+
resp_obj[:raw] = Crack::XML.parse(resp.body)
|
26
|
+
resp_obj.each_key {|key| resp_obj.delete(key) unless key == :success || key == :bucket_name || key == :acl || key == :raw }
|
27
|
+
end
|
28
|
+
return resp_obj
|
29
|
+
end
|
30
|
+
|
31
|
+
alias :bucket_acls :list_acls_for_bucket
|
32
|
+
|
33
|
+
def create_bucket(bucket, options={})
|
34
|
+
options[:send_goog_project_id] = true
|
35
|
+
resp = put(bucket, '/', options)
|
36
|
+
resp_obj = Crack::XML.parse(resp.body)
|
37
|
+
if resp.code == "200"
|
38
|
+
resp_obj.clear
|
39
|
+
resp_obj[:success] = true
|
40
|
+
resp_obj[:bucket_name] = bucket
|
41
|
+
resp_obj[:message] = "Bucket created"
|
42
|
+
end
|
43
|
+
return resp_obj
|
44
|
+
end
|
45
|
+
|
46
|
+
def get_bucket(bucket, options={})
|
47
|
+
resp = get(bucket, '/', options)
|
48
|
+
resp_obj = Crack::XML.parse(resp.body)
|
49
|
+
if resp.code == "200"
|
50
|
+
resp_obj[:success] = true
|
51
|
+
resp_obj[:bucket_name] = bucket
|
52
|
+
contents = resp_obj["ListBucketResult"]["Contents"] ? Array.new : nil
|
53
|
+
resp_obj["ListBucketResult"]["Contents"].is_a?(Array) ? \
|
54
|
+
(contents = resp_obj["ListBucketResult"]["Contents"]) : \
|
55
|
+
(contents[0] = resp_obj["ListBucketResult"]["Contents"]) unless contents.nil?
|
56
|
+
resp_obj[:contents] = contents
|
57
|
+
resp_obj[:raw] = Crack::XML.parse(resp.body)
|
58
|
+
resp_obj.each_key {|key| resp_obj.delete(key) unless key == :success || key == :bucket_name || key == :contents || key == :raw }
|
59
|
+
end
|
60
|
+
return resp_obj
|
61
|
+
end
|
62
|
+
|
63
|
+
alias :bucket_contents :get_bucket
|
64
|
+
|
65
|
+
def delete_bucket(bucket, options={})
|
66
|
+
resp = delete(bucket, '/', options)
|
67
|
+
return Crack::XML.parse(resp.body) unless resp.code == "204"
|
68
|
+
resp_obj = {}
|
69
|
+
resp_obj[:success] = true
|
70
|
+
resp_obj[:bucket_name] = bucket
|
71
|
+
resp_obj[:message] = "Bucket deleted"
|
72
|
+
return resp_obj
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'google_storage/request'
|
2
|
+
require 'google_storage/token'
|
3
|
+
require 'google_storage/bucket'
|
4
|
+
require 'google_storage/object'
|
5
|
+
require 'yaml'
|
6
|
+
|
7
|
+
module GoogleStorage
|
8
|
+
|
9
|
+
class Client
|
10
|
+
|
11
|
+
def initialize(options = {})
|
12
|
+
|
13
|
+
if (options[:config_yml] && !options[:config_yml].nil?) || (defined?(Rails.root) && File.exists?(File.join(Rails.root, 'config', 'google_storage.yml')))
|
14
|
+
config_path = options[:config_yml] ? File.expand_path(options[:config_yml]) : File.join(Rails.root, 'config', 'google_storage.yml')
|
15
|
+
end
|
16
|
+
|
17
|
+
raise " \nCan't find a google_storage.yml file to initialise with..
|
18
|
+
\nIf running inside a Rails Application
|
19
|
+
Please run: rails generate google_storage:install
|
20
|
+
To generate a google_storage.yml file in your config directory
|
21
|
+
\nIf running manually within a script or whatever
|
22
|
+
Initialise GoogleStorage with a path to your config yml file
|
23
|
+
Example: GoogleStorage::Client.new(:config_yml => 'path to your google storage yml')
|
24
|
+
\nIf running a rake task try passing: path='path to your google storage yml'
|
25
|
+
\n\n" unless config_path && File.exists?(config_path)
|
26
|
+
|
27
|
+
config_yml = YAML::load(File.open(config_path))
|
28
|
+
|
29
|
+
@project_id = config_yml['google_config']['x-goog-project-id']
|
30
|
+
|
31
|
+
@client_id = config_yml['web_applications']['client_id']
|
32
|
+
@client_secret = config_yml['web_applications']['client_secret']
|
33
|
+
@client_secret.force_encoding("UTF-8")
|
34
|
+
@refresh_token = config_yml['refresh_token'] if config_yml['refresh_token']
|
35
|
+
|
36
|
+
#TODO Add support for individual permission types
|
37
|
+
if config_yml['google_storage_ids']
|
38
|
+
@gsid_you = config_yml['google_storage_ids']['you'] if config_yml['google_storage_ids']['you']
|
39
|
+
@gsid_owners = config_yml['google_storage_ids']['owners'] if config_yml['google_storage_ids']['owners']
|
40
|
+
@gsid_editors = config_yml['google_storage_ids']['editors'] if config_yml['google_storage_ids']['editors']
|
41
|
+
@gsid_team = config_yml['google_storage_ids']['team'] if config_yml['google_storage_ids']['team']
|
42
|
+
end
|
43
|
+
|
44
|
+
#TODO - make redirect_uri's support multiple urls
|
45
|
+
@redirect_uri = config_yml['web_applications']['redirect_uris']
|
46
|
+
#TODO - maybe add support for API v1 as well... but probably not..
|
47
|
+
@api_version = options[:x_goog_api_version] ? options[:x_goog_api_version] : 2
|
48
|
+
@debug = options[:debug]
|
49
|
+
@timeout = options[:timeout]
|
50
|
+
@host = options[:host] ? options[:host] : 'commondatastorage.googleapis.com'
|
51
|
+
end
|
52
|
+
|
53
|
+
def google_storage_id(id)
|
54
|
+
case id
|
55
|
+
when :you
|
56
|
+
@gsid_you
|
57
|
+
when :owners
|
58
|
+
@gsid_owners
|
59
|
+
when :editors
|
60
|
+
@gsid_editors
|
61
|
+
when :team
|
62
|
+
@gsid_team
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def get(bucket, path, options={})
|
69
|
+
http_request(Net::HTTP::Get, bucket, path, options)
|
70
|
+
end
|
71
|
+
|
72
|
+
def put(bucket, path, options={})
|
73
|
+
http_request(Net::HTTP::Put, bucket, path, options)
|
74
|
+
end
|
75
|
+
|
76
|
+
def delete(bucket, path, options={})
|
77
|
+
http_request(Net::HTTP::Delete, bucket, path, options)
|
78
|
+
end
|
79
|
+
|
80
|
+
def head(bucket, path, options={})
|
81
|
+
http_request(Net::HTTP::Head, bucket, path, options)
|
82
|
+
end
|
83
|
+
|
84
|
+
def post_request(host, path, token, options={})
|
85
|
+
params = options.delete(:params) || {}
|
86
|
+
headers = options.delete(:headers) || {}
|
87
|
+
params[:"client_id"] = @client_id
|
88
|
+
params[:"client_secret"] = @client_secret
|
89
|
+
params[:"grant_type"] = options['grant_type']
|
90
|
+
|
91
|
+
case options['grant_type']
|
92
|
+
when 'authorization_code'
|
93
|
+
params[:"code"] = token
|
94
|
+
params[:"redirect_uri"] = @redirect_uri
|
95
|
+
when 'refresh_token'
|
96
|
+
params[:"refresh_token"] = token
|
97
|
+
end
|
98
|
+
|
99
|
+
construct_post_request(host, path, headers, params, options)
|
100
|
+
end
|
101
|
+
|
102
|
+
def http_request(method, bucket, path, options={})
|
103
|
+
host = bucket ? "#{bucket}.#{@host}" : @host
|
104
|
+
params = options.delete(:params) || {}
|
105
|
+
headers = options.delete(:headers) || {}
|
106
|
+
construct_http_request(host, path, method, headers, params, options)
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
@@ -0,0 +1,107 @@
|
|
1
|
+
|
2
|
+
require 'digest/md5'
|
3
|
+
|
4
|
+
module GoogleStorage
|
5
|
+
class Client
|
6
|
+
|
7
|
+
def get_object(bucket, filename, options={})
|
8
|
+
filename.gsub!(/^\//, "")
|
9
|
+
resp = get(bucket, "/#{filename}", options)
|
10
|
+
return Crack::XML.parse(resp.body) unless resp.code == "200"
|
11
|
+
resp_obj = {}
|
12
|
+
if options[:write_to_file]
|
13
|
+
begin
|
14
|
+
File.open(options[:write_to_file], 'wb') {|f| f.write(resp.body) }
|
15
|
+
rescue Exception => msg
|
16
|
+
return {:error => msg}
|
17
|
+
end
|
18
|
+
resp_obj.clear
|
19
|
+
resp_obj[:success] = true
|
20
|
+
resp_obj[:message] = "File created"
|
21
|
+
resp_obj[:path_to_file] = options[:write_to_file]
|
22
|
+
return resp_obj
|
23
|
+
end
|
24
|
+
resp_obj[:success] = true
|
25
|
+
resp_obj[:filename] = filename
|
26
|
+
resp_obj[:body] = resp.body
|
27
|
+
resp_obj[:type] = resp.header["content-type"]
|
28
|
+
resp_obj[:size] = resp.header["content-length"]
|
29
|
+
|
30
|
+
return resp_obj
|
31
|
+
end
|
32
|
+
|
33
|
+
def put_object(bucket, filename, options={})
|
34
|
+
filename.gsub!(/^\//, "")
|
35
|
+
if options[:path_to_file]
|
36
|
+
begin
|
37
|
+
uploaded_file = File.open(options[:path_to_file])
|
38
|
+
if uploaded_file.respond_to?(:get_input_stream)
|
39
|
+
uploaded_file.get_input_stream { |io| @data = io.read }
|
40
|
+
else
|
41
|
+
uploaded_file.binmode
|
42
|
+
@data = uploaded_file.read
|
43
|
+
end
|
44
|
+
rescue Exception => msg
|
45
|
+
return {:error => msg}
|
46
|
+
end
|
47
|
+
options[:data] = @data
|
48
|
+
end
|
49
|
+
resp = put(bucket, "/#{filename}", options)
|
50
|
+
public_file = (options[:x_goog_acl] && options[:x_goog_acl].match(/public/))
|
51
|
+
return Crack::XML.parse(resp.body) unless resp.code == "200"
|
52
|
+
resp_obj = {}
|
53
|
+
resp_obj[:success] = true
|
54
|
+
resp_obj[:message] = "Object added successfully"
|
55
|
+
resp_obj[:filename] = filename
|
56
|
+
resp_obj[:content_type] = options[:content_type]
|
57
|
+
resp_obj[:url] = public_file ? "http://#{@host}/#{bucket}/#{filename}" : \
|
58
|
+
"https://sandbox.google.com/storage/#{bucket}/#{filename}"
|
59
|
+
resp_obj[:url_type] = public_file ? "public" : "private"
|
60
|
+
return resp_obj
|
61
|
+
end
|
62
|
+
|
63
|
+
def list_acls_for_object(bucket, filename, options={})
|
64
|
+
filename.gsub!(/^\//, "")
|
65
|
+
resp = get(bucket, "/#{filename}?acl", options)
|
66
|
+
resp_obj = Crack::XML.parse(resp.body)
|
67
|
+
if resp_obj["AccessControlList"]
|
68
|
+
resp_obj[:success] = true
|
69
|
+
resp_obj[:object_name] = filename
|
70
|
+
resp_obj[:acl] = resp_obj["AccessControlList"]
|
71
|
+
resp_obj[:raw] = Crack::XML.parse(resp.body)
|
72
|
+
resp_obj.each_key {|key| resp_obj.delete(key) unless key == :success || key == :object_name || key == :acl || key == :raw }
|
73
|
+
end
|
74
|
+
return resp_obj
|
75
|
+
end
|
76
|
+
|
77
|
+
alias :object_acls :list_acls_for_object
|
78
|
+
|
79
|
+
def head_object(bucket, filename, options={})
|
80
|
+
filename.gsub!(/^\//, "")
|
81
|
+
resp = head(bucket, "/#{filename}", options)
|
82
|
+
return resp.header unless resp.code == "200"
|
83
|
+
resp_obj = {}
|
84
|
+
resp_obj[:success] = true
|
85
|
+
resp_obj[:filename] = filename
|
86
|
+
resp_obj[:last_modified] = resp.header['last-modified'] if resp.header['last-modified']
|
87
|
+
resp_obj[:etag] = resp.header['etag'] if resp.header['etag']
|
88
|
+
resp_obj[:content_length] = resp.header['content-length'] if resp.header['content-length']
|
89
|
+
resp_obj[:content_type] = resp.header['content-type'] if resp.header['content-type']
|
90
|
+
resp_obj[:cache_control] = resp.header['cache-control'] if resp.header['cache-control']
|
91
|
+
resp_obj[:date] = resp.header['date'] if resp.header['date']
|
92
|
+
return resp_obj
|
93
|
+
end
|
94
|
+
|
95
|
+
alias :object_head :head_object
|
96
|
+
|
97
|
+
def delete_object(bucket, filename, options={})
|
98
|
+
filename.gsub!(/^\//, "")
|
99
|
+
resp = delete(bucket, "/#{filename}", options)
|
100
|
+
return Crack::XML.parse(resp.body) unless resp.code == "204"
|
101
|
+
resp_obj = {}
|
102
|
+
resp_obj[:success] = true
|
103
|
+
resp_obj[:message] = "Object deleted successfully"
|
104
|
+
return resp_obj
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'net/https'
|
2
|
+
require 'cgi'
|
3
|
+
|
4
|
+
module GoogleStorage
|
5
|
+
class Client
|
6
|
+
|
7
|
+
def construct_post_request(host, path, headers={}, params={}, options={})
|
8
|
+
headers["Host"] = host
|
9
|
+
headers["Date"] = Time.now.utc.strftime('%a, %d %b %Y %H:%M:%S GMT')
|
10
|
+
headers["Content-Type"] = 'application/x-www-form-urlencoded'
|
11
|
+
headers["Content-Length"] = "0"
|
12
|
+
Crack::JSON.parse(_post_http_request(host, path, params, headers, options[:data]))
|
13
|
+
end
|
14
|
+
|
15
|
+
def construct_http_request(host, path, method, headers={}, params={}, options={})
|
16
|
+
raise "\nYou need to acquire a refresh_token before you can make requests to the Google API\n" unless @refresh_token
|
17
|
+
headers["Host"] = host
|
18
|
+
headers["Date"] = Time.now.utc.strftime('%a, %d %b %Y %H:%M:%S GMT')
|
19
|
+
headers["Content-Type"] = options[:content_type] ? options[:content_type] : 'application/x-www-form-urlencoded'
|
20
|
+
headers["Content-Length"] = (options[:data] ? options[:data].size : 0).to_s
|
21
|
+
headers["x-goog-api-version"] = @api_version
|
22
|
+
headers["x-goog-project-id"] = @project_id if options[:send_goog_project_id]
|
23
|
+
headers["Authorization"] = 'OAuth ' + self.refresh_access_token(@refresh_token)["access_token"]
|
24
|
+
param_string = params.empty? ? '' : '?' + params_to_data_string(params)
|
25
|
+
headers["Range"] = options[:range] if options[:range]
|
26
|
+
headers["If-Match"] = options[:filename] if options[:filename]
|
27
|
+
headers["If-Modified-Since"] = options[:if_modified_since] if options[:if_modified_since]
|
28
|
+
headers["If-None-Match"] = options[:if_none_match] if options[:if_none_match]
|
29
|
+
headers["If-Unmodified-Since"]= options[:if_modified_since] if options[:if_modified_since]
|
30
|
+
headers["Content-MD5"] = options[:md5] if options[:md5]
|
31
|
+
headers["x-goog-acl"] = options[:x_goog_acl] if options[:x_goog_acl]
|
32
|
+
|
33
|
+
_http_request(host, path, method, headers, param_string, options[:data])
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
def _post_http_request(host, path, params, headers, data=nil)
|
38
|
+
http = Net::HTTP.new(host, 443)
|
39
|
+
http.use_ssl = true
|
40
|
+
http.set_debug_output $stderr if @debug
|
41
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
42
|
+
|
43
|
+
data ||= params_to_data_string(params)
|
44
|
+
resp = http.post(path, data, headers)
|
45
|
+
|
46
|
+
return resp.body
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
def _http_request(host, path, method, headers, param_string, data=nil)
|
51
|
+
http = Net::HTTP.new(host, 443)
|
52
|
+
http.use_ssl = true
|
53
|
+
http.set_debug_output $stderr if @debug
|
54
|
+
http.read_timeout = @timeout ? @timeout : 15
|
55
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
56
|
+
|
57
|
+
req = method.new(path + param_string)
|
58
|
+
headers.each do |key, value|
|
59
|
+
req[key.to_s] = value
|
60
|
+
end
|
61
|
+
|
62
|
+
response = http.start { http.request(req, data) }
|
63
|
+
return response
|
64
|
+
rescue Timeout::Error
|
65
|
+
$stderr.puts "Timeout accessing #{path}: #{$!}"
|
66
|
+
nil
|
67
|
+
rescue
|
68
|
+
$stderr.puts "Error accessing #{path}: #{$!}"
|
69
|
+
nil
|
70
|
+
end
|
71
|
+
|
72
|
+
def params_to_data_string(params)
|
73
|
+
return "" if params.empty?
|
74
|
+
esc_params = params.collect do |p|
|
75
|
+
encoded = (CGI::escape(p[0].to_s) + "=" + CGI::escape(p[1].to_s))
|
76
|
+
encoded.gsub('+', '%20')
|
77
|
+
end
|
78
|
+
"#{esc_params.join('&')}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module GoogleStorage
|
2
|
+
class Client
|
3
|
+
|
4
|
+
def authorization_url(scope)
|
5
|
+
scope_url = case scope
|
6
|
+
when :read_only
|
7
|
+
'https://www.googleapis.com/auth/devstorage.read_only'
|
8
|
+
when :read_write
|
9
|
+
'https://www.googleapis.com/auth/devstorage.read_write'
|
10
|
+
when :full_control
|
11
|
+
'https://www.googleapis.com/auth/devstorage.full_control'
|
12
|
+
else
|
13
|
+
'https://www.google.com/m8/feeds/'
|
14
|
+
end
|
15
|
+
"https://accounts.google.com/o/oauth2/auth?client_id=#{@client_id}&redirect_uri=#{@redirect_uri}&scope=#{scope_url}&response_type=code"
|
16
|
+
end
|
17
|
+
|
18
|
+
def acquire_refresh_token(token, options={})
|
19
|
+
options['grant_type'] = 'authorization_code'
|
20
|
+
response = post_request('accounts.google.com', '/o/oauth2/token', token, options)
|
21
|
+
return response["refresh_token"] if response["refresh_token"]
|
22
|
+
"Failed to acquire a refresh token. Something went wrong. Try getting a new Auth code."
|
23
|
+
end
|
24
|
+
|
25
|
+
def refresh_access_token(token, options={})
|
26
|
+
options['grant_type'] = 'refresh_token'
|
27
|
+
post_request('accounts.google.com', '/o/oauth2/token', token, options)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
metadata
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: google_storage
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.3
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Lucas Hills
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-06-24 00:00:00.000000000 +10:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: crack
|
17
|
+
requirement: &9643716 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ~>
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 0.1.1
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: *9643716
|
26
|
+
description: A Ruby client library for using the new Google Storage API v2 using OAuth2.0
|
27
|
+
email:
|
28
|
+
- lucas@lucashills.com
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files:
|
32
|
+
- README.textile
|
33
|
+
files:
|
34
|
+
- .gitignore
|
35
|
+
- Gemfile
|
36
|
+
- README.textile
|
37
|
+
- Rakefile
|
38
|
+
- examples/examples.rb
|
39
|
+
- google_storage.gemspec
|
40
|
+
- lib/generators/google_storage/install/install_generator.rb
|
41
|
+
- lib/generators/templates/google_storage.rake
|
42
|
+
- lib/generators/templates/google_storage.yml
|
43
|
+
- lib/google_storage.rb
|
44
|
+
- lib/google_storage/bucket.rb
|
45
|
+
- lib/google_storage/client.rb
|
46
|
+
- lib/google_storage/object.rb
|
47
|
+
- lib/google_storage/request.rb
|
48
|
+
- lib/google_storage/token.rb
|
49
|
+
- lib/google_storage/version.rb
|
50
|
+
has_rdoc: true
|
51
|
+
homepage: https://github.com/2potatocakes/google_storage
|
52
|
+
licenses: []
|
53
|
+
post_install_message:
|
54
|
+
rdoc_options: []
|
55
|
+
require_paths:
|
56
|
+
- lib
|
57
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ! '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
65
|
+
requirements:
|
66
|
+
- - ! '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
requirements: []
|
70
|
+
rubyforge_project:
|
71
|
+
rubygems_version: 1.6.2
|
72
|
+
signing_key:
|
73
|
+
specification_version: 3
|
74
|
+
summary: Google Storage for Developers is a RESTful service for storing and accessing
|
75
|
+
your data on Google's infrastructure
|
76
|
+
test_files: []
|