adding controllers with a namespace admin as a subfolder - ruby-on-rails

i have a simple cms on ROR 3.2.
with this folder scheme:
app |controllers |my controllers
but i wanted to have an "admin" section where i could have some controllers too.
so i created
rails generate controller admin/Users
app | controllers |admin & my admin controllers
so my file is:
users_controller.rb
class Admin::UsersController < ApplicationController
def index
render(:text => "sou o index!")
end
def list
render(:text => "sou o list")
end
end
On my routes i have:
namespace :admin do
resources :users
end
match ':controller(/:action(/:id))(.:format)'
Im new to rails and i cant figure out the solution. Cant find it anywhere.
The PROBLEM is
i try do acess:
http://localhost:3000/admin/users/list
and i get this error:
Unknown action The action 'show' could not be found for
Admin::UsersController

You seem to not have an understanding of how Rails's RESTful routing works by default. I recommend reading the Resource Routing section of the Rails Guides. By default, when using resources in your routes, the show action is what is used to display a particular model record. You can customize this behavior to an extent in that you can change the URL that for the show action, but not the method name in the model:
resources :users, :path_names => { :new => 'list' }
If you are going to use RESTful routing (which you should), you should remove the default route (match ':controller(/:action(/:id))(.:format)'). Also, you can run rake routes at any time from the terminal to see details about your current routing configuration.

Your on the right track, however, there are a few more steps involved to complete your solution for a backend admin CRUD section. Check out the following example of how to create it yourself:
https://stackoverflow.com/a/15615003/2207480

Related

How are resources connected to controllers without a Model

