Issue: When a user (when signed in) creates an order, they are sent to the OrderControllers show page which can only be accessed if signed in by both the buyer and seller. From here they can edit/update their order, etc.
We also have guest_user, someone who isn't signed in, and for them I need a order confirmation in the browser (I'm using Devise gem)
I have created a method:
def order_confirmation
In the OrdersController.
Although, how can I nest this within orders so the page knows which order to show.
Is this possible to nest methods under its' own controllers, or should i just create a small controller only for order confirmations?
For example: example.com/orders/1/order-confirmation
Maybe there are better ways to go about this other than just nesting and creating a controller?
I Tried:
resources :orders do
collection do
get 'order_confirmation'
end
end
With:
def order_confirmation
#order = Order.all.find(params[:id])
end
But it won't work how i want i t seems.
The rake routes gives me:
order_confirmation_orders GET /orders/order_confirmation(.:format)
How can i get?:
order_order_confirmation_orders GET /orders/id/order_confirmation(.:format)
I was able to figure this out from the help of this SO post:
Rails: Custom nested controller actions
By using:
resources :orders do
get 'order_confirmation', :on => :member
end
This creates:
order_confirmation_order GET /orders/:id/order_confirmation(.:format) orders#order_confirmation
Related
So forgive me if my title was a little unclear; I tried to make it as generic as possible.
I have a model User, a ChatRoom model and a ChatRoomUser model (which has no controller). User has a has_many relationship with ChatRoom, through the ChatRoomUser, and vice versa.
I want to make a route to allow the user to leave one of the chat rooms; I have the code to handle the actual leaving of the room, in the UsersController:
def leave
#chosen = ChatRoom.find(params[:chat_room_id])
if #chosen.nil?
redirect_back(fallback_location: chat_room_path(#chosen))
end
current_user.chat_rooms.remove(#chosen)
# If there are no users left in the room, destroy it.
if(!#chosen.users.any?)
#chosen.destroy
end
redirect_to chat_rooms_path
end
and I think that's fine. But I'm unclear as to how to properly set up a route for this. I have in my routes file:
delete '/users/chat_room/:chat_room_id', to: 'users#leave'
but I have no idea how to refer to this in an html.erb file. I've tried <%= link_to users_chat_room_path(#chatroom), method: :delete %> but no dice.
Should I perhaps make a controller for the ChatRoomUsers and make a destroy method on that to handle this? I don't know if that's good practice (making a controller just for a single method).
Thanks in advance for any help.
Routes can be given custom names like this:
delete '/users/chat_room/:chat_room_id',
to: 'users#leave',
as: :leave_room
Now it's available as leave_room_path.
Should I perhaps make a controller for the ChatRoomUsers and make a destroy method on that to handle this? I don't know if that's good practice (making a controller just for a single method).
That is what REST advocates would tell you to do. A controller for just one method is not a bad practice. Bloating your controllers - that's bad practice. Controllers should be as skinny as possible.
Adding to how you might do this RESTfully, and given the object your deleting is a chat_room_user and the only param is :id on a chat_room:
In routes, nest a 'resource' under chat_room for chat_room_user.
resources :chat_room do
resource :chat_room_user, :only => [:create, :delete]
end
Then in the chat_room_users controller
def create
#chat_room = ChatRoom.find_by_id(params[:chat_room_id])
# current_user join a chat room
end
def delete
#chat_room = ChatRoom.find_by_id(params[:chat_room_id])
# current_user leave a chat room
end
In the view, you'd refer to this route as chat_room_chat_room_user_path(#chat_room) and add the method: :method => :delete or :method => :post.
I'm trying to get a simple route working
/agenda_items/5/feed
To do this, I have the following route setup
resources :agenda_items do
member do
get "/feed", to: "comments#feed"
end
end
In each of my controllers, I'm using CanCan to handle the authentication and it works fine, however on this one action I'm having an issue, which I'm pretty sure is down to railsnaming generation. When I runrake routes`, the route above is produced as
feed_agenda_item /agenda_items/:id/feed(.:format) agenda_items/:id#feed
As far as I can tell, CanCan is expecting the :id parameter, to actually be :agenda_item_id so as a result, my parent resource isn't being loaded.
Is there any way I can get rails to change this so that CanCan will work without me having to manually load and authorize the resource, or is there a way I can get CanCan to change what it's looking for on certain actions?
The problem is that your routes are wrong. You try to create a member action for agenda items which routes to the comments controller. If you want a feed of all the commments from a single agenda item you should do something like this:
resources :agenda_items do
resources :comments do
collection do
get :feed
end
end
end
You should now get the following when running rake routes:
feed_agenda_item_comments /agenda_items/:agenda_item_id/feed(.:format) comments#feed
I am using ruby on rails to make a simple social networking site that includes different message boards for each committee of a student group. I want the url structure for each board to look like https://<base_url>/boards/<committee_name> and this will bring the user to the message board for that committee.
My routes.rb file looks like:
resources :committees, only: [:index]
match '/boards/:name', to: 'committees#index(name)'
My index function of committees_controller.rb file looks like:
def index(name)
#posts = Committee.where(name: name)
end
And then I'll use the #posts variable on the page to display all of the posts, but right now when I navigate to https://<base_url>/boards/<committee_name> I get an Unknown Action error, and it says The action 'index(name)' could not be found for CommitteesController.
Could someone guide me through what I have done wrong?
Once I get this working, how would I make a view that reflects this url structure?
Set up your routes like this:
resources :committees, only: [:index]
match '/boards/:name', to: 'committees#show'
and the controller like this:
def index
#committees = Committee.all
end
def show
#committee = Committee.find_by_name!(params[:name])
end
You can't really pass arguments to controller actions the way you were trying to with index(name). Instead, you use the params hash that Rails provides you. The :name part of the route declaration tells Rails to put whatever matches there into params[:name].
You also should be using separate actions for the listing of committees and displaying single committees. Going by Rails conventions, these should be the index and show actions, respectively.
When routing, you only specify the method name, not the arguments:
match '/boards/:name', to: 'committees#show'
Generally you will declare something with resources or match but not both. To stay REST-ful, this should be the show method. Index is a collection method, usually not taking any sort of record identifier.
Arguments always come in via the params structure:
def show
#posts = Committee.where(name: params[:name])
end
Controller methods that are exposed via routes do not take arguments. You may construct private methods that do take arguments for other purposes.
I have two models, User and PushupReminder, and a method create_a_reminder in my PushupReminder controller (is that the best place to put it?) that I want to have create a new instance of a PushupReminder for a given user when I pass it a user ID. I have the association via the user_id column working correctly in my PushupReminder table and I've tested that I can both create reminders & send the reminder email correctly via the Rails console.
Here is a snippet of the model code:
class User < ActiveRecord::Base
has_many :pushup_reminders
end
class PushupReminder < ActiveRecord::Base
belongs_to :user
end
And the create_a_reminder method:
def create_a_reminder(user)
#user = User.find(user)
#reminder = PushupReminder.create(:user_id => #user.id, :completed => false, :num_pushups => #user.pushups_per_reminder, :when_sent => Time.now)
PushupReminderMailer.reminder_email(#user).deliver
end
I'm at a loss for how to run that create_a_reminder method in my code for a given user (eventually will be in a cron job for all my users). If someone could help me get my thinking on the right track, I'd really appreciate it.
Thanks!
Edit: I've posted a sample Rails app here demonstrating the stuff I'm talking about in my answer. I've also posted a new commit, complete with comments that demonstrates how to handle pushup reminders when they're also available in a non-nested fashion.
Paul's on the right track, for sure. You'll want this create functionality in two places, the second being important if you want to run this as a cron job.
In your PushupRemindersController, as a nested resource for a User; for the sake of creating pushup reminders via the web.
In a rake task, which will be run as a cron job.
Most of the code you need is already provided for you by Rails, and most of it you've already got set in your ActiveRecord associations. For #1, in routes.rb, setup nested routes...
# Creates routes like...
# /users/<user_id>/pushup_reminders
# /users/<user_id>/pushup_reminders/new
# /users/<user_id>/pushup_reminders/<id>
resources :users do
resources :pushup_reminders
end
And your PushupRemindersController should look something like...
class PushupRemindersController < ApplicationController
before_filter :get_user
# Most of this you'll already have.
def index
#pushup_reminders = #user.pushup_reminders
respond_with #pushup_reminders
end
# This is the important one.
def create
attrs = {
:completed => false,
:num_pushups => #user.pushups_per_reminder,
:when_sent => Time.now
}
#pushup_reminder = #user.pushup_reminders.create(attrs)
respond_with #pushup_reminder
end
# This will handle getting the user from the params, thanks to the `before_filter`.
def get_user
#user = User.find(params[:user_id])
end
end
Of course, you'll have a new action that will present a web form to a user, etc. etc.
For the second use case, the cron task, set it up as a Rake task in your lib/tasks directory of your project. This gives you free reign to setup an action that gets hit whenever you need, via a cron task. You'll have full access to all your Rails models and so forth, just like a controller action. The real trick is this: if you've got crazy custom logic for setting up reminders, move it to an action in the PushupReminder model. That way you can fire off a creation method from a rake task, and one from the controller, and you don't have to repeat writing any of your creation logic. Remember, don't repeat yourself (DRY)!
One gem I've found quite useful in setting up cron tasks is the whenever gem. Write your site-specific cron jobs in Ruby, and get the exact output of what you'd need to paste into a cron tab (and if you're deploying via Capistrano, total hands-off management of cron jobs)!
Try setting your attr_accessible to :user instead of :user_id.
attr_accessible :user
An even better way to do this however would be to do
#user.pushup_reminders.create
That way the user_id is automatically assigned.
Use nested routes like this:
:resources :users do
:resources :pushup_reminders
end
This will give you params[:user_id] & params[:id] so you can find your objects in the db.
If you know your user via sessions, you won't need to nest your routes and can use that to save things instead.
Using restful routes, I would recommend using the create action in the pushup_reminders controller. This would be the most conventional and Restful way to do this kind of object creation.
def create
#user = User.find(params[:user_id]
#reminder = #user.pushup_reminders.create()
end
If you need to check whether object creation was successful, try using .new and .save
I'm new to rails and having issues with scope. I have two classes, Post and Story. each instance of Post is created with data from a form on the Story show page. One of the parameters for Post is the id of the instance of Story from which it was created. I don't know how to get this id. #story is nil even if I defined it in the controller under def show as #story=Story.find(params[:id])
Thanks!
It really depends on where you're creating the post, and the routing for the controllers. I'm hoping this is in a PostsController, not StoriesController - as it doesn't really belong in the latter.
And so, presuming you're using PostsController, the best approach (given every post is tied to a Story, it sounds), is to have this controller nested within stories in your routes.rb file:
resources :stories do
resources :posts
end
Then, the story id (given the form is set up correctly) is available in params[:story_id] - and creation could look something like this:
story = Story.find params[:story_id]
post = story.posts.create params[:post]
Working off a few assumptions here, but hopefully this is helpful.