Force absolute URIs for a specific controller action - ruby-on-rails-3

I've got a controller action that generates a template for an online credit card payment gateway (Ogone). I need to force all URIs (image links in the layout, stylesheets, javascripts ...) to be absolute in this case, since the page will be filled up and rendered by the payment gateway server itself.
What is the "rails 3 way" of doing that?
Thanks!

I finally solved my problem completely outside of rails, with the use of wget, with option -k, which converts all relative links to absolute links. It my controller, it looks like:
def action_with_relative_links
end
def action_with_absolute_links
render :text => convert_to_absolute_links(:action_with_relative_links)
end
def convert_to_absolute_links(action_param)
`wget -q -k -O/tmp/absolute_links.html #{url_for :action => action_param}`
`cat /tmp/absolute_links.txt`
end
There is still one little thing to correct in this code: the problem that may arise in high concurrency, because of the use of a temporary filesystem file.
I wish there was a way to avoid that hack, and do everything in rails, but I'm pretty happy with the result, and it permits me to keep all my views and layout files unchanged.
I hope it can help someone!

Related

Rails 3 DRY Conditional Layout for iframes

I was recently tasked with loading a portion of my Rails application within an iframe on another website. The relevant pages should be using a different layout file, but only if they're being rendered inside of the iframe. There was a solution proposed here Detect iframe request in a rails app that involved passing a query string parameter.
For example the requesting website could call my application through an iframe with the src of http://foo.com/bar?iframe=true. Then in our controller we could simply check:
def bar
render :template => "iframe" if params[:iframe]
end
This seems like a good solution, but sadly that only works for the initial request as the original query string is completely static. Assuming we have accessible links to other routes within the iframe is there any way of easily relaying the iframe=true request parameter to maintain the correct iframe layout without having to repeat code? Basically I would like to take the DRYest approach possible without breaking any existing functionality. I considered creating another link_to helper which included the logic to relay this parameter if it exists and replacing all of my link_to calls throughout my application; I was wondering if anybody had a better approach though.
I decided to tackle this problem using JavaScript and added the following to my haml layout file:
:javascript
for(i = 0; i< $('a').length; i++)
{
if($('a')[i].href.match(document.domain))
{
$('a')[i].href = $('a')[i].href + "?iframe=true";
}
}
This coupled with my server-side checks for the iframe param will ensure that the appropriate layout is loaded. I decided to only cater this functionality to users who enable JavaScript so it might not be the best solution. The only other problem with this approach lies in controller redirects and forms where I have to manually check for the iframe param and then forward it accordingly - not DRY at all, but I was at least able to put the logic into a controller method. If somebody knowns of a better solution please feel free to leave an answer.

Rails3 not rendering page correctly

