cropper 0.2.1

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.
Files changed (89) hide show
  1. data/.gitignore +8 -0
  2. data/.rspec +2 -0
  3. data/.rvmrc +48 -0
  4. data/Gemfile +7 -0
  5. data/README.md +17 -0
  6. data/Rakefile +31 -0
  7. data/app/assets/images/cropper/progress_stripes.png +0 -0
  8. data/app/assets/images/cropper/scale.png +0 -0
  9. data/app/assets/images/cropper/scale_large.png +0 -0
  10. data/app/assets/images/cropper/scale_small.png +0 -0
  11. data/app/assets/images/cropper/upload_spinner.gif +0 -0
  12. data/app/assets/javascripts/cropper.js.coffee +9 -0
  13. data/app/assets/javascripts/cropper/filedrop.js.coffee +255 -0
  14. data/app/assets/javascripts/cropper/upload_crop_scale.js.coffee +360 -0
  15. data/app/assets/javascripts/lib/es5-shim.js +1105 -0
  16. data/app/assets/javascripts/lib/modernizr.js +4 -0
  17. data/app/assets/stylesheets/cropper/uploads.css.sass +203 -0
  18. data/app/controllers/cropper/application_controller.rb +4 -0
  19. data/app/controllers/cropper/uploads_controller.rb +74 -0
  20. data/app/helpers/cropper/application_helper.rb +4 -0
  21. data/app/models/cropper/upload.rb +231 -0
  22. data/app/views/cropper/uploads/_crop.html.haml +34 -0
  23. data/app/views/cropper/uploads/_error.html.haml +1 -0
  24. data/app/views/cropper/uploads/_pick.html.haml +42 -0
  25. data/app/views/cropper/uploads/edit.html.haml +6 -0
  26. data/app/views/cropper/uploads/new.html.haml +5 -0
  27. data/app/views/cropper/uploads/show.html.haml +1 -0
  28. data/config/locales/en.yml +5 -0
  29. data/config/routes.rb +3 -0
  30. data/cropper.gemspec +32 -0
  31. data/db/migrate/20120510103921_uploads.rb +11 -0
  32. data/db/migrate/20130226190434_uploads_hold_crop_data.rb +8 -0
  33. data/db/migrate/20130402074802_upload_holder.rb +20 -0
  34. data/init.rb +4 -0
  35. data/lib/cropper.rb +159 -0
  36. data/lib/cropper/engine.rb +8 -0
  37. data/lib/cropper/glue.rb +20 -0
  38. data/lib/cropper/routing.rb +23 -0
  39. data/lib/cropper/schema.rb +63 -0
  40. data/lib/cropper/version.rb +3 -0
  41. data/lib/paperclip/geometry_transformation.rb +80 -0
  42. data/lib/paperclip/validators/attachment_height_validator.rb +89 -0
  43. data/lib/paperclip/validators/attachment_width_validator.rb +89 -0
  44. data/lib/paperclip_processors/offset_thumbnail.rb +93 -0
  45. data/lib/tasks/cropper_tasks.rake +4 -0
  46. data/script/rails +8 -0
  47. data/spec/acceptance/acceptance_helper.rb +2 -0
  48. data/spec/controllers/uploads_controller_spec.rb +5 -0
  49. data/spec/dummy/README.rdoc +261 -0
  50. data/spec/dummy/Rakefile +7 -0
  51. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  52. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  53. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  54. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  55. data/spec/dummy/app/mailers/.gitkeep +0 -0
  56. data/spec/dummy/app/models/.gitkeep +0 -0
  57. data/spec/dummy/app/models/thing.rb +4 -0
  58. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  59. data/spec/dummy/config.ru +4 -0
  60. data/spec/dummy/config/application.rb +56 -0
  61. data/spec/dummy/config/boot.rb +10 -0
  62. data/spec/dummy/config/database.yml +25 -0
  63. data/spec/dummy/config/environment.rb +5 -0
  64. data/spec/dummy/config/environments/development.rb +37 -0
  65. data/spec/dummy/config/environments/production.rb +67 -0
  66. data/spec/dummy/config/environments/test.rb +37 -0
  67. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  68. data/spec/dummy/config/initializers/inflections.rb +15 -0
  69. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  70. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  71. data/spec/dummy/config/initializers/session_store.rb +8 -0
  72. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  73. data/spec/dummy/config/locales/en.yml +5 -0
  74. data/spec/dummy/config/routes.rb +6 -0
  75. data/spec/dummy/db/migrate/20120510104910_things.rb +8 -0
  76. data/spec/dummy/db/schema.rb +51 -0
  77. data/spec/dummy/lib/assets/.gitkeep +0 -0
  78. data/spec/dummy/log/.gitkeep +0 -0
  79. data/spec/dummy/public/404.html +26 -0
  80. data/spec/dummy/public/422.html +26 -0
  81. data/spec/dummy/public/500.html +25 -0
  82. data/spec/dummy/public/favicon.ico +0 -0
  83. data/spec/dummy/script/rails +6 -0
  84. data/spec/fixtures/images/icon.png +0 -0
  85. data/spec/fixtures/images/test.jpg +0 -0
  86. data/spec/models/thing_spec.rb +6 -0
  87. data/spec/models/upload_spec.rb +7 -0
  88. data/spec/spec_helper.rb +18 -0
  89. metadata +326 -0
