Rails redirect_to update_path/update_url - ruby-on-rails

In Rails 5, how do you run update on a model, and then execute an update of another controller?
I know it's done with redirect_to, but not sure how exactly. Here is what I have so far:
class BookController < ApplicationController
def update
...
redirect_to author_update_path(id: book.author.id.to_s)
end
end
class AuthorController < ApplicationController
def update
...
redirect_back fallback_location: root_path
end
end
I am on a book page and I updated some fields that belong to the book (like the book title). But then I want to run the update of the author.
I am getting NoMethodError so far...
What do I do?
EDIT
Let me explain it clearly:
My UPDATE action in the authors controller is hundreds of lines long. I need to run it for the nested author. My issue is not in updating the author from within the Books controller update action. My problem is that I need to run the author controller UPDATE action via the UPDATE action of the books controller.
Copy pasting the UPDATE action of the Author controller into the Update action of the Books controller is not an option.

I think that you can do this different, like:
class BookController < ApplicationController
def update
...
#update the author
author = Author.find(book.author.id)
#if you need, you can use Author.update(fields_to_update_with_values)
author.touch
#later redirect
redirect_back fallback_location: root_path
end
end
And you can set in your model associations, an auto "update". RailsGuides say:
If you set the :touch option to true, then the updated_at or
updated_on timestamp on the associated object will be set to the
current time whenever this object is saved or destroyed
Update I recommend that you use a service object and migrate you author controller update code to the service. This is a good practice for easify your controller and helpfull for your issue.
You can follow the Service Objects in Ruby on Rails…and you guide by Rob Race.

For complex actions that need to be shared, I believe the only solution is to extract the actions into a new module. Here is a complete detailed answer for those looking for a similar answer:
class BookController < ApplicationController
def update
# Find the book and then the author of the book
book = Book.find(params[:id])
author = book.author
# create an instance of the author updater and run the update action
author_updater = Modules::AuthorUpdater.new(author)
author_updater.update_author
redirect_back fallback_location: root_path
end
end
class AuthorController < ApplicationController
def update
author = Author.find(params[:id])
# create an instance of the author updater and run the update action
author_updater = Modules::AuthorUpdater.new(author)
author_updater.update_author
redirect_back fallback_location: root_path
end
end
# Place this as author_updater.rb inside lib/modules/
class Modules::AuthorUpdater
def initialize(author)
#author = author
end
def update_author
# update actions go here...
end
end

Related

Passing params[:id] to create method in Rails?

I'm trying to write a create method that collects the ID of the profile the user is currently viewing, along with some other information that is irrelevant to this question. However, because the create method POSTs rather than GETs (as I understand it), the value of params[:id] doesn't exist so it's always null. My code is as follows:
class PostsController < ApplicationController
def new
#Post = Post.new
end
def create
#Post = Post.new(post_params)
#Post.user_id = current_user.id
#Post.target_id = params[:id] #this
if #Post.save
redirect_to :back, notice: "You added a post!"
end
end
private
def post_params
params.require(:post).permit(:body)
end
end
Is there a way to get the value of params[:id] from elsewhere, perhaps from my Users controller in the show method where it actually exists?
Keep in mind that I was successfully able to create a hidden field in the Posts form, but I didn't like the fact that users were able to edit the value using Developer Tools, allowing them to change what profile the post would go to.
If there is a direct relation between the Target and the Post model, you should express this in the controller and model structure: link
This expresses your intention and it provides all the rails automations like routing, url helpers, form helpers, a.s.o.
In your concrete example, my guess is the Target would have many Posts:
class Target < ActiveRecord::Base
has_many :posts
end
class Post < ActiveRecord::Base
belongs_to :target
end
Which would lead to the following route structure:
resources :targets do
resources :posts
end
To create a new post for the current target you would post to:
targets/:target_id/posts
And the target id would be accessed via params[:target_id]

How to organize the Rails structure

I have a very big question about Rails. Suppose that we need to create a web site about blogs, we allow user registration and the user has their own management interface which make it possible for them to add, delete, edit, and select the article and comment. The operation on the article and comment might be used in other positions in the future.
So we have a article model and a comment model.
Now we create a users controller:
class UserController < ApplicationController
def articleList
end
def articleSave
end
def articleUpdate
end
def articleDestroy
end
def articleEdit
end
def articleAdd
end
def commentList
end
def commentDestroy
end
def commentEdit
end
end
But it din't look good, and when the user management control has many features, this user controller will be very big. Should I create an article controller and comment controller to process the request? Just separated into the article controller is like this:
class ArticleController < ApplicationController
def list
end
def save
end
def update
end
def destroy
end
def edit
end
def add
end
end
Comment controller is as follows:
class CommentController < ApplicationController
def list
end
def destroy
end
def edit
end
def update
end
end
I don't know how to organize the structure.
I would have separate controllers for each of the Users Articles and Comments, but nest them with Comments inside Articles, and Articles inside Users. This seems to fit the relationship between the concepts as you've described them
See
http://www.sentia.com.au/blog/when-to-use-nested-controllers-in-your-rails-apps
and
http://guides.rubyonrails.org/routing.html#nested-resources
for more on nesting controllers.

