Ruby on Rails - updating multiple models from the one controller - ruby-on-rails

I'm trying to get my head around saving to multiple models from the one controller and it's probably really simple but I can't figure it out.
I have a User Model that has many loanitems and the loanitems belong to the user associations set up.
In my loanitems controller I want each loanitem create action to update a the user.points
So at the moment I have the following code that doesn't throw any errors but doesn't update the user model either.
def create
#loanitem = current_user.loanitems.build(params[:loanitem])
respond_to do |format|
if #loanitem.save
#loanitem.user.points = #loanitem.user.points + 50
#loanitem.user.save
format.html {redirect_to root_path, :flash => {:success => "Loan Item created" } }
format.xml{render xml: root_path}
else
format.html {render 'pages/home' }
format.xml {render xml: 'pages/home'}
end
end
end
I'm also trying the following variation on a theme
def create
#loanitem = current_user.loanitems.build(params[:loanitem])
respond_to do |format|
if #loanitem.save
current_user.points = current_user.points + 50
current_user.save
format.html {redirect_to root_path, :flash => {:success => "Loan Item created" } }
format.xml{render xml: root_path}
else
format.html {render 'pages/home' }
format.xml {render xml: 'pages/home'}
end
end
end
But should I be sending some message instead to the userupdate controller instead? That currently looks like this ...
def update
#user = User.find(params[:id])
if
#user.update_attributes(params[:user])
redirect_to #user, :flash => { :success => "Profile has been updated!"}
else
#title = "Edit Profile"
render 'edit'
end
end
Or I have heard that the business logic really should all be contained in the model so maybe the method should be written in User.rb and then called by the Loanitems controllers create method?
I know it's a real rookie question but any advice would be really welcome.

It sounds like you need to use a Transaction, so you can modify multiple items as a single atomic unit:
def create
respond_to do |format|
User.transaction do
begin
#loanitem = current_user.loanitems.create!(params[:loanitem]) # raises exception if it can't create
#loanitem.user.update_attributes!(:points => #loanitem.user.points + 50) # raises exception if it can't update
format.html {redirect_to root_path, :flash => {:success => "Loan Item created" } }
format.xml{render xml: root_path}
rescue ActiveRecord::RecordInvalid
format.html {render 'pages/home' }
format.xml {render xml: 'pages/home'}
raise ActiveRecord::Rollback
end
end
end
end
This allows you to have a simple happy-path where multiple objects are updated/created and roll-back all changes so far if anything goes wrong and render your error handling logic. The objects will have the validation messages you can display to the user.

Related

Not working flash messages with respond_to in Rails 3.2

I have this simple action (originally generated by scaffold):
def destroy
#item = Item.find(params[:id])
#item.destroy
respond_to do |format|
format.html { redirect_to :back, :success => 'Post was successfully removed.' }
format.json { head :no_content }
end
end
The page is redirected back, but the alert message is not shown. (snippet for displaying flash messages works well in the whole application, but not here)
What's the problem here?
Thanks
Try this:
format.html { redirect_to :back, :flash => {:success => 'Post was successfully removed.' }}
The flash key is either :notice or :alert. There is no :success key.

Difference between these two create methods in a users controller

