Rails app with Mongoid not saving on update - ruby-on-rails

I'm making a Rails app that uses Mongoid and devise. Currently, the functionality's pretty basic, but it's not working like I expect it to. In particular, update isn't working at all.
I can create a user, but when I go back to update it, it doesn't raise any errors, but also doesn't save. This may be clearer with some code. I've got this in routes:
devise_for :users
resources :users
And this in the controller:
## WORKS PERFECTLY FOR SIGNING USERS UP, AND FLASHES CORRECTLY
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save!
flash[:success] = "Welcome!"
sign_in #user
redirect_to #user
else
render 'new'
end
end
## DOES NOT UPDATE THE USER RECORD, AND DOES NOT FLASH SUCCESS
## IT DOES, HOWEVER, REDIRECT TO SHOW, INSTEAD OF RENDERING EDIT
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.save!
flash[:success] = "Profile edited successfully!"
redirect_to #user
else
render 'edit'
end
end
So yeah. As the code suggests, going through my views to edit an existing user does NOT edit that user, nor does it give the flash saying the users was edited, but it DOES redirect correctly. I currently have NO user validations (though devise might have some) -- but changing all devise-relevant fields doesn't get around the issue, so I don't think it's a silent validation fail.
The form is of the basic
<%= form_for #user do |f| %>
<%= f.label: name %>
<%= f.text_field :name %>
...
<%= f.submit %>
<% end %>
sort.
Not sure what other code could be helpful here. Please let me know. I'm super stumped. Thanks!

you have to pass params you can use update_attributes or update_attributes!
#user = User.find(params[:id])
if #user.update_attributes!(params[:user])
...

Related

Update attributes separately in Rails

