cropper 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
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
+