I have added an updated_by attribute to my guideline model which stores the user_id of the person who updated the guideline. I'd like to display this in my show view as the profile_name of the person who updated_by
in guidelines_controller.rb:
def update
#guideline = Guideline.find(params[:id])
respond_to do |format|
if #guideline.update_attributes(params[:guideline])
#guideline.update_attribute(:updated_by, current_user.id)
This seems to work fine and allocated the current user's id to updated_by.
def show
#guideline = Guideline.find(params[:id])
#created = #user.where(:id=>#guideline.updated_by).first.profile_name
Then my show view
<%= #created %>
The error is
NoMethodError (undefined method `where' for nil:NilClass):
app/controllers/guidelines_controller.rb:137:in `show'
How can I get the profile name from the updated_by id?
You need to call the finder as a class method rather than an object method.
#created = User.where(:id => #guideline.updated_by ).first.profile_name
Or cleaner
#created = User.find(#guideline.updated_by).profile_name
It's also possible you may need to search by #guideline.updated_by.id instead of #guideline.updated_by. In that case it would be:
#created = User.find(#guideline.updated_by.id).profile_name
This line:
#created = #user.where(:id=>#guideline.updated_by).first.profile_name
should read:
#created = User.where(:id=>#guideline.updated_by).first.profile_name
where is a class method on the User model. #user (usually) refers to an instance of a user, which you haven't instantiated yet, in this case. (It is nil; that's why you're getting the NilClassError.)
An even cleaner version of this line would be:
#created = User.find(#guildeline.updated_by).profile_name
Since User.find finds the user for a given user_id.
The error is due to the fact that you call where on #user, not on User. Just call
#created = User.where(:id=>#guideline.updated_by).first.profile_name
Not sure if that's the only problem, but let's deal with them one-by-one.
Related
I want to create a record in join table but rails shows me two errors in two situation, and I don't want to generate a third model.
#channel = Channel.find(params[:channel_id])
if #channel.users.create!(channel_id: params[:channel_id], user_id: params[:user_id])
flash[:success] = "U Succeed:)"
redirect_to request.referrer
else
flash[:danger] = "U Nit Succeed:H"
redirect_to request.referrer
end
second situation
if Channel.users.create!(channel_id: params[:channel_id], user_id: params[:user_id])
flash[:success] = "U Succeed:)"
redirect_to request.referrer
else
flash[:danger] = "U'r Not Succeed:H"
redirect_to request.referrer
end
I want to save attrs in join table. According to rails official site guide, what's wrong?
First error:
unknown attribute 'channel_id' for User.
Second error:
undefined method `users' for Class:0x00007feaa0312058
I am assuming that you have associations like these:
class User < ActiveRecord::Base
has_and_belongs_to_many :channels
end
class Channel < ActiveRecord::Base
has_and_belongs_to_many :users
end
Now you are trying to do like this:
#channel.users.create!(channel_id: params[:channel_id], user_id: params[:user_id])
This will try to create a new User class object as there is no Model in between you just have a mid table. Instead you can do it like this:
# If you don't have the user object already
user = User.find params[:user_id]
# This will create a record in the mid table
#channel.users << user
This will create a new record in the mid table and the existing records will also exist as it is. And if you do like this:
#channel.users = user
This will delete all the existing associated user records from the mid table for this channel and add a new associated record with this user.
And when you try doing like this:
Channel.users.create!(channel_id: params[:channel_id], user_id: params[:user_id])
This is not valid at all because the class Channel doesn't have any direct relation with the User but an instance of Channel class may be associated with instances of User class.
For the first scenario i would suggest you should do it like
#channel.user_ids = #channel.user_ids + [params[:user_id]]
it will create join table records, you can surely try optimised approach for this as you see fit.
you can use push or << method instead of create : Channel.users.push(attrs) or Channel.users << (attrs) and second answer in good too but .ids not very readable
or you can find channel by id and use it : channel.users.create(attrs)
see api.rubyonrails.org and search has_and_belongs_to_many methods in searchbar
I think my question title is bit confusing. But what I am meaning to ask is I am creating my own authentication system using mobile. Just like devise comes with current_user to create a session, I want to know how can I achieve same on a different model.
I have a model called Commuter. It also has a id with it.
A record of commuter looks like this.
Commuter.last
<Commuter id: 867, phone_number: "9483942090">
I am trying to create a session after verfying the mobile number with my controller method as follows:
def verify
#commuter = Commuter.where(phone_number: params[:phone_number]).first
if (#commuter && #commuter.authenticate_otp(params[:otp],drift:300))
#commuter.auth_active = true
if #commuter.save
#Removed from session after verified it
session[:phone_number] = nil
session[:is_verified] = nil
#signed in commuter after verified it
sign_in(:commuter, #commuter)
flash[:notice] = "Your mobile no is verified."
end
else
flash[:alert] = "You have entered wrong otp.Please check again."
end
puts "#{current_commuter.phone_number}"
redirect_to root_path
end
I just a puts there to debug. So right now I am getting current_commuter as undefined local variable for obvious reasons I guess. So I wanted to know how can achieve this session based current commuter ?
You can save the Commuter id in the session as session[:cid] = 1 and create a method on your base controller like this
def current_commuter
#commuter ||= Commuter.find session[:cid]
end
helper_method :current_commuter
I have a method
def call
user.password_reset_sent_at = Time.zone.now
user.save!
user.regenerate_password_reset_token
UserMailer.password_reset(user).deliver_later(queue: "low")
end
def user
#user = User.find_by_email(#params)
end
and I'm trying to reset the password_reset_token and the password_reset_sent_at
User::PasswordReset.new("foo#foobar.com").call
I see the token updated but it does not update the password_reset_sent_at
Every occurrence of user within call is another invocation of the user method, creating another User object from the record in the database and storing it in #user. The effect is similar to if you had written
def call
User.find_by_email(#params).password_sent_at = Time.zone.now
User.find_by_email(#params).save!
... etc ...
end
The changes you make to the first copy of the User record you retrieve, are never saved before you go and get a new copy.
I think the idiom you are aiming for involves defining user like this:
def user
#user ||= User.find_by_email(#params)
end
Defined that way, User.find_by_email will only be called once, and the result stored in #user. Subsequent calls to user will re-use the existing value of #user, a technique called memoization.
I have a system that lets users or guests write a review. If users write a review it is associated with their user_id. If guests write a review they are asked to provide a username which is stored in a column called "guest" in the reviews database.
I'm not entirely sure how to do this but what I've done with the professor_controller is:
def show
#review = Review.where(professor_id: #professor.id).order("created_at DESC")
#avg_review = #review.average(:hw)
if #review.user_id = !nil
#user = User.where(id: #review.user_id)
else
#user = #review.guest
end
render
end
However, this yields an error:
NoMethodError in ProfessorsController#show
undefined method `user_id=' for #<Review::ActiveRecord_Relation:0x007fed19228e28>
I was getting this error even before I put the if statement in. I had the same problem when my controller looked like:
def show
#review = Review.where(professor_id: #professor.id).order("created_at DESC")
#avg_review = #review.average(:hw)
#user = User.where(id: #review.user_id)
end
#review works fine so does #avg_review. The Reviews table has a user_id column and the Users table has an id column.
You are getting an ActiveRecord::Relation (a collection of Reviews), not a single instance of Review. You will need to do Review.where(professor_id: #professor.id).order("created_at DESC").first or Review.find_by_user_id(#professor.id) to return a single instance.
That said, it sounds like this relationship isn't modeled properly, or there's a better way to express what you want to do through other means. Can you route take in the id of a review as a param?
Your #review variable actually holds an ActiveRecord::Relation object, like it clearly says in the error message:
NoMethodError in ProfessorsController#show
undefined method `user_id=' for #<Review::ActiveRecord_Relation:0x007fed19228e28>
That's because where always returns a Relation, even if it finds only one
record.
I am expanding the sample application of Michael Hartl's Rails Tutorial, which you can find here http://ruby.railstutorial.org/chapters/. The objective of the tutorial is to create a twitter-like website, therefore some of the models are User, Micropost and Relationship, where the last one is intended to establish the relationships of follower and followed among the users (just like in twitter) and to define the logic for retrieveng microposts from the database according the user.
That being said, I want to expand the sample application adding the following functionality:
When writing a micropost, this can be retrieved either by all the user's followers by default or by specific followers whose user names are explicitly included in the content of the micropost, with each name preceded by # symbol.
Example:
User ExampleUser posted two microposts:
micropost_1 with content "hello world" will appear in the micropost feed of every follower.
micropost_2 with content "hello world #john #peter" will appear only in peter's and john's micropost feeds (as long as peter and john are the names of actual users that follow ExampleUser).
In order to achieve this objective, so far I have done the following:
1) I added the field "receivers" of type text to the Micropost model through the console lines:
rails g migration AddReceiverToMicropost receivers:text
rake db:migrate
And then I added the following line to the Micropost model
class Micropost < ActiveRecord::Base
serialize :receivers
This allows me to save an array in the receivers field thanks to the serialize functionality (for this I based in the following site: http://amberonrails.com/storing-arrays-in-a-database-field-w-rails-activerecord/)
Thus, I would be able to store a string array with the receivers' names for a given micropost.
2) Next, I tried to solve the problem of extracting the receivers' names from a micropost's content. So I went to the MicropostsController and created the following method:
private
def get_receivers(content)
receivers = []
i = 0
i0 = 0
while i < content.length do
i = content.index('#',i0)
if i.nil?
break
else
i0 = content.index(' ',i)
if i0.nil?
unless i + 1 == content.length
receivers << content[i+1..-1]
end
break
else
unless i + 1 == i0
receivers << content[i+1..i0-1]
end
end
end
end
receivers
end
and then I applied it withing the MicropostsController's create method:
def create
#micropost = current_user.microposts.build(micropost_params)
if #micropost.save
#micropost.update(receivers: get_receivers(#micropost.content)) #this line uses the get_receivers method to set the receivers field
flash[:success] = "Micropost created!"
redirect_to root_url
else
#feed_items = []
render 'static_pages/home'
end
end
The line followed by the comment is the added one.
So far this works properly.
3) Finnaly I tried to update the method that retrieves all the micropost feed of a user (properly signed-in) in order to implement the new functionality. The method is a class method called from_users_followed_by(user), defined in the Micropost model class, and this is the original code (as it appears in the tutorial):
def self.from_users_followed_by(user)
followed_user_ids = "SELECT followed_id FROM relationships
WHERE follower_id = :user_id"
where("user_id IN (#{followed_user_ids}) OR user_id = :user_id",
user_id: user.id)
end
This method is invoked by the feed method of User class, here is the code:
class User < ActiveRecord::Base
.
.
.
def feed
Micropost.from_users_followed_by(self)
end
And finally, feed in turn is invoked within the home method of the StaticPagesController in response to a request of displaying the sites's homepage provided that a user is properly signed-in at the moment:
class StaticPagesController < ApplicationController
def home
if signed_in?
#micropost = current_user.microposts.build
#feed_items = current_user.feed.paginate(page: params[:page])
end
end
So, you can see that the original self.from_users_followed_by(user) method retrieves all the user's followed users' microposts plus his own microposts. But in order to add the receivers filter layer, I updated it to this new version:
def self.from_users_followed_by(user)
followed_user_ids = "SELECT followed_id FROM relationships
WHERE follower_id = :user_id"
microposts = where("user_id IN (#{followed_user_ids}) OR user_id = :user_id",
user_id: user.id)
microposts_filtered = []
microposts.each do |m|
if m.receivers.nil? or m.receivers.length == 0 or m.receivers.include? user.name
microposts_filtered << m
end
end
microposts_filtered
end
As you can see, my attempt was to manually filter the microposts of the microposts Relation returned by the where method and pass the approved ones into the array microposts_filtered. I thought this was going to work, but a problem arises in the StaticPagesController's home method in the line
#feed_items = current_user.feed.paginate(page: params[:page])
and this is that an array has no method called paginate, which is a method of ActiveRecord::Relation class. That's the reason for my title question: how can I add this receivers filter layer in the where method in order to keep the Relation as output, since an array does not allow pagination?
Sorry for the long exposition of the problem but I did it this way for the sake of understanding. I would like to know how I could solve this problem or if there are other better approaches to achieve the same results in a better fashion. Improvements for any part of the whole problem (such as a better method to get the user names from a micropost's content) are welcomed. Thanks in advance.