How to abstract generic code shared among many controllers

In my application, I have an user model/controller. An user can have multiple videos, images and blog items. User, and the items can have comments. So I've got the following controllers
user
user/comments
user/picture
user/picture/comments
user/video
user/video/comments
user/blog
user/blog/comments
The problem is, all the comments controllers are almost identical, and the code is becoming hard to manage. Now I'd like to specify a central place, e.g. a app-level CommentsController, which would have the methods to be called from sub-controllers.
What is the best way to do that?
How would for example the following code look after such a change:
class User::Picture::CommentsController < ApplicationController
def delete_all
#user = User.find(params[:user_id])
#picture = #user.pictures.find(params[:picture_id])
if #picture.has_access(current_user)
#picture.comments.destroy_all
redirect_to :back, :notice=>t(:actionsuccesful)
else
redirect_to :back, :alert=>t(:accessdenied)
end
end
end
The #user && #picture initializations are same among different methods (destroy, delete_all, create, index). Could they be moved into a before_filter which would be a sub-controller specific? And then, delete_all would be implemented in the CommentsController?
If the code is that generic, two options:
1) a module including shared methods
Example:
module CommentsActions
# actions, methods
end
class User::Picture::CommentsController <ApplicationController
include CommentsActions
#your additional actions
end
2) subclassing comment controllers from one controller
Example:
class CommentsController < ApplicationController
# actions, methods, filters etc...
end
class User::Picture::CommentsController < CommentsController
#your additional actions
end

Rendering different controller actions in Rails when using resource-oriented controllers

Say I'm making a Q&A site like StackOverflow. I have two resources: Question and Answer. I'm using default Rails RESTful resource routes, so each resource has its own controller and methods for creating it.
In the /questions/show view, I want to allow the user to submit an answer for the particular question. The form will POST to /answers, which will get routed as a request to the AnswersController with a call to the create method.
If the answer was created, I can simply redirect back to the original question. However, I'm running into trouble dealing with validation failures on the answer object. I need to render the /question/show view and show the validation errors for the answer object. It's not clear to me how to best do this.
Here are example snippets of what the two controllers might look like.
class AnswersController < ApplicationController
def create
#answer = Answer.new(params[:answer])
if #answer.save
redirect_to #answer.question
else
# What should go here??
end
end
end
class QuestionsController < ApplicationController
def show
#question = Question.find(params[:id])
#answer = Answer.new(:question_id => #question.id)
end
end
What should go in the else clause of the AnswersController's create method? A redirect seems wrong, since the error is really caused by the same request. Calling something like render :template => 'questions/show' seems wrong too, since I have to initialize any instance variables that the template depends on.
This style of having separate actions for calling GET to view the form for creating an object and calling POST to actually create the object seems to work well within a single controller.
How can it be done across controllers?
Try this on for size. It redirects, but passes back the dodgy answer object full of errors.
class AnswersController < ApplicationController
def create
#answer = Answer.new(params[:answer])
# stash the dodgy answer if it failed to save
session[:answer] = #answer unless #answer.save
redirect_to #answer.question
end
end
class QuestionsController < ApplicationController
def show
#question = Question.find(params[:id])
# if we have one stashed in the session - grab it from there
# because it probably contains errors
#answer = session[:answer] || Answer.new(:question_id => #question.id)
end
end
Some details need adding (eg clearing it from the session when done) etc

Where do I put 'helper' methods?

In my Ruby on Rails app, I've got:
class AdminController < ApplicationController
def create
if request.post? and params[:role_data]
parse_role_data(params[:role_data])
end
end
end
and also
module AdminHelper
def parse_role_data(roledata)
...
end
end
Yet I get an error saying parse_role_data is not defined. What am I doing wrong?
Helpers are mostly used for complex output-related tasks, like making a HTML table for calendar out of a list of dates. Anything related to the business rules like parsing a file should go in the associated model, a possible example below:
class Admin < ActiveRecord::Base
def self.parse_role_data(roledata)
...
end
end
#Call in your controller like this
Admin.parse_role_data(roledata)
Also look into using (RESTful routes or the :conditions option)[http://api.rubyonrails.org/classes/ActionController/Routing.html] when making routes, instead of checking for request.post? in your controller.
Shouldn't you be accessing the parse_role_data through the AdminHelper?
Update 1: check this
http://www.johnyerhot.com/2008/01/10/rails-using-helpers-in-you-controller/
From the looks of if you're trying to create a UI for adding roles to users. I'm going to assume you have a UsersController already, so I would suggest adding a Role model and a RolesController. In your routes.rb you'd do something like:
map.resources :users do |u|
u.resources :roles
end
This will allow you to have a route like:
/users/3/roles
In your RolesController you'd do something like:
def create
#user = User.find_by_username(params[:user_id])
#role = #user.roles.build(params[:role])
if #role.valid?
#role.save!
redirect_to #user
else
render :action => 'new'
end
end
This will take the role params data from the form displayed in the new action and create a new role model for this user. Hopefully this is a good starting point for you.

Resources