How to do an almost-postback in rails - ruby-on-rails

I don't know what to call this, except that it is almost-a-postback. I'd like my rails app to be able to receive a request independently from somewhere else on the interweb in the form of:
myapp.com/count?id=45&sex=y
And to be able to perform some calculations (update some table etc) and not bother to respond back.
The key here is - I just want to perform the action and nothing else, not display any view or redirect to any other page.
I set up a "count" controller, but it wants some view or web page to go to. Then I thought, maybe routes could do it? But that seems like a bad idea to have code in routes.
Any ideas appreciated.

In your method controller do this:
def method_count
#do calculation stuff
render :nothing => true
end
This will return status 200 (if everything was ok) and nothing will be rendered.
Don't forget to setup your route in config/routes.rb

For API calls it is a good idea to return only an HTTP status code if no content has been requested. You can do this using the head method in ActionController::Base.
def count
# do work
head :success
end

You have to create a controller and put your action into it !
In config/routes.rb :
get "yourcontroller/count"
In yourcontroller.rb :
def count
#some code treating params
end

Related

Rails controller action duplication

I have a controller show action which does some stuff and renders a view but due to some custom routing, I need a totally different action in a totally different controller to perform the same stuff and render the same view.
I don't really wish to duplicate the code. Is there somewhere I can put it and call it from both locations?
Edit:
I basically need to run the following from Collection#Show AND also from Gallery#SplitUrl:
#collection = Collection.find_by_id(params[:id])
if #collection.has_children?
#collections = #collection.children
else
redirect_to [#collection, :albums]
end
I cannot just redirect_to Collection#Show at the end of Gallery#SplitUrl as the redirect causes my custom URL's to be lost as it's a new request.
You could put the view content into a partial (/app/views/home/_example.html.erb) and then use the render "example" command in the HomeController.
The "some stuff" code you talk about could be put into a helper file /app/helpers/... to save you from duplicating code in the two separate controllers although it could depend on what the code is trying to do in the first place.
http://guides.rubyonrails.org/layouts_and_rendering.html
This might provide more information on the subject in general.
I think the simplest approach would be to add a route definition for your new url and map that to your existing controller's action.
Something like follows:
# config/routes.rb
# Existing resource
resources :foos
# The resource in question
resources :bars do
get :show, controller: 'foos', action: 'show', as: bar_foo_common_show
end
Note that this will give you /bar/:id, where id represents a bar resource. So in your foo#show action, your finder needs to be executed on appropriate class. This is when a few lines of hacky codes get added, for e.g. checking for request referer.
I think moving the common code to a function in possibly application_controller and calling that function from both show action would be cleaner approach, but based on my understanding you already have a similar scenario except for the common code in application_controller, and would like to try out a different approach!

Rails: Redirecting from one controller action to another (non-GET)

I have a GET route which, before rendering the view, the Controller checks for an optional param. If it's there, I'd like to bypass rendering the view and "redirect" straight to another controller action. The other action is a PUT route and I realize we can't simply redirect to PUT routes as you could to a GET route.
This is roughly what I've attempted so far but I don't know how to invoke the redirect, as it's impossible with PUT. Perhaps there's some different design pattern for handling this sort of behavior? Thanks in advance.
Controller actions:
def foo
if 'XYZ'
# "redirect" to bar
else
render 'view'
end
end
#bar is a PUT route
def bar
...
end
If you just want to use the bar method given the condition being met
def foo
if 'XYZ'
bar
else
render 'view'
end
end
This would run the bar method and make use of any redirect you have in there. It is more likely that you want to reexamine what it is you are trying to achieve.
If you can give more specifics then you will get a better answer.
If you have your routes for bar action something like this
match '/controller/bar/' => 'controller#bar'
modify it like
match '/controller/bar/' => 'controller#bar' , :via => [:get,:post,:put]

In Rspec, how test a controller action that does not have a route?

In a few of my controllers I have an action that does not have a corresponding route because it is accessed only via a render ... and return in other controller actions.
For example, I have an action
def no_such_page
# displays a generic error screen
end
In my RSpec controller test, how do I 'get' that method and look at the response body?
If I try:
get :no_such_page
response.status.should be(200)
it of course gives the error
No route matches {:controller=>"foo", :action=>"{:action=>:no_such_page}"}
Update
Looking back over your question, it doesn't make sense to me now since you say that you are only accessing this action via render ... and return, but render renders a view, not an action. Are you sure that you even need this action? I think a view spec is the place for this test.
Original answer
It doesn't make sense to test the response code of an action which will never be called via an HTTP request. Likewise get :no_such_page doesn't make sense as you can't "get" the action (there is no route to it), you can only call the method.
In that sense, the best way to test it would be to treat it just like any other method on a class, in this case the class being your controller, e.g. PostsController. So you could do something like this:
describe PostsController do
... other actions ...
describe "no_such_page" do
it "displays a generic error screen" do
p = PostsController.new
p.should_receive(:some_method).with(...)
p.no_such_page
end
end
end
But in fact, judging from what you've written, it sounds to me like your action has nothing in it, and you're just testing the HTML output generated by the corresponding view. If that's the case, then you really shouldn't be testing this in controller specs at all, just test it using a view spec, which is more appropriate for testing the content of the response body.
before :all do
Rails.application.routes.draw do
get '/no_such_page', to: "foo#no_such_page"
end
end
after :all do
Rails.application.reload_routes!
end

Rails Controller Action Doesn't Respond to Changes

I have a subscriptions controller that at the end redirects to :root and a line on my routes file that points a url to that action. At first I was testing the action without any trouble. But yesterday I tried to change something and the server didn't respond to the changes, no matter what I put in the action it always ignores everything and redirects to :root.
This is how my action looks like:
def fail
test = Subscription.new
p test
binding.pry
redirect_to :account
end
This is my routes line:
match 'subscriptions/failed' => 'subscriptions#fail'
And whenever I try to go to subscriptions/failed it ignores the print instruction, the binding instruction and redirects to :root instead of :account. If I comment everything on the action it will still do the same, the only different reaction it is when I delete the action. Probably obvious but I can't see it.
From your problem description, I would go and check if there is a before_filter or around_filter for fail action that redirects to :root. They would exactly do what you describe if the action is called and wont do anything and wont do anything if the action is not called.
May be you can post more code for the related controllers, just to be sure.

How to setup routes when the controller only has edit and update?

I can't seem to figure out how to get my routes setup properly.
In my app, I have a view that lets site owners update their address information. The new and create actions are part of the signup process and are located in the signups_controller. The edit and update actions are in the settings_controller.
When the user goes into the settings area, he/she sees only the edit form. When filled out, the user is then returned to the same form with a flash message, or error message. Here is what the controller looks like:
class SettingsController < ApplicationController
def edit
#account = current_account
#account.companies.first
#account.companies.first.addresses.first
#account.companies.first.phones.first
end
def update
#account = current_account
if #account.update_attributes(params[:account])
redirect_to edit_setting_path
flash[:notice] = "Success!"
else
render :edit
end
end
end
In my routes, I simply have:
resources :settings
The link to this area of the site is a basic RESTful named linke, with the parameter options:
edit_setting_path(:id => current_account.id)
When the user arrives to this page, they see the following URL:
http://domainname.com/settings/1/edit
When they submit the form and get errors, the URL changes to:
http://domainname.com/settings/1
Why is the URL changing -- I'd rather it not? Is there a way to make it stay the same as the initial edit view? I've tried doing a redirect on a failed update, but then I don't get the error messages.
Any ideas?
To answer your "why" question: The URL is changing because it's reflecting the URL of the failed request - which in this case is a PUT request to that URL (/settings/1). You've submitted the form and the submission of that form (correctly) points to that URL. This is a result of the RESTful routes that the helper gives you. Since the logic in your action, falls through to the render :action, there is no redirect and the form simply re-renders on the page using the same data available in this action (which is why you can see the errors).
If you want to redirect back to the edit page, yes, you will lose the errors that have been set in the #account instance variable since the redirect will reset (re-query for) the account.
You could add a route that matches a PUT to /settings/1/edit and point it to your update action and change your form etc. In short, I wouldn't recommend this, but it should work.
completely untested but attemptable:
routes.rb
put "/settings/:id/edit", :to=>"settings#update", :as=>"update_setting"
resources :settings, :except=>:update
your form would also have to submit to the update_setting_path (which also means it's not reusable for a new object... ew)
First you should read up on The Rails Guides for Routing. They will help a lot to understand why its working like that.
Secondly, to accomplish what you are trying to do, you will need to add manual routes via the match call. You'll need something like this.
match '/settings/:id/edit' => "settings#edit"

Resources