views:

52

answers:

2

I have a route that looks like the following:

map.newsletter '/newsletter', :controller => "newsletter" , :action => "newsletter_signup"

There is also a NewsletterController and a Newsletter model. The thing is, I wanted to keep my controller down to one simple method, newsletter_signup. In there I simply check if the request is of type post or of type get. If it's a get, I just give back a static html view saying thanks for signing up to our newsletter. If it's a post, I want to create a new newsletter model and sign the user up. Ideally I'd like to embed this form into my home page, which doesn't call the newsletter controller. I didn't think that URLs such as http://example/newsletter/1 or http://example/newsletter/new made much sense, hence the way I setup the route above.. only http://example/newsletter works.

How would I go about creating this form? It accepts one field, just a textfield that's the user's email. Once they hit submit, it should POST to the newsletter controller and the newsletter_signup method. Since this form is going to be in the footer of every page on my app, do I need to do something like @newsletter = Newsletter.new in my default controller/action?

+2  A: 

What you're proposing isn't quite the Rails way but if you really want to do this, your controller action has access to the request object on which you can call get? or post? to test the method type. See here for more details

The create form could look something like this:

<% form_for(Newsletter.new, :url => { :action => "newsletter_signup" }, :html => { :method => :post}) do |f| %>
  <%= f.text_field :email_address %>
  <%= f.submit "Signup" %>
<% end %>

UPDATE: Providing further color at questioner's request:

Admittedly I am a little unclear about your distinction you make between signing up for a newsletter (what you wanted the GET for) and singing up with the POST. Is it that your application also allows users to sign up for a service other than just the newsletter maybe?

Idiomatically POST/PUT/DELETE are used when a database change is being made and a GET is for a read operation. It strikes me that both kinds of signing up would be database writes (i.e. you have to store the email address somewhere and user info somewhere).

I think you just need a create action (POST) in the NewslettersController for newsletter signups. A signup might be adding an address to the news_letters table which requires creating and saving a NewsLetter model an intermediate step.

User signups (if I've understood that correctly), I'm guessing you've already got going. So in short I think you're dealing with POST operations for both cases. No GET needed. Make sense?

bjg
Thanks for the advice. By the way, I would like to do this the Rails way. Working against the framework is counter intuitive. Can you help me understand why what I'm doing is not the right way of going about it? Does URLs like http://example/newsletter/5 really make sense for a newsletter sign up where I email users once a month about product updates?
randombits
+1  A: 

AFAIK, the "Rails Way" to do this is to use map.resource or map.resources, and if the default names don't make sense you can rename them with the :path_names option.

For example:

map.resource :newsletter, :only => [:new, :destroy], :path_names => { :new => 'signup', :destroy => 'unsign' }

EDIT:

I just realized that I'm missing an equivalent to "create" which would be used to actually make the update to the database ("new" would be to render a page asking if the user wants to sign up to the newsletter). So if you wanted to include that call to action embedded in another page, then the "new" route may not be required.

Making the controller and route

In that case, we are not really talking about a newsletter resource, but a newsletter signup resource. Which would mean the controller would be named NewsletterSignup and the following route would be more appropriate:

map.resource :newsletter_signup, :only => [:create, :destroy]

This would allow you to embed links/buttons into an existing page that send requests to signup or unsign from the newsletter. The actions behind these links would use the normal act and redirect pattern to perform the updates.

The links would look something like:

  POST to /newsletter_signup
DELETE to /newsletter_signup/1

Showing the message

Finally, the Rails Way to show the message "Thanks for signing up to the newsletter" is by using a "flash message". This involves the following code in the create action of the NewsletterSignup controller:

flash[:notice] = 'Thanks for signing up to the newsletter!'

You show this in the view of the page the action then redirects back to (which ever page the signup link/button was on) which can then include the message:

<% if flash[:notice] %>
  <p class="notice"><%= flash[:notice] %></p>
<% end %>

If used commonly a fragment like the above can be added to your layout so it can be made to appear on any page.

fd
@fd. Yes, that's a nice way to express the routing rule. I was more thinking about the proposed use of a GET for updating the newsletters table.
bjg
I just realized I missed out the :create route too, which would be what I would expect to be used for updating the newsletters table. The :new route would be for creating a separate page with which to show a sign up button.
fd