ar-s3-columns 0.0.1

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: 9cbaf52e9ac8fdea731bbf550b27185955806509
4
+ data.tar.gz: f263b3acc8c0a3a8b6eeef8f6fe653abb8295a83
5
+ SHA512:
6
+ metadata.gz: fea92bf6329496d3ed002419fd006702c58287feeb2d4cb0a5a68813034d75b731caa5e6728ec88d568ac2fbdc76c7e91e71bf3ac19594a47871b7c59a103bf0
7
+ data.tar.gz: 6e4aba64fdfe104f92a9ac9898e988a91b6f67aa1f5b055c278a280addcdaceb4ff03d733509bf71da301a9048a89b9d7618e7297335e8ee4ad98369d774db05
data/.gitignore ADDED
@@ -0,0 +1,26 @@
1
+ *.rbc
2
+ capybara-*.html
3
+ .rspec
4
+ /log
5
+ /tmp
6
+ /db/*.sqlite3
7
+ /public/system
8
+ /coverage/
9
+ /spec/tmp
10
+ **.orig
11
+ rerun.txt
12
+ pickle-email-*.html
13
+ config/initializers/secret_token.rb
14
+ config/secrets.yml
15
+ .DS_Store
16
+ Gemfile.lock
17
+
18
+ ## Environment normalisation:
19
+ /.bundle
20
+ /vendor/bundle
21
+
22
+ # these should all be checked in to normalise the environment:
23
+ # Gemfile.lock, .ruby-version, .ruby-gemset
24
+
25
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
26
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 alexchee
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,64 @@
1
+ ar-s3-columns
2
+ =============
3
+
4
+ Adds an column that is backed by S3 for Rails models.
5
+
6
+ To Use:
7
+ Configure AWS-SDK with your AWS creds and set a default bucket to use for S3Columns, probably in an initializer.
8
+ ```
9
+ AWS.config({
10
+ :access_key_id => AWS_ACCESS_KEY,
11
+ :secret_access_key => AWS_SECRET_ACCESS_KEY,
12
+ })
13
+
14
+ S3Columns.default_aws_bucket = "my-bucket"
15
+ ```
16
+
17
+ To configure write options for AWS SDK, set default_s3_write_options:
18
+ ```
19
+ S3Columns.default_s3_write_options = {acl: :authenticated_read }
20
+ ```
21
+ This will set an ACL to each uploaded data as Authenticated Reads.
22
+
23
+
24
+ Include S3Columns and declare columns that will be pushed to S3
25
+ ```
26
+ class User < ActiveRecord::Base
27
+ include S3Columns
28
+
29
+ s3_column_for :extra_data
30
+ ...
31
+ ```
32
+ This will add attribute accessors prefixed with "s3_column_#{column name}"
33
+ ```
34
+ user = User.new
35
+ # Uploads to S3
36
+ user.s3_column_extra_data = {something: 'here'}
37
+
38
+ # reads from S3
39
+ user.s3_column_extra_data
40
+ ```
41
+
42
+ Additional options:
43
+ * s3_key: S3 key for column value
44
+ * s3_bucket: S3 bucket to write to
45
+
46
+ ```
47
+ s3_column_for :metadata, s3_bucket: "other_bucket", s3_key: lambda{|m| "thing/metadata/#{m.name}"}
48
+ ```
49
+
50
+ Before Create hook:
51
+ If you want to automatically upload the columns to S3 on create:
52
+ ```
53
+ class User < ActiveRecord::Base
54
+ include S3Columns
55
+ s3_column_upload_on_create
56
+
57
+ s3_column_for :extra_data
58
+ ...
59
+ ```
60
+ This will create a before_create hook to upload to s3 if the attribute is set in the create:
61
+ ```
62
+ # This will automatically upload extra_data to S3
63
+ User.create(name: 'aww_yeah', extra_data: extra_value)
64
+ ```
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'ar_s3_columns/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "ar-s3-columns"
8
+ spec.version = S3Columns::VERSION
9
+ spec.authors = ["Alex Chee"]
10
+ spec.email = ["alexchee11@gmail.com"]
11
+ spec.summary = %q{ActiveRecord::Model extension to add an column that on AWS S3.}
12
+ spec.description = %q{ActiveRecord::Model extension to write/read an column on AWS S3.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.5"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "minitest"
24
+ spec.add_development_dependency "mocha"
25
+
26
+ spec.add_dependency 'activesupport', '~> 4.0'
27
+ spec.add_dependency 'activemodel', '~> 4.0'
28
+ spec.add_dependency 'rails', '~> 4.0'
29
+ spec.add_dependency 'aws-sdk', '>= 1.39.0'
30
+ spec.add_dependency 'retryable', '>= 1.3.5'
31
+ spec.add_dependency 'simple_uuid', '>= 0.4.0'
32
+ end
@@ -0,0 +1,61 @@
1
+ module S3Columns
2
+ module ClassMethods
3
+
4
+ # Adds before_create to upload each S3 column to S3, to prevent it writing the values to DB.
5
+ # Warning: The default s3_key uses the object's ID which will be nil at this point, should set a custom :s3_key
6
+ def s3_column_upload_on_create
7
+ self.class_eval %Q{
8
+ before_create :s3_column_upload_on_create
9
+ }
10
+ end
11
+
12
+ # Creates reader/writer for given column that will upload the marshalled value to S3
13
+ # Options:
14
+ # * s3_key: S3 key to use, should be a Proc.
15
+ # * s3_bucket: S3 bucket to use
16
+ def s3_column_for(column_name, options={})
17
+ raise "Please set a S3Columns.default_aws_bucket" if S3Columns.default_aws_bucket.blank?
18
+ options[:s3_key] ||= Proc.new{|object| "#{column_name}/#{SimpleUUID::UUID.new.to_guid}"}
19
+ options[:s3_bucket] ||= S3Columns.default_aws_bucket
20
+ self.s3_columns ||= []
21
+ self.s3_columns_keys ||= {}
22
+ self.s3_columns << column_name unless self.s3_columns.include?(column_name)
23
+ self.s3_columns_keys[column_name.to_sym] = options[:s3_key]
24
+ self.s3_columns_buckets ||= {}
25
+ self.s3_columns_buckets[column_name.to_sym] = options[:s3_bucket]
26
+
27
+ # Creates a reader
28
+ self.class_eval %Q{
29
+ def s3_column_#{column_name}
30
+ if self.persisted? && key_path = read_attribute(:#{column_name})
31
+ key = S3Columns.s3_connection.buckets["#{options[:s3_bucket]}"].objects[key_path]
32
+
33
+ if key.exists?
34
+ @#{column_name} ||= Marshal.load(key.read)
35
+ else
36
+ # Retry in case it is not persisted in S3 yet
37
+ Retryable.retryable :on => [AWS::S3::Errors::NoSuchKey], :tries => 10, :sleep => 1 do
38
+ @#{column_name} ||= Marshal.load(key.read)
39
+ end
40
+ end
41
+
42
+ else
43
+ nil
44
+ end
45
+ end
46
+ }
47
+
48
+ # Creates writer
49
+ self.class_eval %Q{
50
+ def s3_column_#{column_name}=(value)
51
+ marshalled_value = Marshal.dump(value)
52
+ key_path = self.class.s3_columns_keys[:#{column_name}].call(self)
53
+ write_options = S3Columns.default_s3_write_options || {}
54
+ S3Columns.s3_connection.buckets["#{options[:s3_bucket]}"].objects[key_path].write(marshalled_value, write_options)
55
+ # update column with new key
56
+ write_attribute(:#{column_name}, key_path)
57
+ end
58
+ }
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,17 @@
1
+ module S3Columns
2
+ module InstanceMethods
3
+ def s3_column_upload_on_create
4
+ self.class.s3_columns.each do |column_name|
5
+ column_value = self.read_attribute(column_name.to_sym)
6
+ next if column_value.blank?
7
+ marshalled_data = Marshal.dump(column_value)
8
+ key_path = self.class.s3_columns_keys[column_name.to_sym].call(self)
9
+ bucket_name = self.class.s3_columns_buckets[column_name.to_sym]
10
+ write_options = S3Columns.default_s3_write_options || {}
11
+ S3Columns.s3_connection.buckets[bucket_name].objects[key_path].write(marshalled_data, write_options)
12
+ # saves key to column
13
+ write_attribute(column_name.to_sym, key_path)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ module S3Columns
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,25 @@
1
+ require 'aws-sdk'
2
+ require 'retryable'
3
+ require 'simple_uuid'
4
+ require 'ar_s3_columns/class_methods'
5
+ require 'ar_s3_columns/instance_methods'
6
+
7
+ module S3Columns
8
+ class <<self
9
+ attr_accessor :default_aws_bucket, :default_s3_write_options
10
+
11
+ def s3_connection
12
+ Thread.current[:aws_s3_connection] ||= AWS::S3.new
13
+ end
14
+ end
15
+
16
+ extend ActiveSupport::Concern
17
+ included do
18
+ class_attribute :s3_columns, :s3_columns_keys, :s3_columns_buckets, :s3_write_options
19
+ s3_write_options ||= {}
20
+
21
+ extend S3Columns::ClassMethods
22
+ include S3Columns::InstanceMethods
23
+
24
+ end
25
+ end
@@ -0,0 +1,199 @@
1
+ require 'test_helper'
2
+ require "minitest/autorun"
3
+ require 'mocha/mini_test'
4
+
5
+ describe "S3Columns" do
6
+ before do
7
+ S3Columns.default_aws_bucket = "test"
8
+ TestMigration.up
9
+ ClassWithS3Columns.delete_all
10
+ # Stubs S3 stuff
11
+ @key_stub = stub(write: nil, read: Marshal.dump(nil))
12
+ @objects_stub = stub(:[] => @key_stub)
13
+ @buckets_stub = stub(:[] => @objects_stub)
14
+ @s3_stub = stub( buckets: (stubs(:[]).returns(stub( objects: stub(read: nil, write:nil) ))) )
15
+ S3Columns.stubs(:s3_connection).returns(@s3_stub)
16
+ end
17
+
18
+ after do
19
+ TestMigration.down
20
+ end
21
+
22
+ describe "configuration" do
23
+ it "raises exception if S3Columns.default_aws_bucket is not set" do
24
+ assert_raises(RuntimeError, "Please set a S3Columns.default_aws_bucket") do
25
+ S3Columns.default_aws_bucket = nil
26
+ ClassWithS3Columns.s3_column_for :extra_data
27
+ end
28
+ end
29
+
30
+ it "sets the default options for AWS SDK writes" do
31
+ S3Columns.default_s3_write_options = {acl: :authenticated_read }
32
+ test_object = ClassWithS3Columns.create(name: "unique")
33
+ value = {something: 'else', goes: 'here'}
34
+
35
+ @key_stub.expects(:write).with(Marshal.dump(value), {acl: :authenticated_read})
36
+ @objects_stub.expects(:[]).returns(@key_stub)
37
+ @buckets_stub.expects(:[]).returns(stub(objects: @objects_stub))
38
+ @s3_stub.expects(:buckets).returns(@buckets_stub)
39
+ test_object.s3_column_extra_data = value
40
+ S3Columns.default_s3_write_options = nil
41
+ end
42
+ end
43
+
44
+ it "reads from db for non S3 column" do
45
+ test_object = ClassWithS3Columns.create(name: 'hey')
46
+ assert_equal "hey", test_object.name
47
+ end
48
+
49
+ it "uploads marshalled value of columns to s3 and save s3 key to db" do
50
+ SimpleUUID::UUID.any_instance.stubs(:to_guid).returns('unique')
51
+
52
+ test_object = ClassWithS3Columns.create(name: "unique")
53
+ value = {something: 'else', goes: 'here'}
54
+
55
+ @key_stub.expects(:write).with(Marshal.dump(value), {})
56
+ @objects_stub.expects(:[]).with("extra_data/unique").returns(@key_stub)
57
+ @buckets_stub.expects(:[]).with("test").returns(stub(objects: @objects_stub))
58
+ @s3_stub.expects(:buckets).returns(@buckets_stub)
59
+ test_object.s3_column_extra_data = value
60
+ assert_equal "extra_data/unique", test_object.read_attribute(:extra_data)
61
+ end
62
+
63
+ it "reads from S3 with key in db and if it exists" do
64
+ test_object = ClassWithS3Columns.create(name: "unique")
65
+ test_object.send(:write_attribute, :extra_data, 'some/key')
66
+
67
+ @key_stub.expects(:exists?).returns(true)
68
+ @key_stub.expects(:read).returns(Marshal.dump("some data"))
69
+ @objects_stub.expects(:[]).with("some/key").returns(@key_stub)
70
+ @buckets_stub.expects(:[]).with("test").returns(stub(objects: @objects_stub))
71
+ @s3_stub.expects(:buckets).returns(@buckets_stub)
72
+ assert_equal test_object.s3_column_extra_data, "some data"
73
+ end
74
+
75
+ it "retries S3 read, when S3 key in db, but does not exists (usually because S3 has not persisted object yet)" do
76
+ test_object = ClassWithS3Columns.create(name: "unique")
77
+ test_object.send(:write_attribute, :extra_data, 'some/key')
78
+
79
+ Retryable.expects(:retryable)
80
+ @key_stub.expects(:exists?).returns(false)
81
+ @objects_stub.expects(:[]).with("some/key").returns(@key_stub)
82
+ @buckets_stub.expects(:[]).with("test").returns(stub(objects: @objects_stub))
83
+ @s3_stub.expects(:buckets).returns(@buckets_stub)
84
+ test_object.s3_column_extra_data
85
+ end
86
+
87
+ it "returns nil and do not hit S3, if key is not in db" do
88
+ @s3_stub.expects(:buckets).at_most(0)
89
+ test_object = ClassWithS3Columns.create(name: "unique")
90
+ assert_nil test_object.s3_column_extra_data
91
+ end
92
+
93
+ describe "options" do
94
+ describe ":s3_bucket" do
95
+ it "uses :s3_bucket for S3 bucket name on reader" do
96
+ ClassWithS3Columns.s3_column_for :extra_data, s3_bucket: "some_bucket2"
97
+ test_object = ClassWithS3Columns.create(name: "something")
98
+ test_object.send(:write_attribute, :extra_data, 'some/key')
99
+
100
+ key_stub = stub(exists?: true, read: Marshal.dump('hey'))
101
+ @buckets_stub.expects(:[]).with("some_bucket2").returns(stub(objects: stub(:[] => key_stub)))
102
+ @s3_stub.expects(:buckets).returns(@buckets_stub)
103
+ test_object.s3_column_extra_data
104
+ end
105
+
106
+ it "uses :s3_bucket for S3 bucket name on writer" do
107
+ ClassWithS3Columns.s3_column_for :extra_data, s3_bucket: "some_bucket2"
108
+ test_object = ClassWithS3Columns.create(name: "something")
109
+ test_object.send(:write_attribute, :extra_data, 'some/key')
110
+
111
+ @buckets_stub.expects(:[]).with("some_bucket2").returns(stub(objects: @objects_stub))
112
+ @s3_stub.expects(:buckets).returns(@buckets_stub)
113
+ test_object.s3_column_extra_data = {here: "we go"}
114
+ end
115
+ end
116
+
117
+ describe ":s3_key" do
118
+ after do
119
+ ClassWithS3Columns.s3_column_for :extra_data
120
+ end
121
+
122
+ it "changes S3 key" do
123
+ ClassWithS3Columns.s3_column_for :extra_data, s3_key: lambda{|m| "something/hardcored/#{m.name}"}
124
+ test_object = ClassWithS3Columns.create(name: "my_thing")
125
+ assert_equal ClassWithS3Columns.s3_columns_keys[:extra_data].call(test_object), "something/hardcored/my_thing"
126
+ end
127
+ end
128
+
129
+ end
130
+
131
+ describe "s3_column_upload_on_create" do
132
+ it "uploads all S3 columns to S3" do
133
+ ClassWithS3Columns.class_eval "
134
+ s3_column_upload_on_create
135
+ "
136
+
137
+ SimpleUUID::UUID.any_instance.stubs(:to_guid).returns('uuid')
138
+ extra_value = {some_data: 'this is extra'}
139
+ options_value = {toggle: true}
140
+ metadata_value = {user: 1}
141
+
142
+ key_mock = mock("S3 Key")
143
+ bucket_objects_mock = mock("S3 Objects")
144
+ bucket_mock = mock('Test S3 Bucket')
145
+ other_bucket_mock = mock("Other S3 Bucket")
146
+ other_bucket_objects_mock = mock('Other S3 Objects')
147
+
148
+ key_mock.expects(:write).with(Marshal.dump(extra_value), {})
149
+ key_mock.expects(:write).with(Marshal.dump(options_value), {})
150
+ key_mock.expects(:write).with(Marshal.dump(metadata_value), {})
151
+
152
+ bucket_objects_mock.expects(:[]).with("thing/options/aww_yeah").at_least(1).returns(key_mock)
153
+ bucket_objects_mock.expects(:[]).with('extra_data/uuid').at_least(1).returns(key_mock)
154
+ other_bucket_objects_mock.expects(:[]).with("thing/meta/aww_yeah").at_least(1).returns(key_mock)
155
+
156
+ bucket_mock.expects(:objects).at_least(2).returns(bucket_objects_mock)
157
+ other_bucket_objects_mock
158
+ other_bucket_mock.expects(:objects).returns(other_bucket_objects_mock)
159
+ buckets_mock = mock("S3 Buckets")
160
+ buckets_mock.expects(:[]).with("other").returns(other_bucket_mock)
161
+ buckets_mock.expects(:[]).with("test").at_least(2).returns(bucket_mock)
162
+ s3_stub = mock("S3 Connection")
163
+ s3_stub.expects(:buckets).at_least(2).returns(buckets_mock)
164
+ # S3Columns.unstub(:s3_connection)
165
+ S3Columns.stubs(:s3_connection).returns(s3_stub)
166
+ test_object = ClassWithS3Columns.create(name: 'aww_yeah', extra_data: extra_value, options: options_value, metadata: metadata_value)
167
+ # test_object = ClassWithS3Columns.create(name: 'aww_yeah', s3_column_extra_data: extra_value, s3_column_options: options_value, s3_column_metadata: metadata_value)
168
+ assert_equal "extra_data/uuid", test_object.read_attribute(:extra_data)
169
+ assert_equal "thing/options/aww_yeah", test_object.read_attribute(:options)
170
+ assert_equal "thing/meta/aww_yeah", test_object.read_attribute(:metadata)
171
+ end
172
+
173
+
174
+ end
175
+
176
+ end
177
+
178
+ S3Columns.default_aws_bucket = "test"
179
+ class ClassWithS3Columns < ActiveRecord::Base
180
+ include S3Columns
181
+ s3_column_for :extra_data
182
+ s3_column_for :options, s3_key: lambda{|m| "thing/options/#{m.name}"}
183
+ s3_column_for :metadata, s3_bucket: "other", s3_key: lambda{|m| "thing/meta/#{m.name}"}
184
+ end
185
+
186
+ class TestMigration < ActiveRecord::Migration
187
+ def self.up
188
+ create_table :class_with_s3_columns, :force => true do |t|
189
+ t.column :name, :string
190
+ t.column :extra_data, :string
191
+ t.column :options, :string
192
+ t.column :metadata, :string
193
+ end
194
+ end
195
+
196
+ def self.down
197
+ drop_table :class_with_s3_columns
198
+ end
199
+ end
@@ -0,0 +1,24 @@
1
+ lib = File.expand_path('../../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+
4
+ require 'active_record'
5
+ require 'ar_s3_columns'
6
+
7
+ # ActiveRecord setup
8
+ dbconfig = {
9
+ :adapter => 'mysql2',
10
+ :database => 's3_columns_test',
11
+ :username => 'root',
12
+ :encoding => 'utf8'
13
+ }
14
+
15
+ database = dbconfig.delete(:database)
16
+
17
+ ActiveRecord::Base.establish_connection(dbconfig)
18
+ begin
19
+ ActiveRecord::Base.connection.create_database database
20
+ rescue ActiveRecord::StatementInvalid => e # database already exists
21
+ end
22
+ ActiveRecord::Base.establish_connection(dbconfig.merge(:database => database))
23
+
24
+ ActiveRecord::Migration.verbose = false
metadata ADDED
@@ -0,0 +1,195 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ar-s3-columns
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Alex Chee
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-05-21 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: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: mocha
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: activesupport
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '4.0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '4.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: activemodel
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '4.0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '4.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rails
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '4.0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '4.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: aws-sdk
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: 1.39.0
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: 1.39.0
125
+ - !ruby/object:Gem::Dependency
126
+ name: retryable
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: 1.3.5
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: 1.3.5
139
+ - !ruby/object:Gem::Dependency
140
+ name: simple_uuid
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: 0.4.0
146
+ type: :runtime
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: 0.4.0
153
+ description: ActiveRecord::Model extension to write/read an column on AWS S3.
154
+ email:
155
+ - alexchee11@gmail.com
156
+ executables: []
157
+ extensions: []
158
+ extra_rdoc_files: []
159
+ files:
160
+ - ".gitignore"
161
+ - Gemfile
162
+ - LICENSE
163
+ - README.md
164
+ - ar-s3-columns.gemspec
165
+ - lib/ar_s3_columns.rb
166
+ - lib/ar_s3_columns/class_methods.rb
167
+ - lib/ar_s3_columns/instance_methods.rb
168
+ - lib/ar_s3_columns/version.rb
169
+ - tests/s3_columns_test.rb
170
+ - tests/test_helper.rb
171
+ homepage: ''
172
+ licenses:
173
+ - MIT
174
+ metadata: {}
175
+ post_install_message:
176
+ rdoc_options: []
177
+ require_paths:
178
+ - lib
179
+ required_ruby_version: !ruby/object:Gem::Requirement
180
+ requirements:
181
+ - - ">="
182
+ - !ruby/object:Gem::Version
183
+ version: '0'
184
+ required_rubygems_version: !ruby/object:Gem::Requirement
185
+ requirements:
186
+ - - ">="
187
+ - !ruby/object:Gem::Version
188
+ version: '0'
189
+ requirements: []
190
+ rubyforge_project:
191
+ rubygems_version: 2.2.2
192
+ signing_key:
193
+ specification_version: 4
194
+ summary: ActiveRecord::Model extension to add an column that on AWS S3.
195
+ test_files: []