Removing url scope segment with url helpers - ruby-on-rails-3

I'm writing an app that makes use of AngularJS, so the app is setup to route all requests to the main home page where angular takes over. All routes are then defined within an api scope which angular uses to retrieve the data. It's setup though, that if the user navigates to a page with a normal URL, then when it redirects to the home page, it maintains that URL which angular then uses to load the correct state.
What I now want to do, is be able to use URL helpers within rails to generate my URL's, but not have the generated URL's include the /api of the scope. Is there any way I can get around this?
routes.rb looks a bit like
scope "/api", shallow_path: "/api" do
... normal stuff here ...
end
And if I try using one of the helpers,
meeting_url(#meeting, subdomain: "test")
the url it generates is
http://test.domain.com/api/meetings/1
Is there a way I can have it strip the /api?

I don't believe there's a built-in way to do it.
But, you're in ruby, so there are plenty of ways to do what you want.
One way to go, since you're in your own app, is do monkey-patch String:
class String
def no_api
self.gsub(/\/api/, '')
end
end
meeting_url(#meeting, subdomain: 'test').no_api #=> http://test.domain.com/meetings/1
If you find that distasteful, you can define a method on ApplicationController or in a helper module:
def no_api(url)
url.gsub(/\/api/, '')
end
etc. etc. etc.

Related

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.

Rails 3.2 Mountable App locale getting duplicated

I am seeing a scenario where my locale parameter is getting duplicated once I step into a mountable app. My app looks like so:
#routes.rb
resources :blogs
scope "(:locale)" do
mount Auth::Engine => '/auth'
end
If I am at a blogs page and look at an auth link it looks like this /en-us/auth/signout which works well, but as soon as I load into any page rendered by the mountable app, for example the profile page /en-us/auth/myprofile', the sign out link now looks like /en-us/auth/signout?locale=en-us
I don't want the querystring getting cluttered, what is causing the app to correctly set the locale parameter and then duplicate it in the querystring?
my bet is that your application controller has something like:
def set_locale
# code here
end
def default_url_options
# code here
end
Since your engine controller inherits from application controller, set_locale may be triggered twice.
Potential Solution: Check if the code is in an engine, then don't trigger locale setting.
This can be done with: self.controller_path().split("/").first == engine_name
I had the same problem. I was setting the default url options on the controller (by redefining default_url_options or by using self.default_url_options=).
The solution was to use Rails.application.routes.default_url_options = instead.

Engine routes in Application Controller

I have a before_filter hook in my main app's application controller that does something like: (It doesn't just put a link in the flash, there is a message, but it isn't relevant to the question, it just accesses the route in the method)
class ApplicationController < ActionController::Base
before_filter :set_link
def set_link
flash[:notice] = items_path
end
end
This works fine for the app, however when I go into the controllers for an engine I made I get the exception
No route matches {:controller=>"items", :action=>"index"}
I understand that when in the engine, the routes helpers are for the engine unless prefixed with main_app
So changing the method in the application controller to
def set_link
flash[:notice] = main_app.items_path
end
Gets rid of the exception but I really don't want to have to do that. Is there another solution to getting the engine to recognize the main_app routes?
EDIT:
This also happens if the application layout calls path helpers. So if the engine is designed to integrated into the main_app's layout then this issue will crop there up too.
Mountable engines are designed to work like this, that is isolate the main app routes and the engine routes.
If you want the two sets of routes to be merged, you can use a non-isolated engine. The first step is removing the isolated_namespace method call in your engine definition:
module MyEngine
class Engine < Rails::Engine
isolate_namespace MyEngine # remove this line
end
end
The second step is to convert your routes in my_engine/config/routes.rb, you should go from this:
MyEngine::Engine.routes.draw do
# stuff that routes things
end
to this:
Rails.application.routes.draw do
# stuff that routes things
end
and remove the mount method call in your application's routes:
App::Application.routes.draw do
mount MyEngine::Engine => "/engine" # remove this line
end
The main advantages of doing it this way would be:
No need to monkey-patch rails. I know devise does this, but this could be a leftover from the days when engines didn't exist in rails.
No need to mount the engine in the application routes. On the other hand, this could backfire if you'd like to control more precisely the insertion point as all you engine routes would be called after (or before, I don't have the answer to this question) your main routes.
If you're looking for documentation on engines, the rails docs for the Engine class are a pretty good starting point. I'd strongly recommend that you read them (in case you haven't yet) if you're interested in the subject.
I figured out how to do this. The problems lies within the isolated namespace. In order to integrate the engine with the app and share the same layout (which may have path helpers from the main app) I did this:
Firstly I removed config/routes.rb from the engine
Then I removed the isolate_namespace from the engine class
module MyEngine
class Engine < Rails::Engine
- isolate_namespace MyEngine
end
end
end
I added a file that was loaded in the engine:
module ActionDispatch::Routing
class Mapper
def mount_my_engine_at(mount_location)
scope mount_location do
#Declare all your routes here
end
end
end
end
Finally, in the main app's config/routes.rb instead of 'mount'ing the engine, you can call your method
mount_my_engine_at "mount_location"
This will basically 'mount' your engine as part of the main app instead of being isolated from it. It is similar to how Devise does it too.
You can keep the isolate_namespace.
In your engine routes.rb
MyEngine::Engine.routes.draw do
...
root to: "something#index"
end
Rails.application.routes.draw do
get "something", to: "my_engine/something#index"
end
And then in the main app routes.rb
Rails.application.routes.draw do
mount MyEngine::Engine => "/anything_you_want"
root to: "main#index"
end
This way you can choose what routes you want to expose (and which you do not)
You can keep the isolate_namespace, as strongly recommended by Rails Engine guide, and do this:
# inside your main_app's config/routes.rb
Rails.application.routes.draw do
root to: 'home#index'
mount MyEngine::Engine, at: "/"
end
# inside your engine's controller
module MyEngine
class SomeController << ::ApplicationController
# include main_app's route helpers
helper Rails.application.routes.url_helpers
end
end
And inside your gem, make sure all the url helpers are prefixed with the correct routing proxy method (e.g. my_engine.pages_path).
Your main_app's layout and engine's controller will route and link to those url helpers correctly to the main app. You don't have to add "main_app" prefix anywhere to the main app. The only downside is you're mounting your engine's routes at main_app's root path, which could collide with any routes by the same name. This is expected anyway if you were to do non-isolate_namespace.
The easiest way is to draw the routes in both the main app, and the engine, so that they are accessible to both:
[MyEngine::Engine, App::Application].each do |app|
app.routes.draw do
# Declare all your routes here
end
end

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