papermill 1.4.3 → 2.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -108,15 +108,6 @@ Maybe you don't want users to use your application as a thumbnailing farm for th
108
108
  * Brute solution: pass :use_url_key to true in the options (config/initializers/papermill.rb). A crypted hash unique to your application and to each asset and to the requested style will be added to the URL. No more happy-guessing of anything. Do that first before going live, or you'll have to migrate all assets...
109
109
  * pass :alias_only to true. This will disable the possibility to generate thumbnails with a papermill string in the url, but won't do anything for the member area thing. Plus you will have to use aliases only, form helpers included (pass :thumbnail => { :style => :some_alias })
110
110
 
111
- === Restricted-area/back-office
112
-
113
- Go to the options and look for :
114
- :authorize_create => true,
115
- :authorize_update_and_destroy => true,
116
- :authorize_multiple_modification => true,
117
-
118
- You will find a quick & dirty solution to pass your authorizations rules in before_filters, before any asset gets hurt.
119
-
120
111
  == Usage
121
112
 
122
113
  Assetable is the class that has_many papermill_assets (i.e. the class with the papermill declaration)
@@ -125,23 +116,11 @@ Assetable is the class that has_many papermill_assets (i.e. the class with the p
125
116
 
126
117
  You can have a generic association and as many declarative associations as you want in your model. Papermill will always use specific if found.
127
118
 
128
- article.rb
129
-
130
- class Article < ActiveRecord::Base
131
- papermill :class_name => ColorAsset, other_options.. # generic, will use ColorAsset instead of PapermillAsset (ColorAsset must be an STIed PapermillAsset)
132
- end
133
-
134
- entry.rb
135
-
136
- class Entry < ActiveRecord::Base
137
- papermill :mug_shot, other_options.. # specific association on :mug_shot
138
- papermill :diaporama, :class_name => ColorAsset, other_options.. # specific association on :diaporama will use ColorAssets
139
- end
140
-
141
- color_asset.rb # You can add columns to papermill_assets and use STI on PapermillAsset to extend defaults abilities (or re-open PapermillAsset/monkey-patch it/use a polymorphic association on PapermillAsset to extend it with behaviors..)
142
-
143
- class ColorAsset < PapermillAsset
144
- named_scope :red, :conditions => {:color => 'red'}
119
+ class Article
120
+ papermill :images
121
+ papermill :pdf_version
122
+ papermill :cover_image
123
+ papermill :illustrations
145
124
  end
146
125
 
147
126
  === Form helpers
@@ -154,8 +133,8 @@ Example form:
154
133
 
155
134
  # Now I need to be able to upload as many documents as I need, and sort them at will
156
135
  # no document should be bigger than 1MB (respect the quoting!)
157
- # and I don't want any dashboard mass_edit feature, just mass_delete (delete all)
158
- f.assets_upload :documentation, :swfupload => { :file_size_limit => "'1 MB'" }, :dashboard => [:mass_delete]
136
+ # and I don't want the mass_edit feature
137
+ f.assets_upload :documentation, :swfupload => { :file_size_limit => "'1 MB'" }, :mass_edit => false
159
138
 
160
139
  # I need to display *one* cover *image*, format will be 200x200
161
140
  # targetted_size will give the uploader hints when cropping the image after upload : desired display size and wanted aspect-ratio.
@@ -195,10 +174,6 @@ With FormTagHelpers, use (image_upload_tag | images_upload_tag | asset_upload_ta
195
174
 
196
175
  image_upload_tag @article, :cover_image, :targetted_size => "200x200"
197
176
 
198
- For resources not linked to any assetable model, you can use upload_tags without any Assetable
199
-
200
- image_upload_tag "#{current_organization.name}_logo", :targetted_size => "200x200"
201
-
202
177
  === Asset editing
203
178
 
204
179
  * double-click on any uploaded asset in any form-helper to access & edit his properties
@@ -292,28 +267,15 @@ Example:
292
267
 
293
268
  === Resource access
294
269
 
295
- Papermill generates an #assets(:key, *args) named_scope
296
- @article.assets(:illustrations)
297
- @article.assets(:illustrations, :order => "created_at DESC")
298
- @article.assets(:illustrations).red.first
299
- # etc.
270
+ Papermill generates an #<association_key> association
300
271
 
301
- With declarative papermill associations, Papermill also generates an #<association_key>(*args) named_scope
302
- @entry.mug_shot.first
303
- @entry.diaporama
304
- @entry.diaporama(:order => "created_at DESC")
305
- @entry.diaporama.red
306
- # === @entry.diaporama(:conditions => {:color => "red"})
307
- # === @entry.assets(:diaporama, :conditions => {:color => "red"})
272
+ @entry.mug_shots.first
273
+ @entry.diaporamas.each do |image| ..
308
274
  # etc.
309
-
310
- Or for non-assetable resources :
311
- PapermillAsset.key("#{current_organization.name}_logo").first
312
- ColorAsset.all.red
313
-
275
+
314
276
  === Using PapermillAsset
315
277
 
316
- @asset = @entry.mug_shot.first
278
+ @asset = @entry.mug_shots.first
317
279
  image_tag @asset.url # original
318
280
  image_tag @asset.url("100x>") # assuming asset is an image
319
281
  image_tag @asset.url(:big) # assuming you have a :big alias
data/TODO.txt CHANGED
@@ -0,0 +1,7 @@
1
+ # default papermill association with method_missing/define_method chain
2
+ # extend watermark/copyright possibilities
3
+ # refresh images when cropped
4
+ # authorize images before edit/crop
5
+ # documentation for papermill_associations
6
+ # file search helper for papermill_associations
7
+ # tests, loads of them
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.4.3
1
+ 2.0.0.pre
@@ -1,27 +1,8 @@
1
1
  class PapermillController < ApplicationController
2
2
  unloadable
3
3
  prepend_before_filter :load_asset, :only => [ "show", "destroy", "update", "edit", "crop" ]
4
- prepend_before_filter :load_old_asset_and_assetable, :only => ["create"]
5
- prepend_before_filter :load_assets, :only => [ "sort", "mass_delete", "mass_edit", "mass_thumbnail_reset" ]
6
4
  skip_before_filter :verify_authenticity_token, :only => [:create] # not needed (Flash same origin policy)
7
5
 
8
- before_filter :authorize_create, :only => [:create]
9
- before_filter :authorize_update_and_destroy, :only => [:update, :destroy]
10
- before_filter :authorize_multiple_modification, :only => [:sort, :mass_delete, :mass_edit, :mass_thumbnail_reset]
11
-
12
- def authorize_create
13
- eval(Papermill::options[:authorize_create])
14
- end
15
-
16
- def authorize_update_and_destroy
17
- eval(Papermill::options[:authorize_update_and_destroy])
18
- end
19
-
20
- def authorize_multiple_modification
21
- eval(Papermill::options[:authorize_multiple_modification])
22
- end
23
-
24
-
25
6
  def show
26
7
  # first escaping is done by rails prior to route recognition, need to do a second one on MSWIN systems to get original one.
27
8
  params[:style] = CGI::unescape(params[:style]) if Papermill::MSWIN
@@ -35,17 +16,16 @@ class PapermillController < ApplicationController
35
16
  def create
36
17
  @asset = params[:asset_class].constantize.new(params.reject{|k, v| !(PapermillAsset.columns.map(&:name)+["Filedata", "Filename"]).include?(k)})
37
18
  if @asset.save
38
- @old_asset.destroy if @old_asset
39
- output = render_to_string(:partial => "papermill/asset", :object => @asset, :locals => { :gallery => params[:gallery], :thumbnail_style => params[:thumbnail_style], :targetted_size => params[:targetted_size] })
19
+ output = render_to_string(:partial => "papermill/asset", :object => @asset, :locals => { :gallery => params[:gallery], :thumbnail_style => params[:thumbnail_style], :targetted_size => params[:targetted_size], :field_name => params[:field_name], :field_id => params[:field_id] })
40
20
  render :update do |page|
41
- page << %{ jQuery('##{params[:Fileid]}').replaceWith('#{escape_javascript output}'); }
42
- page << %{ jQuery('#papermill_asset_#{@old_asset.id}').remove() } if @old_asset
21
+ page << %{ jQuery('##{params[:Fileid]}').replaceWith('#{escape_javascript output}') }
22
+ page << %{ jQuery('##{params[:Oldfileid]}').remove() } if params[:Oldfileid]
43
23
  end
44
24
  else
45
25
  render :update do |page|
46
- page << %{ notify('#{@asset.name}', '#{escape_javascript @asset.errors.full_messages.join("<br />")}', 'error'); }
47
- page << %{ jQuery('##{params[:Fileid]}').remove(); }
48
- page << %{ jQuery('#papermill_asset_#{@old_asset.id}').show(); } if @old_asset
26
+ page << %{ notify('#{@asset.name}', '#{escape_javascript @asset.errors.full_messages.join("<br />")}', 'error') }
27
+ page << %{ jQuery('##{params[:Fileid]}').remove() }
28
+ page << %{ jQuery('##{params[:Oldfileid]}').show() } if params[:Oldfileid]
49
29
  end
50
30
  end
51
31
  end
@@ -68,58 +48,33 @@ class PapermillController < ApplicationController
68
48
  end
69
49
  end
70
50
  end
71
-
72
- def destroy
73
- @asset.destroy
74
- render :update do |page|
75
- page << %{ jQuery("#papermill_asset_#{params[:id]}").remove(); }
76
- end
77
- end
78
51
 
79
- def sort
80
- @assets.each_with_index do |asset, index|
81
- asset.update_attribute(:position, index + 1)
82
- end
83
- render :nothing => true
84
- end
85
-
86
- def mass_delete
52
+ def add_list
53
+ @assets = PapermillAsset.find(params[:ids]).sort_by{|asset|params[:ids].index(asset.id.to_s)} unless params[:ids].blank?
54
+ output = render_to_string(:partial => "papermill/asset", :collection => (@assets || []), :locals => { :gallery => params[:gallery], :thumbnail_style => params[:thumbnail_style], :targetted_size => params[:targetted_size], :field_name => params[:field_name], :field_id => params[:field_id] })
87
55
  render :update do |page|
88
- @assets.each do |asset|
89
- page << %{ jQuery("#papermill_asset_#{asset.id}").remove(); } if asset.destroy
90
- end
56
+ page << %{ close_popup(); jQuery('##{params[:field_id]}').html('#{escape_javascript output}'); }
91
57
  end
92
58
  end
93
59
 
60
+ def browser
61
+ ids_list = params[params[:field_id] + "_papermill_asset"]
62
+ @selected_assets = ids_list.blank? ? [] : params[:asset_class].constantize.find(ids_list).sort_by{|asset| ids_list.index(asset.id.to_s)}
63
+ @other_assets = params[:asset_class].constantize.all - @selected_assets
64
+ render :action => "browser", :layout => false
65
+ end
66
+
94
67
  def mass_edit
68
+ @assets = (params[(params[:list_id] + "_papermill_asset").to_sym] || []).map{ |id| PapermillAsset.find(id, :include => "assetable") }
95
69
  @assets.each { |asset| asset.update_attribute(params[:attribute], params[:value]) }
96
70
  render :update do |page|
97
71
  page << %{ notify("", "#{ escape_javascript t("papermill.updated", :resource => @assets.map(&:name).to_sentence) }", "notice"); } unless @assets.blank?
98
72
  end
99
73
  end
100
74
 
101
- def mass_thumbnail_reset
102
- @assets.each &:destroy_thumbnails
103
- render :update do |page|
104
- page << %{ notify("", "#{ escape_javascript t("papermill.updated", :resource => @assets.map(&:name).to_sentence) }", "notice"); } unless @assets.blank?
105
- end
106
- end
107
-
108
75
  private
109
76
 
110
- def load_old_asset_and_assetable
111
- unless params[:gallery]
112
- @old_asset = PapermillAsset.find(:first, :conditions => {:assetable_type => params[:assetable_type], :assetable_id => params[:assetable_id], :assetable_key => params[:assetable_key]})
113
- end
114
- @assetable = params[:assetable_type].constantize.find_by_id(params[:assetable_id])
115
- end
116
-
117
77
  def load_asset
118
- @asset = PapermillAsset.find(params[:id] || (params[:id0] + params[:id1] + params[:id2]).to_i, :include => "assetable")
119
- @assetable = @asset.assetable
120
- end
121
-
122
- def load_assets
123
- @assets = (params[:papermill_asset] || []).map{ |id| PapermillAsset.find(id, :include => "assetable") }
78
+ @asset = PapermillAsset.find(params[:id] || (params[:id0] + params[:id1] + params[:id2]).to_i)
124
79
  end
125
80
  end
@@ -1,5 +1,5 @@
1
- <%- dom_id = "papermill_asset_#{asset.id}" -%>
2
- <%- delete_link = %{<a onclick="if(confirm('#{escape_javascript t("papermill.delete-confirmation", :resource => asset.name)}')){ jQuery.ajax({async:true, dataType:'script', type:'delete', url:'#{papermill_url(asset)}'})}; return false;" href="#" class="delete"><img title="#{escape_javascript t("papermill.delete", :resource => asset.name)}" src="/papermill/images/delete.png" alt="delete"/></a>} %>
1
+ <%- dom_id = "#{field_id}_papermill_asset_#{asset.id}" -%>
2
+ <%- delete_link = %{<a onclick="$('##{dom_id}').remove(); return false;" href="#" class="delete"><img title="#{escape_javascript t("papermill.delete", :resource => asset.name)}" src="/papermill/images/delete.png" alt="delete"/></a>} %>
3
3
 
4
4
  <div id="<%= dom_id %>" class="asset" title="<%= t("papermill.#{(thumbnail_style) ? "thumbnail-" : ""}edit-title", :resource => asset.name) %>" onDblClick="popup(jQuery(this).attr('rel')); return false;" rel="<%= edit_papermill_url(asset, :targetted_size => targetted_size) %>">
5
5
  <%= delete_link %>
@@ -8,4 +8,5 @@
8
8
  <%- else -%>
9
9
  <%= render :partial => "papermill/raw_asset", :object => asset %>
10
10
  <%- end -%>
11
+ <%= hidden_field_tag field_name, asset.id.to_s, :id => nil %>
11
12
  </div>
@@ -0,0 +1,21 @@
1
+ <% params.delete(:action) %>
2
+ <form onsubmit="jQuery.ajax({data:jQuery.param(jQuery(this).serializeArray()), dataType:'script', type:'post', url:'<%= add_list_papermill_path(params) %>'}); close(); return false;" method="post">
3
+ <ul style="width:800px;display:block">
4
+ <% @selected_assets.each do |asset| %>
5
+ <li style="display:block; float:left">
6
+ <input type=checkbox id="papermill_asset_<%= asset.id %>" name="ids[]" value="<%= asset.id %>" checked='checked' />
7
+ <label for="papermill_asset_<%= asset.id %>"><%= image_tag asset.url("50x") %></label>
8
+ </li>
9
+ <% end -%>
10
+ <hr style="clear:both;margin:10px;"></hr>
11
+
12
+ <% @other_assets.each do |asset| %>
13
+ <li style="display:block; float:left">
14
+ <input type=checkbox id="papermill_asset_<%= asset.id %>" name="ids[]" value="<%= asset.id %>" />
15
+ <label for="papermill_asset_<%= asset.id %>"><%= image_tag asset.url("50x") %></label>
16
+ </li>
17
+ <% end -%>
18
+ </ul>
19
+ <div style="clear:both;padding:10px;"></div>
20
+ <%= submit_tag "Valider" %>
21
+ </form>
data/config/routes.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  ActionController::Routing::Routes.draw do |map|
2
- map.resources :papermill, :collection => { :sort => :post, :mass_edit => :post, :mass_delete => :post, :mass_thumbnail_reset => :post }, :member => { :crop => :get, :receive_from_pixlr => :post }
2
+ map.resources :papermill, :collection => { :mass_edit => :post, :add_list => :post, :browser => :get }, :member => { :crop => :get }
3
3
  map.connect "#{Papermill::options[:papermill_url_prefix]}/#{Papermill::compute_paperclip_path.gsub(":id_partition", ":id0/:id1/:id2")}", :controller => "papermill", :action => "show", :requirements => { :style => /.*/ }
4
4
  end
data/demo.txt CHANGED
@@ -9,9 +9,10 @@ rake "db:migrate"
9
9
  file "app/models/article.rb", <<-END
10
10
  class Article < ActiveRecord::Base
11
11
  validates_presence_of :title
12
- papermill :thumbnail => {:width => 100, :height => 75} # catch-all for non-specified associations, will create assets/asset methods.
13
12
  papermill :image_gallery, :class_name => ImageAsset, :images_only => true, :thumbnail => {:width => 75, :height => 100}
14
- # image_gallery association (set with define_method)
13
+ papermill :thumbnail, :class_name => ImageAsset, :images_only => true
14
+ papermill :my_assets
15
+ papermill :my_other_asset
15
16
  end
16
17
  END
17
18
 
@@ -41,8 +42,8 @@ file "app/views/articles/_form.html.erb", <<-END
41
42
  <%= f.text_field :title %><br /><br />
42
43
  <%= f.label :image_gallery %><br />
43
44
  <%= f.images_upload(:image_gallery) %><br /><br />
44
- <%= f.label :my_other_image %><br />
45
- <%= f.image_upload(:my_other_image) %> <br /><br />
45
+ <%= f.label :thumbnail %><br />
46
+ <%= f.image_upload(:thumbnail) %><br /><br />
46
47
  <%= f.label :my_assets %><br />
47
48
  <%= f.assets_upload(:my_assets) %><br /><br />
48
49
  <%= f.label :my_other_asset %><br />
@@ -64,24 +65,24 @@ file "app/views/articles/show.html.erb", <<-END
64
65
  <% end %>
65
66
  </p>
66
67
  <br /><br />
67
- <b>@article.assets(:my_other_image).first :</b>
68
+ <b>@article.thumbnail.first :</b>
68
69
  <p>
69
- <% image = @article.assets(:my_other_image).first %>
70
+ <% image = @article.thumbnail.first %>
70
71
  <%= link_to(image_tag(image.url("100x100#")), image.url) if image %>
71
72
  </p>
72
73
  <br /><br />
73
- <b>@article.assets(:my_assets).each :</b>
74
+ <b>@article.my_assets.each :</b>
74
75
  <p>
75
76
  <ul>
76
- <% @article.assets(:my_assets).each do |asset| %>
77
+ <% @article.my_assets.each do |asset| %>
77
78
  <li><%= link_to asset.name, asset.url %></li>
78
79
  <% end %>
79
80
  </ul>
80
81
  </p>
81
82
  <br /><br />
82
- <b>@article.assets(:my_other_asset).first :</b>
83
+ <b>@article.my_other_asset.first :</b>
83
84
  <p>
84
- <% asset = @article.assets(:my_other_asset).first %>
85
+ <% asset = @article.my_other_asset.first %>
85
86
  <%= link_to(asset.name, asset.url) if asset %>
86
87
  </p>
87
88
 
@@ -17,7 +17,8 @@ module Papermill
17
17
  # Associated PapermillAsset subclass (must be an STI subclass of PapermillAsset)
18
18
  # :class_name => "PapermillAsset",
19
19
 
20
-
20
+ # You can use the included join table if you need your assets to be associated to more than one assetable
21
+ # :through => false,
21
22
 
22
23
 
23
24
  #@@@@@@@@@@@@@@@@@@@ form-helper parameters @@@@@@@@@@@@@@@@@@@@@@@
@@ -34,14 +35,12 @@ module Papermill
34
35
 
35
36
  # Dashboard is only for galleries
36
37
  # You can remove/change order of HTML elements.
37
- # See below for dashboard
38
38
 
39
- # :form_helper_elements => [:upload_button, :container, :dashboard],
40
-
41
- # Dashboard elements
42
- # You can remove/change order of HTML elements. You can add :mass_thumbnail_reset to add a link to reset all thumbnails, although you shouldn't need it.
39
+ # :form_helper_elements => [:upload_button, :container, :mass_edit],
43
40
 
44
- # :dashboard => [:mass_edit, :mass_delete ],
41
+ # Batch edit a single field of all the assets in a form field
42
+
43
+ # :mass_edit => true,
45
44
 
46
45
  # Attributes editable at once for all assets in a gallery
47
46
 
@@ -177,14 +176,6 @@ module Papermill
177
176
 
178
177
  #@@@@@@@@@@@@@@@@@ Application-wide parameters @@@@@@@@@@@@@@@@@@@@
179
178
 
180
- # Default named_scope name for catch-all :papermill declaration
181
-
182
- # :base_association_name => :assets,
183
-
184
- # Do you want papermill_assets to touch your assetable when CRUDed? (true|false|updated_at_field_name)
185
-
186
- # :touch => false,
187
-
188
179
  # Set to true to require aliases in all url/path, disabling the
189
180
  # Don't forget to give an alias value to options[:thumbnail][:style] if true!
190
181
 
@@ -222,46 +213,6 @@ module Papermill
222
213
 
223
214
  # If you use those defaults, the first asset will end-up in RAILS_ROOT/public/system/papermill/000/000/001/original/my_first_asset_name.ext
224
215
  # You'll access it with my_domain.com/system/papermill/000/000/001/original/my_first_asset_name.ext
225
-
226
- # You can add authorization support. The code is eval'ed directly in the controller (don't ask why, the answer is full of 'unloadable', 'ApplicationController has been removed from the module tree but is still active', etc.)
227
-
228
- # :authorize_create => "true",
229
- # :authorize_multiple_modification => "true",
230
- # :authorize_update_and_destroy => "true",
231
-
232
- # For example, this is my own setup.
233
- # adapt the authorization part (can_edit(Assetable)) to your own authorization solution
234
-
235
- # :authorize_create => %{
236
- # unless @assetable.nil? || current_user.can_edit?(@assetable)
237
- # render :update do |page|
238
- # page << %{notify("Wrong credentials", "You can't create an asset here", "error");}
239
- # page.remove params[:Fileid]
240
- # page.show "papermill_asset_" + @old_asset.id.to_s if @old_asset
241
- # end
242
- # false
243
- # end
244
- # },
245
- # :authorize_update_and_destroy => %{
246
- # unless @asset.try(:assetable).nil? || current_user.can_edit?(@asset.assetable)
247
- # render :update do |page|
248
- # page << %{notify("Wrong credentials", "You can't edit or destroy assets here", "error");}
249
- # end
250
- # false
251
- # end
252
- # },
253
- # :authorize_multiple_modification => %{
254
- # authorized = true
255
- # @assets && @assets.each do |asset|
256
- # authorized = authorized && current_user.can_edit?(asset.assetable)
257
- # end
258
- # unless authorized
259
- # render :update do |page|
260
- # page << %{notify("Wrong credentials", "You can't do edit or destroy assets here", "error");}
261
- # end
262
- # false
263
- # end
264
- # }
265
216
  }
266
217
  end
267
218
  end
@@ -3,33 +3,57 @@ class <%= migration_name %> < ActiveRecord::Migration
3
3
  create_table :papermill_assets do |t|
4
4
 
5
5
  # Paperclip fields (required)
6
- t.string :file_file_name
7
- t.string :file_content_type
8
- t.integer :file_file_size
6
+ t.string :file_file_name
7
+ t.string :file_content_type
8
+ t.integer :file_file_size
9
9
 
10
10
  # Papermill fields (required)
11
- t.integer :position # sets are ordered by position
11
+ t.integer :position # sets are ordered by position
12
12
 
13
- t.belongs_to :assetable, :polymorphic => true
13
+ t.belongs_to :assetable, :polymorphic => true
14
+ t.string :assetable_key
15
+ t.string :type # PapermillAsset STI
16
+ t.string :title # filename not transformed, without file extension, for your own use
14
17
 
15
- t.string :assetable_key
16
- t.string :type # STI
17
- t.string :title # filename not transformed, without file extension, for your own use
18
+ # Papermill magical fields (You'll need to configure :mass_editable_fields/:editable_fields accordingly to be able to modify them with Papermill helpers)
18
19
 
19
- # Example additionals fields (configure :mass_editable_fields/:editable_fields accordingly)
20
- t.string :alt
21
- t.string :copyright # Content can be used to add copyright on thumbnails
22
- t.text :description
20
+ t.string :copyright # copyright content
21
+ t.string :copyright_im_command # copyright ImageMagick command
22
+ t.string :watermark # watermark URI
23
+ t.string :watermark_im_command # watermark ImageMagick content
24
+
25
+ # Example additionals fields (configure :mass_editable_fields/:editable_fields accordingly to be able to modify them with Papermill helpers)
26
+
27
+ t.string :alt
28
+ t.text :description
23
29
  t.timestamps
24
30
  end
25
31
 
26
32
  change_table :papermill_assets do |t|
27
- t.index [:assetable_id, :assetable_type, :assetable_key, :position], { :name => "papermill_index" }
33
+ t.index [:assetable_id, :assetable_type, :assetable_key, :position], { :name => "papermill_assets_index" }
28
34
  t.index [:assetable_key, :position] # for non assetable assets
29
35
  end
36
+
37
+ # If you want to associate an asset to more than one assetable, use the (:through => true) option to use the join-table and get an assetable with smtg like
38
+ # has_many :assets, :through => :papermill_associations
39
+
40
+ create_table :papermill_associations, :force => true do |t|
41
+ t.belongs_to :assetable, :polymorphic => true
42
+ t.string :assetable_key
43
+ t.integer :position
44
+ t.belongs_to :papermill_asset
45
+ t.timestamps
46
+ end
47
+
48
+ change_table :papermill_associations do |t|
49
+ t.index [:assetable_id, :assetable_type, :assetable_key, :position], { :name => "papermill_associations_index" }
50
+ t.index :papermill_asset_id
51
+ end
52
+
30
53
  end
31
54
 
32
55
  def self.down
33
56
  drop_table :papermill_assets
57
+ drop_table :papermill_associations
34
58
  end
35
59
  end
@@ -21,6 +21,16 @@ module PapermillObjectExtensions
21
21
  end
22
22
  end
23
23
 
24
+ module PapermillArrayExtensions
25
+ def map_with_index!
26
+ each_with_index do |e, idx| self[idx] = yield(e, idx); end
27
+ end
28
+
29
+ def map_with_index(&block)
30
+ dup.map_with_index!(&block)
31
+ end
32
+ end
33
+
24
34
  module PapermillFormtasticExtensions
25
35
  def image_upload_input(method, options)
26
36
  self.label(method, options_for_label(options)) +