Skip to content

Style migrations #974

Closed
wants to merge 12 commits into from
+545 −214
View
12 Gemfile.lock
@@ -60,6 +60,9 @@ GEM
rack-test (>= 0.5.4)
selenium-webdriver (~> 2.0)
xpath (~> 0.1.4)
+ capybara-webkit (0.11.0)
+ capybara (>= 1.0.0, < 1.2)
+ json
childprocess (0.3.2)
ffi (~> 1.0.6)
climate_control (0.0.3)
@@ -72,8 +75,10 @@ GEM
diff-lcs (>= 1.1.3)
gherkin (~> 2.11.0)
json (>= 1.4.6)
+ daemons (1.1.8)
diff-lcs (1.1.3)
erubis (2.7.0)
+ eventmachine (0.12.10)
excon (0.16.7)
fakeweb (1.3.0)
ffi (1.0.11)
@@ -165,6 +170,10 @@ GEM
rack (~> 1.0)
tilt (~> 1.1, != 1.3.0)
sqlite3 (1.3.6)
+ thin (1.3.1)
+ daemons (>= 1.0.9)
+ eventmachine (>= 0.12.6)
+ rack (>= 1.0.0)
thor (0.15.4)
tilt (1.3.3)
treetop (1.4.10)
@@ -187,6 +196,8 @@ DEPENDENCIES
bourne
bundler
capybara
+ capybara-webkit
+ cocaine (~> 0.2)
cucumber (~> 1.2.1)
fakeweb
fog (>= 1.4.0, < 1.7.0)
@@ -200,3 +211,4 @@ DEPENDENCIES
rake
shoulda
sqlite3
+ thin
View
58 README.md
@@ -535,60 +535,24 @@ end
Deployment
----------
-Paperclip is aware of new attachment styles you have added in previous deploys. The only thing you should do after each deployment is to call
-`rake paperclip:refresh:missing_styles`. It will store current attachment styles in `RAILS_ROOT/public/system/paperclip_attachments.yml`
-by default. You can change it by:
+When you add a new style you can write a migration to ease the
+deployment. Do this using the `add_style` and `remove_style` migration
+schema helpers.
```ruby
-Paperclip.registered_attachments_styles_path = '/tmp/config/paperclip_attachments.yml'
-```
-
-Here is an example for Capistrano:
-
-```ruby
-namespace :deploy do
- desc "build missing paperclip styles"
- task :build_missing_paperclip_styles, :roles => :app do
- run "cd #{release_path}; RAILS_ENV=production bundle exec rake paperclip:refresh:missing_styles"
+class AddThumbnailsToUser < ActiveRecord::Migration
+ def up
+ add_style :users, :avatar, thumbnail: '24x24#'
end
-end
-
-after("deploy:update_code", "deploy:build_missing_paperclip_styles")
-```
-
-Now you don't have to remember to refresh thumbnails in production every time you add a new style.
-Unfortunately it does not work with dynamic styles - it just ignores them.
-If you already have a working app and don't want `rake paperclip:refresh:missing_styles` to refresh old pictures, you need to tell
-Paperclip about existing styles. Simply create a `paperclip_attachments.yml` file by hand. For example:
-
-```ruby
-class User < ActiveRecord::Base
- has_attached_file :avatar, :styles => {:thumb => 'x100', :croppable => '600x600>', :big => '1000x1000>'}
-end
-
-class Book < ActiveRecord::Base
- has_attached_file :cover, :styles => {:small => 'x100', :large => '1000x1000>'}
- has_attached_file :sample, :styles => {:thumb => 'x100'}
+ def down
+ remove_style :users, :avatar, :thumbnail
+ end
end
```
-Then in `RAILS_ROOT/public/system/paperclip_attachments.yml`:
-
-```yml
----
-:User:
- :avatar:
- - :thumb
- - :croppable
- - :big
-:Book:
- :cover:
- - :small
- - :large
- :sample:
- - :thumb
-```
+The above migration will process just the `thumbnail` style when going
+up, and will remove the `thumbnail` files when going down.
Testing
-------
View
63 features/rake_tasks.feature
@@ -1,63 +0,0 @@
-Feature: Rake tasks
-
- Background:
- Given I generate a new rails application
- And I run a rails generator to generate a "User" scaffold with "name:string"
- And I run a paperclip generator to add a paperclip "attachment" to the "User" model
- And I run a migration
- And I add this snippet to the User model:
- """
- attr_accessible :name, :attachment
- has_attached_file :attachment, :path => ":rails_root/public/system/:attachment/:style/:filename"
- """
-
- Scenario: Paperclip refresh thumbnails task
- When I modify my attachment definition to:
- """
- has_attached_file :attachment, :path => ":rails_root/public/system/:attachment/:style/:filename",
- :styles => { :medium => "200x200#" }
- """
- And I upload the fixture "5k.png"
- Then the attachment "medium/5k.png" should have a dimension of 200x200
- When I modify my attachment definition to:
- """
- has_attached_file :attachment, :path => ":rails_root/public/system/:attachment/:style/:filename",
- :styles => { :medium => "100x100#" }
- """
- When I successfully run `bundle exec rake paperclip:refresh:thumbnails CLASS=User --trace`
- Then the attachment "original/5k.png" should exist
- And the attachment "medium/5k.png" should have a dimension of 100x100
-
- Scenario: Paperclip refresh metadata task
- When I upload the fixture "5k.png"
- And I swap the attachment "original/5k.png" with the fixture "12k.png"
- And I successfully run `bundle exec rake paperclip:refresh:metadata CLASS=User --trace`
- Then the attachment should have the same content type as the fixture "12k.png"
- And the attachment should have the same file size as the fixture "12k.png"
-
- Scenario: Paperclip refresh missing styles task
- When I upload the fixture "5k.png"
- Then the attachment file "original/5k.png" should exist
- And the attachment file "medium/5k.png" should not exist
- When I modify my attachment definition to:
- """
- has_attached_file :attachment, :path => ":rails_root/public/system/:attachment/:style/:filename",
- :styles => { :medium => "200x200#" }
- """
- When I successfully run `bundle exec rake paperclip:refresh:missing_styles --trace`
- Then the attachment file "original/5k.png" should exist
- And the attachment file "medium/5k.png" should exist
-
- Scenario: Paperclip clean task
- When I upload the fixture "5k.png"
- And I upload the fixture "12k.png"
- Then the attachment file "original/5k.png" should exist
- And the attachment file "original/12k.png" should exist
- When I modify my attachment definition to:
- """
- has_attached_file :attachment, :path => ":rails_root/public/system/:attachment/:style/:filename"
- validates_attachment_size :attachment, :less_than => 10.kilobytes
- """
- And I successfully run `bundle exec rake paperclip:clean CLASS=User --trace`
- Then the attachment file "original/5k.png" should exist
- But the attachment file "original/12k.png" should not exist
View
26 features/regenerating_styles.feature
@@ -0,0 +1,26 @@
+Feature: Regenerating styles
+
+ Scenario: Adding a new style
+ Given I generate a new rails application
+ And I have made a simple avatar on the user model
+ And I upload an avatar to the user model
+ When I add the following style to the user avatar:
+ """
+ large: '124x124#'
+ """
+ And I change the user show page to show the large avatar
+ Then I see a missing large avatar on the user show page
+ When I generate the "add_large_thumbnail_to_user_avatar" migration as follows:
+ """
+ def up
+ add_style :users, :avatar, large: '124x124#'
+ end
+
+ def down
+ remove_style :users, :avatar, :large
+ end
+ """
+ And I run the up database migration
+ Then I see the large avatar on the user show page
+ When I run the down database migration
+ Then I see a missing large avatar on the user show page
View
12 features/step_definitions/rails_steps.rb
@@ -1,4 +1,8 @@
Given /^I generate a new rails application$/ do
+ in_current_dir do
+ FileUtils.rm_rf(APP_NAME)
+ end
+
steps %{
When I run `bundle exec #{new_application_command} #{APP_NAME} --skip-bundle`
And I cd to "#{APP_NAME}"
@@ -13,6 +17,8 @@
gem "capybara"
gem "gherkin"
gem "aws-sdk"
+ gem 'thin'
+ gem 'jquery-rails'
"""
And I configure the application to use "paperclip" from this project
And I reset Bundler environment variable
@@ -56,7 +62,7 @@
Given I overwrite "app/views/users/show.html.erb" with:
"""
<p>Name: <%= @user.name %></p>
- <p>Attachment: <%= image_tag @user.attachment.url %></p>
+ <p>Default attachment: <%= image_tag @user.attachment.url %></p>
"""
}
end
@@ -84,10 +90,6 @@
end
end
-Given /^I reload my application$/ do
- Rails::Application.reload!
-end
-
When /^I turn off class caching$/ do
in_current_dir do
file = "config/environments/test.rb"
View
104 features/step_definitions/regnerating_styles_steps.rb
@@ -0,0 +1,104 @@
+require 'active_support/core_ext/string'
+
+Given /^I have made a simple avatar on the user model$/ do
+ run_simple(%{bundle exec #{generator_command} scaffold user})
+ run_simple(%{bundle exec #{generator_command} paperclip user avatar})
+ run_simple(%{bundle exec rake db:migrate})
+ write_file('app/views/users/new.html.erb', <<-VIEW)
+ <%= form_for @user, :html => { :multipart => true } do |f| %>
+ <%= f.label :avatar %>
+ <%= f.file_field :avatar %>
+ <%= submit_tag 'Submit' %>
+ <% end %>
+ VIEW
+ write_file('app/views/users/show.html.erb', <<-VIEW)
+ <p>Thumbnail attachment: <%= image_tag @user.avatar.url(:thumbnail) %></p>
+ VIEW
+ write_file('app/models/user.rb', <<-MODEL)
+ class User < ActiveRecord::Base
+ has_attached_file :avatar, styles: { thumbnail: '8x8#' }
+ attr_accessible :avatar
+ end
+ MODEL
+end
+
+Given /^I upload an avatar to the user model$/ do
+ FakeWeb.allow_net_connect = true
+ in_current_dir { RailsServer.start_unless_started(ENV['PORT'], ENV['DEBUG']) }
+
+ require 'capybara/webkit'
+ Capybara.current_driver = :webkit
+ Capybara.app_host = RailsServer.app_host
+
+ visit '/users/new'
+ attach_file('Avatar', File.expand_path('test/fixtures/5k.png'))
+ click_button 'Submit'
+ sleep 1
+end
+
+When /^I add the following style to the user avatar:$/ do |string|
+ write_file('app/models/user.rb', <<-MODEL)
+ class User < ActiveRecord::Base
+ has_attached_file :avatar, styles: { thumbnail: '8x8#', #{string} }
+ attr_accessible :avatar
+ end
+ MODEL
+end
+
+When /^I change the user show page to show the large avatar$/ do
+ write_file('app/views/users/show.html.erb', <<-VIEW)
+ <p>Large attachment: <%= image_tag @user.avatar.url(:large) %></p>
+ VIEW
+end
+
+Then /^I see a missing large avatar on the user show page$/ do
+ visit '/users'
+ click_link 'Show'
+
+ page.source =~ %r{img.*src="/([^"]+large[^"]+)\?.*"}
+ image_path = $1
+ image_path.should_not be_blank
+
+ in_current_dir do
+ File.should_not be_exist(File.join('public',image_path))
+ end
+end
+
+When /^I generate the "(.*?)" migration as follows:$/ do |migration_name, code|
+ run_simple(%{bundle exec ./script/rails generate migration #{migration_name}})
+ migration_filename = in_current_dir do
+ Dir[File.join('db', 'migrate', "*#{migration_name}.rb")].first
+ end
+ write_file(migration_filename, <<-MIGRATION)
+ class #{migration_name.classify} < ActiveRecord::Migration
+ #{code}
+ end
+ MIGRATION
+end
+
+When /^I run the up database migration$/ do
+ run_simple('bundle exec rake db:migrate')
+end
+
+When /^I run the down database migration$/ do
+ migration_filename = in_current_dir do
+ Dir[File.join('db', 'migrate', '*')].sort.last
+ end
+ migration_filename =~ %r{.*/(\d+)_[^/]+.rb}
+ version = $1
+ version.should_not be_blank
+ run_simple("bundle exec rake db:migrate:down VERSION=#{version}")
+end
+
+Then /^I see the large avatar on the user show page$/ do
+ visit '/users'
+ click_link 'Show'
+
+ page.source =~ %r{img.*src="/([^"]+large[^"]+)\?.*"}
+ image_path = $1
+ image_path.should_not be_blank
+
+ in_current_dir do
+ File.should be_exist(File.join('public',image_path))
+ end
+end
View
9 features/support/env.rb
@@ -8,4 +8,13 @@
Before do
@aruba_timeout_seconds = 120
+
+ if ENV['DEBUG']
+ @puts = true
+ @announce_stdout = true
+ @announce_stderr = true
+ @announce_cmd = true
+ @announce_dir = true
+ @announce_env = true
+ end
end
View
2 features/support/rails.rb
@@ -11,6 +11,8 @@
end
After do
+ RailsServer.stop
+
ORIGINAL_BUNDLE_VARS.each_pair do |key, value|
ENV[key] = value
end
View
130 features/support/rails_server.rb
@@ -0,0 +1,130 @@
+require 'net/http'
+
+# Starts a Rails application server in a fork and waits for it to be responsive
+class RailsServer
+ HOST = 'localhost'.freeze
+
+ class << self
+ attr_accessor :instance
+ end
+
+ def self.start_unless_started(*a)
+ start(*a) unless started?
+ end
+
+ def self.start(port = nil, debug = nil)
+ self.instance = new(port, debug)
+ self.instance.start
+ self.instance
+ end
+
+ def self.stop
+ self.instance.stop if started?
+ self.instance = nil
+ end
+
+ def self.get(path)
+ self.instance.get(path)
+ end
+
+ def self.post(path, data)
+ self.instance.post(path, data)
+ end
+
+ def self.run(port, silent)
+ if silent
+ require 'stringio'
+ $stdout = StringIO.new
+ $stderr = StringIO.new
+ end
+
+ require './config/environment'
+ require 'thin'
+
+ if Rails::VERSION::MAJOR == 3
+ rails = Rails.application
+ else
+ rails = ActionController::Dispatcher.new
+ end
+ app = Identify.new(rails)
+
+ Thin::Logging.silent = silent
+ Rack::Handler::Thin.run(app, :Port => port, :AccessLog => [])
+ end
+
+ def self.app_host
+ self.instance.app_host
+ end
+
+ def self.started?
+ !self.instance.nil?
+ end
+
+ def initialize(port, debug)
+ @port = (port || 3001).to_i
+ @debug = debug
+ end
+
+ def start
+ command = ['ruby', "-r#{__FILE__}", '-e', "RailsServer.run(#{@port},#{(!@debug).inspect})"]
+ puts command.join(' ') if @debug
+ @pid = spawn(*command)
+ wait_until_responsive
+ end
+
+ def stop
+ if @pid
+ Process.kill('INT', @pid)
+ Process.wait(@pid)
+ @pid = nil
+ end
+ end
+
+ def get(path)
+ puts "GET #{path}" if @debug
+ Net::HTTP.start(HOST, @port) { |http| http.get(path) }
+ end
+
+ def post(path, data)
+ puts "POST #{path}\n#{data}" if @debug
+ Net::HTTP.start(HOST, @port) { |http| http.post(path, data) }
+ end
+
+ def wait_until_responsive
+ 20.times do
+ if responsive?
+ return true
+ else
+ sleep(0.5)
+ end
+ end
+ raise "Couldn't connect to Rails application server at #{HOST}:#{@port}"
+ end
+
+ def responsive?
+ response = Net::HTTP.start(HOST, @port) { |http| http.get('/__identify__') }
+ response.is_a?(Net::HTTPSuccess)
+ rescue Errno::ECONNREFUSED, Errno::EBADF
+ return false
+ end
+
+ def app_host
+ "http://#{HOST}:#{@port}"
+ end
+
+ # From Capybara::Server
+
+ class Identify
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ if env["PATH_INFO"] == "/__identify__"
+ [200, {}, 'OK']
+ else
+ @app.call(env)
+ end
+ end
+ end
+end
View
2 lib/paperclip/railtie.rb
@@ -14,8 +14,6 @@ class Railtie < Rails::Railtie
Paperclip::Attachment.default_options.merge!(app.config.paperclip_defaults)
end
end
-
- rake_tasks { load "tasks/paperclip.rake" }
end
class Railtie
View
25 lib/paperclip/schema.rb
@@ -1,4 +1,6 @@
require 'active_support/deprecation'
+require 'paperclip/style_adder'
+require 'paperclip/style_remover'
module Paperclip
# Provides helper methods that can be used in migrations.
@@ -43,6 +45,29 @@ def drop_attached_file(*args)
ActiveSupport::Deprecation.warn "Method `drop_attached_file` in the migration has been deprecated and will be replaced by `remove_attachment`."
remove_attachment(*args)
end
+
+ def add_style(table_name, attachment_name, styles)
+ StyleAdder.run(model_enumerator(table_name), attachment_name, styles)
+ end
+
+ def remove_style(table_name, attachment_name, style_name)
+ StyleRemover.run(model_enumerator(table_name), attachment_name, style_name)
+ end
+
+ private
+
+ def model_enumerator(table_name)
+ model_class(table_name).enum_for(:find_each)
+ end
+
+ def model_class(table_name)
+ model_class_name = table_name.to_s.singularize.camelize
+ begin
+ model_class_name.constantize
+ rescue NameError
+ raise ArgumentError, "found no model named #{model_class_name}"
+ end
+ end
end
module TableDefinition
View
32 lib/paperclip/style_adder.rb
@@ -0,0 +1,32 @@
+require 'paperclip/style_migration'
+require 'paperclip/style_adder'
+
+module Paperclip
+ class StyleAdder < StyleMigration
+ def run(styles)
+ each_attachment do |attachment|
+ file = Paperclip.io_adapters.for(attachment)
+ attachment.instance_variable_set('@queued_for_write', {:original => file})
+
+ attachment.send(:post_process, *styles_for(attachment, styles))
+
+ attachment.save
+ end
+ end
+
+ private
+
+ def styles_for(attachment, styles)
+ expected_styles = attachment.send(:styles).keys
+ if subset?(styles.keys, expected_styles)
+ styles.keys
+ else
+ raise ArgumentError, "unsupported styles; excepted any of #{expected_styles}"
+ end
+ end
+
+ def subset?(smaller, larger)
+ (smaller - larger).empty?
+ end
+ end
+end
View
30 lib/paperclip/style_migration.rb
@@ -0,0 +1,30 @@
+require 'active_support/inflector'
+
+module Paperclip
+ class StyleMigration
+ def self.run(model_enumerator, attachment_name, *style_info)
+ new(model_enumerator, attachment_name).run(*style_info)
+ end
+
+ def initialize(model_enumerator, attachment_name)
+ @model_enumerator = model_enumerator
+ @attachment_name = attachment_name
+ end
+
+ protected
+
+ def each_attachment(&block)
+ @model_enumerator.each do |model|
+ block.call(attachment_for(model))
+ end
+ end
+
+ def attachment_for(model)
+ begin
+ model.send(@attachment_name)
+ rescue NoMethodError
+ raise ArgumentError, "found no attachment named #{@attachment_name} on #{model}"
+ end
+ end
+ end
+end
View
13 lib/paperclip/style_remover.rb
@@ -0,0 +1,13 @@
+require 'paperclip/style_migration'
+require 'paperclip/style_remover'
+
+module Paperclip
+ class StyleRemover < StyleMigration
+ def run(style_name)
+ each_attachment do |attachment|
+ attachment.clear(style_name)
+ attachment.save
+ end
+ end
+ end
+end
View
94 lib/tasks/paperclip.rake
@@ -1,94 +0,0 @@
-module Paperclip
- module Task
- def self.obtain_class
- class_name = ENV['CLASS'] || ENV['class']
- raise "Must specify CLASS" unless class_name
- class_name
- end
-
- def self.obtain_attachments(klass)
- klass = Paperclip.class_for(klass.to_s)
- name = ENV['ATTACHMENT'] || ENV['attachment']
- raise "Class #{klass.name} has no attachments specified" unless klass.respond_to?(:attachment_definitions)
- if !name.blank? && klass.attachment_definitions.keys.map(&:to_s).include?(name.to_s)
- [ name ]
- else
- klass.attachment_definitions.keys
- end
- end
- end
-end
-
-namespace :paperclip do
- desc "Refreshes both metadata and thumbnails."
- task :refresh => ["paperclip:refresh:metadata", "paperclip:refresh:thumbnails"]
-
- namespace :refresh do
- desc "Regenerates thumbnails for a given CLASS (and optional ATTACHMENT and STYLES splitted by comma)."
- task :thumbnails => :environment do
- klass = Paperclip::Task.obtain_class
- names = Paperclip::Task.obtain_attachments(klass)
- styles = (ENV['STYLES'] || ENV['styles'] || '').split(',').map(&:to_sym)
- names.each do |name|
- Paperclip.each_instance_with_attachment(klass, name) do |instance|
- instance.send(name).reprocess!(*styles)
- unless instance.errors.blank?
- puts "errors while processing #{klass} ID #{instance.id}:"
- puts " " + instance.errors.full_messages.join("\n ") + "\n"
- end
- end
- end
- end
-
- desc "Regenerates content_type/size metadata for a given CLASS (and optional ATTACHMENT)."
- task :metadata => :environment do
- klass = Paperclip::Task.obtain_class
- names = Paperclip::Task.obtain_attachments(klass)
- names.each do |name|
- Paperclip.each_instance_with_attachment(klass, name) do |instance|
- if file = Paperclip.io_adapters.for(instance.send(name))
- instance.send("#{name}_file_name=", instance.send("#{name}_file_name").strip)
- instance.send("#{name}_content_type=", file.content_type.to_s.strip)
- instance.send("#{name}_file_size=", file.size) if instance.respond_to?("#{name}_file_size")
- instance.save(:validate => false)
- else
- true
- end
- end
- end
- end
-
- desc "Regenerates missing thumbnail styles for all classes using Paperclip."
- task :missing_styles => :environment do
- # Force loading all model classes to never miss any has_attached_file declaration:
- Dir[Rails.root + 'app/models/**/*.rb'].each { |path| load path }
- Paperclip.missing_attachments_styles.each do |klass, attachment_definitions|
- attachment_definitions.each do |attachment_name, missing_styles|
- puts "Regenerating #{klass} -> #{attachment_name} -> #{missing_styles.inspect}"
- ENV['CLASS'] = klass.to_s
- ENV['ATTACHMENT'] = attachment_name.to_s
- ENV['STYLES'] = missing_styles.join(',')
- Rake::Task['paperclip:refresh:thumbnails'].execute
- end
- end
- Paperclip.save_current_attachments_styles!
- end
- end
-
- desc "Cleans out invalid attachments. Useful after you've added new validations."
- task :clean => :environment do
- klass = Paperclip::Task.obtain_class
- names = Paperclip::Task.obtain_attachments(klass)
- names.each do |name|
- Paperclip.each_instance_with_attachment(klass, name) do |instance|
- unless instance.valid?
- attributes = %w(file_size file_name content_type).map{ |suffix| "#{name}_#{suffix}".to_sym }
- if attributes.any?{ |attribute| instance.errors[attribute].present? }
- instance.send("#{name}=", nil)
- instance.save(:validate => false)
- end
- end
- end
- end
- end
-end
View
2 paperclip.gemspec
@@ -49,4 +49,6 @@ Gem::Specification.new do |s|
s.add_development_dependency('fakeweb')
s.add_development_dependency('railties')
s.add_development_dependency('actionmailer')
+ s.add_development_dependency('thin')
+ s.add_development_dependency('capybara-webkit')
end
View
3 test/attachment_test.rb
@@ -30,6 +30,7 @@ class AttachmentTest < Test::Unit::TestCase
end
should "process :original style first" do
+ rebuild_model
file = File.new(fixture_file("50x50.png"), 'rb')
rebuild_class :styles => { :small => '100x>', :original => '42x42#' }
dummy = Dummy.new
@@ -43,6 +44,7 @@ class AttachmentTest < Test::Unit::TestCase
end
should "not delete styles that don't get reprocessed" do
+ rebuild_model
file = File.new(fixture_file("50x50.png"), 'rb')
rebuild_class :styles => { :small => '100x>',
:large => '500x>',
@@ -1364,5 +1366,4 @@ def do_after_all; end
assert_file_not_exists(@path)
end
end
-
end
View
65 test/schema_test.rb
@@ -6,10 +6,13 @@ class SchemaTest < Test::Unit::TestCase
include ActiveSupport::Testing::Deprecation
def setup
+ super
rebuild_class
+ register_recording_processor
end
def teardown
+ super
Dummy.connection.drop_table :dummies rescue nil
end
@@ -64,7 +67,7 @@ def teardown
Dummy.connection.create_table :dummies, :force => true
end
- context "migrating up" do
+ context '#add_attachment' do
context "with single attachment" do
setup do
Dummy.connection.add_attachment :dummies, :avatar
@@ -111,7 +114,7 @@ def teardown
end
end
- context "migrating down" do
+ context "#remove_attachment" do
setup do
Dummy.connection.change_table :dummies do |t|
t.column :avatar_file_name, :string
@@ -196,5 +199,63 @@ def teardown
end
end
end
+
+ context '#add_style' do
+ should 'process the specific style' do
+ dummy_with_avatar(thumbnail: '24x24', large: '124x124')
+
+ Dummy.connection.add_style :dummies, :avatar, large: '124x124'
+
+ assert RecordingProcessor.has_processed?(large: '124x124')
+ assert !RecordingProcessor.has_processed?(thumbnail: '24x24')
+ end
+
+ should 'raise if the style is missing' do
+ dummy_with_avatar(thumbnail: '24x24')
+
+ assert_raise ArgumentError do
+ Dummy.connection.add_style :dummies, :avatar, missing_style: '24x24'
+ end
+ end
+
+ should 'raise if the attachment is missing' do
+ dummy_with_avatar(thumbnail: '24x24')
+
+ assert_raise ArgumentError do
+ Dummy.connection.add_style :dummies, :missng_attachment, thumbnail: '24x24'
+ end
+ end
+
+ should 'raise if the model is missing' do
+ assert_raise ArgumentError do
+ Dummy.connection.add_style :missing_model, :avatar, thumbnail: '24x24'
+ end
+ end
+ end
+
+ context '#remove_style' do
+ should 'remove files for the specific style' do
+ dummy_with_avatar(large: '24x24')
+ large_path = Dummy.first.avatar.path(:large)
+ original_path = Dummy.first.avatar.path(:original)
+
+ Dummy.connection.remove_style :dummy, :avatar, :large
+
+ assert !File.exist?(large_path)
+ assert File.exist?(original_path)
+ end
+ end
+ end
+
+ def dummy_with_avatar(styles)
+ rebuild_model styles: styles, processors: [:recording]
+
+ file = File.new(fixture_file("50x50.png"), 'rb')
+ dummy = Dummy.new
+ dummy.avatar = file
+ dummy.save
+ file.close
+
+ RecordingProcessor.clear
end
end
View
29 test/style_adder_test.rb
@@ -0,0 +1,29 @@
+require './test/helper'
+require 'paperclip/style_adder'
+
+class StyleAdderTest < Test::Unit::TestCase
+ should 'process the specific style' do
+ register_recording_processor
+
+ Dummy = rebuild_model styles: { thumbnail: '24x24' }, processors: [:recording]
+ file = File.new(fixture_file("50x50.png"), 'rb')
+ dummy = Dummy.new
+ dummy.avatar = file
+ dummy.save
+ file.close
+
+ Dummy.class_eval do
+ has_attached_file :avatar, styles: { thumbnail: '24x24', large: '124x124' }, processors: [:recording]
+ Paperclip.reset_duplicate_clash_check!
+ end
+
+ dummy_enumerator = Dummy.all
+
+ RecordingProcessor.clear
+
+ Paperclip::StyleAdder.run dummy_enumerator, :avatar, large: '124x124'
+
+ assert RecordingProcessor.has_processed?(large: '124x124')
+ assert !RecordingProcessor.has_processed?(thumbnail: '24x24')
+ end
+end
View
24 test/style_remover_test.rb
@@ -0,0 +1,24 @@
+require './test/helper'
+require 'paperclip/style_remover'
+
+class StyleRemoverTest < Test::Unit::TestCase
+ should 'process the specific style' do
+ register_recording_processor
+
+ Dummy = rebuild_model styles: { large: '24x24' }, processors: [:recording]
+ file = File.new(fixture_file("50x50.png"), 'rb')
+ dummy = Dummy.new
+ dummy.avatar = file
+ dummy.save
+ file.close
+
+ large_path = dummy.avatar.path(:large)
+ original_path = dummy.avatar.path(:original)
+ dummy_enumerator = Dummy.all
+
+ Paperclip::StyleRemover.run dummy_enumerator, :avatar, :large
+
+ assert !File.exist?(large_path)
+ assert File.exist?(original_path)
+ end
+end
View
24 test/support/recording_processor.rb
@@ -0,0 +1,24 @@
+class RecordingProcessor
+ def self.make(file, options, attachment)
+ @style_hashes ||= []
+ @style_hashes << options
+ File.new('/etc/passwd')
+ end
+
+ def self.clear
+ @style_hashes = []
+ end
+
+ def self.has_processed?(expected_style_hash)
+ expected_geometries = expected_style_hash.values
+ @style_hashes && @style_hashes.any? do |style_hash|
+ expected_geometries.include?(style_hash[:geometry])
+ end
+ end
+end
+
+def register_recording_processor
+ Paperclip.configure do |c|
+ c.register_processor :recording, RecordingProcessor
+ end
+end
Something went wrong with that request. Please try again.