This is driving me crazy! I have the two models Lion and Cheetah. Both inherit from Wildcat.
class Wildcat < ActiveRecord::Base; end
class Lion < Wildcat; end
class Cheetah < Wildcat; end
STI is used here.
They all get handled through the controller WildcatsController. There, I have a before_filer to get the type of wildcat from the params[:type] and all the other stuff to use the correct class.
In my routes.rb, I created the following routes:
resources :lions, controller: 'wildcats', type: 'Lion'
resources :cheetahs, controller: 'wildcats', type: 'Cheetah'
If I now want to use the path helpers, that I get from the routes (lions_path,lion_path,new_lion_path, etc.), everything is working as expected, except the show and the new paths. For example lions_path returns the path /lions. The new path returns /lions/new?type=Lion. Same with the show path. When I try to enter /lions/new to my root domain it correctly adds the type param in the background.
So, my question is, why does Rails add the type parameter to the url if I use the path helper? And why only for new and show?
I am running Rails 4.0.0 with Ruby 2.0 using a fresh Rails app.
Why using type? Why not use inherited controllers?
resources :lions
resources :cheetahs
Then
class LionsController < WildCatsController
end
class CheetahController < WildCatsController
end
class WildCatsController < ApplicationController
before_filter :get_type
def index
#objs = #klass.scoped
end
def show
#obj = #klass.find(params[:id])
end
def new
#obj = #klass.new
end
# blah blah
def get_type
resource = request.path.split('/')[0]
#klass = resource.singularize.capitalize.constantize
end
I just had this problem. You could try to shutdown the server, remove the /tmp directory and restart.
Rails routes and controller parameters
Related
I have a 3 controllers with the method show namely
class CarController < ApplicationController
def show
end
end
class MotorcycleController < ApplicationController
def show
end
end
class TravelController < ApplicationController
def show
end
end
In my routes, i want the show method of all tree to follow the same url structure /:company_id/:id
So given an example that my home page url is https://localhost:3000, company id is 1 and id is 2, if i go to the show method of car controller, my url should be http://localhost:3000/1/2
At the moment, i did this in my routes
get '/:company_id/:id' => 'travel_controller#show', as: 'travel_insurance_product'
get '/:company_id/:id' => 'car_controller#show', as: 'car_insurance_product'
get '/:company_id/:id' => 'motorcycle_controller#show', as: 'motorcycle_insurance_product'
But when i trigger the car show method, it goes to the travel controller method
Is this possible to be done in ruby?
No, it's not possible. How is the router supposed to know if the id is for a Car, Motorcycle, or Travel?
BTW, the convention is for the controller to be in the plural form (such as MotorcyclesController).
Also BTW, routing is a Rails phenomenon, not a Ruby phenomenon.
So I want when I access: site.com/panel to look into /app/controller/panel/index_controller.rb
Before I start I'm new to ruby, I started a couple hours ago
So in my routes.rb I have this
namespace :panel do
root 'index#index'
resources :index
end
And I created a file called index_controller.rb in /app/controller/panel/index_controller.rb which looks like this
class IndexController < ApplicationController
def index
#foo = "Foo"
end
end
Now when I go to site.com/panel I get this: superclass mismatch for class IndexController
What I did wrong?
Also can I setup different views and layout here to use for the controllers inside /app/controller/panel/*_controller.rb
replace this
class IndexController < ApplicationController
with
class Panel::IndexController < ApplicationController
update:
to automatically generate namespaced controller you can use rails build in generator like this
rails g controller panel/users
this will generate Panel::Users < ApplicationController controller under app/controllers/panel/users_controller.rb
Since you've namespaced the index resource routes within panel, you'll need to prefix your IndexController declaration to reflect this:
# app/controllers/index_controller.rb
class Panel::IndexController < ApplicationController
Then, you can similarly reflect the namespace in your filesystem in order to get Rails to properly invoke the correct views:
/app/views/panel/index/index.html.erb
/app/views/panel/index/show.html.erb
... etc
A note: the Rails convention is that routes that are declared as resources should be named plural, as this denotes an entirely resourceful class. Thus, according to this paradigm, index should actually be indexes. However, I suspect you may mean to use a singular route, in which case the declaration would be as follows:
namespace :panel do
resource :index
end
Which creates the following singular routes (which may conform better to what you're trying to accomplish):
panel_index POST /panel/index(.:format) panel/indices#create
new_panel_index GET /panel/index/new(.:format) panel/indices#new
edit_panel_index GET /panel/index/edit(.:format) panel/indices#edit
GET /panel/index(.:format) panel/indices#show
PUT /panel/index(.:format) panel/indices#update
DELETE /panel/index(.:format) panel/indices#destroy
If I do:
rails generate scaffold account/user username
I get a controller that looks like this:
class Account::UsersController < ApplicationController
def index
#account_users = Account::User.all
end
...
end
If I include the Account Module, then it looks like all the database calls don't need to be prefixed with "Account::". I.e.
class Account::UsersController < ApplicationController
include Account
def index
#account_users = User.all #this works because I included the Account Module above
end
...
end
Now if I were to move my
controllers/account/users_controller.rb
file to:
controllers/admin/account/users_controller.rb
The file looks like this (note: I also corrected my routes file after this move):
class Admin::Account::UsersController < ApplicationController
include Account
def index
#account_users = User.all #this call does not work now
end
...
end
But I get an error saying "uninitialized constant Admin::Account::UsersController::User"
It looks like rails is trying to make a database call on the "User" model without the "Account::" module in front of it.
So how does including modules in controllers work? Why does this not work when I move my controller into a different file (and leave the model in the same location from the generated scaffold) but it works with the scaffold generated files? How can I fix this issue?
Resolving the name of a module is done relative to the current module. Try and change it to:
include ::Account
or
include ::Admin::Account
(depending on the module in which your User model is defined)
This will tell ruby to look in the global namespace for the module Account
I guess I didn't realize you can just explicitly require the path to the module you would like to include. I learned this after reading up on modules some more...
So adding an explicit call to "require 'account/user'" just outside the controller class makes it so including the module in the controller works.
I have a little database with a movies that I saw. Now when I want to display a detail of a movie, so the profile of wanted movie is on the address example.com/movies/21.
But I would like to have a profile page of every movie on the nicer URL address, for example example.com/lords-of-the-rings.
How can I do it?
In your model, store the url name into a new field, like Movie.permalink
In config/routes.rb :
MyApp::Application.routes.draw do
match "movies/:permalink" => "movies#show"
end
And in your controller :
class MoviesController < ApplicationController
def show
#movie = Movie.find_by_permalink( params[:permalink] )
end
end
For more on routes in rails : http://guides.rubyonrails.org/routing.html
Consider using the slugged gem: https://github.com/Sutto/slugged
Many folks like this approach.
It's rails 3+
Just to help guide the answers, would you allow something like:
http://example.com/movies/lord-of-the-rings
If so, grabbing the params[:id] from that URL is easy.
You can automate the generation of that last :id by changing to_param of the model:
class User < ActiveRecord::Base
def to_param # overridden
name
end
end
Then you could change the show method of the controller to reflect the new :id format.
user = User.find_by_name(params[:id])
How do I find the name of the namespace or module 'Foo' in the filter below?
class ApplicationController < ActionController::Base
def get_module_name
#module_name = ???
end
end
class Foo::BarController < ApplicationController
before_filter :get_module_name
end
None of these solutions consider a constant with multiple parent modules. For instance:
A::B::C
As of Rails 3.2.x you can simply:
"A::B::C".deconstantize #=> "A::B"
As of Rails 3.1.x you can:
constant_name = "A::B::C"
constant_name.gsub( "::#{constant_name.demodulize}", '' )
This is because #demodulize is the opposite of #deconstantize:
"A::B::C".demodulize #=> "C"
If you really need to do this manually, try this:
constant_name = "A::B::C"
constant_name.split( '::' )[0,constant_name.split( '::' ).length-1]
For the simple case, You can use :
self.class.parent
This should do it:
def get_module_name
#module_name = self.class.to_s.split("::").first
end
For Rails 6.1
self.class.module_parent
Hettomei answer works fine up to Rails 6.0
DEPRECATION WARNING: Module#parent has been renamed to module_parent. parent is deprecated and will be removed in Rails 6.1.
This would work if the controller did have a module name, but would return the controller name if it did not.
class ApplicationController < ActionController::Base
def get_module_name
#module_name = self.class.name.split("::").first
end
end
However, if we change this up a bit to:
class ApplicatioNController < ActionController::Base
def get_module_name
my_class_name = self.class.name
if my_class_name.index("::").nil? then
#module_name = nil
else
#module_name = my_class_name.split("::").first
end
end
end
You can determine if the class has a module name or not and return something else other than the class name that you can test for.
I know this is an old thread, but I just came across the need to have separate navigation depending on the namespace of the controller. The solution I came up with was this in my application layout:
<%= render "#{controller.class.name[/^(\w*)::\w*$/, 1].try(:downcase)}/nav" %>
Which looks a bit complicated but basically does the following - it takes the controller class name, which would be for example "People" for a non-namespaced controller, and "Admin::Users" for a namespaced one. Using the [] string method with a regular expression that returns anything before two colons, or nil if there's nothing. It then changes that to lower case (the "try" is there in case there is no namespace and nil is returned). This then leaves us with either the namespace or nil. Then it simply renders the partial with or without the namespace, for example no namespace:
app/views/_nav.html.erb
or in the admin namespace:
app/views/admin/_nav.html.erb
Of course these partials have to exist for each namespace otherwise an error occurs. Now the navigation for each namespace will appear for every controller without having to change any controller or view.
my_class.name.underscore.split('/').slice(0..-2)
or
my_class.name.split('::').slice(0..-2)
With many sub-modules:
module ApplicationHelper
def namespace
controller.class.name.gsub(/(::)?\w+Controller$/, '')
end
end
Example: Foo::Bar::BazController => Foo::Bar
No one has mentioned using rpartition?
const_name = 'A::B::C'
namespace, _sep, module_name = const_name.rpartition('::')
# or if you just need the namespace
namespace = const_name.rpartition('::').first
I don't think there is a cleaner way, and I've seen this somewhere else
class ApplicationController < ActionController::Base
def get_module_name
#module_name = self.class.name.split("::").first
end
end
I recommend gsub instead of split. It's more effective that split given that you don't need any other module name.
class ApplicationController < ActionController::Base
def get_module_name
#module_name = self.class.to_s.gsub(/::.*/, '')
end
end