Method Defined in Controller Throwing NoMethodError - ruby-on-rails

My application has Dwellings and Roomies. I am building some authentication into the Dwelling view - only users who are roomies of the current dwelling should be able to view certain data - all other users will see a different view.
To enable this, I have created an is_roomie? method in the Users Controller. The method looks like this:
## is_roomie? method in Users_Controller.rb ##
def is_roomie?
roomie_ids = []
#dwelling.roomies.each do |r|
roomies_ids << r.id
end
roomie_ids.include?(current_user.id)
end
I call this method in the Dwelling view as follows:
## show.html.erb (Dwelling) ##
....
<% if current_user && current_user.is_roomie? %>
....
When I loaded the page after implementing this, I get the following NoMethoderror:
NoMethodError in Dwellings#show
Showing >/Volumes/UserData/Users/jraczak/Desktop/Everything/rails_projects/Roomie/roomie/app/views/dwellings/show.html.erb where line #5 raised:
undefined method `is_roomie?' for #User:0x00000102db4608>
For some background, I did try this as a Dwelling method and moved this into the User model to no avail. Thanks in advance for any and all insight!

current_user is a User object, not a UsersController object, so you cannot call the method you've defined on that object. When you think about it in this context, you'll see that you should define this method on User.
Try something like this in app/model/user.rb:
class User < ActiveRecord::Base
# ...
def roomie?(dwelling)
dwelling.roomies.include?(self)
end
end
Looking at this, though, we can improve the code by moving it into the Dwelling class in app/models/dwelling.rb:
class Dwelling < ActiveRecord::Base
# ...
def roomie?(user)
roomies.include?(user)
end
end
You would then use this in the view with:
<% if current_user && #dwelling.roomie?(current_user) %>

The current_user object does not have a method is_roomie?. This is a method in your controller. You can call the method in your show action and make it available for the view like so:
#in UsersController.rb
def show
#is_roomie = is_roomie?
end

Related

Decorator/Presenter/Exhibits and helpers

I have read:
Concerns, Decorators, Presenters, Service Objects, Helpers, Help me Decide
and trying to figure out the difference between presenters, view objects, decorators, exhibits, and helpers.
I have multiple active record models that I need to display in a view using the show method.
Examples of what I need to display are:
ClassModule SomeTypeOfPattern
def name
User.name
end
def car_name
User.car.listing.car_name
end
def car
User.car
end
def car_marketing
User.car.marketing
end
# AND 20 to 30 other similar delegations/methods from 4 related tables
end
So if I delegate these relationships, what should the class/module be called? A presenter? Decorator? View Object? I am so confused by all these terms, but want to follow convention.
The example you are showing looks like a Presenter to me.
A presenter is an object that presents other information with its own interface.
If you changed what you have just a little, you could use it like this:
presenter
class UserCarPresenter
attr_reader :user
def initialize(user)
#user = user
end
def name
user.name
end
def car_name
user.car.listing.car_name
end
def car
user.car
end
def car_marketing
user.car.marketing
end
end
controller
class CarsController < ApplicationController
def show
#user = UserPresenter.new(user)
end
end
view
<h1><%= #user.name %></h1>
<h2><%= #user.car_name %></h1>

accessing devise current_user within model

hi i am trying to access current_user within a model for the purpose of creating an element on the fly with find_or_create_by.
the following is the method within my model
def opponent_name=(name)
self.opponent = Opponent.find_or_create_by_name_and_team_id(name,current_user.team_id) if name.present?
end
but the error i am getting is
NameError in EventsController#create
undefined local variable or method `current_user' for #<Event:0x007fb575e92000>
current_user is not accessible from within model files in Rails, only controllers, views and helpers.
What you should do is to pass the current_user.team_id to the opponent_name method like this:
def opponent_name=(name, current_user_team_id)
self.opponent = Opponent.find_or_create_by_name_and_team_id(name,current_user.team_id) if name.present?
end
Access current_user in Model File:
# code in Applcation Controller:
class ApplicationController < ActionController::Base
before_filter :global_user
def global_user
Comment.user = current_user
end
end
#Code in your Model File :
class Comment < ActiveRecord::Base
cattr_accessor :user # it's accessible outside Comment
attr_accessible :commenter
def assign_user
self.commenter = self.user.name
end
end
Pardon me, if It violates any MVC Architecture Rules.
Its not a good way to access the current_user in a model, this logic belongs to the controller. But if you realy cant find a workaround you should put it into a thread. But keep in mind this is not the way how it should be build.
https://rails-bestpractices.com/posts/2010/08/23/fetch-current-user-in-models/
Rails 5.2 introduced current attributes:
https://api.rubyonrails.org/classes/ActiveSupport/CurrentAttributes.html
but as always... you must have in mind that using global states like this might let to some unpredictable behaviour 🤷‍♀️ :
https://ryanbigg.com/2017/06/current-considered-harmful

Get current_user in Rails form validation by defining a virtual attribute

