I have an application in RAILS, it is composed of a set of API, a basic website, and an admin dashboard.
For the API routing I have no problems, as they belong to a model and a controller and are compliant with the RAILS RESTful pattern (a controller for each model, and a method for each HTTP method).
What I'm not comfortable with is writing routes and controllers for website.
The main website is at / so the default route is root :to => "home#index"and I have
Routes for the website pages which look like
get "home/index"
get "map/index"
get "api/index"
get "cgu/index"
get "legal/index"
Which I think it is not good, as I have a controller per view and I need to define a get for each views.
Now for the dashboard I tried a different approach.
It is at /dashboard, the default route is match "dashboard" => "dashboard#index" and here is few pages as an examples
get "dashboard/index"
get "dashboard/users"
get "dashboard/users_stats"
get "dashboard/routes"
get "dashboard/routes_stats"
get "dashboard/charts"
get "dashboard/financial"
So for the dashboard I have a massive dashboard_controller, which contains a def method for each dashboard pages. IE:
#dashboard/users
def users
#users = User.all
respond_to do |format|
format.html {render :layout => 'dashboard'}
end
end
the controller of the dashboard is at /controller but for views and assets I have put it in /folder/dashboard/
Here is 2 questions:
What is the best way to build the home website and dashboard ? Should I have a controller per page or a global controller where I have a method per pages ? (Which I find very convenient, less code).
How should I organize my routes to avoid to set a get "something/something" for each page ?
Or it is normal with RAILS that I have a route defined for each of my page ? I'm fairly new.
EDIT:
To clarify, the dashboard is built around an existing application with API that follow RESTFul Rails pattern:
resources :users
resources :routes
But the dashboard is not tied to any existing resources, it only do stats about those resources.
If you have custom controller action names, then yes, you'll need to define every route. If you use Restful routes, then you can define them easily as
resources :users
which will automatically create routes for actions: index, show, edit, update, create and destroy.
This might be helpful: http://guides.rubyonrails.org/routing.html
For your dashboard, which probably is bringing together a lot of resources, so they'll probably be custom methods. I'd suggest focusing on building your app by individual resource. Then, once you've defined them all, build your dashboard.
I agree with everything that other people have said here. You should definitely try to be more RESTful and create more routes like this:
resources :users
However, there is usually a controller that is not RESTful (usually called pages or static) that serves pages such as Privacy, About Us, etc, etc. For those routes, I usually do this:
['api', 'privacy', 'us'].each do |p|
get p, :controller => 'pages', :action => p
end
check this guide out if you haven't;
I think you haven't embraced the MVC concept of Rails.
For example, say you have "users". You should have users_controller.rb, users.rb (model), /views/users (view) under the app directory.
users_controller contains the index, show, create, etc default actions and your custom actions like stats
user.rb contains instance/static/helper methods
/views/users/ contains templates that corresponds to actions in the controller.
Related
I have a website that is already up and running. I would like to add a new page on my Ruby on rails Website and would like to make the page look completely different.
Different Layout, Different Theme, Everything.
I have created a new scaffold for these pages. but am unsure how to achieve the desired results.
You can create the assets for your theme to make the page look completely different. Rails uses the default app layout at app/views/layouts/application.html.erb
However, you can create another layout with your theme and render the view using:
render layout: "<your-layout-name>"
For different ways to render different layouts, you can also refer to this railscast
Yes, you can do this by passing some params from controller action and checking in application.html.erb
Suppose you have users_controller and home as controller action as your root path
Then in users controller
def home
params.store(:diff_page, true)
end
In application.html.erb
In your application content is the id for main div inside body then
#content{:class => (params[:diff_page] ? "back-black" : "back-gradient")}
back-black and back-gradient both are css classes which you can define different style and page, background image anything you want
You can also call different partial too, based on params
- if params[:diff_page]
= render :partial => 'users/new_page'
- else
= render :partial => "layouts/page"
We do this on one of the sites i help manage: we have two completely different domains, each of which with its own totally unique look and feel, running off the same rails app. The second domain is called "cmw" within our project structure, and is set up as follows.
routes
You can put a condition on routes which looks at the request domain, and only runs that route if the domain matches the condition. So, we put all the cmw routes at the top of the routes file, all with the same condition. Then, underneath, the regular site routes. When we get a request to "cmwdomain.com", it will trigger one of the cmw routes. Requests to "maindomain.com" will fall through all the cmw routes, because they have the condition, which fails for this domain, and hit one of the routes below which are for the main site.
eg
#################### START OF CMW ROUTES: ALL HAVE A CONDITION ON THEM TO DIFFERENTIATE THEM FROM REGULAR ROUTES ###############
cmw = { :host => CMW_HOST_REGEX }
map.resources :licenses, :controller => "cmw/licenses", :conditions => cmw
map.resources :users, :controller => "cmw/users", :conditions => cmw
#REGULAR ROUTES
map.resources :licenses, :controller => "licenses"
map.resources :users, :controller => "users"
controllers
The above system sends all of the cmw requests through to controllers in the app/controllers/cmw folder. Normally, all controllers would extend ApplicationController. In our case, we have a CmwController which extends ApplicationController, and is kind of like the CMW-specific version of ApplicationController. This has its own default layout and various protected methods and helpers which are specific to the cmw site. Then, all the cmw controllers extend CmwController instead of ApplicationController.
views
To match the views to controllers, we have a folder app/views/cmw where all of the different cmw controllers' corresponding view folders live.
This system is nice and clean. The second "cmw" site has its own routes, controllers and views, but accesses the same models and database as the main site: two faces of the same app.
I am trying to make a stupid simple CMS for one of my clients using rails. I generated scaffolding for Page and have been successful at creating and rendering those pages, however, the routing is ugly.
Right now, if I want to view the home page, I get a url like this: example.com/pages/1
I'd like to accomplish 2 things:
How do I set the routing so that example.com automagically grabs the page named "home"
and
How do I set the routing so that example.com/page/page_name performs a
#page = Page.find_by name: 'page_name'
Q1:
How do I set the routing so that example.com automagically grabs the page named "home"
In `routes.rb:
root :to => '[controller]#[action]'
#'pages#home' for example, if your home page is in `pages_controller`
# and uses the `home` action
Q2:
How do I set the routing so that example.com/page/page_name performs a
#page = Page.find_by name: 'page_name'
match '/page/:name', :to => 'pages#some_name', :as => :some_name
this would generate the following in $rake routes:
some_name /page/:name(.:format) pages#some_name
when you link to (or redirect, or otherwise access) this page, you'd write
link_to "click this link!", some_name_path(#SomeModel.some_name)
To accomplish the first thing you need to open the file routes.rb which is in the config folder and add the following:
root to: "home#index"
I'm assuming you have a controller named home which contains a method called index and this is the one that displays the home page.
If you want to make it the same as example.com/pages then you would have to use
root to: "pages#index"
to make a general rule you need to use
root to: "controller#method"
Don't forget to remove the index.html from the public folder too.
I recommend you make the blog application presented here:
http://guides.rubyonrails.org/getting_started.html
It can help you understand more.
Here's a solution that assumes your controller is named pages_controller instead of page_controller.
Add these routes in config/routes.rb:
root to: 'pages#show', page_name: 'home'
get 'pages/:page_name', to: 'pages#show'
For the controller app/controllers/pages_controller.rb:
class PagesController < ApplicationController
def show
#page = Page.find_by(name: params[:page_name])
end
end
Note: In rails4 the find_by dynamic finders have been deprecated... so if you're app is on 4 you should look into updating them. These docs have further details.
Also, if you're just trying to get static looking urls then I would definitely go with Marian Theisen's suggestion and use friendly_id.
I'm looking to "automatically" generate routes based on actions defined in a controller. resources routing (as far as I know) only automatically generates routes & helpers for http verbs. In this instance I'm serving mostly static pages using rails and have no need for http verbs in my controller.
Specifically:
In a controller I have defined actions which refer to those mostly static pages.
def action
end
In the routes file I have a bunch of
match "/url" => 'controller#action'
I'd like all those matched routes to be generated automatically based on the actions in the controller. Something CONCEPTUALLY along the lines of:
for actions in controller.erb do |action|
'match "/action" => "controller#action"
end
Is this possible? Would I write the code in the routes file directly?
I also have some nested actions to consider... a controller action may be:
def action
def nested_action
end
end
I'd appreciate any thoughts on this matter. Thanks.
What's wrong with the normal /:controller/:action idea?
That won't deal with nested actions, but... I'm having a difficult time understanding why you'd ever want that.
You can do something like this:
controller :controller_name do
get "path/action" => :method_name, :as => :path_action
post "path/save" => :method_name, :as => :path_save
end
That is, you can group different routes within a controller using the method above.
I'm not very familiar with routing, but here is my dilemma:
I have a photos controller with the usual show, edit, etc. views. I am trying to build a view to moderate photos. I have a moderate.html.erb view under my photos views. I have also defined moderate method in my photos_controller. If I try to access this view like /photos/moderate I get Couldn't find Photo with ID=moderate.
Am I building this the correct way, or does moderate need to have its own separate controller and view? Seems silly to me for that to be the case. Is this just something I need to configure in my routes?
UPDATE:
I've added this to my routes:
resources :photos do
resources :comments
collection do
get 'moderate'
end
end
Still getting the same Couldn't find Photo with ID=moderate message when I go to /photos/moderate...
UPDATE:
Crap! Just figured out the problem. I had a before_filter running that needed to ignore the moderate action... It's now working fine. Sorry for the trouble.
You can add a further restful action. Take a look at the docs
So you're trying to split out the concern of being able to edit and view photos?
If you wanted to keep your actions RESTful, you could create a "moderate" namespace and put all the actions that require authentication for photos in that namespace.
For instance,
#routes.rb
namespace :moderate do
resources :photos # will create paths like '/moderate/photos/', '/moderate/photos/1'
end
#controllers/moderate/photos_controller.rb
class Moderate::PhotosController < ActionController
before_filter :authorize_moderator!
#your standard restful actions like 'index', 'show', 'edit, 'update' would go in here.
end
I've recently joined the world of Rails app development (Rails3) and I may be abusing resourceful routing.
The default resourceful routing makes some really convenient helper methods for the URLs which I use constantly. My problem is that I have controllers that I specified the routing as resourceful simply to take advantage of those helper methods. I have some basic site navigation that has no business with resources.
resource :home do
member do
get 'main'
get 'about'
get 'login'
get 'help'
end
end
Is there a better way to do what I've been doing? Anything that doesn't require that I manually add routing entries each time I have a new controller action?
Just to clarify, I want to specify routing for a controller without having to explicitly add any new actions but I also want it to auto-generate helper methods. So far, I have to explicitly add routes for each action I want that for. I can get something similar by doing this (in a non-resourceful way),
match 'home/about' => 'home#about'
But I don't want to have to write that very every route that doesn't fall into the convention.
Here's another simpler one. Just add a generic route to the bottom of your routes.rb
match ":controller/:action"
and it will map directly to the specified action of the specified controller. You can be a bit more specific if you like. For example, using get instead of match to restrict to HTTP GET requests, specifying the applicaple controllers etc.
get ":controller/:action", :constraints => { :controller => /home|help/ }
You can look into your controller for public instance methods and generate routes automatically.
# routes.rb
HomeController.public_instance_methods(false).select{|m| !(m.to_s =~ /^_/)}.each do |m|
match "home/#{m}", :action => m, :controller => HomeController, :as => "home_#{m}"
end
This will take the explicit(non-inherited) public instance methods from your controller, and select the ones that don't begin with an underscore(because underscored ones are generated methods for filters, the rest are actual actions). Then it will generate a named route for each.
Keep in mind that routes.rb is processed only at server startup so you will have to restart the server after you add new actions.