views:

119

answers:

2

I actually have two questions. I've read the Rails guide and a couple of other articles, but I haven't been able to translate what I read into working routes. I have an application that allows the uploading of images from several different contexts. I'd like the URI to express the proper context so that the following URIs access the same page:

/images/upload
/photos/upload

In this example, I've overridden the new_image_path to use upload for descriptive purposes. I have the override working, but using :as to map images to photos only appears to work one way (with :as => 'photos' in place, the /images routes don't work). Is there a way to make multiple routes point to the same place?

I also have a couple of different ways to upload images/photos/etc. The standard method with a single image per form or a batch method where the user uploads a zip file and that archive is extracted and each of its images is saved.

It seems like the most semantic way to do this is by adding a handler component to the URI (e.g. /images/upload/batch), but I'm not sure how to handle this. The default route path seems pretty general for something that would only be required for images, but I also don't want to be so specific with a named route for the entire bit. What's the best way to do something like this?

Thanks.

Update: Based on jonnii's answer to my first question, I've added the following to my routes.rb file:

map.resources :images, :path_names => { :new => 'upload' }
map.resources :photos, :controller => 'Images', :path_names => { :new => 'upload' }

That seems to do the trick for allowing me to use /images/ and /photos/ interchangeably.

+1  A: 

I'm assuming you're doing your photos routes using resources:

map.resources :photos

If that's the case you should be able to define a second resource pointing to the same controller as the photos resource:

map.resources :uploads, :controller => 'PhotosController'

I haven't tested this, but I don't see why something like this wouldn't work..


Question 2:

There are a few different ways you can do batch uploads, I think the best way is to have a separate resource as you're most likely going to have a different UI for it. For example you might do:

map.resources :batch_uploads

This would probably be enough if you were going to take batch uploads as a zip.

An option that's slightly more complicated but takes advantage of the rails niceties (and lets be honest, who doesn't want to take advantage of that??) is something with nested child forms and accepts_nested_attributes_for. This would be useful if you wanted to allow a user to attach more than one image to a form at time.

For example, if your model was something like:

class User < AR:B
  has_many :photos
end

You could have a route like:

map.resources :users do |u|
  u.resources :photos, :collection => {:get => :new_batch, :post => create_batch}
end

In your new_batch view you would have a form_for @user with a user_form.fields_for :photos. You can add a new form using ajax, whatever and post it all at once.

If you wanted to keep the same semantics as you have now and didn't want to add any more routes you could extend your model to do something different based on the filename of what is being uploaded.

For example if you were using paperclip for attachments you could stop processing the attachment if the filename ends with .zip (this code is not guaranteed to work, I'm doing it from memory):

def is_zip?
    attachment.filename.ends_with?('.zip')
end

before_attachment_process do |attachment|
  false if is_zip?
end

before_filter :process_bulk_attachment, :if => :is_zip?
def process_bulk_attachment
    ... extract the zip and save each image in it ...
    false
end

The beauty of this is that it's part of the model. You should always aim for fat models and skinny controllers!

I hope this gives you a few ideas and/or points you in the right direction.

jonnii
You're right and I totally missed this in the Rails guide. Hell, the guide's example is even the _exact_ one that I needed. I've marked your answer up one, but I don't want to mark it answered unless/until the second question is answered. Really appreciate your help with the first. I've updated my original question with how I applied your answer.
Rob Wilkerson
That was a total mare to type =) Hope it's useful!!
jonnii
Wow. That's a hell of a lot of code for what seems like a simple operation (I do appreciate the effort, though :-). All I really want is to be able to call /images/upload/batch and have that call ImagesController::new with a "batch" parameter that I can forward along so that I can slightly modify the default form partial. Is there an easier way than this?
Rob Wilkerson
I listed a few different options, having a separate controller would probably be the easiest/quickest way.
jonnii
A: 

I've gotten a little closer to what I'm going for:

map.resources :images, :path_names => { :new => 'upload' }
map.resources :images, :new => { :batch => :get }

The former allows me to use /images/upload instead of /images/new, as shown in jonnii's answer to my first question. The latter allows me to specify a second route to "new" functionality via /images/new/batch which calls ImagesController#batch. I was hoping to be able to use /images/upload/batch, but this may have to do.

Clearly, I still have a long way to go before I really understand routing in Rails. jonnii, if I'm just rehashing part of what you've already said, I apologize. I may have to plead ignorance with respect to much of your answer to question 2.

Rob Wilkerson