Recognizing route with regex? - ruby-on-rails

Let's say that I have a postback url that comes in as
http://domain/merkin_postback.cgi?id=987654321&new=25&total=1000&uid=3040&oid=123
and other times as:
http://domain/merkin_postback.php?id=987654321&new=25&total=1000&uid=3040&oid=123
If my route definition is
map.purchase '/merkin_postback', :controller => 'credit_purchases', :action => 'create'
it barks that either of the two forms above is invalid.
Should I be using regex to recognize either of the two forms?

This isn't a routing issue, it's a content format issue. You should be using respond_to.
class CreditPurchasesController < ActionController::Base
# This is a list of all possible formats this controller might expect
# We need php and cgi, and I'm guesses html for your other methods
respond_to :html, :php, :cgi
def create
# ...
# Do some stuff
# ...
# This is how you can decide what to render based on the format
respond_to do |format|
# This means if the format is php or cgi, then do the render
format.any(:php, :cgi) { render :something }
# Note that if you only have one format for a particular render action, you can do:
# format.php { render :something }
# The "format.any" is only for multiple formats rendering the exact same thing, like your case
end
end
end

Related

Exception for incorrect format in rails

Im trying to test if the format send through the request url is json or not?
so in link_to I sent the format like this
<%= link_to "Embed", {:controller=>'api/oembed' ,:action => 'show',:url => catalog_url, format: 'xml'} %>
In the relevant controller I catch the param and raise the exception like this
format_request = params[:format]
if format_request != "json"
raise DRI::Exceptions::NotImplemented
end
but the exception wont display instead the server simply ran into internal error but if I changed the param inside the controller then exception displayed so if the url is like this
<%= link_to "Embed", {:controller=>'api/oembed' ,:action => 'show',:url => catalog_url, format: 'json'} %>
format_request = "xml"
if format_request != "json"
raise DRI::Exceptions::NotImplemented
end
why 501 exception does not triggered if I send the format as xml in url? Im doing it for the testing purpose that in case if someone send the request with wrong format 501 expetion show up
Use ActionController::MimeResponds instead of badly reinventing the wheel:
# or whatever your base controller class is
class ApplicationController < ActionController::API
# MimeResponds is not included in ActionController::API
include ActionController::MimeResponds
# Defining this in your parent class avoids repeating the same error handling code
rescue_from ActionController::UnknownFormat do
raise DRI::Exceptions::NotImplemented # do you really need to add another layer of complexity?
end
end
module Api
class OembedController < ApplicationController
def oembed
respond_to :json
end
end
end
If you don't use respond_to Rails will implicitly assume that the controller responds to all response formats. But if you explicitly list the formats you respond to with a list of symbols (or the more common block syntax) Rails will raise ActionController::UnknownFormat if the request format is not listed. You can rescue exceptions with rescue_from which lets you use inheritance instead of repeating yourself with the same error handling.
As #max mentions, sending the format: 'xml' is unnecessary because Rails already knows the format of the request.
<%= link_to "Embed", {:controller=>'api/oembed' ,:action => 'show',:url => catalog_url } %>
In the controller:
def oembed
respond_to do |format|
format.json { # do the thing you want }
format.any(:xml, :html) { # render your DRI::Exceptions::NotImplemented }
end
end
Or if you want more control you could throw to a custom action:
def oembed
respond_to do |format|
format.json { # do the thing you want }
format.any(:xml, :html) { render not_implemented }
end
end
def not_implemented
# just suggestions here
flash[:notice] = 'No support for non-JSON requests at this time'
redirect_to return_path
# or if you really want to throw an error
raise DRI::Exceptions::NotImplemented
end
If you really want to reinvent the wheel (it's your wheel, reinvent if you want to):
I'd rename format to something else, it's probably reserved and might give you problems
<%= link_to "Embed", {:controller=>'api/oembed' ,:action => 'show',:url => catalog_url, custom_format: 'xml'} %>
Then, in your controller, you need to explicitly allow this parameter:
def oembed
raise DRI::Exceptions::NotImplemented unless format_params[:custom_format] == 'json'
end
private
def format_params
params.permit(:custom_format)
end

Ruby on Rails: Pass Parameters to View

When a user makes a request to the url /mobile in my Rails app, I would like a parameter to automatically be appended to the URL that gets loaded after the request (something like /mobile?tree_width=5)
I have tried a few things, all of which have not worked.
The closest I have gotten is this in my controller:
def mobile
respond_to do |format|
format.html{
# pass tree width here
render mobile_project_steps_path(#project, :tree_width => #project.tree_width)
}
end
end
I am getting the error
Missing template /projects/8/steps/mobile?tree_width=5
But I think this path should exist according to my rake routes:
mobile_project_steps GET /projects/:project_id/steps/mobile(.:format) steps#mobile
How do I add a param to the URL from a controller?
You need to check if the param is missing and if it is redirect to current action with extra param. I would squeeze it with in before_action:
before_action :check_tree_width, only: :mobile
def mobile
# Your actual logic
end
private
def check_tree_width
redirect_to(tree_width: #project.tree_width) unless params[:tree_width].present?
end

Organizing and routing AJAX calls in Ruby on Rails

I am using Rails 4 and I have played with AJAX. I have successfully got it to work. The page that have Ajax links on it is the show.html.erb. It has a feed which have posts, and the posts has comments. To avoid loading everything I use Ajax for loading pieces of the post list and the comment list. Much like Facebook does.
I have organised it the following way: the pages controller checks if there is an ajax_action query parameter present. If it does it renders the JavaScript-response. It checks with a before_action. The value of the ajax_action are load_posts, load_comments and load_replies. They map to private methods in the pages controller.
Does this sound like a good approach when I need different Ajax methods on the same page? Or do you do it another way?
code from the pages controller:
before_action :delegate_ajax, only: :show
def delegate_ajax
ajax_action = params[:ajax_action]
unless ajax_action.nil?
ajax_handlers = {'load_posts' => 'load_posts', 'load_comments' => 'load_comments'}
send(ajax_handlers[ajax_action])
end
end
def load_posts
# Some logic (hidden for now)
respond_to do |format|
format.js { render 'load_posts', layout: false }
end
end
def load_comments
# Some logic (hidden for now)
respond_to do |format|
format.js { render 'load_comments', layout: false }
end
end
At least doing it like this keeps the routes.rb oblivious to all the AJAX routes.

How do I make my app respond to a format based on a querystring parameter?

For example, I would like
/apples/123?_format=json
to act like
/apples/123.json
where it renders the *.json.* templates, executes respond_to {|format| format.json {...}}, etc.
Is this at all possible?
Thanks!
You can do the following to disable Rails' automatic handling of the .ext format:
constraints format: false do
resources :apples
# ...
end
Then, and this is a bit gross but I don't see a better way to do this at the moment, you can do the following to update ActionController on what format to serve:
class ApplicationController < ActionController::Base
before_filter :set_format_from_query_string
private
def set_format_from_query_string
request.format = params.fetch(:_format, 'json')
end
end
This will allow your respond_to block to toggle based on the _format query string parameter and uses json as the default format.

Mocking/Stubbing an Application Controller method with Mocha (Using Shoulda, Rails 3)

While writing functional tests for a controller, I came across a scenario where I have a before_filter requesting some information from the database that one of my tests requires. I'm using Factory_girl to generate test data but I want to avoid hitting the database when its not explicitly needed. I'd also like to avoid testing my before_filter method here (I plan to test it in a separate test). As I understand, mocking/stubbing is the way to accomplish this.
My question is, what is the best way to mock/stub this method in this scenario.
My before filter method looks for a site in the db based on a subdomain found in the URL and sets an instance variable to be used in the controller:
#application_controller.rb
def load_site_from_subdomain
#site = Site.first(:conditions => { :subdomain => request.subdomain })
end
My controller that uses this method as a before_filter:
# pages_controller.rb
before_filter :load_site_from_subdomain
def show
#page = #site.pages.find_by_id_or_slug(params[:id]).first
respond_to do |format|
format.html { render_themed_template }
format.xml { render :xml => #page }
end
end
As you can see, it relies on the #site variable to be set (by the before_filter). During testing however, I'd like to have the test assume that #site has been set, and that it has at least 1 associated page (found by #site.pages). I'd like to then test my load_site_from_subdomain method later.
Here is what I have in my test (using Shoulda & Mocha):
context "a GET request to the #show action" do
setup do
#page = Factory(:page)
#site = Factory.build(:site)
# stub out the #page.site method so it doesn't go
# looking in the db for the site record, this is
# used in this test to add a subdomain to the URL
# when requesting the page
#page.stubs(:site).returns(#site)
# this is where I think I should stub the load_site_from_subdomain
# method, so the #site variable will still be set
# in the controller. I'm just not sure how to do that.
#controller.stubs(:load_site_from_subdomain).returns(#site)
#request.host = "#{ #page.site.subdomain }.example.com"
get :show, :id => #page.id
end
should assign_to(:site)
should assign_to(:page)
should respond_with(:success)
end
This leaves me with an error in my test results telling me that #site is nil.
I feel like I'm going about this the wrong way. I know it would be easy to simply just Factory.create the site so it exists in the db, but as I said earlier, I'd like to reduce the db usage to help keep my tests speedy.
Try stubbing out 'Site.first' since it the the setting of the #site var that you need to stub and not the returned var from the before_filter.
The reason why your #site is nil because your load_site_from_subdomain does the value assignment for #site -- it does not return any value hence your stubbing for load_site_from_subdomain simply doesn't assign the value to #site. There are two work-arounds for this:
First way:
Change load_site_from_subdomain to just do a return value:
def load_site_from_subdomain
Site.first(:conditions => { :subdomain => request.subdomain })
end
and then remove the before_filter :load_site_from_subdomain and change your show to:
def show
#site = load_site_from_subdomain
#page = #site.pages.find_by_id_or_slug(params[:id]).first
respond_to do |format|
format.html { render_themed_template }
format.xml { render :xml => #page }
end
end
And then do the stubbing in the test:
#controller.stubs(:load_site_from_subdomain).returns(#site)
that ensure our #site is stubbed indirectly via load_site_from_subdomain
Second way
To stub the Site.first, I do not really like this approach as in functional test, we do not really care about how the model is retrieved but the behaviour of the respond. Anyway, if you feel like going this path, you could stub it out in your test:
Site.stubs(:first).returns(#site)

Resources