undefined method `author=' for #<Class:0x9a785a8> - ruby-on-rails

I am trying to assign the author of a post automatically when he posts or edits. I have defined a helper function in application_controller.rb which checks for the current user and returns the object if signed in. But I get this error, and I don't seem to understand what's going wrong.
application_controller.rb
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
helper_method :current_user
My controller action:
def create
#article=Article.find(params[:article_id])
#review=#article.reviews.assign_author(current_user.name).create(review_params)
redirect_to article_path(#article)
end
My model:
def self.assign_author(name)
self.author=name
end
Can anyone point what is the mistake I am making? I am sure its pretty small, but I don't seem to get it.

#review = #article.reviews.assign_author(current_user.name).create(review_params)
This is not how you add a new element to a collection. Should be something like this
#review = #article.reviews.build(review_params)
#review.assign_author(current_user.name)
#review.save
And assign_author method should be instance method (no self.)
def assign_author(name)
self.author = name
end

#article.reviews returns a collection of reviews, so you have to apply the author for all of them:
#review=#article.reviews.map{ |review| review.assign_author(current_user.name) }.create(review_params)

It seems like you need to restructure a bit.
#review=#article.reviews.assign_author(current_user.name).create(review_params)
is not something I typically work with. Instead, I would do something like this:
def create
#article=Article.find(params[:article_id])
review=Review.create(review_params)
review.assign_author(current_user.name)
#article.add_review(review)
redirect_to article_path(#article)
end
That's how I would normally expect a create method to look.

Related

How should I add a custom property to a rails helper method?

I have a Rails helper method...
def current_user
#user = User.find(cookies[:user_id]) if cookies[:user_id]
end
And I want to add a non-database property to it...
self.custom_property = true
So I can access it in my view.
<%= current_user.custom_property %>
But, Rails says "Undefined method: custom_property"
Here's the full code:
def current_user
#user = User.find(cookies[:user_id]) if cookies[:user_id]
#user.ribbon_array ||= []
self.is_moderator = #user.ribbon_array.include?(1) ? true : false
end
I'd like to do it like this so in my view I can check current_user.is_moderator. Elsewhere in my code I have an #is_moderator variable that's specific to each page, but this one would be used across the whole app and specific to the current_user as opposed to the user on the profile.
To accomplish this
I'd like to do it like this so in my view I can check
current_user.is_moderator. Elsewhere in my code I have an
#is_moderator variable that's specific to each page, but this one
would be used across the whole app and specific to the current_user as
opposed to the user on the profile.
I recommend you to implement a method on your user model called
def is_moderator_of(page)
# returns true or false here
end
Or better yet, use a authorization gem like cancan
EDIT:
Also, this
#user = User.find(cookies[:user_id]) if cookies[:user_id]
#user.ribbon_array ||= []
will generate "Method not found: ribbon_array for nil" if there is no cookies[:user_id]
Edit
My bad without too much thought. In this case maybe a better solution is to add another helper method. Helpers themselves are not so object oriented, so you should be able to tolerate this kind of solution as well.
def current_user
#current_user = User.find(cookies[:user_id]) if cookies[:user_id]
end
def is_moderator?
ribbon = #current_user.ribbon_array || []
ribbon.include?(1)
end
In your app/models/user.rb file:
class User << ActiveRecord::Base
attr_accessor :custom_property
...
end
Then your view code should work.
Alternatively, you could just set #custom_property = true in the helper, and use #custom_property in the view.

DRY controllers for nested routes in Ruby

I have a project that's set up with the following models. Each -> represents a has_many relation:
Users->Goals->Milestones
My routes for the Milestones look like this:
user_goal_milestones GET /users/:user_id/goals/:goal_id/milestones(.:format) milestones#index
POST /users/:user_id/goals/:goal_id/milestones(.:format) milestones#create
new_user_goal_milestone GET /users/:user_id/goals/:goal_id/milestones/new(.:format) milestones#new
edit_user_goal_milestone GET /users/:user_id/goals/:goal_id/milestones/:id/edit(.:format) milestones#edit
user_goal_milestone GET /users/:user_id/goals/:goal_id/milestones/:id(.:format) milestones#show
PUT /users/:user_id/goals/:goal_id/milestones/:id(.:format) milestones#update
DELETE /users/:user_id/goals/:goal_id/milestones/:id(.:format) milestones#destroy
I find myself in many of the "functions" in the Milestones controller doing a lot of this:
def index do
#user = User.find(params[:user_id])
#goal = Goal.find(params[:goal_id])
end
def edit do
#user = User.find(params[:user_id])
#goal = Goal.find(params[:goal_id])
end
How can I modify my controller so I don't have to define #user and #goal all the time?
I tried putting them directly at the top, right after the start of the class definition block, but it didn't work.
If the params are always the same you can create a method like this
def set_user_and_goal
#user = User.find(params[:user_id])
#goal = Goal.find(params[:goal_id])
end
and put it in a before_filter at the top
before_filter :set_user_and_goal
and set it to whatever action you like
before_filter :set_user_and_goal, :only => [:edit, :index]
Edit:
Also, to make sure that it doesn't blow up in your face, you can do
#user = params.has_key?(:user_id) ? User.find(params[:user_id]) : nil
and as requested.. make sure that the goal belongs to the user by doing something like
#goals = #user.goals.find(params[:goal_id])
you an always define your own helper methods
def goal_milestone(goal)
user_goal_milestone(goal.user, goal)
end
You can add it to your application_helper, and then use any in any of your views. This would create the small helper methods as you asked in your question.
looking for a gem that does this for you didn't show me anything, but you can code this yourself in a generic way.

Testing views with rspec - undefined current_user in helper file when rendering view

I'm using rspec, with devise and trying to test my views.
In a helper function which my view calls, I use the variable current_user
This works fine in practice, but using rspec, I get a sad 'undefinied variable or method current_user' error.
I've tried a lot of things that don't work.
#user = FactoryGirl.build_stubbed(:user)
assign(current_user, #user)
sign_in :user, #user
def current_user
#user
end
current_user = #user
view.stub!(:current_user).and_return #user
assign[:currrnet_user] = #user
Anyone know how to do this? Thanks in advance.
I had to do this to get it to work.
RSpec.describe 'foos/index.html.erb', type: :view do
before do
controller.singleton_class.class_eval do
def current_user
User.find_by(username: 'schmoe')
end
helper_method :current_user
end
render
end
...
end
So, the solution to helper methods is to stub them. I don't really like this solution, because I see it as a bug, but it is the way to do what needs to be done.

pass a variable across multiple controllers in rails

I wanted a variable #user to be able to accessible across all the other controllers. How do i go with this.
Here is an example
Class User
def self.current=(u)
#current_user = u
end
def self.current
#current_user
end
end
You have to set User.current = somewhere, for example in your application controller.
Then in another model or controller just call User.current
You may want to have a current_user function into your ApplicationController, something like :
def current_user
#current_user ||= User.find( session[:user_id] ) if session[:user_id].present?
end
helper_method :current_user
You may now call current_user from all your controllers and views. #Intrepidd's method is cool too.
Variables are destroyed between each call to an action.
You must re-instantiate the #user each time.
To make it clean, you could do that in a before_filter
If you mean that you want the current user (for example), you could make a method/function in your model and call that.

Authlogic - current_user or #current_user?

please help me to understand something. In Authlogic example in UsersController it's always used #current_user, so for instance:
def show
#user = #current_user
end
(taken from http://github.com/binarylogic/authlogic_example/blob/master/app/controllers/users_controller.rb)
Why is that? In my controllers I use just current_user instead of #current_user.
And besides - Authlogic works perfectly for me, but I don't see magic columns being populated (like last_login_at or last_login_ip). Should I initialize them somehow specifically besides just adding into migration?
UPD
After some investigation, I found that if there're only fields last_login_at and last_login_ip from "Magic fields", then they will not be populated. If I add a full set of magic fields, it is working perfectly.
UPD2
My concern regarding current_user is only about UsersController: why does it have #current_user and not current_user?
current_user is typically a method defined in app/controllers/application_controller.rb which sets the #current_user instance variable if it is not already defined -- here is an example:
def current_user_session
return #current_user_session if defined?(#current_user_session)
#current_user_session = UserSession.find
end
def current_user
return #current_user if defined?(#current_user)
#current_user = current_user_session && current_user_session.record
end
Re the "magic columns", these should be set by Authlogic automatically. For example, if your user sessions controller logs in a user:
#user_session = UserSession.new(params[:user_session])
#user_session.save
Authlogic should write the last_login_at and last_login_ip attributes for you. More info in the Authlogic docs under Module: Authlogic::Session::MagicColumns
As for last_login_at and last_login_ip, do you have current_login_at and current_login_ip fields in your table ? last_login_at and last_login_ip are set with the values of current_login_at and current_login_ip before they are reset.
I think the code from the example isn't a really good example.
You shouldn't use #current_user to set the #user variable. Because it won't work if the ApplicationController#current_user method isn't called before show action of the UserController. Basically they are both exactly the same after current_user is called once.
the User Controller should look like this
class UserController < ApplicationController
def show
#user = current_user
end
end
As for the Magic Columns I have no Idea why they don't work for you.

Resources