paperclip-gcs 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1f1a924ccfce1e00e1528f8dd388dc410f4b9810
4
+ data.tar.gz: 1df9b5cc7b0ca186322ef4a3f02564bcf00ffa11
5
+ SHA512:
6
+ metadata.gz: 91ef9146b4d9c3531f8b19c0ff54a226a7ab3c6fd10cb6ca0735f8e13b5a5b31fafad1b94962dc2c2a46e9ca9b60b21b410a1063abdd6079bd15c1b69b2c3656
7
+ data.tar.gz: 42fd00c5d874ac04e83919f1b6cbb5c0751dc2b3df6936dd8f3dd4ca54918039503281ca3970c625b82cad98bc144d5bfffeb26b4735ed47c4eb17dd537a0aef
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.1
5
+ before_install: gem install bundler -v 1.15.0
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in paperclip-gcs.gemspec
4
+ gemspec
@@ -0,0 +1,217 @@
1
+ # paperclip-gcs
2
+
3
+ paperclip-gcs is a Paperclip storage driver for storing files in a Google Cloud Storage.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'paperclip-gcs'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ ``` shell
16
+ $ bundle
17
+ ```
18
+
19
+ Or install it yourself as:
20
+
21
+ ``` shell
22
+ $ gem install paperclip-gcs
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ The GCS storage engine has been developed to work as similarly to S3 storage configuration as is possible. This gem can be configured in a Paperclip initializer as follows:
28
+
29
+ ``` ruby
30
+ Paperclip::Attachment.default_options[:storage] = :gcs
31
+ Paperclip::Attachment.default_options[:gcs_bucket] = "your-bucket"
32
+ Paperclip::Attachment.default_options[:url] = ":gcs_path_url"
33
+ Paperclip::Attachment.default_options[:path] = ":class/:attachment/:id/:style/:filename"
34
+ Paperclip::Attachment.default_options[:gcs_credentials] = {
35
+ project: ENV["GCS_PROJECT"],
36
+ keyfile: ENV["GCS_KEYFILE"],
37
+ }
38
+
39
+ ```
40
+
41
+ Or, at the level of the model such as in the following example:
42
+
43
+ ``` ruby
44
+ has_attached_file :avatar,
45
+ storage: :gcs,
46
+ gcs_bucket: "your-bucket",
47
+ gcs_credentials: {
48
+ project: "your-project",
49
+ keyfile: "path/to/your/keyfile",
50
+ }
51
+ ```
52
+
53
+ See also http://www.rubydoc.info/gems/paperclip/Paperclip/Storage/S3
54
+
55
+ ## Configuration
56
+
57
+ ### gcs_bucket
58
+
59
+ GCS bucket name.
60
+
61
+ ### gcs_credentials
62
+
63
+ You can provide the project and credential information to connect to the Storage service, or if you are running on Google Compute Engine this configuration is taken care of for you.
64
+
65
+ #### project
66
+
67
+ Project identifier for GCS. Project are discovered in the following order:
68
+
69
+ * Specify project in `project`
70
+ * Discover project in environment variables `STORAGE_PROJECT`, `GOOGLE_CLOUD_PROJECT`, `GCLOUD_PROJECT`
71
+ * Discover GCE credentials
72
+
73
+ #### keyfile
74
+
75
+ Path of GCS service account credentials JSON file. Credentials are discovered in the following order:
76
+
77
+ * Specify credentials path in `keyfile`
78
+ * Discover credentials path in environment variables `GOOGLE_CLOUD_KEYFILE`, `GCLOUD_KEYFILE`
79
+ * Discover credentials JSON in environment variables `GOOGLE_CLOUD_KEYFILE_JSON`, `GCLOUD_KEYFILE_JSON`
80
+ * Discover credentials file in the Cloud SDK's path
81
+ * Discover GCE credentials
82
+
83
+ #### bucket (optional)
84
+
85
+ Here you can specify the GCS bucket name. If `gcs_bucket` also has a bucket specification, the value of` gcs_bucket` will be used.
86
+
87
+ ### gcs_options
88
+
89
+ #### retries
90
+
91
+ Number of times to retry requests on server error.
92
+
93
+ #### timeout
94
+
95
+ Default timeout to use in requests.
96
+
97
+ ### gcs_protocol
98
+
99
+ The protocol for the URLs generated to your GCS assets. Can be either 'http', 'https', or an empty string to generate protocol-relative URLs. Defaults to empty string.
100
+
101
+ ### gcs_host_alias
102
+
103
+ The fully-qualified domain name (FQDN) that is the alias to the GCS domain of your bucket. Used with the :gcs_alias_url url interpolation. See the link in the url entry for more information about GCS domains and buckets.
104
+
105
+ ### gcs_host_name
106
+
107
+ If you are using a bucket in a custom domain, write host_name.
108
+
109
+ ### gcs_encryption_key
110
+
111
+ You can also choose to provide your own AES-256 key for server-side encryption. See also [Customer-supplied encryption keys](https://cloud.google.com/storage/docs/encryption#customer-supplied).
112
+
113
+ ### gcs_metadata
114
+
115
+ User provided web-safe keys and arbitrary string values that will returned with requests for the file as "x-goog-meta-" response headers.
116
+
117
+ You can set metadata on a per style bases by doing the following:
118
+
119
+ ``` ruby
120
+ gcs_metadata: {
121
+ thumb: { "foo" => "bar" }
122
+ }
123
+ ```
124
+
125
+ Or globally:
126
+
127
+ ``` ruby
128
+ gcs_metadata: { "foo" => "bar" }
129
+ ```
130
+
131
+ ### gcs_permissions
132
+
133
+ Permission for the object in GCS. Acceptable values are:
134
+
135
+ * `auth_read` - File owner gets OWNER access, and allAuthenticatedUsers get READER access.
136
+ * `owner_full` - File owner gets OWNER access, and project team owners get OWNER access.
137
+ * `owner_read` - File owner gets OWNER access, and project team owners get READER access.
138
+ * `private` - File owner gets OWNER access.
139
+ * `project_private` - File owner gets OWNER access, and project team members get access according to their roles.
140
+ * `public_read` - File owner gets OWNER access, and allUsers get READER access.
141
+
142
+ Default is nil (bucket default object ACL). See also [official document](https://cloud.google.com/storage/docs/access-control/lists).
143
+
144
+ You can set permissions on a per style bases by doing the following:
145
+
146
+ ``` ruby
147
+ gcs_permissions: {
148
+ thumb: :public_read
149
+ }
150
+ ```
151
+
152
+ Or globally:
153
+
154
+ ``` ruby
155
+ gcs_permissions: :public_read
156
+ ```
157
+
158
+ ### gcs_storage_class
159
+
160
+ Storage class of the file. Acceptable values are:
161
+
162
+ * `dra` - Durable Reduced Availability
163
+ * `nearline` - Nearline Storage
164
+ * `coldline` - Coldline Storage
165
+ * `multi_regional` - Multi-Regional Storage
166
+ * `regional` - Regional Storage
167
+ * `standard` - Standard Storage
168
+
169
+ You can set storage class on a per style bases by doing the following:
170
+
171
+ ``` ruby
172
+ gcs_storage_class: {
173
+ thumb: :multi_regional
174
+ }
175
+ ```
176
+
177
+ Or globally:
178
+
179
+ ``` ruby
180
+ gcs_storage_class: :multi_regional
181
+ ```
182
+
183
+ ### Interpolates
184
+
185
+ #### :gcs_alias_url
186
+
187
+ ``` ruby
188
+ "#{attachment.gcs_protocol}//#{attachment.gcs_host_alias}/#{attachment.path(style)}"
189
+ ```
190
+
191
+ #### :gcs_path_url
192
+
193
+ ``` ruby
194
+ "#{attachment.gcs_protocol}//#{attachment.gcs_host_name}/#{attachment.gcs_bucket_name}/#{attachment.path(style)}"
195
+ ```
196
+
197
+ #### :gcs_domain_url
198
+
199
+ ``` ruby
200
+ "#{attachment.gcs_protocol}//#{attachment.gcs_bucket_name}.#{attachment.gcs_host_name}/#{attachment.path(style)}"
201
+ ```
202
+
203
+ #### :asset_host
204
+
205
+ ``` ruby
206
+ "#{attachment.path(style)}"
207
+ ```
208
+
209
+ ## Development
210
+
211
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
212
+
213
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
214
+
215
+ ## Contributing
216
+
217
+ Bug reports and pull requests are welcome on GitHub at https://github.com/daichirata/paperclip-gcs.
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/*_test.rb"]
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "paperclip/gcs"
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(__FILE__)
@@ -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 @@
1
+ require "paperclip/storage/gcs"
@@ -0,0 +1,5 @@
1
+ module Paperclip
2
+ module Gcs
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,164 @@
1
+ require "google-cloud-storage"
2
+
3
+ require "paperclip/gcs/version"
4
+ require "paperclip/storage/gcs/bucket_repository"
5
+ require "paperclip/storage/gcs/client_repository"
6
+ require "paperclip/storage/gcs/credentials_resolver"
7
+
8
+ module Paperclip
9
+ module Storage
10
+ module Gcs
11
+ DEFAULT_GCS_HOST_NAME = "storage.googleapis.com"
12
+
13
+ def self.extended(base)
14
+ base.instance_eval do
15
+ @gcs_options = @options[:gcs_options] || {}
16
+ @gcs_credentials = @options[:gcs_credentials]
17
+ @gcs_bucket_name = @options[:gcs_bucket]
18
+ @gcs_protocol = @options[:gcs_protocol]
19
+ @gcs_host_alias = @options[:gcs_host_alias]
20
+ @gcs_host_name = @options[:gcs_host_name]
21
+ @gcs_encryption_key = @options[:gcs_encryption_key]
22
+ @gcs_metadata = normalize_style(@options[:gcs_metadata])
23
+ @gcs_permissions = normalize_style(@options[:gcs_permissions])
24
+ @gcs_storage_class = normalize_style(@options[:gcs_storage_class])
25
+
26
+ unless @options[:url].to_s.match(%r{\A:gcs_(alias|path|domain)_url\z}) || @options[:url] == ":asset_host".freeze
27
+ @options[:path] = path_option.gsub(/:url/, @options[:url]).sub(%r{\A:rails_root/public/system}, "".freeze)
28
+ @options[:url] = ":gcs_path_url".freeze
29
+ end
30
+ end
31
+
32
+ Paperclip.interpolates(:gcs_alias_url) do |attachment, style|
33
+ "#{attachment.gcs_protocol}//#{attachment.gcs_host_alias}/#{attachment.path(style)}"
34
+ end unless Paperclip::Interpolations.respond_to?(:gcs_alias_url)
35
+
36
+ Paperclip.interpolates(:gcs_path_url) do |attachment, style|
37
+ "#{attachment.gcs_protocol}//#{attachment.gcs_host_name}/#{attachment.gcs_bucket_name}/#{attachment.path(style)}"
38
+ end unless Paperclip::Interpolations.respond_to?(:gcs_path_url)
39
+
40
+ Paperclip.interpolates(:gcs_domain_url) do |attachment, style|
41
+ "#{attachment.gcs_protocol}//#{attachment.gcs_bucket_name}.#{attachment.gcs_host_name}/#{attachment.path(style)}"
42
+ end unless Paperclip::Interpolations.respond_to?(:gcs_domain_url)
43
+
44
+ Paperclip.interpolates(:asset_host) do |attachment, style|
45
+ "#{attachment.path(style)}"
46
+ end unless Paperclip::Interpolations.respond_to?(:asset_host)
47
+ end
48
+
49
+ def expiring_url(time = 3600, style = default_style)
50
+ if file_path = path(style)
51
+ gcs_bucket.signed_url(file_path, expires: time)
52
+ else
53
+ url(style)
54
+ end
55
+ end
56
+
57
+ def exists?(style = default_style)
58
+ if original_filename
59
+ gcs_bucket.find_file(path(style))
60
+ else
61
+ false
62
+ end
63
+ rescue Google::Cloud::Error
64
+ false
65
+ end
66
+
67
+ def flush_writes
68
+ @queued_for_write.each do |style, file|
69
+ log("saving #{path(style)}")
70
+
71
+ opts = {
72
+ content_type: file.content_type,
73
+ encryption_key: gcs_encryption_key,
74
+ acl: gcs_permissions(style),
75
+ storage_class: gcs_storage_class(style),
76
+ metadata: gcs_metadata(style),
77
+ }
78
+ gcs_bucket.upload_file(file.path, path(style), opts)
79
+ end
80
+ after_flush_writes
81
+ @queued_for_write = {}
82
+ end
83
+
84
+ def flush_deletes
85
+ @queued_for_delete.each do |path|
86
+ begin
87
+ log("deleting #{path}")
88
+
89
+ gcs_bucket.file(path).delete
90
+ rescue Google::Cloud::Error
91
+ # Ignore this.
92
+ end
93
+ end
94
+ @queued_for_delete = []
95
+ end
96
+
97
+ def copy_to_local_file(style, local_dest_path)
98
+ log("copying #{path(style)} to local file #{local_dest_path}")
99
+
100
+ gcs_bucket.file(path(style)).download(local_dest_path, encryption_key: gcs_encryption_key)
101
+ rescue Google::Cloud::Error => e
102
+ warn("#{e} - cannot copy #{path(style)} to local file #{local_dest_path}")
103
+ false
104
+ end
105
+
106
+ def gcs_protocol
107
+ unwrap_proc(@gcs_protocol, self)
108
+ end
109
+
110
+ def gcs_host_alias
111
+ unwrap_proc(@gcs_host_alias, self)
112
+ end
113
+
114
+ def gcs_host_name
115
+ unwrap_proc(@gcs_host_name, self) || DEFAULT_GCS_HOST_NAME
116
+ end
117
+
118
+ def gcs_bucket_name
119
+ (unwrap_proc(@gcs_bucket_name, self) || gcs_credentials[:bucket]) or
120
+ raise ArgumentError, "missing required :gcs_bucket option"
121
+ end
122
+
123
+ private
124
+
125
+ def normalize_style(opts)
126
+ opts = { default: opts } unless opts.respond_to?(:merge)
127
+ opts.merge(default: opts[:default])
128
+ end
129
+
130
+ def unwrap_proc(obj, *args)
131
+ obj.respond_to?(:call) ? obj.call(*args) : obj
132
+ end
133
+
134
+ def gcs_encryption_key
135
+ unwrap_proc(@gcs_encryption_key, self)
136
+ end
137
+
138
+ def gcs_permissions(style = default_style)
139
+ unwrap_proc(@gcs_permissions[style] || @gcs_permissions[:default], self, style)
140
+ end
141
+
142
+ def gcs_storage_class(style = default_style)
143
+ unwrap_proc(@gcs_storage_class[style] || @gcs_storage_class[:default], self, style)
144
+ end
145
+
146
+ def gcs_metadata(style = default_style)
147
+ unwrap_proc(@gcs_metadata[style] || @gcs_metadata[:default], self, style)
148
+ end
149
+
150
+ def gcs_credentials
151
+ @_gcs_credentials ||= CredentialsResolver.resolve(unwrap_proc(@gcs_credentials, self))
152
+ end
153
+
154
+ def gcs_client
155
+ @_gcs_client ||= ClientRepository.find(gcs_credentials.slice(:project, :keyfile).merge(
156
+ @gcs_options.slice(:scope, :retries, :timeout)))
157
+ end
158
+
159
+ def gcs_bucket
160
+ @_gcs_bucket ||= BucketRepository.find(gcs_client, gcs_bucket_name)
161
+ end
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,25 @@
1
+ module Paperclip
2
+ module Storage
3
+ module Gcs
4
+ class BucketRepository
5
+ include Singleton
6
+
7
+ CACHE_KEY = self.class.name.freeze
8
+
9
+ def self.find(client, bucket_name)
10
+ instance.find(client, bucket_name)
11
+ end
12
+
13
+ def find(client, bucket_name)
14
+ buckets[[client, bucket_name]] ||= client.bucket(bucket_name)
15
+ end
16
+
17
+ private
18
+
19
+ def buckets
20
+ Thread.current[CACHE_KEY] ||= {}
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,29 @@
1
+ module Paperclip
2
+ module Storage
3
+ module Gcs
4
+ class ClientRepository
5
+ include Singleton
6
+
7
+ CACHE_KEY = self.class.name.freeze
8
+
9
+ def self.find(config)
10
+ instance.find(config)
11
+ end
12
+
13
+ def find(config)
14
+ clients[config] ||= Google::Cloud.storage(
15
+ config[:project],
16
+ config[:keyfile],
17
+ config.slice(:scope, :retries, :timeout)
18
+ )
19
+ end
20
+
21
+ private
22
+
23
+ def clients
24
+ Thread.current[CACHE_KEY] ||= {}
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ module Paperclip
2
+ module Storage
3
+ module Gcs
4
+ module CredentialsResolver
5
+ module_function
6
+
7
+ def resolve(credentials)
8
+ cred = case credentials
9
+ when File
10
+ YAML.load(ERB.new(File.read(credentials.path)).result)
11
+ when String, Pathname
12
+ YAML.load(ERB.new(File.read(credentials)).result)
13
+ when Hash
14
+ credentials
15
+ when NilClass
16
+ {}
17
+ else
18
+ raise ArgumentError, ":gcs_credentials is not a path, file, nor a hash"
19
+ end
20
+ (cred.stringify_keys[env] || cred).symbolize_keys
21
+ end
22
+
23
+ def env
24
+ (defined?(Rails) ? Rails.env : nil).to_s
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "paperclip/gcs/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "paperclip-gcs"
8
+ spec.version = Paperclip::Gcs::VERSION
9
+ spec.authors = ["Daichi HIRATA"]
10
+ spec.email = ["daichirata@gmail.com"]
11
+ spec.summary = "Extends Paperclip with Google Cloud Storage"
12
+ spec.description = "Extends Paperclip with Google Cloud Storage"
13
+ spec.homepage = "https://github.com/daichirata/paperclip-gcs"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
16
+ f.match(%r{^(test|spec|features)/})
17
+ end
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_runtime_dependency "paperclip", ">= 4.0"
23
+ spec.add_runtime_dependency "google-cloud-storage", "~> 1.0"
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.15"
26
+ spec.add_development_dependency "rake", "~> 10.0"
27
+ spec.add_development_dependency "minitest", "~> 5.0"
28
+ end
metadata ADDED
@@ -0,0 +1,127 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: paperclip-gcs
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Daichi HIRATA
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-06-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: paperclip
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '4.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '4.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: google-cloud-storage
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.15'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.15'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: minitest
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '5.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '5.0'
83
+ description: Extends Paperclip with Google Cloud Storage
84
+ email:
85
+ - daichirata@gmail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - ".travis.yml"
92
+ - Gemfile
93
+ - README.md
94
+ - Rakefile
95
+ - bin/console
96
+ - bin/setup
97
+ - lib/paperclip-gcs.rb
98
+ - lib/paperclip/gcs/version.rb
99
+ - lib/paperclip/storage/gcs.rb
100
+ - lib/paperclip/storage/gcs/bucket_repository.rb
101
+ - lib/paperclip/storage/gcs/client_repository.rb
102
+ - lib/paperclip/storage/gcs/credentials_resolver.rb
103
+ - paperclip-gcs.gemspec
104
+ homepage: https://github.com/daichirata/paperclip-gcs
105
+ licenses: []
106
+ metadata: {}
107
+ post_install_message:
108
+ rdoc_options: []
109
+ require_paths:
110
+ - lib
111
+ required_ruby_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ required_rubygems_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ requirements: []
122
+ rubyforge_project:
123
+ rubygems_version: 2.5.1
124
+ signing_key:
125
+ specification_version: 4
126
+ summary: Extends Paperclip with Google Cloud Storage
127
+ test_files: []