@@ -0,0 +1,34 @@
1
+ - upload ||= @upload
2
+ - prefix = upload.holder_column
3
+ - multiplier ||= (params[:multiplier] || 1).to_i
4
+
5
+ - target_w = upload.crop_width / multiplier
6
+ - target_h = upload.crop_height / multiplier
7
+
8
+ - max_w = upload.precrop_width / multiplier
9
+ - max_h = upload.precrop_height / multiplier
10
+
11
+ - initial_w = upload.scale_width? ? upload.scale_width / multiplier : max_w * 3 / 4
12
+ - initial_h = upload.scale_height? ? upload.scale_height / multiplier : max_h * 3 / 4
13
+
14
+ - offset_l = upload.offset_left? ? upload.offset_left / multiplier : (target_w - initial_w)/2
15
+ - offset_t = upload.offset_top? ? upload.offset_top / multiplier : (target_h - initial_h)/2
16
+
17
+ .cropper
18
+ .preview{:style => "width: #{initial_w}px; height: #{initial_h}px; top: #{offset_t}px; left: #{offset_l}px"}
19
+ %img{:src => upload.file.url(:precrop)}
20
+
21
+ %p.drag_instructions
22
+ = t :drag_instructions
23
+
24
+ = fields_for upload.holder do |hf|
25
+ = hf.fields_for :"#{upload.holder_column}_upload", upload do |uf|
26
+ %fieldset.crop
27
+ = uf.hidden_field :id, :data => {:"clear-on-detach" => true}
28
+ = uf.hidden_field :offset_left, :value => offset_l, :class => 'ol'
29
+ = uf.hidden_field :offset_top, :value => offset_t, :class => 'ot'
30
+ = uf.hidden_field :multiplier, :value => multiplier
31
+ .scaler
32
+ %p.range
33
+ = uf.range_field :scale_width, :min => target_w, :max => max_w, :value => initial_w, :class => 'sw'
34
+ = uf.hidden_field :scale_height, :value => initial_h, :class => 'sh'
@@ -0,0 +1 @@
1
+ = @upload.errors[:file].first
@@ -0,0 +1,42 @@
1
+ - upload ||= @upload
2
+ - holder ||= @holder || upload.holder
3
+ - extra_controls ||= false
4
+
5
+ - multiplier ||= params[:multiplier] || 1
6
+
7
+ = fields_for holder do |hf|
8
+ = hf.fields_for :"#{upload.holder_column}_upload", upload do |uf|
9
+ .uploadbox{:data => {:action => "upload", :url => cropper.uploads_path(:holder_id => holder.id, :holder_type => holder.class.to_s.underscore, :holder_column => upload.holder_column, :multiplier => multiplier, :format => :js)}}
10
+ .prompt
11
+ = link_to t(:click_to_upload), '#', :data => {:action => "pick"}
12
+ .droppable
13
+ .note
14
+ = t :drop_to_upload
15
+ .img<
16
+ - if upload.persisted?
17
+ - width = upload.crop_width / multiplier
18
+ - height = upload.crop_height / multiplier
19
+ = link_to image_tag(upload.file.url(:cropped), :style => "width: #{width}px; height: #{height}px"), cropper.edit_upload_url(upload, :multiplier => multiplier), :data => {:action => "recrop"}
20
+ = link_to t(:detach_upload), '#', :class => 'detach', :data => {:action => 'detach'}
21
+
22
+ %p.file
23
+ = uf.file_field :file, :class => 'file_upload', :accept => "image"
24
+
25
+ %p.instructions
26
+ - unless upload.persisted?
27
+ = link_to t(:click_to_upload), '#', :data => {:action => "pick"}
28
+ %span.droppable
29
+ = t :drop_to_upload
30
+
31
+ - if extra_controls
32
+ .controls
33
+ #uploader.upload.button
34
+ = link_to t(:upload), '#', :data => {:action => "pick"}
35
+ .edit.button
36
+ - if upload && upload.persisted?
37
+ = link_to t(:edit_crop), cropper.edit_upload_url(upload, :multiplier => multiplier), :data => {:action => "recrop"}
38
+ - else
39
+ = link_to t(:edit_crop), '#', :data => {:action => "recrop"}, :class => 'unavailable'
40
+ .cancel.button
41
+ = link_to t(:cancel), '#'
42
+
@@ -0,0 +1,6 @@
1
+ / This we show when setting the crop parameters of an upload.
2
+ / It is usually either summoned by a click or returned on a
3
+ / redirect from uploads#create. For more direct editing you could
4
+ / also nest cropper/uploads/crop in the recipient's edit form.
5
+ .edit_upload
6
+ = render :partial => "cropper/uploads/crop"
@@ -0,0 +1,5 @@
1
+ / This we show when first creating an upload. It is a standard base
2
+ / from which to launch the javascript picker/uploader that will create
3
+ / an upload object and move us into edit (ie crop) mode.
4
+ .upload
5
+ = render :partial => "cropper/uploads/pick"
@@ -0,0 +1 @@
1
+ = image_tag @upload.file.url(:cropped)
@@ -0,0 +1,5 @@
1
+ en:
2
+ click_to_upload: "Click anywhere on the picture to choose and upload an image"
3
+ drop_to_upload: "(or you can just drop a file here)"
4
+ drag_instructions: "Drag slider to resize. Drag image to change crop."
5
+ click_to_upload: "Click to choose and upload an image"
@@ -0,0 +1,3 @@
1
+ Cropper::Engine.routes.draw do
2
+ resources :uploads
3
+ end
@@ -0,0 +1,32 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "cropper/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "cropper"
7
+ s.version = Cropper::VERSION
8
+ s.authors = ["William Ross"]
9
+ s.email = ["will@spanner.org"]
10
+ s.homepage = ""
11
+ s.summary = %q{A simple but specific way to attach (re)croppable uploads to any model}
12
+ s.description = %q{Provides a mechanism for uploading, cropping and reusing images in any of your models.}
13
+
14
+ s.rubyforge_project = "cropper"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_dependency "rails", "~> 3.2.0"
22
+ s.add_dependency('paperclip', '~> 3.3')
23
+ s.add_dependency('delayed_job_active_record')
24
+
25
+ s.add_development_dependency "rake"
26
+ s.add_development_dependency "rspec-rails"
27
+ s.add_development_dependency "shoulda-matchers"
28
+ s.add_development_dependency "capybara"
29
+ s.add_development_dependency "acts_as_fu"
30
+ s.add_development_dependency "sqlite3"
31
+
32
+ end
@@ -0,0 +1,11 @@
1
+ class Uploads < ActiveRecord::Migration
2
+ def change
3
+ create_table :cropper_uploads do |t|
4
+ t.attachment :file
5
+ t.string :original_extension
6
+ t.integer :original_width
7
+ t.integer :original_height
8
+ t.timestamps
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,8 @@
1
+ class UploadsHoldCropData < ActiveRecord::Migration
2
+ def change
3
+ add_column :cropper_uploads, :scale_width, :integer
4
+ add_column :cropper_uploads, :scale_height, :integer
5
+ add_column :cropper_uploads, :offset_left, :integer
6
+ add_column :cropper_uploads, :offset_top, :integer
7
+ end
8
+ end
@@ -0,0 +1,20 @@
1
+ class UploadHolder < ActiveRecord::Migration
2
+ def change
3
+ add_column :cropper_uploads, :holder_id, :integer
4
+ add_column :cropper_uploads, :holder_type, :string
5
+ add_column :cropper_uploads, :holder_column, :string
6
+
7
+ Cropper::Upload.reset_column_information
8
+ Cropper::Upload.all.each do |u|
9
+ puts "finding match for upload #{u.id}"
10
+ if person = Droom::Person.where(:image_upload_id => u.id).first
11
+ puts "-> #{person.formal_name}"
12
+ %w{offset_left offset_top scale_width scale_height}.each do |col|
13
+ u.update_column(col.to_sym, person.send(:"image_#{col}"))
14
+ end
15
+ u.update_column(:holder_type, "Droom::Person")
16
+ u.update_column(:holder_column, 'image')
17
+ end
18
+ end
19
+ end
20
+ end
data/init.rb ADDED
@@ -0,0 +1,4 @@
1
+ require File.join(File.dirname(__FILE__), "lib", "cropper")
2
+ require 'cropper/railtie'
3
+
4
+ Cropper::Railtie.insert
@@ -0,0 +1,159 @@
1
+ require "paperclip"
2
+ require "paperclip/validators/attachment_height_validator"
3
+ require "paperclip/validators/attachment_width_validator"
4
+ require "paperclip/geometry_transformation"
5
+ require "paperclip_processors/offset_thumbnail"
6
+ require "cropper/engine"
7
+ require "cropper/routing"
8
+ require 'cropper/glue'
9
+ require 'open-uri'
10
+
11
+ module Cropper
12
+ # Module configuration is handled in a simple way by accessors on the Cropper module. At the moment
13
+ # there aren't many. Any, in fact, but we do hold some structural information about the uploading classes.
14
+ #
15
+ mattr_accessor :uploadable_classes, :upload_options
16
+
17
+ class << self
18
+ def uploadable_classes
19
+ @@uploadable_classes ||= {}
20
+ end
21
+
22
+ def upload_options
23
+ @@upload_options ||= {}
24
+ end
25
+
26
+ def declare_uploadable(klass, column, options)
27
+ k = klass.to_s.underscore.to_sym
28
+ uploadable_classes[k] ||= []
29
+ uploadable_classes[k].push(column.to_sym)
30
+ upload_options[k] ||= {}
31
+ upload_options[k][column.to_sym] = {
32
+ :precrop_geometry => options[:styles][:precrop],
33
+ :crop_geometry => options[:geometry]
34
+ }
35
+ end
36
+
37
+ def crop_geometry(klass, column)
38
+ k = klass.to_s.underscore.to_sym
39
+ upload_options[k][column.to_sym][:crop_geometry]
40
+ end
41
+
42
+ def precrop_geometry(klass, column)
43
+ k = klass.to_s.underscore.to_sym
44
+ Rails.logger.warn "precrop_geometry(#{k.inspect}, #{column.inspect})"
45
+ upload_options[k][column.to_sym][:precrop_geometry]
46
+ end
47
+ end
48
+
49
+ # Cropper::ClassMethods is included into ActiveRecord::Base in the same way as the Paperclip module.
50
+ # It adds a `has_upload` class method that defines an attachment and adds several instance methods
51
+ # that will return the values that determine its cropping. Those values are usually but not
52
+ # necessarily given by the user.
53
+ #
54
+ module ClassMethods
55
+ ## Defining upload columns
56
+ #
57
+ # *has_upload* brings in the whole machinery of receiving and cropping an uploaded file. The options given are exactly the same
58
+ # as paperclip's `has_attached_file` call, with the addition of a 'crop' parameter that gives the target dimensions. The crop
59
+ # value is applied in the upload object, then any other styles you specify here are applied to that cropped image when it is
60
+ # passed to this model.
61
+ #
62
+ # The practical effect of this is that your :original file always has your :crop dimensions.
63
+ #
64
+ # You can also set an optional :precrop option to set the dimensions of the file that is presented in the cropping interface.
65
+ # The default is 1600x1600<, which is often going to be too big and may cause unwanted scaling-up of your source image.
66
+ # The precrop geometry is also applied in the upload object, when a file is first uploaded.
67
+ #
68
+ # The call usually looks like this:
69
+ #
70
+ # class User < ActiveRecord::Base
71
+ # has_upload :avatar,
72
+ # :precrop => '1440x960<
73
+ # :crop => '720x480#'
74
+ # :styles => {
75
+ # :thumbnail => "60x60#"
76
+ # }
77
+ #
78
+ # The crop geometry will always be treated as a fixed target shape: that is, as though it ended in '#'. Any other suffix will be
79
+ # removed. The other styles that you define can be in any form that paperclip understands.
80
+ #
81
+ # A model can make more than one has_upload call. The methods we define here are all prefixed with the association name, so you
82
+ # usually end up calling methods like `person.icon_upload`.
83
+ #
84
+ def has_upload(attachment_name=:image, options={})
85
+ # unless !table_exists? || column_names.include?("#{attachment_name}_upload_id")
86
+ # raise RuntimeError, "has_upload(#{attachment_name}) called on class #{self.to_s} but we have no #{attachment_name}_upload_id column" unless
87
+ # end
88
+
89
+ # allow uploads to be assigned to this class and column in the UploadsController.
90
+ options.reverse_merge!(:geometry => "960x640#", :whiny => true, :styles => {})
91
+ options[:styles].reverse_merge!({:precrop => "1600x1600<"})
92
+ Cropper.declare_uploadable(self, attachment_name, options)
93
+
94
+ ### Upload association
95
+ #
96
+ # [uploads](/app/models/upload.html) are the original image files uploaded and cropped by this person.
97
+ #
98
+ has_many :uploads, :class_name => "Cropper::Upload", :as => :holder # so what happens when we call this twice?
99
+
100
+ # Ok, I give in. We have to require an image_upload_id column. It's silly trying to fake the whole association machine.
101
+ belongs_to :"#{attachment_name}_upload", :class_name => "Cropper::Upload", :autosave => true
102
+
103
+ # accepts_nested_attributes_for :"#{attachment_name}_upload"
104
+ attr_accessible :"#{attachment_name}_upload_attributes"
105
+ after_save :"connect_to_#{attachment_name}_upload"
106
+
107
+ #...but we still need to intervene to set the holder_column column of the upload when it is assigned
108
+ define_method :"#{attachment_name}_upload_with_holder_column=" do |upload|
109
+ upload.holder_column = attachment_name
110
+ send :"#{attachment_name}_upload_without_holder_column=", upload
111
+ end
112
+ alias_method_chain :"#{attachment_name}_upload=", :holder_column
113
+
114
+ #...or built.
115
+ define_method :"build_#{attachment_name}_upload_with_holder_column" do |attributes={}, options={}|
116
+ attributes[:holder_column] = attachment_name
117
+ attributes[:holder_type] ||= self.class.to_s unless attributes[:holder]
118
+ send :"build_#{attachment_name}_upload_without_holder_column", attributes, options
119
+ end
120
+ alias_method_chain :"build_#{attachment_name}_upload", :holder_column
121
+
122
+ # when a new holder is created, the interface means that the upload will have preceded it. In that
123
+ # case we will have forced the setting of holder_type to allow crop-dimension lookups. When eventually
124
+ # saving, we have to make sure that holder_id is also present.
125
+ define_method :"connect_to_#{attachment_name}_upload" do
126
+ if upload = send(:"#{attachment_name}_upload")
127
+ upload.update_column(:holder_type, self.class.to_s)
128
+ upload.update_column(:holder_id, self.id)
129
+ upload.update_column(:holder_column, attachment_name)
130
+ end
131
+ Rails.logger.warn "connect_to_#{attachment_name}_upload: upload is #{upload.inspect}. Self is #{self.inspect}"
132
+ true
133
+ end
134
+
135
+ define_method :"#{attachment_name}_upload_attributes=" do |attributes|
136
+ # assign_nested_attributes_for_image_upload_association(:image_upload, attributes, mass_assignment_options)
137
+ if upload_id = attributes.delete('id')
138
+ self.send :"#{attachment_name}_upload=", Cropper::Upload.find(upload_id)
139
+ end
140
+ if upload = self.send(:"#{attachment_name}_upload")
141
+ Rails.logger.warn "setting upload attributes for #{upload.inspect} -> #{attributes.inspect}"
142
+ upload.assign_attributes(attributes)
143
+ end
144
+ end
145
+
146
+ ### Attachment
147
+ #
148
+ # Eventually we get to the point. Image attachments work in the usual Paperclip way except that they are always received from
149
+ # an upload object, presumably in an already-cropped form.
150
+ # If this class then generates other styles, they are built from that cropped image rather than the file originally uploaded.
151
+ #
152
+ has_attached_file attachment_name, options
153
+
154
+ # Note: updates to the attached file are pushed to the holder from the upload after processing is complete, so we won't always
155
+ # have them immediately. Best course is to display the upload cropped rather than the holder original.
156
+
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,8 @@
1
+ module Cropper
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace Cropper
4
+ initializer "cropper.integration" do
5
+ ActiveRecord::Base.send(:include, Cropper::Glue)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,20 @@
1
+ require 'cropper/schema'
2
+
3
+ module Cropper
4
+ module Glue
5
+ def self.included base #:nodoc:
6
+
7
+ # Extend ActiveRecord::Base with Cropper::ClassMethods, as defined in cropper.rb.
8
+ #
9
+ base.extend ClassMethods
10
+
11
+ # Load migration helpers into all the right places.
12
+ #
13
+ if defined?(ActiveRecord)
14
+ ActiveRecord::ConnectionAdapters::AbstractAdapter.send(:include, Cropper::Schema)
15
+ ActiveRecord::ConnectionAdapters::Table.send(:include, Cropper::Schema)
16
+ ActiveRecord::ConnectionAdapters::TableDefinition.send(:include, Cropper::Schema)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,23 @@
1
+ module ActionDispatch::Routing
2
+
3
+ class Mapper
4
+ def cropper_for(*res)
5
+ options = res.extract_options!
6
+ res.map!(&:to_sym)
7
+
8
+ # options[:path] ||= "cropper"
9
+ # options[:as] ||= :cropper
10
+ # options[:path] = "#{options[:path]}/" unless options[:path].last == '/'
11
+ # mount Cropper::Engine => options[:path], :as => options[:as]
12
+
13
+ Rails.application.routes.draw do
14
+ resources :uploads, :module => :cropper
15
+ res.each do |resource|
16
+ resources resource do
17
+ resources :uploads, :module => :cropper
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,63 @@
1
+ module Cropper
2
+ # Provides helpers that can be used in migrations.
3
+ # Copied from, and often makes calls on, the equivalent file in Paperclip.
4
+ #
5
+ module Schema
6
+ UPLOAD_COLUMNS = {
7
+ :file_name => :string,
8
+ :content_type => :string,
9
+ :file_size => :integer,
10
+ :updated_at => :datetime,
11
+ :upload_id => :integer,
12
+ :scale_width => :integer,
13
+ :scale_height => :integer,
14
+ :offset_left => :integer,
15
+ :offset_top => :integer
16
+ }
17
+
18
+ def self.included(base)
19
+ ActiveRecord::ConnectionAdapters::Table.send :include, TableDefinition
20
+ ActiveRecord::ConnectionAdapters::TableDefinition.send :include, TableDefinition
21
+ ActiveRecord::ConnectionAdapters::AbstractAdapter.send :include, Statements
22
+ end
23
+
24
+ module Statements
25
+ def add_upload(table_name, *attachment_names)
26
+ raise ArgumentError, "Please specify attachment name in your add_upload call in your migration." if attachment_names.empty?
27
+
28
+ attachment_names.each do |attachment_name|
29
+ UPLOAD_COLUMNS.each_pair do |column_name, column_type|
30
+ unless column_exists? table_name, "#{attachment_name}_#{column_name}"
31
+ add_column(table_name, "#{attachment_name}_#{column_name}", column_type)
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+
38
+ def remove_upload(table_name, *attachment_names)
39
+ raise ArgumentError, "Please specify attachment name in your remove_upload call in your migration." if attachment_names.empty?
40
+
41
+ attachment_names.each do |attachment_name|
42
+ COLUMNS.each_pair do |column_name, column_type|
43
+ if column_exists? table_name, "#{attachment_name}_#{column_name}"
44
+ remove_column(table_name, "#{attachment_name}_#{column_name}")
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ module TableDefinition
52
+ def cropped_attachment(*attachment_names)
53
+ attachment_names.each do |attachment_name|
54
+ UPLOAD_COLUMNS.each_pair do |column_name, column_type|
55
+ column("#{attachment_name}_#{column_name}", column_type)
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ end
62
+ end
63
+