Is there a difference between
def create
#user = User.new(params[:user])
if #user.save
redirect_to root_url, :notice => "Signed up!"
else
render :new
end
end
and
def create
#user = User.new(params[:user])
respond_to do |format|
if #user.save
format.html { redirect_to(:users, :notice => 'Registration successfull. Check your email for activation instructions.') }
format.xml { render :xml => #user, :status => :created, :location => #user }
else
format.html { render :action => "new" }
format.xml { render :xml => #user.errors, :status => :unprocessable_entity }
end
end
end
Ignore the error and notice issues, my main question is the difference between using xml format and not using it, they seem to do the exact thing.
Using respond_to with different format than html give you the ability to have the response in the specified format (useful for web-service).
In that case (User creation) I don't think it is really useful, but it's all up to you!
Not using respond_to like your first exemple will simply render html.
More infos about respond_to here:
http://apidock.com/rails/ActionController/MimeResponds/InstanceMethods/respond_to

How to handle update of a single user model data separately: user info, password, extra information

I want to present users with separate pages/dialogs for editing their own information. However, the information is held in a single model (called User). Now I'm trying to find the best approach for handling the update calls from partials. My code currently:
def edit
render :layout=>!request.xhr?
end
def edit_password
render :layout=>!request.xhr?
end
def edit_extra
unless #user.extra
#user.build_extra
#user.extra.value = 2047
end
render :layout=>!request.xhr?
end
def update
respond_to do |format|
if #user.update_attributes(params[:user])
format.html { redirect_to #user, :notice => 'User was successfully updated.' }
format.json { head :no_content }
else
format.html { render :action => "edit", :layout=>!request.xhr? }
end
end
end
The thing is, all forms in methods (edit, edit_password and edit_extra) call the update method. However, there are two problems:
If the data parsing isn't validated, user is presented with the "edit" form, which is incorrect.
I want to have a password confirmation on extra data. User shouldn't be able to edit that information unless they supply a correct password.
I would like to make more generalized solution than just duplicating the update -code. The largest problem is rendering correct layout (edit, edit_password) based on the current action.
For now, I solved the problem by creating separate edit_section parameter that will be handled in update.
def update
respond_to do |format|
if #user.update_attributes(params[:user])
format.html { redirect_to #user, :notice => (t :actionsuccesful) }
format.json { head :no_content }
else
action = if params[:edit_section] then "edit_" + params[:edit_section] else "edit" end
format.html { render :action => action, :layout=>!request.xhr? }
end
end
end
And in forms (edit_password, etc)
=form_for(#user, :remote => true) do |f|
= hidden_field_tag :edit_section, "password"

ruby on rails redirecting to old param

I have an option for a user to update their username in their profile. However, when the url for their profile has been set as localhost/user/username and when they submit their changes, they are redirected to their old username (not the new updated one).
Here is my update from users_controller.rb
Any suggestions?
def update
#user = User.find_by_username(params[:id])
#page_title = "Edit Profile"
respond_to do |format|
if #user.update_attributes(params[:user])
format.html { redirect_to(user_url,
:notice => "Your profile has been saved.") }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #user.errors,
:status => :unprocessable_entity }
end
end
end
also, I'm using
def to_param
username
end
Doesn't user_url take #user as an argument? How have you defined your route for that?
One thing I can think of immediately is #user.reload!.

Rails code generated with ORM mangomapper throws NoMethodError (undefined method `each' for "4d2aeaea4403baa84a000005":String)

I am absolutely totally news to Rails and to MongoDB. I have been following tutorials from a good book and create my first Rails app with a light Twitter copy. Everything went fine and smooth.
But as part of my learning process I wanted to build the same app using MongoDB rather than the default SGBD.
I therefore configured mongo and installed the mongo_mapper gem. Everything has been configured properly following this tutorial: http://www.mongodb.org/display/DOCS/Rails+3+-+Getting+Started. Then I struggled a little bit to allow Rails generate to work without throwing me the --orm not specified error. In order to get past this I had to add the rails3-generators gem and add it to the Gemfile.
Once all this was done, everything worked fine. I was able to successfully launch the Rails server.
I added a User controller thanks to the generate. The page works fine and even lists the users I have previously added:
However all the other actions, showing, editing, deleting, etc. are not working (creating works, but then it goes to show and the errors comes):
It's virtually the same error for all different actions.
The one difference I can notice right off the bat is that with the non MongoDB db, the id's of the user was starting at 1, etc. but here with MongoDB it looks like a randomly generated id that is much more complex and that is not of type int: 4d2ae91d4403baa84a000002
I am thinking that this may be creating the issues, since all action are using the id as a parameter... but I have no idea how to fix this. I have looked at the ruby generated code and it looks alright to me (extremely similar to the code generate for the default db).
Any help would be greatly appreciated ! I don't know how to go forward with my project without solving a simple generate code with mongodb.
Thanks,
Alex
ps:
please that I did not write any of this code at all. everything has been generated, which is kinda why I expected to work from the get go...
as asked here is the code for users_controllers:
class UsersController < ApplicationController
# GET /users
# GET /users.xml
def index
#users = User.all
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #users }
end
end
# GET /users/1
# GET /users/1.xml
def show
#user = User.first(params[:id])
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #user }
end
end
# GET /users/new
# GET /users/new.xml
def new
#user = User.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #user }
end
end
# GET /users/1/edit
def edit
#user = User.first(params[:id])
end
# POST /users
# POST /users.xml
def create
#user = User.new(params[:user])
respond_to do |format|
if #user.save
format.html { redirect_to(#user, :notice => 'User was successfully created.') }
format.xml { render :xml => #user, :status => :created, :location => #user }
else
format.html { render :action => "new" }
format.xml { render :xml => #user.errors, :status => :unprocessable_entity }
end
end
end
# PUT /users/1
# PUT /users/1.xml
def update
#user = User.first(params[:id])
respond_to do |format|
if #user.update(params[:user])
format.html { redirect_to(#user, :notice => 'User was successfully updated.') }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #user.errors, :status => :unprocessable_entity }
end
end
end
# DELETE /users/1
# DELETE /users/1.xml
def destroy
#user = User.first(params[:id])
#user.destroy
respond_to do |format|
format.html { redirect_to(users_url) }
format.xml { head :ok }
end
end
end
Hummm so it seems I found the pb...
I replaced:
#user = User.first(params[:id])
by
#user = User.find(params[:id])
But again, this code was generated... so where does the error come from ? Is there a "bug" in rails3-generators ? Or somehow I screwed up the generation ?
Alex

Resources