I'm new into rails and i have a question. I'm coding one Rails application for organize group attivity. I had realized a controller for Group Notification (when a new member was included into the group he/her recived one notification). I want to create a mechanism for notifications on new polls created. Do I need a new Controller or can i use the same?
If you really want custom controllers, you certainly can.
However, I find they make things more confusing as they don't follow the standard structure and I often forget about them and why I made them.
I'd do my best to stick to the practice of having controllers that match my models.
Here's the way I would structure your app:
Models:
Member (or maybe you call them users)
Group
Poll
Controllers:
Used for things like create, show, list, update, delete a record
MembersController
GroupController
PollsController
When doing more than basic CRUD operations, I focus on what is the "primary" or "parent" object and see if I can make it fit a CRUD operation.
So let's take your example of adding a member to a group:
If you have a form for a member where you can select an existing group, or maybe even create a new group, I would say member is the "primary" object and I would use the MembersController#create and MembersController#update actions. And I would use accepts_nested_attributes:
class Member < ApplicationRecord
has_many :groups
accepts_nested_attributes_for :groups
end
If you have the opposite (a group form where you add or remove members), I would flip this, using GroupsController#create and GroupsController#update with nested attributes.
class Group < ApplicationRecord
has_many :members
accepts_nested_attributes_for :members
end
This way your routes are very standard:
resources :members
resources :groups
resources :polls
You can use this same logic for notifications of polls.
Poll would be the primary object, so you could handle this logic within the PollsController#create and PollsController#update
If you want to break away from the RESTful routes, I would still use these controllers.
E.g.:
# routes
resources :members do
# the view with the form to choose a group
get 'choose_group', on: :member
# the controller action to receive the form data
post 'add_to_group', on: :member
end
# controller
class MembersController < ApplicationController
def choose_group
#member = Member.find(params[:id]
#groups = Group.all
end
def add_to_group
#member = Member.find(params[:id]
#member.groups << Group.find(params[:group_id]
if #member.save
redirect_to 'some/other/place'
else
render 'choose_group'
end
end
end
Related
This is mostly an api design question. I have a Rails api which has routes for Users and routes for Schools. I would like to make a single call from my front end application to the api with a param of UserId which returns all of the schools associated with that user.
What is the best way to do that? Should I create a new route in UsersController called user-schools? Or a new route in SchoolsController called schools-user? Or create an entirely new controller called user-schools? Thanks for any guidance!
PS: Getting the records from ActiveRecord in the controller is not the problem. The problem is how to best design this api.
The RESTful way to define this would be through a nested route:
GET /users/:user_id/schools
The same basic design principles apply here for API and "classic" applications.
You can define this by nesting the calls to the resources macro:
resources :users do
resources :schools, only: [:index]
end
This will route /users/:user_id/schools to SchoolsController#index. While you can "sniff" for the user_id param:
class SchoolsController
# GET /schools
# GET /users/1/schools
def index
schools = if params[:user_id].present?
user = User.find(params[:user_id])
user.schools
else
School.all
end
render json: schools
end
end
A cleaner design is to use a seperate controller for the nested context:
resources :users do
resources :schools, only: [:index], module: :users
end
module Users
class SchoolsController < ApplicationController
# GET /users/1/schools
def index
user = User.find(params[:user_id])
render json: user.schools
end
end
end
This controller only does a single job. You could also name it UserSchoolsController but splitting your controllers into folders (and namespaces) makes it easier to organize them.
I'm writing a simple Rails app and I'm wondering what to name a controller that creates accounts.
Some background: Users create a schedule and it's publicly visible. There are users and events. An event can have multiple event_sessions. That's pretty much it.
During registration a user is created, an event is created, and sessions are created. So where do I put this, the UsersController? And, if account creation includes all this other stuff, do I put it in a new controller? If so, what do I call the controller — ExternalSiteController? AccountController?
I would start with something like the following, and tweak as necessary:
class UsersController < ActionController::Base
def create
# ...
User.create(user_params)
# ...
end
end
class User < ActiveRecord::Base
after_create :setup_initial_event
has_many :events
DEFAULT_EVENT_PARAMS = {
# ...
}
def setup_initial_event
events.create(DEFAULT_EVENT_PARAMS)
end
end
class Event < ActiveRecord::Base
after_create :setup_initial_sessions
belongs_to :user
has_many :sessions
def setup_initial_sessions
# You get the idea
end
end
If you don't have an account model (in which case AccountsController would be perfect), I'd put the code in the UsersController. User is probably the most complex and important model of the three (the registration of a user is what's kicking everything off, after all). Of course, you can create any object in any controller (i.e. you can call User.create() in the EventsController).
I am having some difficulties passing in parameters to my controller. I created an Single table inheritance model in my model file.
class Account < ActiveRecord::Base
belongs_to :user
end
class AdvertiserAccount < Account
end
class PublisherAccount < Account
end
I setted up my routes table with nested resources
resources :advertiser_accounts do
resources :campaigns
end
I want to be able to pass the current account_id (an account_id from one of my two subclasses of account) to my campaign controller file.
A URL that I would use is http://127.0.0.1:3000/advertiser_accounts/1/campaigns
Since my resource for the url is advertiser_accounts and not accounts, I am not able to get the parameter :account_id.
class CampaignsController < ApplicationController
def index
#account = current_user.accounts.find_by_id(params[:account_id])
end
end
is there a shortcut to get the current resource or the id? Am I passing in parameters correctly? It seems confusing to call many find_by_id in the controller. Any help is appreciated.
Edit Possible solution:
One of the solutions that I was thinking was setting a type in my routes and then in my controller I would use case statement then get params[:advertiser_account_id] but that seems very tedious and messy. Especially if I will need to copy and paste a list of case statements in each action.
routes.rb
resources :advertiser_accounts, :type => "AdvertiserAccounts" do
resources :campaigns
end
campaigns_controller.rb
def index
case params[:type]
when "AdvertiserAccounts"
#account = current_user.accounts.find_by_id(params[:advertiser_account_id])
when "PublisherAccounts"
#account = current_user.accounts.find_by_id(params[:publisher_account_id])
end
end
Try this out:
resources :advertiser_accounts, :as => "account" do
resources :campaigns
end
that should give you
/advertiser_accounts/:account_id/campaigns/:id(.:format)
You can try with "becomes" method in your controller.
In your private method where you're looking for the account_id you would have:
#account = Account.find(params[:account_id]).becomes Account
In my "routes.rb" file I have the following line:
resource :users
which gives me a bunch of named routes for accessing my User model in a RESTful manner.
Now, I've made some additions to the User model including creating a special class of user. These are still stored in the User model but there is a "special" flag in the database that identifies them as special.
So, is it possible to create special_users resource? For example, I'd like to have a "special_users_path" as a named route to "/special_users" which will return an index of only the special users when you perform a GET on the URL.
Is there a way to do this?
In Rails routing, a 'resource' refers to the standard 7 routes that are created for RESTful resources: index, show, new, create, edit, update and destroy. Normally that is enough, but sometimes you might want to create another action.
In the model, you want to create a scope that only returns special users:
class User < ActiveRecord::Base
scope :special, where(:special => true)
end
On the controller side, there are two ways to go about this. What you are suggesting is the creation of an additional action:
match "/users/special" => "users#special"
resource :users
In the controller, your special action would return the scope you just created:
class UsersController < ApplicationController
def special
#users = User.special
end
end
That will do what you ask, but I would suggest NOT doing it this way. What if you add other flags later that you want to search by? What if you want to search by multiple flags? This solution isn't flexible enough for that. Instead, keep the routes the way they are:
resource :users
and just add an additional line to your controller:
class UsersController < ApplicationController
def index
#users = User.all
#users = #users.special if params[:special]
end
end
and now, when you want to display special users, simply direct the user to /users?special=true
This approach is much more future-proof, IMO.
(This answer is assuming Rails-3. If you're still using 2.3 let me know)
You could set the special_users as a resource:
resource :special_users
If you need to point it to a special controller, you could specify it with:
resource :special_users, :controller => :users
But I would really suggest you to not creating another controller for retrieving a kind of user, but using a param to get them:
class UsersController < ApplicationController
def index
users = case params[:type].to_s
when "special"
User.special_users # Using named scopes
else
User.all
end
end
end
When you use the users_path to call the special users:
users_path(:type => :special)
To set up nested resources in Rails, I have seen example routes given like this:
map.resources :players
map.resources :teams, :has_many => :players
By doing this, you can visit teams/1/players and see a list. But it lists all players, not just those that belong to team 1.
How can I list only the resources that are associated with the parent resource?
You need to load the team first. A common practice is to do this in a before filter.
class PlayersController < ActionController::Base
before_filter :get_team
def get_team
#team = Team.find(params[:team_id])
end
def index
#players = #team.players # add pagination, etc., if necessary
end
def show
#player = #team.players.find(params[:id])
end
end
Note that the code above insists that you specify a team. If you want the same controller to work for both, you need to change it slightly (i.e. check for params[:team_id]).
You can use the excellent inherited_resources gem to DRY this up if you controller logic is straightforward.
The problem has little to do with map.resources and routing in general.
Note, players are not fetched magically by the framework: there's some action in some controller processing teams/1/players request and your code there fetches list of players to show. Examining that action (or posting here) should help.