How to create a route without an associated View? - ruby-on-rails

I want to create a route such as
get '/referrals/send_invite/:email_address'
I will be calling this route via remote: true and GET. However if I issue the GET request in my browser, the route will still try to find a View, thus leading me to:
Template is missing
Is there a way that I could tell rails that the send_invite method in Referrals Controller doesn't have a view associated?
I would hope that this could be accomplished by just using rails routes.
Thanks.

Not completely sure, but I am guessing you want to start the action send_invite so you are not interested in an actual result, correct?
You could do something like
def send_invite
SomeMailer.mail(:email => params[:email_address])
# or queue it or whatever
head :ok
end
Note that that is not the only option, you could also do something like
render :text => "The mail has been sent to #{params[:email_address]}"
or
render :json => {:result => 'ok', :email_adress => params[:email_address]}
Also note this should be a POST, since this action is not idempotent (a GET should not have side-effects).
Hope this helps.

More likely than not your template missing is coming from not having a sendinvite.js.erb. Try putting a blank one in your view folder to see that sorts it out.

Related

How to skip_before_filter for a redirect in a action method?

I have a controller which has several methods and one of them has a redirect at the end.
def launch
do_something
params[:hey] = "heyo"
redirect_to("/tasks")
end
All actions has a before_filter which sets some access control headers.
What i want is;
I want to have this before filter for "launch" method, but not for the redirect in it.
How can i achieve this ?
When i set this below, it removes filter for both launch method and "/tasks" redirect, but i only want to remove it for the redirect.
skip_filter :set_access_control_headers, :only => :launch
Thanks in advance!
Update:
This method lives in an engine, that's why I have different access control headers than main application. I do not want to mess with main applications code since i create specific engine and routes for each customer.
And the redirect in the end goes to main application.
You can set param in your redirect and in filter check if param is not exist than skip
Example: redirect_to edit_multiple_items_path, :notice => 'items updated', {:page => ##, :method => 'GET'}
It might be because I focused too much on doing it the "Rails" way.
I fixed the issue by simply removing the headers manually just before the redirect and it all worked fine now.
headers.delete('Access-Control-Allow-Origin')
headers.delete('Access-Control-Request-Method')
headers.delete('Access-Control-Allow-Methods')
redirect(/tasks?#{options.to_query})
Thanks for the answers though!

Issue with Redirect not functioning in Rail 2

I have this redirection in the middle of my controller so if something isn't there, it will redirect you to a new area of the site if needed. Here is the problem. It is just ignoring the redirect in the code. It looks like this.
if conditions
redirect_to "/new/address"
end
I can't even figure out why it won't redirect, but I know it makes it into the conditional and literally just ignores the redirect. What am I missing here!?
I am using Rails 2 and Ruby 1.8
What are you seeing? Is it raising an error? Is there an error message in the console when running "script/server"? For this to work, there should be something to handle the path "new", typically a controller called "NewsController" (plural form) or some rule in the routes.rb file.
If you are seeking to create a new address, then you may be looking for something like
if conditions
redirect_to new_address_path
end
Did you try to use rails paths instead of a string path there?
Try something like
redirect_to :action => 'new'
if the method is inside this controller, or something like
redirect_to :controller => 'adress', :action => 'new'
To see if the result changes.
Don't forget that a redirect_to or render call in a Rails action do not terminate the method. Method execution continues to the end before the redirect/render is performed. So if you're looking to terminate execution of the method at that point add a return statement. The usual pattern is:
redirect_to(<my route>) and return

New Action Not Working

I have a standard User controller with the normal set of actions (index, show, new, edit, etc) and I'm trying to add a new action named 'profile'. I added the following code:
def profile
#user = User.find(session[:user_id])
end
I also created a new view for the action (app/views/users/profile.html.erb), but whenever I try to view that page I get an error:
ActiveRecord::RecordNotFound in UsersController#show
Couldn't find User with ID=profile
...
Apparently it's hitting the show action. I'm guessing that means I need to add something to my routes to make this work, but I don't know what. So far I just have the two default routes and the map.root line which I uncommented:
map.root :controller => "home"
map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'
So really I have two questions:
What do I have to do in order to enable my new action?
Why don't the existing routes cover this situation? Other urls consisting of just the controller and action work just fine (e.g. http://localhost:3000/users/new). Why not this one? Shouldn't it just evaluate to :controller = users, :action = profile, :id = nil?
Try putting something like this in your routes.rb file:
map.user_profile '/users/:id/profile', :controller => "users", :action => 'profile', :conditions => {:method => :get}
I think possibly the reason it's doing this is because you are not matching either of the defaults, because you are not setting :id (even though it is detecting your action as the id). I don't know what your URL looks like, but I have a feeling that if you tried http://localhost:3000/users/123124124/profile, it MIGHT work, even without the new line in routes.
Are you intentionally trying to get the id from session[:user_id] instead of params[:id]? Is this supposed to be displaying a public profile?
Are you sure, that the session contains the user_id the first time you load the page?
Hard to say without seeing all the code, but my guess is there may be some strangeness because your model and controller have the same name. I'd try renaming the controller before changing anything else (remember to change the name of the views/users directory too).
See also this other stack overflow post: Rails cannot find model with same name as Ruby class
Long shot maybe.
Solution for your problem configure your routes.rb in such a way that id should be passed as the parameter .
configure in routes.rb as below
map.profile '/profile/:id',:controller=>'users',:action=>'profile'
when you want to access your profile page use it with the following URL
http://localhost:3000/profile
Make sure once you login , u handle the session and store the userid in the session variable .
Good luck !

How do can you make redirect_to use a different HTTP request?

At the end of one of my controller actions I need to redirect to a page that only accepts put requests. I have been trying to figure out how to get redirect_to to use a put request but to no success.
Is this possible? Or is there another way to accomplish this?
I don't think you are able to do this, and I suspect that the limitation is part of HTTP itself.
When using redirect_to - the redirection happens as a "302 Moved" header unless otherwise specified in the parameters.
Having a look at the HTTP Spec itself doesn't reveal any way to change the type of request the browser makes via redirect.
HTTP Redirects:
This class of status code indicates
that further action needs to be taken
by the user agent in order to fulfill
the request. The action required MAY
be carried out by the user agent
without interaction with the user if
and only if the method used in the
second request is GET or HEAD.
I think you may need to use JavaScript to achieve this functionality, or perhaps rethink the flow of control in your application.
If the action is in the same controller as where you're trying to redirect from, simply call the action and render the template like so:
def show
index
render :action => "index"
end
If it's not, then I don't know how you do that.
Ok, so I found a solution to my problem. I found a very good write up on the situation here. My implementation looks like this:
private
def redirect_post(redirect_post_params)
controller_name = redirect_post_params[:controller]
controller = "#{controller_name.camelize}Controller".constantize
# Throw out existing params and merge the stored ones
request.parameters.reject! { true }
request.parameters.merge!(redirect_post_params)
controller.process(request, response)
if response.redirected_to
#performed_redirect = true
else
#performed_render = true
end
end
Then I called this method like this:
redirect_post :controller => 'registrations', :action => 'order', :_method => 'put', :authenticity_token => params[:authenticity_token]
So I was able to 'fake' a put request by making a post request (using redirect_post) and then assigning 'put' to a _method param. If you look at a normal put request all it is a post from a form with a _method param. So its a bit hackish but it gets the job done.
Also, you have to make sure that when you call redirect_post the values of your hash are strings otherwise errors will be thrown.
You could redirect to a different page that issues the put request from the client, using Javascript.

Is it a bad idea to reload routes dynamically in Rails?

I have an application I'm writing where I'm allowing the administrators to add aliases for pages, categories, etc, and I would like to use a different controller/action depending on the alias (without redirecting, and I've found that render doesn't actually call the method. I just renders the template). I have tried a catch all route, but I'm not crazy about causing and catching a DoubleRender exception that gets thrown everytime.
The solution for this I've come up with is dynamically generated routes when the server is started, and using callbacks from the Alias model to reload routes when an alias is created/updated/destroyed.
Here is the code from my routes.rb:
Alias.find(:all).each do |alias_to_add|
map.connect alias_to_add.name,
:controller => alias_to_add.page_type.controller,
:action => alias_to_add.page_type.action,
:navigation_node_id => alias_to_add.navigation_node.id
end
I am using callbacks in my Alias model as follows:
after_save :rebuild_routes
after_destroy :rebuild_routes
def rebuild_routes
ActionController::Routing::Routes.reload!
end
Is this against Rails best practices? Is there a better solution?
Ben,
I find the method you're already using to be the best. Using Rails 3, you'd have to change the code a bit, to:
MyNewApplication::Application.reload_routes!
That's all.
Quick Solution
Have a catch-all route at the bottom of routes.rb. Implement any alias lookup logic you want in the action that route routes you to.
In my implementation, I have a table which maps defined URLs to a controller, action, and parameter hash. I just pluck them out of the database, then call the appropriate action and then try to render the default template for the action. If the action already rendered something, that throws a DoubleRenderError, which I catch and ignore.
You can extend this technique to be as complicated as you want, although as it gets more complicated it makes more sense to implement it by tweaking either your routes or the Rails default routing logic rather than by essentially reimplementing all the routing logic yourself.
If you don't find an alias, you can throw the 404 or 500 error as you deem appropriate.
Stuff to keep in mind:
Caching: Not knowing your URLs a priori can make page caching an absolute bear. Remember, it caches based on the URI supplied, NOT on the url_for (:action_you_actually_executed). This means that if you alias
/foo_action/bar_method
to
/some-wonderful-alias
you'll get some-wonderful-alias.html living in your cache directory. And when you try to sweep foo's bar, you won't sweep that file unless you specify it explicitly.
Fault Tolerance: Check to make sure someone doesn't accidentally alias over an existing route. You can do this trivially by forcing all aliases into a "directory" which is known to not otherwise be routable (in which case, the alias being textually unique is enough to make sure they never collide), but that isn't a maximally desirable solution for a few of the applications I can think of of this.
First, as other have suggested, create a catch-all route at the bottom of routes.rb:
map.connect ':name', :controller => 'aliases', :action => 'show'
Then, in AliasesController, you can use render_component to render the aliased action:
class AliasesController < ApplicationController
def show
if alias = Alias.find_by_name(params[:name])
render_component(:controller => alias.page_type.controller,
:action => alias.page_type.action,
:navigation_node_id => alias.navigation_node.id)
else
render :file => "#{RAILS_ROOT}/public/404.html", :status => :not_found
end
end
end
I'm not sure I fully understand the question, but you could use method_missing in your controllers and then lookup the alias, maybe like this:
class MyController
def method_missing(sym, *args)
aliased = Alias.find_by_action_name(sym)
# sanity check here in case no alias
self.send( aliased.real_action_name )
# sanity check here in case the real action calls a different render explicitly
render :action => aliased.real_action_name
end
def normal_action
#thing = Things.find(params[:id])
end
end
If you wanted to optimize that, you could put a define_method in the method_missing, so it would only be 'missing' on the first invocation, and would be a normal method from then on.

Resources