I am writing a simple Rails3 application running in a sub URL that works well except for one problem. If I do not add a ".html" extension at the end of a URL for the "index" method of any of the controllers, the request returns a blank page. It does not matter which controller I request, the "index" method always returns completely empty, including if I curl the URL. I have an "index.html.erb" file in each of the controllers, with simple but complete HTML, and if I do include the ".html" extension in the URL, everything works fine. Here are some example URLs and their results:
http://my.application.url/appname/pages -- returns a blank page.
http://my.application.url/appname/pages.html -- returns the correct HTML page
http://my.application.url/appname/pages/new -- returns the correct HTML form
http://my.application.url/appname/pages/1 -- returns the correct HTML page
http://my.application.url/appname/pages/1/edit -- returns the correct HTML form
My routes file looks similar to this:
My::Application.routes.draw do
scope "/appname" do
resources :posts
resources :pages
root :to => 'home#index'
end
end
The applicable part of my controller looks similar to this:
class PagesController < ApplicationController
def index
#pages = Page.all
respond_to do |format|
format.html
end
end
...
end
As I said, the index method is the only one that is having this problem. I have tried everything I can think of, including adding My::Application.default_url_options = {:format => "html"} in application.rb (which works except when I need to do a redirect_to from the controller), and I am at a loss. The app is using Thin as an application server proxied behind Apache 1.3 (which I unfortunately cannot change, and this doesn't seem to be an issue anyway because hitting the Thin server directly results in the same problem). Any ideas would be much appreciated.
So, the answer seems to be that if the URL does not specify the .html extension, the application will serve out assets (from the asset pipeline) of the same name as the controller, at least while running in the development environment. Once I removed the [controllername].css.scss and [controllername].js.coffee files (both of which were unused anyway) that were auto-generated when I created the controllers from the assets folder, the application worked correctly. Just for kicks, I tried leaving them in and running rake assets:precompile, but the behavior persisted until the files were actually removed. This still seems counterintuitive, and I am contemplating filing this as a bug.

Render layout without a controller

I'm writing a Rails 3.2 app with backbone, and since I only need rails to render one page, I have no need for a controller to back the index page.
Is there a way to render the layout (application.html.erb) without a controller? I imagine it would be a configuration in the routes.rb file?
My first thought was to move it to index.html in the /public directory, but I need to take advantage of erb for javascript includes and CSRF helpers, etc.
I get that you don't need the controller to do anything, but Rails is "opinionated" software; it expects a controller and a view, because that is the way it was designed, and trying to work around that is going to give you a lot of trouble.
Just
create an empty controller class in /app/controllers/main_controller.rb
create an empty view file /app/views/main/index.html.erb
set up a route like :root => 'main#index'
Easy peasy.

rspec render_views ignores layouts? want to test static cached page does not display flash message

I'm trying to test that "static" pages (they're ERB, but get cached), generated through rails, don't render any stray flash notices left over by the authentication system (Devise) or wherever else.
I've tried writing this controller spec, but it appears that response.body only renders the template, not its layouts?
describe "so that static caching can be used" do
render_views
specify "flash notices are not rendered" do
# edit: the following flash lines don't do anything
# it's not the right flash object, this one is intended for
# inspecting after request not setting before request
flash[:notice] = "flash boo"
flash[:error] = "flash boo"
flash[:alert] = "flash boo"
get :show, :page => 'privacy_policy'
response.body.should have_content('flash boo')
end
end
class StaticPagesController < ApplicationController
layout 'master'
def show
response.headers['Cache-Control'] = "public, max-age=#{6.hours}"
render "static_pages/#{params[:page]}"
end
end
I've tried changing to a layout which does render flash notices, and even inserting the text into the layout template, but can't make the spec fail.
Is there a way to ask rspec to render the template with the appropriate layouts as well?
Is a controller spec the wrong way to try and do this?
It seems out of place, as it's more to do with which layouts are being used, and their contents, but the rendering process starts at the controller, it receives the result, and I can manipulate the flash hash contents before rendering.
Versions:
rails (3.0.10),
rspec-rails (2.6.1),
rspec-core (2.6.4)
Thanks, Nick
Turns out this isn't the right approach. It should be an integration test (cucumber, request spec, etc) as it's testing several layers. That and rails doesn't seem to render the templates inside their layouts at this level.
So in integration test:
Set up a flash message by making a request to a controller which does nothing else (create a dummy controller & routing in your test code), then hit the page of concern and make sure the flash notice isn't rendered.
e.g. https://gist.github.com/1178383
It seems like a long way round but it will cover it.

Internal Redirects with Rails 3

I am trying to implement a generic vanity url system in Rails 3. Generic in the sense that the vanity url isn't tied to a specific model. It is similar to the Vanities gem where I have a VanityUrlController that is hit from all the vanity urls. The difference is that I don't want to do an external redirect from foo.com/username to foo.com/users/1 or foo.com/product-name to foo.com/products/1. I want the vanity url to to stick around and have the the VanityUrlContoller do an internal redirect that mimics the corresponding show action.
I know what controller and action I want to dispatch the internal redirect to, but I am having problems with actually dispatching it. This is where I am at the moment:
TargetController.new.process("show", request.env)
It seems to start processing the new "request," but there are key pieces missing... like the actual request object.
Any thoughts or pointers would be very appreciated.
Update:
I ran across the dispatch method in ActionController which seems to get me a little farther.
TargetController.new.dispatch("show", request)
I have two problems with this, 1) it is listed as a private api method so if there is another way to do this, I'd rather that, and 2) even though it is rendering the show template for the TargetController, it is complaining about "Missing template vanity_urls/show."
UPDATE
Here is the basics of the solution we came up with. We do some other things like forcing encodings and checking some other application specific stuff, but this should be all you need to get going.
This goes at the very bottom of your routes.rb file so your vanity routes don't clobber your other named routes.
# Vanity routes.
match ':id', :as => 'vanity', :to => proc { |env|
id = env["action_dispatch.request.path_parameters"][:id]
vain_object = <method to find the object you want to display>
if vain_object.nil?
# render your 404 page
'application#404'
else
model = vain_object.class.model_name
# figure out the controller you want to go to
controller = [model.pluralize.camelize,"Controller"].join.constantize
# reset the :id parameter with the id of the object to be displayed
env["action_dispatch.request.path_parameters"][:id] = vain_object.id
# do your internal redirect
controller.action("show").call(env)
end
}
You'll also want to be careful while creating your vanity routes, so they don't collide with your other controllers. Some other useful things to know about are:
Rails.application.routes.routes.any? { |r| r.requirements[:controller] == vanity_url }
Which tells you if your vanity_url has the same name as a current controller.
Rails.application.routes.recognize_path("/#{vanity_url}", :method => :get)
Which tells you if this maps to anything already.
Sure, there are a couple of hacks along the way, but it works like a charm.
Try the FriendlyId plugin. It seems to do exactly what you want. Video tutorial:
http://railscasts.com/episodes/314-pretty-urls-with-friendlyid