Trying to update 2 attributes to a User model, this is my current code in the Users controller:
def update
#user = User.find(params[:id])
if #user.update_attributes(songkickID: params[:user][:songkickID], jamID: params[:user][:jamID])
redirect_to #user
else
redirect_to #user
end
end
The Songkick ID and the Jam ID are entered into 2 different fields. However, with the current code, if I attempt to update the Jam ID on its own, it updates that attribute, but then redirects to the user page (as expected), where the Songkick ID is now nil. Upon entering the Songkick ID again, the Jam ID becomes nil. I suppose this is because they are both part of the same if statement in the controller?
I attempted to use an elsif for the jamID params, but it does not seem to recognise at all (i.e. won't update that attribute for the user). Also attempted || conditional operator.
EDIT: Here's the 2 different forms:
<%= form_for(#user) do |f| %>
<%= f.text_field :jamID, :id=>"jamURL" %>
<%= f.submit "Jam ID", :onclick => "changeImg()", id: "saveJam" %>
<% end %>
and
<%= form_for(#user) do |f| %>
<%= f.text_field :songkickID %>
<%= f.submit "Songkick ID", :type => :image, :src => image_path("songkicklogo.png"), id: "skLogo" %>
<% end %>
And I tried modifiying the code to update_column, but I get wrong number of arguments (1 for 2).
EDIT 2: Following layout from Hartl's Rails Tutorial, I attempted this to define strong parameters:
private
def user_params
params.require(:user).permit(:songkickID, :jamID)
end
But I still get the Forbidden Attributes Error?
EDIT 3: The following code passes, but I worry it doesn't adhere to Rails 4 strong parameters:
Controller:
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
end
def edit
#user = User.find(params[:id])
end
def user_params
params.require(:user).permit(:songkickID, :jamID)
end
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
redirect_to #user
else
redirect_to #user
end
end
end
If I move update to below the update method, I get an undefined variable/method error for user_params, and I cannot make it private.
So - why are you explicitly naming the attributes in your update_attributes?
You should be able to use the following:
#user.update_attributes(params[:user])
Remember that if you've named your form fields correctly, params[:user] is a hash that will already have the keys you want (:songkickID etc)
Now - you will get one of two things coming through to your action, which you then pass through to update_attributes as:
{:songkickID => someID}
{:jamID => someOtherID}
which will correctly update your user and only change the one that is passed.
The problem with your earlier code was that what you passed to update attribute was:
{:songkickID => someID, :jamID => nil}
{:songkickID => nil, :jamID => someOtherID}
which was deliberately overwriting the other id with the nil you passed.
EDIT from OP: Thanks for this, and here's my final controller code:
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
redirect_to #user
else
redirect_to #user
end
end
private
def user_params
params.require(:user).permit(:songkickID, :jamID)
end
end
In last case scenario:
def update
if params[:user][:songkickID]
received_param = { songkickID: params[:user][:songkickID] }
elsif params[:user][:jamID]
received_param = { jamID: params[:user][:jamID] }
end
#user.update_attributes(received_param)
redirect_to #user
end
Note: I removed the last condition since it wasn't useful

ArgumentError in Users#new First argument in form cannot contain nil or be empty

I'm doing a ruby on rails tutorial and all of a sudden going to the signup page returns this:
Anyone know a fix? Or for that matter what went wrong? It seemed very sudden and I don't think I changed anything that would affect the signup page...
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
end
def create
#user = User.new(user_params)
if #user.save
redirect_to #user
else
render 'new'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
end
It means that your #user object was not initialized..
You need to initialize your #user variable. Here are a couple of options.
Make sure your new method initializes it in your UsersController class:
def new
#user = User.new
end
Another option is to just use a new object right in the form:
<%= form_for(User.new) do |f| %>
And yet another option, is to in your view, before echoing the form_for, put:
<% #user = User.new %>
My best recommendation would be to put the variable in the controller like I did in the first example.

Template is missing in action

I wrote a "follow" method in UsersController
def start_following
#user = current_user
#user_to_follow = User.find(params[:id])
unless #user_to_follow == #user
#follow_link = #user.follow_link.create(:follow_you_id => #user_to_follow.id, :user_id => #user.id)
#user.save
flash[:start_following] = "You started following" + #user_to_follow.name
else
flash[:cant_follow] = "You cannot follow yourself"
end
end
Pretty simple. And In the view, I have
<%= link_to 'Follow', follow_user_path(#user) %>
In routes,
resources :users do
member do
get 'follow' => "users#start_following", :as => 'follow'
When I click on the link, it complains: Missing template users/start_following
So, how do I make it just stay on the same page after the action?
The view page that I want to stay on is Show view of the user is to be followed.
ex: users/{user_id}. Is simply redirecting not a solution? I thought adding redirect_to {somewhere} would get rid of the error, but it didn't.
I would redirect to the user in question. If you are using the standard resourceful routes then you can just do
redirect_to(#user_to_follow)
As an aside it's generally considered bad practice to have GET requests that make changes - people normally use put/patch/post/delete requests for those. You can fall afoul of browsers pre-fetching links without the user actually clicking on them.
try:
redirect_to :back, :notice => "successfully followed someone..."
Yes redirect_to solves your problem, I suspect you forgot to add it to both branches of the unless
The code would look like this:
def start_following
#user = current_user
#user_to_follow = User.find(params[:id])
unless #user_to_follow == #user
#follow_link = #user.follow_link.create(:follow_you_id => #user_to_follow.id, :user_id => #user.id)
#user.save
flash[:start_following] = "You started following" + #user_to_follow.name
else
flash[:cant_follow] = "You cannot follow yourself"
end
redirect_to #user_to_follow
end

Rails 3 -- After Failed Sign In, Errors Do No Populate

Edit: Hmm this is interested. I just noticed, my signup route is /signup. But, once I submit the information and the form returns invalid, I'm in the route /users .
I'm building a simple app to learn rails, and I've learned to set up an authentication system.
Today, I added a new plans table, to make different subscriptions for users. The Plan model has_many users, and users belong to plans. After implementing this, I see that if I enter invalid information, error messages do not show up in the view anymore.
I have the following code in the application.html.erb file for it show up -- >
<% flash.each do |key, value| %>
<div class="alert alert-<%= key %>"><%= value %></div>
<% end %>
It works fine on other notices, but it isn't showing the error messages.
One thing to also note, is that if I enter an email incorrectly for example, Rails shows me that it was the problem by highlighting it in red(edited CSS previously to do that). But, the error messages themeselves are nowhere to be found :P
Here's my Users controller :
class UsersController < ApplicationController
before_filter :signed_in_user, only: [:show]
before_filter :correct_user, only: [:show]
def show
#user = User.find(params[:id])
end
def new
plan = Plan.find(params[:plan_id])
#user = plan.users.build
end
def create
#user = User.new(params[:user])
if #user.save
sign_in #user
flash[:success] = "Welcome to the Sample App!"
redirect_to #user
else
render 'new'
end
end
def index
if current_user
redirect_to(user_path(current_user))
else
redirect_to(root_path)
end
end
private
def signed_in_user
unless signed_in?
store_location
redirect_to login_url, notice: "Please sign in."
end
end
def correct_user
#user = User.find(params[:id])
redirect_to(root_path) unless current_user?(#user)
end
end
I did some research, and people have been saying it might because of redirects, but I'm not sure If I'm doing that.
When first visiting the signup though, it's in the form of /signup?plan_id=1, to populate a hidden field with the plan_id in the signup form. When it shows the error screen, the plan_id is no longer there, which I assumed is okay since it already POSTed it. Does that have anything to do with it?
I think the problem is that the error messages you're expecting to see are errors on the User object, not stored in the flash. Try this in your view:
<% #user.errors.full_messages.each do |error_message| %>
<div class="alert"><%= error_message %></div>
<% end %>
See the docs for ActiveModel::Errors for more info.

How to set up acts_as_follower

I'm using the gem acts_as_follower in a rails app. I set it up and it works (In console), however I'm clueless as to how to set it up in a view.
I want to make a button correspond to the user.follow and user.stop_following methods.
The github doesn't explain this. Help please.
You can create controller actions that you link to. For example in an app I have the following two actions added to a user controller. Once the routes are also setup I use the url helpers to link to the actions from my view, and end up displaying the flash messages via javascript callbacks.
UsersController:
def follow
#user = User.find(params[:id])
if current_user
if current_user == #user
flash[:error] = "You cannot follow yourself."
else
current_user.follow(#user)
RecommenderMailer.new_follower(#user).deliver if #user.notify_new_follower
flash[:notice] = "You are now following #{#user.monniker}."
end
else
flash[:error] = "You must <a href='/users/sign_in'>login</a> to follow #{#user.monniker}.".html_safe
end
end
def unfollow
#user = User.find(params[:id])
if current_user
current_user.stop_following(#user)
flash[:notice] = "You are no longer following #{#user.monniker}."
else
flash[:error] = "You must <a href='/users/sign_in'>login</a> to unfollow #{#user.monniker}.".html_safe
end
end
config/route.rb:
resources :users do
member do
get :follow
get :unfollow
end
end
Then in your view you can use the url helper to link to the controller action:
<%= link_to "Unfollow", unfollow_user_path(#user) %>

Resources