google_storage 0.0.3
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 +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: []
|