InstanceVariableAssumption: UsersController assumes too much for instance variable '#user' - ruby-on-rails

I'm following Michael Hartl tutorial Rails course. On chapter 7, I run Reek on UsersController and I got this following warning:
app/controllers/users_controller.rb -- 1 warning:
1:InstanceVariableAssumption: UsersController assumes too much for
instance variable '#user'
[https://github.com/troessner/reek/blob/master/docs/Instance-Variable-Assumption.md]
Here is my code:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new user_params
if #user.save
flash[:success] = t "welcome_to_app"
redirect_to #user
else
render "new"
end
end
def show
#user = User.find_by id: params[:id]
return if #user
flash[:danger] = t "not_exist_user"
redirect_to root_path
end
private
def user_params
params.require(:user).permit :name, :email, :password,
:password_confirmation
end
end
Kindly explain why I got this error InstanceVariableAssumption and how to fix this.

That looks like one of the smells that reek recommends disabling on the GitHub page. According to this bug report filed on the project, it appears this is just due to rails making use/encouraging patterns that reek doesn't like by default.

Instance Variable Assumption says:
Classes should not assume that instance variables are set or present outside of the current class definition.
Well, this is where Convention over configuration comes in Rails. Rails has its own way of doing things, and the class: UsersController has a dependency upon the class: User.
The variable: #user is an instance variable defined in UsersController, but this is an object of class User. And this is how the things are done in Rails, and you will find it everywhere.
Just assume that this isn't perfect according to Reek gem, but this is how the things are done in Ruby on Rails.

We could use like this:
attr_reader :user, :users
Then all #user, #users can be written like user or users.

Related

Rails When should I use strong parameters?

I am not sure if I understand the concept of strong parameters correctly. I should use strong parameters to params that I will use only to edit some data? Or I should use them for every params I want to get in controller? For example I want to get data between two dates, so I need date1 and date2 as params. Should I use here strong params or not?
The easiest way to understand when you should use strong parameters is to understand what a mass assignment volunerability is. In Rails 3 you could do the following:
class CreateUsers < ActiveRecord::Migration[3.0]
def change
create_table :users do |t|
t.string :email
t.string :encrypted_password
t.boolean :admin
t.timestamps
end
end
end
class UserController < ApplicationController
def create
#user = User.new(params[:user])
if #user.save
redirect_to #user
else
render :new
end
end
end
Here we are just passing a "hash" (its actually an ActionController::Parameters instance) straight into the model. All a malicous user has to do here is request:
POST /users?users[admin]=1
And they have created an admin account. In 2012 Egor Homakov famously exploited one such loophole in Github to commit to the Rails repository.
Such an attack is trivial to perform with cURL or by using the web inspector to manipulate a form.
If we whitelist which attributes the user should be able to pass:
class UserController < ApplicationController
def create
#user = User.new(
params.require(:user)
.permit(:email, :password, :password_confirmation)
)
if #user.save
redirect_to #user
else
render :new
end
end
end
Then this avoids the vulnerability - strong parameters is really just a simple DSL for slicing and dicing nested hash like structures. What changed in Rail 4 is that when you pass a n instance of ActionController::Parameters to a model an exception is raised unless calling #permitted? on the parameters object returns true. This avoids a mass assignment vulnerability from occuring simply due to programmer laziness or ignorance.
It does not sanitize your inputs in any other way. Like for example it won't prevent SQL injection or remote code execution if you treat user input carelessly.
You don't need strong parameters if you're passing parameters one by one like in this very contrived example:
class UserController < ApplicationController
def create
#user = User.new do |u|
u.email = params[:user][:email]
u.password = params[:user][:password]
u.password_confirmation = params[:user][:password_confirmation]
end
if #user.save
redirect_to #user
else
render :new
end
end
end

RoR Controllers: Create / Update methods: #record.save vs #record.errors.any?

Ok, this might get shot down as more "style preference" than "detail question", but Google is failing me.
In my self-taught journey (starting in Rails 2), I learned to do this:
class UsersController < ApplicationController
...
def update
#user = User.find(params[:user_id])
...
if #user.save
# handle success
else
# handle failure
end
end
...
end
But lately (Rails 4/5) I'm seeing a pattern of using this:
class UsersController < ApplicationController
...
def update
#user = User.find(params[:user_id])
...
#user.save
if #user.errors.any?
# handle failure
else
# handle success
end
end
...
end
What's the deal? Am I missing some sort of improvement?
Spotted in the wild:
(these two are from the same codebase generated at a hackathon where I first noticed the change)
events_controller.rb
registrations_controller.rb
(and now I'm searching through my browser history, I'll post more as I re-find)
The second pattern is used a little in a different way.
When you set your attributes and use save it is redundant.
But often you can use create method. So the second pattern would look like this:
def create
#user = User.create(user_params)
if #user.errors.any?
# handle failure
else
# handle success
end
end
As create method return an instance as a result.
I think update in this approach is used more for consistency and copypaste as update method returns boolean and you can use the first pattern for update.
def update
if #user.update(user_params)
# handle failure
else
# handle success
end
end

