I have a "dashboard" type page that show records from many different Models (a Parent Model with many associated Child Models) and uses logic that I currently keep in the Parent Controller. I am new to rails and wondering if it would be better to create a separate Dashboard controller or and keep the Parent Controller just for add/edit/index/destroy of the Parent records.
Is there a best practice for this type of page?
I am using Rails 4...if that matters.
You're right that this is more a RESTful dilemma and not related to any specific framework. It depends on the role of the Parent and Child Models and whether the dashboard 'exposes' any other additional resources. For the sake of simplicity I'm going the pretend the parent model is an Author and the child model is a Book.
If the dashboard only contains a collection of children related to a parent model you could consider using Nested Resources. E.g URLs similar to /authors/{author_id}/books (and alias to /dashboard if required):
# routes.rb
resources :authors do
resources :books
end
The logic then belongs in the BooksController:
class BooksController < ApplicationController
def index
if params[:author_id]
#author = Author.find(params[:author_id])
#books = #author.books
return render '/books/author_index'
else
# Code for simple top level RESTful resource. E.g '/books'
# ...
#books = Book.all
return render 'index'
end
end
end
However if your dashboard is more broad and touches multiple domains, you could consider the dashboard as a resource in itself (e.g it's own DashboardController) as described in this answer.
Related
I am using slugs in my project to give my params an other name but I have two params called: "how-does-it-work".
(.../investor/how-does-it-work)
(.../customer/how-does-it-work)
I would like to use the slugs as how they are currently set.
Is there a way to do that?
Create two distinct routes/controllers, and simply query the corresponding ActiveRecord model in the show action. Assuming there is a slug field on your models:
Rails.application.routes.draw do
resources :customers
resources :investors
end
class CustomersController < ApplicationController
def show
#customer = Customer.find_by(slug: params[:id])
end
end
class InvestorsController < ApplicationController
def show
#investor= Investor.find_by(slug: params[:id])
end
end
This is probably the most conventional way to solve this problem in Rails. If you are using the friendly_id gem, the same approach more or less applies, except for maybe the query itself.
Hope this helps.
So, is /investor/ and /customer/ both parts of the slug?
If that's the case, you can split the string, and do a search based on the "how-does-it-work" in the grouping of "investor" or "customer".
If investor and customer are both parts of the routes, you shouldn't have a difficult time there, because they're pointing to two different controller methods. You should be able to write a search based on each of those methods that correspond to the data. If the data is the same, all your doing is pointing the controller to the correct model data with the correct params.
If you're using friendlyId, it usually has built in candidate matching. Also, if you're meaning to match multiple pages to the same slug (which I've done in the past), you can display a results page if you'd like too, by rendering based on the quantity of results.
I'm in the process of learning Ruby on Rails, and now I have created the mobile version of my application.
I created the relation between models ans controller is one-one. Now I want to make changes to manage three models from one controller. I have read and watch videos a lot about how to do this but, it doesn't work when I try to do it in my application.
Models:
class Subject < ActiveRecord::Base
has_many :pages
class Page < ActiveRecord::Base
belongs_to :subject
has_many :sections
class Section < ActiveRecord::Base
belongs_to :page
Controller:
class SubjectsController < ApplicationController
has_mobile_fu
layout "admin"
before_action :confirm_logged_in
def index
#subjects = Subject.newest_first
#pages = #subjects.pages.sorted
end
This is the error:
NoMethodError (undefined method pages' for # <ActiveRecord::Relation::ActiveRecord_Relation_Subject:0x007fbbf3c9b218>):
app/controllers/subjects_controller.rb:10:inindex'
The application works well if I keep each model managed by its controller. The problem started now that I want to control multiple models from one controller.
Can definitely use multiple models in a single controller. The issue here is you're calling a method that doesnt exist for the active record relation.
An active record relation is typically a collection of returned objects from a query using active record. So the newest_first is returning multiple, not just one. If you want to get all pages for the subjects and sort them, you can do this:
#subjects = Subject.newest_first
#pages = #subjects.map(&:pages).flatten.sort { |a, b| a.title <=> b.title }
Can switch the attribute on which you wish to sort by. The map function goes through each one, and returns the object of which i passed in the symbol. It's a shortcut for:
#subjects.map { |subject| subject.pages }
The flatten then takes that array of active record relations and flattens it into a single array. I then just use the array sort.
Edit Here's a way you can do it using the database:
#subjects = Subject.newest_first
#pages = Page.where.not(:subject_id => nil).order(:title)
MVC
Something else you'll benefit from is to look at the MVC Programming Pattern:
Rails is famous for its strict coherence to the Model-View-Controller pattern, as it works like this:
You send a request to your app
Rails "routes" your request to a specific controller / action
The controller will then collate data from your Models
The controller will then render a view to display this data
The relationship between models and controllers is exclusive; meaning you don't have to call certain models from a controller, etc.
So the basic answer is no, you don't need to call a single model from a controller. However, you do need to ensure you have the correct model associations set up, as per the explanation below:
Associations
The caveat here, is that since Ruby is object-orientated (and Rails, by virtue of being built on Ruby, also being so), it's generally considered best practice to build your application around objects
"Objects" are basically elaborate variables (constructed from your Model classes), but the pattern behind making OOP work properly is super important - everything from Rails' routes to your controller actions are designed to be object-ORIENTATED
Each time you initiate an instance of a Model, Rails is actually building an object for you to use. This object allows you to call / use a series of attributes / methods for the object, allowing you to create the experience you require with Rails
--
The bottom line -
I would highly recommend examining the ActiveRecord Associations in your models (which will determine whether you need to call a single model or not):
#app/controllers/subjects_controller.rb
Class SubjectsController < ApplicationController
def index
#subjects = Subject.newest_first #-> good use of OOP
#posts = # this is where your error occurs (`.posts` is only an attribute of each `Subject` object instance, which is fixed using the accepted answer)
end
end
Hopefully this gives you some more ideas about how to construct Rails applications
What is the Rails way of implementing a view for a data model that contains elements with a has_many/belongs_to relationship?
Here is the model I want to show in list form:
Driver
class Driver < ActiveRecord::Base
...
has_many :cars
end
Here is the model that should be listed underneath each driver:
Car
class Car < ActiveRecord::Base
...
belongs_to :driver
end
I want to show a list of drivers with their respective cars.
This raises two questions:
1 . How do I most efficiently list both in one page? What would my controller and view have to look like? If I simply do
#drivers = Driver.all
I do not seem to get access to the cars in the view.
2 . If I only list the drivers, and load the cars upon request using AJAX, would I use a cars_controller and a drivers_controller? How would I approach writing tests for this scenario?
Expanding on what you already have:
//controller
#drivers = Driver.all
//view
#drivers.each do |d|
puts d.messages
# OR
d.messages.each do |m|
puts m.title
end
end
The same can be done inversely with #cars = Car.all.
1) If you have a route you can access it directly
routes.rb
resources :drivers do
resources :cars
end
to see avialable routes call rake routes
Then, when you initialize all drivers with #drivers = Driver.all you can access each driver and do driver.cars and access each car with .each
So, in the view it will look like #drivers.each do |driver| then you can do driver.cars.each .. if its empty you can build an empty one with driver.cars.build
for efficiency check http://www.ibm.com/developerworks/web/library/wa-rails3/ and https://github.com/orslumen/record-cache
2) You can use the drivers controller, unless you want to use that for specific pages.
Since you can access the driver's relations from the page and you are technically in a driver's page with different functionality then keep it in the drivers_controller. If you feel that you are doing other stuff you can create a new controller for that.
I have a blog with posts in multiple categories. I'd like to give each category an individual landing page that lists all of the bog posts in that category.
What is the appropriate way to generate the routes and controller actions for each of these landing pages? Would it violate the spirit of REST to create multiple index-esque actions (one action per category) in my posts controller? If so, how else should I do it?
For example, my blog might have two categories, "Music" and "Movies".
GET /posts/ # would list all posts.
GET /music/ # would list all posts in the "Music" category.
GET /movies/ # would list all posts in the "Movies" category.
Apologies if this question has an obvious answer, or if I'm asking the wrong question entirely. I'm new to both Rails and REST and I'm trying to understand the best way to structure applications.
I'm not sure it's totally in REST spirit (i do not fully understand it yet), so i'll leave that part of the question to someone else. As the collection method exists to extend RESTful routes, i assume that it is permitted as long as you don't abuse of it.
I don't think, though, that having routes with no "/posts/" prefix is a good thing, because it would induce that the "/music/" path for instance relates to a completely different resource.
you can do something like this :
(in routes.rb)
resources :posts do
collection do
get 'music'
get 'movies'
end
end
... and then add index-like actions to your controller, e.g.:
def music
#posts = Post.where( category: 'music')
render :index
end
if you have a limited and constant set of categories, this can be DRYed up this way:
class Post < ActiveRecord::Base
CATEGORIES = [:music,:movies,:art,:jokes,:friends,:whatever].freeze
end
class PostsController < ApplicationController
Post::CATEGORIES.each do |category|
eval <<-INDEX_LIKE_ACTIONS
def #{category}
#posts = Post.where( category: '#{category}' )
render :index
end
INDEX_LIKE_ACTIONS
end
end
resources :posts do
collection do
Post::CATEGORIES.each {|category| get category.to_s}
end
end
I'm working on a rails app and using a singular resource. However the controller name for the singular resource is plural.
Eg map.resource activity_report expectes the activity_reports_controller.
The explanation given in the rails 3 guide is: "... you might want to use the same controller for a singular route and a plural route..." That is a reasonable explanation, but what is the use case for using the same controller to handle a singular route and a plural route?
In a RESTful Rails application there is usually a mapping of one controller per RESTful resource. For example, let's say we wanted a controller to process user logins (/session) but also to provide a list of users who are currently logged in (/sessions). Logically we could put both of those responsibilities within a SessionsController:
class SessionsController < ApplicationController
# GET /sessions
# Display a list of logged in users
def index
...
end
# GET /session/new
# Display the login form
def new
...
end
# POST /session
# Authenticate a user
def create
...
end
end
An alternative would be to split the functionality for listing logged in users out into a separate administration controller.
You can use it.
class UsersController < Application
end
map.resource :user
map.resources :users
Another situation in which I can imagine using it would be, let's say (and this isn't necessarily the business model you'd want, but stay with me for a moment) you are going to make a site of film reviews, and film information. So, on the one hand you'd have the link to your list of the latest reviews be a plural resource route, something like this:
http://yoursite.com/reviews?count=5
So, in this case, you have a controller for the collection, right? But you're only going to review each movie once. So what if you wanted to provide an easy access to a movie's review?
http://yoursite.com/movies/pirates_of_the_carribean_2/review
Well, there's a nested single resource route, because a movie has_one review, right?