What is wrong with the way I named my routes?
Do I need to change my folder structure and my module name to
:car_registration
/app/controllers/car_registration/steps_controller.rb
I would prefer to use the formatting I have, if possible.
Routes
scope module: 'CarRegistration' do
resources :steps
end
Controller
/app/controllers/CarRegistration/steps_controller.rb
module CarRegistration
class StepsController < ApplicationController
include Wicked::Wizard
steps :step1, :step2, step3
def show
#form_object_model ||= form_object_model_for_step(step)
render_wizard
end
def update
#form_object_model = form_object_model_for_step(step)
render_wizard #form_object_model
end
private
def form_object_model_for_step(step)
"CarRegistration::#{step.camelize}".constantize.new
end
end
end
ERROR
'CarRegistration/steps' is not a supported controller name. This can
lead to potential routing problems. See
http://guides.rubyonrails.org/routing.html#specifying-a-controller-to-use
(ArgumentError)
It looks like you have at least two problems. You are using a folder structure like:
app/controllers/CarRegistration/steps_controller.rb
That is unconventional. Instead, it should be:
app/controllers/car_registration/steps_controller.rb
Then, your routes should look like:
scope module: :car_registration do
resources :steps
end
Related
I have an email_template model that has a nested resource moves to handle moving an email_template from one folder to another.
However, I want to namespace these actions in a :templates namespace because I have several other resources that are template items as well.
Since I'm namespacing, I don't want to see templates/email_templates/:id in the URL, I'd prefer to see templates/emails/:id.
In order to accomplish that I have the following:
# routes.rb
namespace :templates do
resources :emails do
scope module: :emails do
resources :moves, only: [:new, :create]
end
end
end
Everything works fine when I do CRUD actions on the emails, since they are just using the :id parameter. However, when I use the nested moves, the parent ID for the emails keeps coming across as :email_id and not :email_template_id. I'm sure this is the expected behavior from Rails, but I'm trying to figure out how the parent ID is determined. Does it come from the singular of the resource name in the routes, or is it being built from the model somehow?
I guess it's ok to use templates/emails/:email_id/moves/new, but in a perfect world I'd prefer templates/emails/:email_template_id/moves/new just so developers are clear that it's an email_template resource, not a email.
# app/controllers/templates/emails_controller.rb
module Templates
class EmailsController < ApplicationController
def show
#email_template = EmailTemplate.find(params[:id])
end
end
end
# app/controllers/templates/emails/moves_controller.rb
module Templates
module Emails
class MovesController < ApplicationController
def new
# Would prefer to reference via :email_template_id parameter
#email_template = EmailTemplate.find(params[:email_id])
end
def create
#email_template = EmailTemplate.find(params[:email_id])
# Not using strong_params here to demo code
if #email_template.update_attribute(:email_tempate_folder_id, params[:email_template][:email_template_folder_id])
redirect_to some_path
else
# errors...
end
end
end
end
end
You could customize the parameter as:
resources :emails, param: :email_template_id do
...
end
I have api set up like this:
class Dashboard < Api
def self.inherited(subclass)
super
subclass.instance_eval do
prefix 'dashboard'
#...
end
end
def self.company_id(path)
':company_id' + path
end
helpers do
def current_company
#current_company ||= Company.find(params[:company_id]) if params[:company_id]
end
end
end
Problem: I inherit class Employee from Dashboard, and what I want to achieve: resource, which inherits from Dashboard, should be accessed by it's namespace '/dashboard/companies/:company_id/employees', with current_company working correct.
I feel tiring each time to provide full route instead of namespace convenience:
get 'companies/:company_id/employees'
#...
end
But this won't give result needed:
namespace :companies do
namespace :employees do
...
end
end
From what I undertand, you're looking for dynamic namespace, isn't it? You can define dynamic namespaces using a string instead of just a symbol. In the given string, each :something part indicates a parameter, same thing as what you may be used to in Rails or Sinatra routes syntax. In the endpoints, you'll then be able to access params[:something] as you usually do.
For instance, in your case, you might use something like this :
namespace 'companies/:company_id' do
namespace :employees do
get do
# Will respond with the available params (containing :company_id)
body params
end
end
end
I want to create a method that, when called from a controller, will add a nested resource route with a given name that routes to a specific controller. For instance, this...
class Api::V1::FooController < ApplicationController
has_users_route
end
...should be equivalent to...
namespace :api do
namespace :v1 do
resources :foo do
resources :users, controller: 'api_security'
end
end
end
...which would allow them to browse to /api/v1/foo/:foo_id/users and would send requests to the ApiSecurityController. Or would it go to Api::V1::ApiSecurityController? It frankly doesn't matter since they're all in the same namespace. I want to do it this way because I want to avoid having dozens of lines of this:
resources :foo do
resources :users, controller: 'api_security'
end
resources :bar do
resources :users, controller: 'api_security'
end
Using a method is easier to setup and maintain.
I'm fine as far as knowing what to do once the request gets to the controller, but it's the automatic creation of routes that I'm a little unsure of. What's the best way of handling this? The closest I've been able to find is a lot of discussion about engines but that doesn't feel appropriate because this isn't separate functionality that I want to add to my app, it's just dynamic routes that add on to existing resources.
Advice is appreciated!
I ended up building on the blog post suggested by #juanpastas, http://codeconnoisseur.org/ramblings/creating-dynamic-routes-at-runtime-in-rails-4, and tailoring it to my needs. Calling a method from the controllers ended up being a bad way to handle it. I wrote about the whole thing in my blog at http://blog.subvertallmedia.com/2014/10/08/dynamically-adding-nested-resource-routes-in-rails/ but the TL;DR:
# First draft, "just-make-it-work" code
# app/controllers/concerns/user_authorization.rb
module UserAuthorization
extend ActiveSupport::Concern
module ClassMethods
def register_new_resource(controller_name)
AppName::Application.routes.draw do
puts "Adding #{controller_name}"
namespace :api do
namespace :v1 do
resources controller_name.to_sym do
resources :users, controller: 'user_security', param: :given_id
end
end
end
end
end
end
end
# application_controller.rb
include UserAuthorization
# in routes.rb
['resource1', 'resource2', 'resource3'].each { |resource| ApplicationController.register_new_resource(resource) }
# app/controllers/api/v1/user_security_controller.rb
class Api::V1::UserSecurityController < ApplicationController
before_action :authenticate_user!
before_action :target_id
def index
end
def show
end
private
attr_reader :root_resource
def target_id
# to get around `params[:mystery_resource_id_name]`
#target_id ||= get_target_id
end
def get_target_id
#root_resource = request.fullpath.split('/')[3].singularize
params["#{root_resource}_id".to_sym]
end
def target_model
#target_model ||= root_resource.capitalize.constantize
end
def given_id
params[:given_id]
end
end
I'm trying to create a Downloadable concern with Rails 4.0.4 that I can mix in to all of my downloadable resources. Let's use the Documents resource as an example of something that should be downloadable:
#documents_controller.rb
class DocumentsController < ApplicationController
include Downloadable
before_action :set_document
#Other actions....
#Download
def download
send #document.document_full_path
end
private
# Use callbacks to share common setup or constraints between actions.
def set_document
#document = Document.find(params[:id])
end
end
The Downloadable module looks like (simple for now, but I want to add some validation code to check if the file actually exists and is readable, plus stream it and/or use x-file, etc):
#app/controller/concerns/downloadable
module Downloadable extend ActiveSupport::Concern
def send(filepath)
send_file filepath
end
end
And in my routes I have defined a downloadable concern:
#routes.rb
MyApp::Application.routes.draw do
concern :downloadable do
get 'download', on: :member
end
resources :documents, concerns: :downloadable
root 'welcome#index'
end
When I spin up my puma server and try to go to 0.0.0.0:3000/documents/ I get an error:
ActionController::MissingFile in DocumentsController#index
Cannot read file _process_action_callbacks
Extracted source (around line #4):
3 def send(filepath)
4 send_file filepath
5 end
app/controllers/concerns/downloadable.rb:4:in `send'
Before refactoring this as a concern I had it working using send_file(#document.document_full_path) directly in the 'download' method of the DocumentsController. I didn't get any errors until I refactored it as a concern in the controller and in routes.rb. Does anyone know what could be causing this?
The problem here was namespace pollution; the 'send' method did not resolve to my Downloadable module. I'm not exactly sure where it did resolve but in the end all I did was rename the method to 'send_to_user'. I also could have fixed it by making the call with the scope explicitly resolved 'Downloadable::send', but I think this this syntax is less sugary than just renaming the method.
After this change my Downloadable concern looks like:
module Downloadable extend ActiveSupport::Concern
def send_to_user(args={})
send_file args[:filepath]
end
end
And in my documents controller I have:
#documents_controller.rb
class DocumentsController < ApplicationController
include Downloadable
before_action :set_document
#Other actions....
#Download
def download
send_to_user filepath: #document.document_full_path
end
private
# Use callbacks to share common setup or constraints between actions.
def set_document
#document = Document.find(params[:id])
end
end
routes.rb is the same as above.
Playing with Rails and controller inheritance.
I've created a controller called AdminController, with a child class called admin_user_controller placed in /app/controllers/admin/admin_user_controller.rb
This is my routes.rb
namespace :admin do
resources :admin_user # Have the admin manage them here.
end
app/controllers/admin/admin_user_controller.rb
class AdminUserController < AdminController
def index
#users = User.all
end
end
app/controllers/admin_controller.rb
class AdminController < ApplicationController
end
I have a user model which I will want to edit with admin privileges.
When I try to connect to: http://localhost:3000/admin/admin_user/
I receive this error:
superclass mismatch for class AdminUserController
This error shows up if you define two times the same class with different superclasses. Maybe try grepping class AdminUserController in your code so you're sure you're not defining it two times. Chances are there is a conflict with a file generated by Rails.
To complete what #Intrepidd said, you can wrap your class inside a module, so that the AdminUserController class doesn't inherit twice from ApplicationController, so a simple workaround would be :
module Admin
class AdminUserController < AdminController
def index
#users = User.all
end
end
end
I fixed it by creating a "Dashboard" controller and an "index" def. I then edited my routes.rb thusly:
Rails.application.routes.draw do
namespace :admin do
get '', to: 'dashboard#index', as: '/'
resources :posts
end
end