How to fix controller error in Ruby on Rails and get Paperclip up and running

I have been trying for quite a while to get Paperclip up and running on my website and have followed the step-by-step process outlined on github multiple times and it still won't work. I really need to get this up and running as soon as possible. When I run the code through localhost, I get the message "undefined method `id' for nil:NilClass." This is located in line 20 (commented below). Why doesn't it appear as an error for the identical line under the owners method?
class SurveysController < ApplicationController
def new
#survey = Survey.new
end
def create
end
def survey_params
params.require(:survey).permit(:name, :email, :password)
end
def owners
#survey = Survey.new(survey_params)
#survey.user_id=current_user.id
#survey.save
end
def seeker
#survey = Survey.new
#survey.user_id=current_user.id # line with the error (line 20)
#survey.save
end
private
def survey_params
params.require(:survey).permit(:first_name, :last_name, :email, :looking_for, :moving_to, :gender, :coed, :age, :roommate_type, :housing_type, :roommates_estimate, :roommates_amount, :roommates_group, :roommates_names, :max_rent, :move_in, :move_out, :bedrooms, :amenities, :apartment_pet, :roommate_pet, :hometown, :school, :company, :terms, :avatar, :wake_up, :bedtime, :smoke, :smokeoften, :smokesocially, :smokequit, :drink, :drinkoften, :drinksocially, :drinkquit, :drugs, :drugsoften, :drugssocially, :drugsquit, :interest, :sexualactivity, :sexprivacy, :roommatesexprivacy, :overnight, :overnightoften, :roommateovernight, :realty, :availability, :rent, :address, :otherroom, :age_min, :age_max, :age_mode, :pad_photo, :user_status, :sociability, :tidiness, :question, :noise, :political, :religion, :user_id)
end
def idcheck
end
end
What am I doing wrong and how can I get Paperclip up and running? I would really appreciate any kind of assistance I can get with this because I have a pretty close deadline.
Where are you setting current_user?
Generally this is set via a "sign_in" method or some such, eg:
app/helpers/sessions_helper.rb
module SessionsHelper
def sign_in(user)
# "permanent" = expires: 20.years.from_now.utc
cookies.permanent[:remember_token] = user.remember_token
self.current_user = user
end
def current_user=(user)
#current_user = user
end
def current_user
return unless cookies[:remember_token]
#current_user ||= User.find_by(remember_token: cookies[:remember_token])
end
end
Your specific methods likely vary, but that general pattern is often assumed to be in place in your project. Wherever you handle login flow, the current user should be set to some variable that you can refer to via your controllers. You need to make sure you're setting and using that variable.
Additionally, you should have a before_filter in the controller to ensure only logged-in users can access these actions.
do you also use Devise?
if yes,
please add
before_filter :authenticate_user!, only: [:seeker]
after
class SurveysController < ApplicationController
then Devise will ask you login before you do the seeker action.
If you don't use devise, would you mind post your Gemfile or let me know where did you get this current_suer

I defined a method but still getting an error 'rails undefined method'

I'm doing an authentication application. I have this code
class UsersController < ApplicationController
def new
#user = User.new
#title = "User Sign Up"
end
def create
#user = User.new(params[:user])
sign_in_check #user
if #user.save
#flash[:status] = true
#flash[:alert] = "You have successfully signed up!!"
#sign_in_check #user
redirect_to root_path, :flash => { :success => "Welcome to the Bakeshop"}
else
#title = "User Sign Up"
render 'new'
end
end
end
This is a simple sign-up code, and whenever I try and sign up, rails returns an error:
undefined method `sign_in_check' for #<UsersController:0x68c0a90>
but I defined a method sign_in_check in my Users_helper.rb:
module UsersHelper
def sign_in_check(user)
#some stuff to enable session
end
end
Does anyone have an idea why this is happening, and how to fix it?
The reason is your method is a helper. Helpers will be available in views with matching name by default, but not open to controllers without setting.
Two ways to fix:
Allow this helper in UsersController
class UsersController < ApplicationController
helper :user #This will expose UsersHelper module to UsersController
Instead, put this method into ApplicationController. I would prefer this due to the method's nature.
Include your UserHelper in your UserController as follows and you should be able to use any methods defined within the helper.
class UsersController < ApplicationController
include UsersHelper
...
end
This is usually put in the application controller
class ApplicationController < ActionController::Base
def sign_in_check(user)
#some stuff to enable session
end
end
Helpers are used for views. If you want to use it in both - you can do that, but that doesn't sound like what you're looking for here.
just include your helper module in your controller
class UsersController < ApplicationController
helper :user
...
end
Thanks

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