Rails form validation is designed to go in the model most easily. But I need to make sure the current user has the required privileges to submit a post and the current_user variable is only accessible in the controller and view.
I found this answer in a similar question:
You could define a :user_gold virtual attribute for Book, set it in the controller where you have access to current_user and then incorporate that into your Book validation.`
How can I set this up with my post and user controller so that the current_user variable is accessible in the model?
Solution:
This whole thing is wrong from an application design perspective as #Deefour's answer pointed out. I changed it so my view doesn't render the form unless the condition is true.
The "similar question" is saying you can do something like this
class YourModel < ActiveRecord::Base
attr_accessor :current_user
# ...
end
and then in your controller action you can do something like
#your_model = YourModel.find(params[:id])
#your_model.current_user = current_user
#your_model.assign_attributes(params[:your_model])
if #your_model.valid?
# ...
You can then use self.current_user within YourModel's validation methods.
Note I don't think this is what you should be doing though, as I don't consider this "validation" as much as "authorization". An unauthorized user shouldn't even be able to get the part of your action where such an update to a YourModel instance could be saved.
As for doing the authorization with Pundit as requested, you'd have a file in app/policies/your_model.rb
class YourModelPolicy < Struct.new(:user, :your_model)
def update?
user.some_privilege == true # change this to suit your needs, checking the "required privileges" you mention
end
end
Include Pundit in your ApplicationController
class ApplicationController < ActionController::Base
include Pundit
# ...
end
Then, in your controller action you can do simply
def update
#your_model = YourModel.find(params[:id])
authorize #your_model
# ...
The authorize method will call YourModelPolicy's update? method (it calls the method matching your action + ? by default) and if a falsy value is returned a 403 error will result.
Authorization shouldn't be done in models. Models have already many responsibilities don't you think?
That's a controller thing, and actually you can have the logic in other place using some gem like cancan and in your controller you would do something like:
authorize! :create, Post
You can define a "virtual attribute" in your model like this:
class Book < ActiveRecord::Base
attr_accessor :current_user
end
Its value can be set directly in your controller like this:
class BooksController < ApplicationController
def create
book = Book.new
book.current_user = current_user
book.save!
end
end
And inside your model's validation routine, you can access it like any other ActiveRecord field:
def validate_user_permission
errors[:current_user] = "user does not have permission" unless current_user.is_gold?
end
I can't remember if this is the case with ActiveRecord, but you might be able to set virtual attributes via the mass-assignment methods like create, update, and new in the controller:
def create
Book.create!(current_user: current_user)
end
In order to do that, you would probably have to add the following line to your model to enable mass-assignment of that virtual attribute:
attr_accessible :current_user
I agree with Ismael - this is normally done in the controller. It's not an attribute of the model, it's a permission issue and related to the controller business logic.
If you don't need all the power of a gem like CanCan, you can role your own.
class BooksController < ApplicationController
before_filter :gold_required, :only => :create
def create
book = Book.new
book.save!
end
# Can be application controller
private
def gold_required
return current_user && current_user.is_gold?
end
end
You may want to put the filter on the 'new' method as well.

Adding custom internal method to a controller

I'm new with RoR and I have a controller (UsersController) where I wish to verify the existence of a certain session before anything. Since the session verification code is the same for several methods and I don't want to repeat myself, I decided to make a new method in my controller to check the sessions:
class UsersController < ApplicationController
def index
end
def show
end
def new
if self.has_register_session?
# Does something
else
# Does something else
end
end
def edit
end
def create
end
def update
end
def destroy
end
def self.has_register_session?
# true or false
end
end
And when I run the page /users/new, I got this error:
undefined method `has_register_session?' for #<UsersController:0x1036d9b48>
Any idea?
self when you define the method refers to the UsersController class object, but within the instance method new, self refers to the instance of UsersController.
You can either make your method an instance method:
def has_register_session?
# code
end
You can then get rid of the self when calling has_register_session? in new as well.
Or call the method on the class:
if UsersController.has_register_session?
# code
end
instead of referencing UsersController explicitly you could do self.class.
Note that you likely want the first solution: making has_register_session? an instance method.
By doing def self.blah you've created a class method whereas you want an instance method.
You might also want to make the method protected - all public methods are exposed as actions by default.

Undenfined local variable or method when the view call a method

I'm trying give a "Welcome Message" to my users with that:
#welcome_controller.rb
class WelcomeController < ApplicationController
def hi
#current_user
if (#current_user)
#welr = '¡Bienvenido' + current_user + ' a nuestra web!'
else
#weli = "¡Bienvenido invitado, no dude en registrarse!"
end
end
end
#hi.html.erb Only the call
<%= hi %>
When I initialize my server the controller give me this message:
undefined local variable or method `hi' for
I have tried many wways of repairing this but I can't.
You need to define hi as a helper_method in your controller. Something like
class WelcomeController < ApplicationController
helper_method :hi
def hi
# your stuff here...
end
end
See http://apidock.com/rails/AbstractController/Helpers/ClassMethods/helper_method for more info
That's not how you use controller methods. In Rails, methods defined on a controller are used to 'set up' the data needed for a particular view, or to handle a given request. They're not supposed to be called directly by a view.
For what you're trying to do, you need to add a helper method to WelcomeHelper. So, assuming you want http://yourapp.dev/welcome/ to output the message above, this is what you'd need:
# app/controllers/welcome_controller.rb
class WelcomeController < ApplicationController
def index
# Explicitly defining the `index` method is somewhat redundant, given
# that you appear to have no other logic for this view. However, I have
# included it for the sake of example.
end
end
# app/views/welcome/index.html.erb
<%= greeting %>
# app/helpers/welcome_helper.rb
class WelcomeHelper
# All methods in WelcomeHelper will be made available to any views
# that are part of WelcomeController.
def welcome
if (#current_user)
# You may need to change this to something like `#current_user.name`,
# depending on what #current_user actually is.
'¡Bienvenido' + #current_user + ' a nuestra web!'
else
"¡Bienvenido invitado, no dude en registrarse!"
end
end
end
This article may help you :
Ruby on Rails: Accessing Controller Methods from Your View
Just write:
<% #controller.hi %>

Resources