Rails Engines--how to bypass main_app for named routes - ruby-on-rails-3

In this Stack Overflow question, the end of the question begins to address this issue, but it wasn't the main question, which was answered there already.
I have a module that's included both in ApplicationController and in MyEngineController. Suppose some of the module's instance methods use login_url or some other named route. It works from ApplicationController but not from MyEngineController. I could replace that with main_app.login_url, which will make it work for both, but that seems very verbose, and anyway, what if the code is already part of a gem dependency? How do I make it work post-facto from the engine I'm coding?
When I try using any named helper, here's what I get:
ActionController::RoutingError:
No route matches {:action=>"new", :controller=>"sessions"}
actionpack-3.2.8/lib/action_dispatch/routing/route_set.rb:532:in `raise_routing_error'
actionpack-3.2.8/lib/action_dispatch/routing/route_set.rb:528:in `rescue in generate'
actionpack-3.2.8/lib/action_dispatch/routing/route_set.rb:520:in `generate'
actionpack-3.2.8/lib/action_dispatch/routing/route_set.rb:561:in `generate'
actionpack-3.2.8/lib/action_dispatch/routing/route_set.rb:586:in `url_for'
actionpack-3.2.8/lib/action_dispatch/routing/url_for.rb:148:in `url_for'
actionpack-3.2.8/lib/action_dispatch/routing/route_set.rb:213:in `login_url'
Note that :controller=>"sessions" would have to be :controller=>"/sessions" (with the slash to put it at root namespace), but that's not an option I can pass to named routes AFAIK. I tried adding this code:
extend ActiveSupport::Concern
included do
main_app.install_helpers(self)
end
in MyEngineController, but it didn't help. (install_helpers is very poorly documented. Can anyone explain what it's supposed to do?)

For now, I'm going with this solution (in MyEngineController):
protected
def url_for options=nil
begin
super options
rescue ActionController::RoutingError
main_app.url_for options
end
end
I'm not such a fan of it because, in general, exceptions shouldn't be used for flow control. So what other suggestions do you have?

Related

Rails path helper generate dot or undefined method instead of slash

Route defined as follows
resources :purchases do
collection do
put :wirecardtest
end
end
Controller actions redirect in one of the following manners with associated error generated
format.html { redirect_to wirecardtest_purchase_path(#purchase)
undefined method `wirecardtest_purchase_path'
format.html { redirect_to wirecardtest_purchases_path(#purchase)
/purchases/wirecardtest.44
Behaviour is identical when putting code in view.
The ressource is defined in plural mode, as it ought to be. The redirect, as it is supposed to call a specific ressource should call the singular model-action mode (in plural it would generate the period).
I don't understand how I got into this damned-if-you-do, damned-if-you-don't position.
wirecardtest_purchases PUT /purchases/wirecardtest(.:format) purchases#wirecardtest
That's your mistake right there.. the path is generated as 'wirecardtest_purchases' but you are using 'wirecardtest_purchase' note the missing 's' to pluralize the 'purchase'.
Remember its a collection. So the path method is pluralized by rails.
When in doubt rake routes :)
---Update---
Improving the answer (check comments). Need here is to actually define a route as :member and not a :collection if you want to act upon a single object. Referring to Rails Docs,
resources ::purchases do
member do
get 'wirecardtest'
end
end

How to rescue custom exceptions coming from a middleware in Rails 3.2?

I have a Rails 3.2 application that uses Apartment, which is used as a middleware. Apartment throws an Apartment::SchemaNotFound exception and there is no way to rescue it with rescue_from from the ApplicationController. I thought I'd use config.exceptions_app as described in point #3 in this blog post, but I can't set the router as the exception app, I assume I have to create my own.
So the question is: How do I proceed?
We've purposefully left Apartment pretty minimal to allow you the handle the exception itself without really needing any Rails specific setup.
I'd do something similar to what #jenn is doing above, but I wouldn't bother setting a rack env and dealing with it later, just handle the response completely in rack.
It's typical for instance that you maybe just want to redirect back to / on SchemaNotFound
You could do something like
module MyApp
class Apartment < ::Apartment::Elevators::Subdomain
def call(env)
super
rescue ::Apartment::TenantNotFound
[302, {'Location' => '/'}, []]
end
end
end
This is a pretty raw handling of the exception. If you need something to happen more on the Rails side, then #jenn's answer should also work.
Check out the Rack for more details
I had a similar problem with another piece of middleware throwing a custom exception, so I haven't actually looked at Apartment at all, but maybe something like this:
#app/middleware/apartment/rescued_apartment_middleware.rb
module Apartment
class RescuedApartmentMiddleware < Apartment::Middleware
def call(env)
begin
super
rescue Apartment::SchemaNotFound
env[:apartment_schema_not_found] = true # to be later referenced in your ApplicationController
#app.call(env) # the middleware call method should return this, but it was probably short-circuited by the raise
end
end
end
end
Then in your environment:
config.middleware.use(Apartment::RescuedApartmentMiddleware, etc)
To access the env variable you set from ApplicationController or any controller:
if request.env[:apartment_schema_not_found]
#handle
end
Combo of How to rescue from a OAuth::Unauthorized exception in a Ruby on Rails application? and How do I access the Rack environment from within Rails?

spec view helper in gem

I'm writing a rails 3 railtie which defines some view helpers. Now I wonder how to spec the view helper methods because I cannot instantiate a module. I already searched and read a lot, but I just couldnt get it working.
view_helper_spec.rb
require 'spec_helper'
module MyRailtie
describe ViewHelper, :type => :helper do
describe "style_tag" do
it "should return a style tag with inline css" do
helper.style_tag("body{background:#ff0}").should ==
body{background:#ff0}
EOT
end
end
end
end
It always complains that "helper" is not defined. It seems that :type => :helper doesn't to anything.
The gemspec requires the rspec and the rspec-rails gems in development/ test mode.
I think I found the solution (it's painfully obvious once you think about it). Just stick this at the top of your spec:
require 'action_view'
require 'active_support'
include ViewHelper
include ActionView::Helpers

Single Controller, multiple (inherited) classes (rails 3)

I have a base class inherited by 2 others via Single Table Inheritance. I want all subclasses to share the same controller/views for various reasons-the only real difference is in the model's functionality.
However, when I try to use link_to "stuff", instance_of_child I get complaints about being unable to find the correct page.
I've tried messing with match '/subclass' => redirect('/parent') but that yields weird links that make no sense. Any suggestions? I'm pretty new at rails, and I admit that my understanding of routes.rb is still limited-however, I'm not entirely sure that is even where I should be looking.
From http://www.alexreisner.com/code/single-table-inheritance-in-rails:
If you’ve ever tried to add STI to an
existing Rails application you
probably know that many of your
link_to and form_for methods throw
errors when you add a parent class.
This is because ActionPack looks at
the class of an object to determine
its path and URL, and you haven’t
mapped routes for your new subclasses.
You can add the routes like so, though
I don’t recommend it:
# NOT recommended:
map.resources :cars, :as => :vehicles, :controller => :vehicles
map.resources :trucks, :as => :vehicles, :controller => :vehicles
map.resources :motorcycles, :as => :vehicles, :controller => :vehicles
This only alleviates a particular
symptom. If we use form_for, our form
fields will still not have the names
we expect (eg: params[:car][:color]
instead of params[:vehicle][:color]).
Instead, we should attack the root of
the problem by implementing the
model_name method in our parent class.
I haven’t seen any documentation for
this technique, so this is very
unofficial, but it makes sense and it
works perfectly for me in Rails 2.3
and 3:
def self.inherited(child)
child.instance_eval do
def model_name
Vehicle.model_name
end
end
super
end
This probably looks confusing, so let
me explain:
When you call a URL-generating method
(eg: link_to("car", car)), ActionPack
calls model_name on the class of the
given object (here car). This returns
a special type of string that
determines what the object is called
in URLs. All we’re doing here is
overriding the model_name method for
subclasses of Vehicle so ActionPack
will see Car, Truck, and Motorcycle
subclasses as belonging to the parent
class (Vehicle), and thus use the
parent class’s named routes
(VehiclesController) wherever URLs are
generated. This is all assuming you’re
using Rails resource-style (RESTful)
URLs. (If you’re not, please do.)
To investigate the model_name
invocation yourself, see the Rails
source code for the
ActionController::RecordIdentifier#model_name_from_record_or_class
method. In Rails 2.3 the special
string is an instance of
ActiveSupport::ModelName, in Rails 3
it’s an ActiveModel::Name

Rails 3 namespacing requires model to be defined twice?

I'm pulling my hair out trying to understand namespacing in Rails 3. I've tried following a few different tutorials, and the only way I can get my models to work is if I define my model in both the base directory and my namespace directory.
If I only define the model in the namespace directory it expects it to define both Model and Namespace::Model, as below:
LoadError (Expected .../app/models/plugins/chat.rb to define Chat):
or
LoadError (Expected .../app/models/plugins/chat.rb to define Plugins::Chat):
I'm sure I'm missing something obvious, but I could really use a pointer in the right direction.
Here are the relevant excerpts.
/models/plugins/chat.rb
class Plugins::Chat
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
...
end
/controllers/plugins/chats_controller.rb
class Plugins::ChatsController < Plugins::ApplicationController
load_and_authorize_resource
...
end
/config/routes.rb
namespace :plugins do
resources :chats
end
/config/application.rb
config.autoload_paths += Dir["#{config.root}/app/models/**/"]
Edit
This is some kind of bad interaction with CanCan, the gem we're using for permissions. The line load_and_authorize_resource is somehow at fault. Will keep digging...
I noticed a reference to load_and_authorize_resource in your controller. This method is used by the CanCan gem to create an instance of your model and then test if the user has access to it. If you are using a namespaced model you will need to specify the class:
class Plugins::ChatsController < Plugins::ApplicationController
load_and_authorize_resource :class "Plugins::Chat"
...
end
It sounds like at some point you're referencing the Chat constant \by itself before it's loaded. Rails then tries to find that by looking at models/chat.rb, can't find it, and complains. Check your constant usage (the backtrace should tell you where it's being invoked from), and clean it up, and Rails should be less complain-y.