paperclip-storage-tmp 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ /.bundle
2
+ /Gemfile.lock
3
+ /pkg
4
+ /.rvmrc
5
+ /vendor/paperclip
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
@@ -0,0 +1,5 @@
1
+ ## 0.0.1 (2012-05-11):
2
+
3
+ * Implement `Paperclip::Storage::Tmp.clear` which clears all
4
+ attachments from the filesystem (and from the "virtual" filesystem)
5
+ * Provide a `:tmp` storage option for Paperclip attachments
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'http://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in paperclip-storage-tmp.gemspec
4
+ gemspec
5
+
6
+ gem 'paperclip', path: 'vendor/paperclip'
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Olek Janiszewski
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,154 @@
1
+ # paperclip-storage-tmp
2
+
3
+ This gem allows you to configure Paperclip 2.x to use your temporary directory to store the attachments. The attachments are deleted as soon as your Ruby process exits, or whenever you ask the gem to delete them (e.g. after every test), ensuring isolated state for each test.
4
+
5
+ Thanks to this gem your `public/system` directory is no longer cluttered with attachments dangling in there after your test suite has finished running.
6
+
7
+ It also prevents the attachments created by tests from conflicting with attachments uploaded in development. Storing the images using the default, `filesystem` storage both in development and test environments may lead to false positives and other unexpected behaviour in your tests.
8
+
9
+ As an example, imagine you upload an avatar in development. It will probably land in a path similar to `public/system/users/123/avatars/original/hey_mom_its_me.png` (assuming the user's ID is 123). If you feed your application with some kind of fixtures, you probably have hundreds of such files. Now imagine you also have the following test:
10
+
11
+ describe User do
12
+ it 'assigns a default avatar' do
13
+ user = User.new
14
+
15
+ # This method is supposed to assign a default
16
+ # avatar to the user (called "hey_mom_its_me.png"),
17
+ # and save the record
18
+ user.assign_default_avatar!
19
+
20
+ # Yikes! False positive alert!
21
+ user.avatar.should be_present
22
+ end
23
+ end
24
+
25
+ In the test above, `user.avatar.present?` will check if a file `public/system/users/:id/avatars/original/hey_mom_its_me.jpg` exists. That file could as well have been uploaded in development, and even if your method `assign_default_avatar!` is not doing what you expect it to, your test is still passing.
26
+
27
+ Just like you wouldn't want to use the same database in development and test, you probably don't want to use the same storage directory (which also is a kind of a database).
28
+
29
+ ## Compatibility with Paperclip versions
30
+
31
+ Please note that this gem has been written with Paperclip 2.x in mind (extracted from and battle-tested in an application dependent on Paperclip 2.4.0). The gemspec declares a rather loose dependency on Paperclip of '~> 2.0', so make sure the gem is behaving as expected. Since it's supposed to be used only in test environment, it shouldn't be harmful to just give it a try. If you confirm that it's working for the version of Paperclip you're using, let me know.
32
+
33
+ Any pull requests increasing compatibility with other versions welcome!
34
+
35
+ ## Installation
36
+
37
+ Add this line to your application's Gemfile:
38
+
39
+ gem 'paperclip-storage-tmp', group: 'test'
40
+
41
+ And then execute:
42
+
43
+ $ bundle
44
+
45
+ Or install it yourself as:
46
+
47
+ $ gem install paperclip-storage-tmp
48
+
49
+ ## Usage
50
+
51
+ ### Configuring Paperclip to use the temporary storage
52
+
53
+ To use the tmp storage, simply provide the `:storage` option with the value of `:tmp`, e.g.:
54
+
55
+ class User < ActiveRecord::Base
56
+ has_attached_file :avatar, storage: :tmp
57
+ end
58
+
59
+ Unless you're drunk, you probably want to do this conditionally:
60
+
61
+ class User < ActiveRecord::Base
62
+ has_attached_file :avatar, storage: (Rails.env.test? ? :tmp : :filesystem)
63
+ end
64
+
65
+ Or use some kind of environment-aware config parameter:
66
+
67
+ class User < ActiveRecord::Base
68
+ has_attached_file :avatar, storage: MyApp.config.paperclip.storage
69
+ end
70
+
71
+ If you want to configure this option globally for all attachments, use an initializer:
72
+
73
+ # in config/initializers/paperclip.rb
74
+ Paperclip::Attachment.default_options[:storage] = MyApp.config.paperclip.storage
75
+
76
+ ### Configuring your tests to delete the attachments after every test
77
+
78
+ The most important part is actually "rolling back the filesystem" after every test, so that the next test will run with isolated state. That's where the `Paperclip::Storage::Tmp.clear` method comes in handy. Call this method in the `teardown`/`after` block of your test framework. Here's an example for `RSpec`:
79
+
80
+ # in spec/spec_helper.rb
81
+ RSpec.configure do |config|
82
+ config.after do
83
+ Paperclip::Storage::Tmp.clear
84
+ end
85
+ end
86
+
87
+ ## Caveats
88
+
89
+ Beware that the file name assigned to the model attribute (`<attachment>_file_name`) is different than the name of the assigned/uploaded file (it's the name of the temporary file - a unique string).
90
+
91
+ Also, Paperclip doesn't know that the file doesn't physically exist in `public/system`, so you can't use `Attachment#path` to access the physical file. You can use `attachment.to_file.path` to find the actual location of the attachment on disk.
92
+
93
+ Here are a couple of specs, which expose the expected behaviour of this gem. The specs markes with `# FAIL` expose the caveats:
94
+
95
+ describe User do
96
+ describe 'avatar' do
97
+ let(:user) { User.create!(avatar: File.new('spec/fixtures/hey_mom_its_me.png')) }
98
+ subject { user.avatar }
99
+
100
+ it { should exist }
101
+ its(:content_type) { should eq('image/png') }
102
+ its(:original_filename) { should eq('hey_mom_its_me.png') }
103
+
104
+ its(:path) { should eq(Rails.root + "/public/system/avatars/1/original/hey_mom_its_me.png") }
105
+ its(:to_file) { should be_a(File) }
106
+
107
+ it 'is actually stored in /tmp' do
108
+ File.exists?(subject.path).should be_false # FAIL
109
+ subject.to_file.path.should match(%r{^/tmp/})
110
+ end
111
+
112
+ it 'copies the assigned file' do
113
+ File.read(subject.to_file).should eq(File.read(avatar_file))
114
+ end
115
+
116
+ it 'stores the file in an imagemagick-friendly way' do
117
+ geometry = Paperclip::Geometry.from_file(subject.to_file)
118
+ geometry.width.should eq(256)
119
+ geometry.height.should eq(256)
120
+ end
121
+
122
+ it 'stores the file attributes in the model' do
123
+ user.avatar_file_name.should eq('hey_mom_its_me.png')
124
+ user.avatar_content_type.should eq('image/png')
125
+ user.avatar_file_size.should eq(File.size(avatar_file))
126
+ end
127
+
128
+ it 'can handle assignment from File' do
129
+ new_user = User.new(avatar: avatar_file)
130
+ new_user.avatar_file_name.should eq('hey_mom_its_me.png')
131
+ end
132
+
133
+ it 'can persist assignment from File' do
134
+ new_user = User.create!(avatar: avatar_file)
135
+ new_user.reload.avatar_file_name.should eq('hey_mom_its_me.png')
136
+ end
137
+
138
+ # :(
139
+ it 'cannot handle assignment from Paperclip::Attachment' do
140
+ new_user = User.new(avatar: subject)
141
+ new_user.avatar_file_name.should_not eq('hey_mom_its_me.png') # FAIL
142
+ end
143
+ end
144
+ end
145
+
146
+ ## Contributing
147
+
148
+ In development, the Gemfile points to a version of Paperclip from `vendor/paperclip`, so make sure you have a clone of Paperclip there (e.g. `git clone git://github.com/thoughtbot/paperclip vendor`). You can checkout any version you want to test against.
149
+
150
+ 1. Fork it
151
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
152
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
153
+ 4. Push to the branch (`git push origin my-new-feature`)
154
+ 5. Create new Pull Request
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,3 @@
1
+ require 'paperclip-storage-tmp/version'
2
+ require 'paperclip'
3
+ require 'paperclip/storage/tmp'
@@ -0,0 +1,7 @@
1
+ module Paperclip
2
+ module Storage
3
+ module Tmp
4
+ VERSION = "0.0.1"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,42 @@
1
+ module Paperclip
2
+ module Storage
3
+ module Tmp
4
+ def self.fs
5
+ @fs ||= {}
6
+ end
7
+
8
+ # Deletes the temporary files and releases references to them
9
+ def self.clear
10
+ fs.each_value {|f| f.unlink }
11
+ @fs = nil
12
+ end
13
+
14
+ def exists?(style_name = default_style)
15
+ Tmp.fs.key?(path(style_name))
16
+ end
17
+
18
+ def to_file(style_name = default_style)
19
+ @queued_for_write[style_name] || (File.new(Tmp.fs[path(style_name)], 'rb') if exists?(style_name))
20
+ end
21
+
22
+ def flush_writes
23
+ @queued_for_write.each do |style_name, file|
24
+ Tmp.fs[path(style_name)] = to_tempfile(file)
25
+ end
26
+
27
+ after_flush_writes
28
+ @queued_for_write = {}
29
+ end
30
+
31
+ def flush_deletes
32
+ @queued_for_delete.each do |path|
33
+ if file = Tmp.fs.delete(path)
34
+ file.unlink
35
+ end
36
+ end
37
+
38
+ @queued_for_delete = []
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,20 @@
1
+ # encoding: utf-8
2
+ require File.expand_path('../lib/paperclip-storage-tmp/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ['Olek Janiszewski']
6
+ gem.email = ['olek.janiszewski@gmail.com']
7
+ gem.description = %q{Store Paperclip attachments in your temporary directory}
8
+ gem.summary = %q{Keep your tests clean by isolating test attachments from development, and between tests}
9
+ gem.homepage = 'https://github.com/exviva/paperclip-storage-tmp'
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.test_files = gem.files.grep(%r{^spec/})
13
+ gem.name = 'paperclip-storage-tmp'
14
+ gem.require_paths = ['lib']
15
+ gem.version = Paperclip::Storage::Tmp::VERSION
16
+
17
+ gem.add_runtime_dependency 'paperclip', '~> 2.0'
18
+ gem.add_development_dependency 'rspec'
19
+ gem.add_development_dependency 'sqlite3'
20
+ end
@@ -0,0 +1,3 @@
1
+ class User < ActiveRecord::Base
2
+ has_attached_file :avatar, storage: :tmp
3
+ end
@@ -0,0 +1,105 @@
1
+ require 'spec_helper'
2
+
3
+ describe Paperclip::Storage::Tmp do
4
+ after do
5
+ Paperclip::Storage::Tmp.clear
6
+ end
7
+
8
+ let(:avatar_file) { File.new('spec/fixtures/hey_mom_its_me.png') }
9
+
10
+ [proc { user.avatar }, proc { user.reload.avatar }].each do |subject_proc|
11
+ describe 'assigning an attachment' do
12
+ let(:user) { User.create!(avatar: avatar_file) }
13
+ subject(&subject_proc)
14
+
15
+ it { should exist }
16
+ its(:content_type) { should eq('image/png') }
17
+ its(:original_filename) { should eq('hey_mom_its_me.png') }
18
+
19
+ its(:path) { should eq(Rails.root + "/public/system/avatars/1/original/hey_mom_its_me.png") }
20
+ its(:to_file) { should be_a(File) }
21
+
22
+ it 'is actually stored in /tmp' do
23
+ File.exists?(subject.path).should be_false
24
+ subject.to_file.path.should match(%r{^/tmp/})
25
+ end
26
+
27
+ it 'copies the assigned file' do
28
+ File.read(subject.to_file).should eq(File.read(avatar_file))
29
+ end
30
+
31
+ it 'stores the file in an imagemagick-friendly way' do
32
+ geometry = Paperclip::Geometry.from_file(subject.to_file)
33
+ geometry.width.should eq(256)
34
+ geometry.height.should eq(256)
35
+ end
36
+
37
+ it 'stores the file attributes in the model' do
38
+ user.avatar_file_name.should eq('hey_mom_its_me.png')
39
+ user.avatar_content_type.should eq('image/png')
40
+ user.avatar_file_size.should eq(File.size(avatar_file))
41
+ end
42
+
43
+ it 'can handle assignment from File' do
44
+ new_user = User.new(avatar: avatar_file)
45
+ new_user.avatar_file_name.should eq('hey_mom_its_me.png')
46
+ end
47
+
48
+ it 'can persist assignment from File' do
49
+ new_user = User.create!(avatar: avatar_file)
50
+ new_user.reload.avatar_file_name.should eq('hey_mom_its_me.png')
51
+ end
52
+
53
+ # :(
54
+ it 'cannot handle assignment from Paperclip::Attachment' do
55
+ new_user = User.new(avatar: subject)
56
+ new_user.avatar_file_name.should_not eq('hey_mom_its_me.png')
57
+ end
58
+ end
59
+
60
+ describe 'not assigning an attachment' do
61
+ let(:user) { User.create! }
62
+ subject(&subject_proc)
63
+
64
+ it { should_not exist }
65
+ its(:content_type) { should be_nil }
66
+ its(:original_filename) { should be_nil }
67
+ its(:path) { should be_nil }
68
+ its(:to_file) { should be_nil }
69
+ end
70
+ end
71
+
72
+ describe 'destroying an attachment' do
73
+ let(:user) { User.create!(avatar: avatar_file) }
74
+ subject do
75
+ @path_before_destroy = user.avatar.to_file.path
76
+ user.destroy
77
+
78
+ user.avatar
79
+ end
80
+
81
+ it { should_not exist }
82
+
83
+ it 'should be deleted from the filesystem' do
84
+ subject
85
+ File.exists?(@path_before_destroy).should be_false
86
+ end
87
+ end
88
+
89
+ describe 'clear' do
90
+ let(:user) { User.create!(avatar: avatar_file) }
91
+ subject { Paperclip::Storage::Tmp.clear }
92
+
93
+ it 'deletes files' do
94
+ path = user.avatar.to_file.path
95
+ subject
96
+ File.exists?(path).should be_false
97
+ end
98
+
99
+ it 'deletes the files from virtual fs' do
100
+ user.avatar.should exist
101
+ subject
102
+ user.avatar.should_not exist
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'spec_helper' do
4
+ it 'successfully connects to an in-memory database' do
5
+ User.connection_config[:adapter].should eq('sqlite3')
6
+ User.connection_config[:database].should eq(':memory:')
7
+ User.create!
8
+ User.count.should eq(1)
9
+ end
10
+
11
+ it 'rolls back between examples' do
12
+ User.count.should eq(0)
13
+ end
14
+
15
+ it 'defines Rails.root' do
16
+ Rails.root.should eq(File.expand_path('../..', __FILE__))
17
+ end
18
+ end
19
+
20
+ describe User do
21
+ it 'is an active record' do
22
+ User.new.should be_an(ActiveRecord::Base)
23
+ end
24
+
25
+ it 'has an avatar' do
26
+ User.new.avatar.should be_a(Paperclip::Attachment)
27
+ end
28
+ end
@@ -0,0 +1,18 @@
1
+ require 'bundler'
2
+ Bundler.setup
3
+
4
+ require 'paperclip-storage-tmp'
5
+ Dir[File.dirname(__FILE__) + '/support/*.rb'].each {|f| require f }
6
+
7
+ RSpec.configure do |config|
8
+ config.treat_symbols_as_metadata_keys_with_true_values = true
9
+ config.run_all_when_everything_filtered = true
10
+ config.filter_run :focus
11
+
12
+ config.around do |example|
13
+ ActiveRecord::Base.transaction do
14
+ example.run
15
+ raise ActiveRecord::Rollback
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,15 @@
1
+ require 'active_record'
2
+ ActiveRecord::Base.send(:include, Paperclip::Glue)
3
+ require 'fixtures/user'
4
+
5
+ ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
6
+
7
+ ActiveRecord::Schema.define do
8
+ create_table :users do |t|
9
+ t.string :avatar_file_name
10
+ t.string :avatar_content_type
11
+ t.integer :avatar_file_size
12
+ t.datetime :created_at
13
+ t.datetime :updated_at
14
+ end
15
+ end
@@ -0,0 +1,3 @@
1
+ module Rails
2
+ def self.root; File.expand_path('../../..', __FILE__); end
3
+ end
@@ -0,0 +1 @@
1
+ Paperclip.logger = Logger.new(STDOUT).tap {|l| l.level = Logger::WARN }
File without changes
metadata ADDED
@@ -0,0 +1,122 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: paperclip-storage-tmp
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Olek Janiszewski
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-05-11 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: paperclip
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '2.0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '2.0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: sqlite3
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: Store Paperclip attachments in your temporary directory
63
+ email:
64
+ - olek.janiszewski@gmail.com
65
+ executables: []
66
+ extensions: []
67
+ extra_rdoc_files: []
68
+ files:
69
+ - .gitignore
70
+ - .rspec
71
+ - CHANGELOG.md
72
+ - Gemfile
73
+ - LICENSE
74
+ - README.md
75
+ - Rakefile
76
+ - lib/paperclip-storage-tmp.rb
77
+ - lib/paperclip-storage-tmp/version.rb
78
+ - lib/paperclip/storage/tmp.rb
79
+ - paperclip-storage-tmp.gemspec
80
+ - spec/fixtures/hey_mom_its_me.png
81
+ - spec/fixtures/user.rb
82
+ - spec/lib/paperclip/storage/tmp_spec.rb
83
+ - spec/meta_spec.rb
84
+ - spec/spec_helper.rb
85
+ - spec/support/active_record.rb
86
+ - spec/support/fake_rails.rb
87
+ - spec/support/paperclip.rb
88
+ - vendor/.gitkeep
89
+ homepage: https://github.com/exviva/paperclip-storage-tmp
90
+ licenses: []
91
+ post_install_message:
92
+ rdoc_options: []
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ! '>='
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ none: false
103
+ requirements:
104
+ - - ! '>='
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ requirements: []
108
+ rubyforge_project:
109
+ rubygems_version: 1.8.23
110
+ signing_key:
111
+ specification_version: 3
112
+ summary: Keep your tests clean by isolating test attachments from development, and
113
+ between tests
114
+ test_files:
115
+ - spec/fixtures/hey_mom_its_me.png
116
+ - spec/fixtures/user.rb
117
+ - spec/lib/paperclip/storage/tmp_spec.rb
118
+ - spec/meta_spec.rb
119
+ - spec/spec_helper.rb
120
+ - spec/support/active_record.rb
121
+ - spec/support/fake_rails.rb
122
+ - spec/support/paperclip.rb