I am writing a basic application that scrapes data. I have the following in my routes.rb.
Rails.application.routes.draw do
constraints subdomain: 'api' do
namespace :api, path: '/' do
resources :apps, :only => :show
end
end
In controllers I have something like this although I am not sure how are resources connected to Controller.
class AppsController < ApplicationController
def show
puts "this works"
respond_to do |format|
format.json { render json: #user }
end
end
def apps
puts "my app"
end
end
Also, I dont have a Model. Does that mean that in my resources :apps calls a method in AppsController called apps?
If I wanted it t call apps then how's it possible ?
how does a controller in rails know what route does it belong to
I am trying to add a GET /apps?filter=5 that returns my scraped data in the form of JSON and with filter as parameter to that it means that return 5 JSON objects to me
#config/routes.rb
constraints subdomain: 'api' do
namespace :api, path: '/' do
get :apps, to: "apps#apps", on: :collection #-> api.url.com/apps
end
end
A much more coherent way to do it would be...
#config/routes.rb
constraints subdomain: 'api' do
namespace :api, path: '/' do
resources :apps #-> api.url.com/apps -> apps#index
end
end
I think you're getting confused with how Rails works, especially with your data.
I post this all the time, maybe it will help you:
As you can see, your request is not tied to a specific "model", nor is a controller bound to it either. I'll explain the importance of the MVC (Model View Controller) aspect of rails in a minute.
Your thought process is that each request / resource has to have a corresponding model / dataset to pull from. Strictly, this is not the case, although many would believe it to be.
Remember that Rails is just an application framework - it has to work with all the same protocols & restrictions as the other frameworks & languages out there.
When you send a request to Rails (through your browser URL), it takes that request, and matches it to the appropriate controller. This controller action will then pull data from your model (if you've set it up like that), render the view with that data, and return the processed HTML to the browser.
Thus, you don't have to have a model bound to a particular controller action, or anything. You just need to make sure your controllers & views are mapped accordingly.
OOP
I think the part you're getting hooked up on is the object orientated nature of Ruby / Rails.
Although every part of the Rails framework is meant to work with objects, this only applies on a request-basis.
For example, whilst it's typically recommended to keep your controllers resourceful, you don't have to adhere to that methodology if you don't want to. Many newbies don't know the difference.
Thus, when you use the following:
#config/routes.rb
constraints subdomain: 'api' do
namespace :api, path: '/' do
resources :apps, only: :show #-> api.url.com/:id -> apps#show
end
end
... what you're denoting is a controller bound by its resourceful nature. This would typically be expected to use model data, but it's not essential...
In controllers I have something like this although I am not sure how
are resources connected to Controller.
Rails.application.routes.draw provides a DSL which hooks into Rack (the interface between the HTTP server and Rails). It generates rules for where to route the response from Rack.
The DSL is provides has a lot of ways to do the same things. In this example, the resources :apps, :only => :show line basically says you want to generate all of the REST verbs for the AppsController, but you only want the :show verb, so the router will only generate a route to AppsController#show. Note that you can run rake routes to get a list of your routes.
Also, I dont have a Model. Does that mean that in my resources :apps
calls a method in AppsController called apps? If I wanted it t call
apps then how's it possible ?
Models are totally separate abstractions. Once the code reaches your controller you are in plain Ruby land until you return out of that controller action. Models are simply plain Ruby objects with the ability to talk to the database.
In your code if you wanted to call apps from the show method (or action) then you can just call it from there since it's in the same scope.
how does a controller in rails know that ok that is my route. In this case, apps
I'm not sure I understand this question, could you elaborate?
I am trying to add a GET /apps?filter=5 that returns my scraped data in the form of JSON and with filter as parameter to that it means
that return 5 JSON objects to me
For one, you'll need to add a route for /apps. There are several ways you can do this. Here's one approach. I'm going to call it index instead of apps since that's more conventional:
# config/routes.rb
get '/apps' => 'apps#index'
# app/controllers/apps_controller.rb
class AppsController < ApplicationController
respond_to :json
def index
limit = params[:filter].to_i
#users = User.first(limit) # Implement this however you wish
respond_with(#users)
end
end
My syntax might be a little off here with the respond_to and respond_with, but it should explain how the controller routes
Routing simply maps URLs to a controller/action, the existence of a model with the same name does not matter.
To get to the apps action that you defined in the AppsController you need to define a route that maps to apps#apps < This syntax means AppsController, apps action.
An example of a route that would map to the AppsController apps action:
get '/apps', to: "apps#apps"
This is a weird example. It's not conventional to have a def apps action inside AppsController, what exactly are you trying to accomplish with this action?
If you want a rest call to /apps that returns a JSON list of apps, then this all you need to do.
router
resources :apps, only: [:index]
controller
class AppsController < ActionController::Base
def index
puts "This is the index route in AppsController"
end
end
In the router, when you specify resource :apps, only: [:index]. This routes the request GET /apps to AppsController#index

Overriding URL helpers in Rails

I have a Model (Show) in Rails that is accessed via a subdomain rather than a standard REST URL. In the file app/helpers/url_helper.rb I have the following method:
def show_url(show)
root_url(subdomain: show.subdomain)
end
In controllers, this works perfectly. I can test it with puts show_url(#show) and it outputs the subdomain of the show as expected: http://test.example.com. In integration tests, however, the method doesn't work, and the default one generated by rails is used instead. If I run puts show_url(#show) there, I just get http://example.com. How do I use this custom URL helper in my integration tests?
Edit:
routes.rb section regarding this subdomain stuff:
constraints(lambda do |request|
request.subdomain.present? && request.subdomain != 'www'
end) do
get '/' => 'shows#show', as: :show
get '/edit' => 'shows#edit', as: :edit_show
end
This is based loosely around a Railscast on subdomain matching.
Try defining its route without the default "show" action:
# config/routes.rb
resources :show, except: :show
Sounds a bit confusing since your model is called Show, but what it's doing is defining all the standard restful routes (index, new, create, edit, update, delete) except for "show", e.g.
Or another way:
resources :show, only: %w(index new create edit update delete)
I would really consider doing some refactoring and renaming the Show model.

Ruby on Rails : add a new route

I'm new with RoR so this is a newbie question:
if I have a controller users_controller.rb and I add a method foo, shouldn't it create this route?
http://www.localhost:3000/users/foo
because when I did that, I got this error:
Couldn't find User with id=foo
I of course added a view foo.html.erb
EDIT:
I added to routes.rb this code but I get the same error:
resources :users do
get "signup"
end
This doesn't work automatically in rails 3. You'll need to add
resource :users do
get "foo"
end
to your routes.rb
You'll definitely want to have a look at http://guides.rubyonrails.org/routing.html, it explains routing pretty well.
Rails is directing you to the show controller and thinks that you're providing foo as :id param to the show action.
You need to set a route that will be dispatched prior to being matched as /users/:id in users#show
You can accomplish this by modifying config/routes.rb by adding the following to replace your existing resource describing :users
resource :users do
get "foo"
end
Just to add to the other answers, in earlier versions of Rails there used to be a default route
match ':controller(/:action(/:id))(.:format)'
which gave the behaviour you describe where a request of the form controller/action would call the given method on the given controller. This line is still in routes.rb but is commented out by default. You can uncomment it to enable this behaviour but the comment above it explains why this is not recommended:
# This is a legacy wild controller route that's not recommended for RESTful applications.
# Note: This route will make all actions in every controller accessible via GET requests.
At the schema ':controller/:action(.:format)', you can also easily do the following
resources :users do
get "foo", on: :collection
end
or
resources :users do
collection do
get 'foo'
end
end
http://guides.rubyonrails.org/routing.html#adding-collection-routes

Trying to create a method in rails controller

I'm still trying to get the hang of rails and i'm trying to create a simple app with a form where i can enter the data and then submit it and it will be stored in the db. I got this very simple by starting a new project and then running:
$ rails generate scaffold RSVP firstName:string lastName:string
Now i want to redirect to a thank you page after adding a new record via the form.
I've manually added the method below to my rsvps_controller.rb file:
# GET /rsvps/thank_you
def thank_you
respond_to do |format|
format.html # thank_you.html.erb
format.json { render json: #rsvps }
end
end
This line is in my routes file:
resources :rsvps
My question is, when i run rake routes, i don't see a route for my thank_you method. Shouldn't my :rsvps resource pick up my thank_you route and how does the routes know which controller method are which http calls(get, post, put, etc)?
In order to get a route that will hit that action in your controller you should have in your routes.rb file something like:
resources :rsvps do
member { get :thank_you }
end
or
resources :rsvps do
collection { get :thank_you }
end
it depends for if you want to access the resource you've just created or not.
You can take a look # http://guides.rubyonrails.org/routing.html it should help you understating the routing mechanism better.
Adding on to what wlad said, the resources :rsvps things in your routes.rb file creates a set of default routes that are going to be needed by most models. Things like index, new, create, or show. The reason your thank_you action isn't showing up in rake routes is because thank_you isn't one of the actions that were so common that they needed to be included out of the box without extra code.
If you are going to need to load a rsvp model on the thank you page to display any data in that model then you will need to use a member route. The :id in the route will be there because this is a resources member route and has to be associated with a particular resource. There has to be something in the url to know what to load.
resources :rsvps do
member { get :thank_you } #GET /rsvps/:id/thank_you(.:format)
end
If you just want a generic route that points to that controller action then you can use something like this:
match "/rsvps/thank_you" => "rsvps#thank_you", as: "rsvp_thank_you"
You can add more actions to any controller but rails will not treat this functions as actions unless you specify them in routes file. It will be treated as just another function in controller class created by user.
so if you want to add the thank_you function as action you need to add this to routes file.
There are multiple ways of doing so as others have explained in their answers.
adding-more-restful-actions
Using member and collection inside resources.
Use member when you want the function to be used only with the some model object.
eg: preview for photo id 1
GET /photos/:id/preview
In our example you member if you want such route and functionality.
GET /rsvps/:id/thank_you
Note :id in params is needed when you specify it as a member action.
resources :rsvps do
member do
get :thank_you #GET /rsvps/:id/thank_you(.:format)
end
end
Use collection if you want to call the action directly like
GET /rsvps/thank_you(.:format)
in resources
resources :rsvps do
collection do
get :thank_you #GET /rsvps/thank_you(.:format)
end
end
You need to specify the type of action (GET|POST) while adding it to routes.
In our example thank_you has been specified as a GET action. You can choose either.
You can create you own preety_urls or non restful routes using match.
(This will also require to have the action defined in resources block).
check out more on match here http://guides.rubyonrails.org/routing.html#non-resourceful-routes
I suggest you to go through this awesome documentation created by the rails team.(once more ;) ) http://guides.rubyonrails.org/routing.html
Cheers.
As you have said you just want to show a thank you page for each rsvp so a member route should be used. like this
resources :rsvps do
member get :thank_you
end
You should use collection of thank_you when you want to show all or some specific collection of thank_you.
when you include this and run rake routes again you will see the new http action there.

Namespace, static pages, and inherited controllers, what files in what folders?

I'm building an admin control panel (attempting to ;) ).
I have been looking at Backend administration in Ruby on Rails and as suggested I am trying to make Admin::AdminController that checks for admin and sets the layout etc.
But I'm also trying to set a route in it that routes /admin to /admin/dash
From my understanding of reading http://guides.rubyonrails.org/routing.html#controller-namespaces-and-routing , specifically section 2.6,
Admin::AdminController
tells rails that Admin is the name space, AdminController is the controller which is a subclass (extension?, implementation of the interface?) of ApplicationController. Which would imply the controller should live in app/controllers/admin/ and be called admin_controller.rb.
But what I want is
AdminController
I get errors like:
uninitialized constant Admin::Controller
My code for the route:
match :admin, :to => 'admin/admin#dash'
namespace :admin do
# Directs to /admin/resources/*
match '/dash', to: '#dash'
resources :users, :pictures
end
I have put the controller in app/controllers/admin, app/controllers and the combinations with
class Admin::AdminController < ApplicationController
before_filter :admin_user
# / ** STATIC ADMIN PAGES ** /
def dash
end
end
or class AdminController < ApplicationController.
Edit: Maybe it's my understanding of routing.
Example:
namespace :admin do
get "/dash"
vs.
namespace :admin do
match "/dash" to "admin#dash"
vs.
namespace...
match "/dash" to "#dash"
The first one makes it so i can display a dash via the controller, i.e. admin/dash would be controlled by
AdminController < ApplicationControler
def dash
end
Does the second one route admin/admin/dash to admin/dash?
TL/DR:
I think my confusion comes from syntax or my poor understanding of RESTful practices or maybe even class / object inheritance in ruby.
Thanks for helping this n00b out. :)
Side question: can I change my code to be minimized until someone opens it like a spoiler so it doesn't crowd things up if I find more information and add it?
I think your initial approach was correct, but you need to change it a little.
1) insert the /admin => /admin/dash inside the namespace (imho, its better to redirect it)
match 'admin' => redirect('admin/dash')
or
namespace :admin do
match '/', to: 'admin#dash'
end
2) You can't match '/dash' to '#dash' since you're not inside a resource block, you're inside a namespace block, so it doesnt' have implied controller.
namespace :admin do
match 'dash', to: 'admin#dash'
# This will go to Admin::AdminController#dash
# (first Admin because of the namespace,
# and the second because of the controller name)
end
hope it works :D
What you want is "scope" in routing.
scope "/admin" do
resources :articles
end
Which will route /admin/articles to ArticlesController (without Admin:: prefix)
Documentation covers almost every possible case.
http://edgeguides.rubyonrails.org/routing.html

Resources