I have 2 models:
User model and Profile Model.
My association is as follows:
Class User < ActiveRecord::Base
has_one :profile
end
Class Profile < ActiveRecord::Base
belongs_to :user
validates user_id, presence: true
end
My Controllers is as follows:
USER:
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
if logged_in?
#micropost = current_user.microposts.build
#profile = current_user.build_profile
#new_comment = Comment.build_from(#user, current_user.id, ' ')
end
#microposts = #user.microposts.paginate(page: params[:page])
#profile_player = #user.profile
end
end
PROFILE:
class ProfilesController < ApplicationController
before_action :logged_in_user, only: [:create, :destroy]
before_action :correct_user, only: [:destroy]
def create
#profile = current_user.build_profile(profile_params)
if #profile.save
flash[:success] = 'Profile Has Been Published'
# redirect_to request.referrer || root_url
redirect_to users_url
else
render :'pages/home'
end
end
def update
#profile.update(profile_params)
redirect_to user_url
end
def destroy
end
private
def profile_params
params.require(:profile).permit(:name, :age, :nationality, :country, :city, :height, :weight,
:dominant_hand, :play_position, :highschool, :college, :team,
:awards, :highlights)
end
def correct_user
#profile = current_user.profile.find_by(id: params[:id])
redirect_to root_url if #profile.nil?
end
end
Now, what I'm trying to do is render the profile view partial in the user show page (following Michael Hartl's tutorial):
hence I'm rendering the view via the instance variable I created in Users Controller show action for profile:
def show
##username = params[:id] ==============> this is intended to show the user name instead
# of the user id in the address bar
#user = User.find(params[:id])
if logged_in?
#micropost = current_user.microposts.build
#profile = current_user.build_profile
#new_comment = Comment.build_from(#user, current_user.id, ' ')
end
#microposts = #user.microposts.paginate(page: params[:page])
#profile_player = #user.profile
end
so, in my user show page:
I'm rendering the profile view like this:
Now here is the error I run into, my profile saves correctly when the form is submitted, however, when I return to the User show page (I'm rendering the profile form in the user home page) to view the profile, I get the error:
'nil' is not an ActiveModel-compatible object. It must implement :to_partial_path.
<div class="current-user-persona">
<%= render #profile_player %> =====> highlighted section for error
</div>
I'm not sure what I'm doing wrong here, can you help me?
been staring at this for days.
Related
I have a model called "Clients". The Clients model belongs to the Users model (Users model is associated with devise). The Client can do payments to me manually (cash only). When the client does this payment, I give them access to more pages in the website. To do this, I was thinking I add a migration to the Client model like:
rails generate migration add_paid_to_clients paid:boolean
Then when the clients pay the money, I can just go to their profile and click on an option (maybe a checkbox) saying the client has paid. Only the admin(me) can see this part. The way I would implement that is, in the user profile view:
<% if current_user.user_type == :client%>
userinformation....blablabla
<% elsif current_user.user_type == :admin %>
userinformation....blablabla
AND the checkbox I talked about.
<% end %>
The user_type is a predefined function to figure out if the current_user is admin or just a client. I have done this part.
So in the private pages(the pages you can get access to after paying), I can have some logic like If current_user.paid? THEN show them this page.
How can I implement the checkbox part? After the migration(if there is a better way,please let me know),how can I just "flip the switch" to let them get more access?
My Clients contoller:
class ClientController < ApplicationController
before_action :find_client, only: [:show, :edit, :update, :destroy]
def index
end
def show
end
def new
#client = current_user.build_client
end
def create
#client = current_user.build_client(client_params)
if #client.save
redirect_to clients_path
else
render 'new'
end
end
def edit
end
def update
end
def destroy
#client.destroy
redirect_to root_path
end
private
def client_params
params.require(:client).permit(:name, :company, :position, :number, :email, :client_img)
end
def find_client
#client = Client.find(params[:id])
end
end
This is not real cash, this is a project for school.
Something like this?
In the form for the admin:
<%= form_for #user do |f| %>
<%= check_box_tag 'paid', 'yes', true %>
<%= f.submit "Update" %>
<% end %>
Then in the controller:
class ClientController < ApplicationController
before_action :find_client, only: [:show, :edit, :update, :destroy]
def index
end
def show
end
def new
#client = current_user.build_client
end
def create
#client = current_user.build_client(client_params)
if #client.save
redirect_to clients_path
else
render 'new'
end
end
def edit
end
def update
end
def destroy
#client.destroy
redirect_to root_path
end
private
def client_params
if current_user.user_type == :admin
# This is different because only admin can update paid
# To make a string a boolean
params[:client][:paid] = params[:client][:paid] == 'yes'
params.require(:client).permit(:paid, :name, :company, :position, :number, :email, :client_img)
else
params.require(:client).permit(:name, :company, :position, :number, :email, :client_img)
end
end
def find_client
#client = Client.find(params[:id])
end
end
you can do this more properly using the protected attributes, described here
After that you want to make a PaidController
class PaidController < ApplicationController
before_filter :authorize_paid
def authorize_paid
return head(:forbidden) unless current_user.paid?
end
end
Then make sure that all the protected pages inherit from the PaidController
I have some polymorphic relationships set up and are working well for the primary purpose. That is for a User to be able to Comment on both Articles and Coffeeshops.
However I'm struggling with being able to display the users list of comments on their profile page. In the future I also want the user to be able to 'favourite' and 'want to go to' different coffeeshops which I would also want to show up on their profile page. I'm hoping once I get the logic for display current comments, the rest will be a breeze ;)
So what I have:
Models
class User < ApplicationRecord
has_many :comments
end
class Comment < ApplicationRecord
belongs_to :user
belongs_to :commentable, polymorphic: true
end
class Coffeeshop < ApplicationRecord
has_many :comments, as: :commentable
end
class Article < ApplicationRecord
has_many :comments, as: :commentable
end
Comment Controller
class CommentsController < ApplicationController
before_action :load_commentable
before_action :authenticate_user!
before_action :comment_auth, only: [:edit, :update, :destroy]
def index
#comments = #commentable.comments
end
def new
#comment = #commentable.comments.new
end
def create
#comment = #commentable.comments.new(allowed_params)
#comment.user_id=current_user.id if current_user
if #comment.save
redirect_to #commentable, notice: "Comment created."
else
render :new
end
end
def update
#comment = Comment.find(params[:id])
if #comment.update(comment_params)
redirect_to #commentable
else
render 'edit'
end
end
def destroy
#comment = Comment.find(params[:id])
#commentable = #comment.commentable
if #comment.destroy
flash[:success] = "Comment Destroyed!"
redirect_to :back
end
end
private
def allowed_params
params.require(:comment).permit(:name, :body)
end
def load_commentable
resource, id = request.path.split('/')[1,2]
#commentable = resource.singularize.classify.constantize.find(id)
end
def comment_params
params.require(:comment).permit(:body).merge(user_id: current_user.id)
end
Profile Controller
class ProfileController < ApplicationController
before_action :authenticate_user!
def index
end
def show
#user = User.find.current_user(params[:id])
#comments = #commentable.comments
end
In views/profile/show.html.erb. I was trying to do:
<h3>Your Latest Comment</h3>
<%=#comment.user.body%>
But this clearly isn't right as I get Couldn't find User without an ID. From ProfileController#show
update
If I change ProfileController to
before_action :authenticate_user!
def index
#user = User.find.current_user(params[:user_id])
end
def show
#comments = #commentable.comments
end
I get an error for undefined comments.
ok first return this to show moving it to index is not solving a problem the index is not called so write show like this.
def show
#user = current_user #you get instance of a user that is logged in
#comments = #user.comments
end
I do not know if you have user_id in your comment migration but if you do not have you must write
class User < ApplicationRecord
has_many :comments, as: :commentable
end
view
<h3>Your Latest Comment</h3>
<%=#comments.try(&:last).try(&:body)%>
I am trying to implement nested answers into comments, which are nested into auctions.
There is a auctions.rb model, which:
has_many :comments, dependent: :destroy
has_many :answers, :through => :comments
a comments.rb model, which:
belongs_to :auction
has_many :answers, dependent: :destroy
a answers.rb model, which:
belongs_to :comment
the answers_controller inherits from the comments_controller:
class AnswersController < CommentsController
before_action :all_answers, only: [:index, :create, :update, :destroy]
before_action :set_answer, only: [:edit, :update, :destroy]
respond_to :html, :js
# New Answer (Form)
def new
#answer = Answer.new
#comments.answers.build
end
# Create Answer
def create
#answer = #comment.answers.build(answer_params)
#answer.user_id = current_user.id
#answer.save
end
# Edit Answer
def update
#answer.update!(answer_params)
end
# Delete Answer
def destroy
#answer = Comment.find(params[:id])
#comment = #answer.comment
#answer.destroy
end
private
def all_answers
#answers = #comment.answers.all
end
def set_answer
#answer = #comment.answers.find(params[:id])
end
def answer_params
params.require(:comment).permit(:body)
end
end
The Error:
NoMethodError in Auctions#show app/views/comments/_comment.html.erb
where line #20 raised: undefined method `answers' for nil:NilClass
<div class="col s12" id="answer-form" style="display:none;"></div>
</div>
<div class="row">
<div class="col s12" id="answers"><%= render #comment.answers %></div>
</div>
With <%= render #comment.answers %> I want to display all existing answers below the related comment. What am I doing wrong?
auction_controller
class AuctionsController < ApplicationController
# Index of all auctions
def index
#auctions = Auction.all
end
# Show Auction by :id
def show
#auction = Auction.find(params[:id])
# Find Seller by ID
#seller = User.find(#auction.user_id)
# Find highest bid, by finding all related bids and ordering in descending and picking the first
#highest_bid = Bid.where(auction_id: params[:id]).order("amount DESC").first
# Find product
#product = Product.find(#auction.product_id)
end
# New Auction Form
def new
#auction = Auction.new
end
# Edit Auction
def edit
#auction = Auction.find(params[:id])
end
# Create new Auction
def create
# Create new Auction
#auction = Auction.new(auction_params)
# Save Id of User (Seller)
#auction.user_id = current_user.id
# If auction was created successfully
if #auction.save
# display the created auction
redirect_to #auction, :notice => "Auction created"
else
# display Form again if unsuccessful
render 'new'
end
end
# Update existing Auction
def update
#auction = Auction.find(params[:id])
# Validation
if #auction.update(auction_params)
redirect_to #auction, :notice => "Auction updated"
else
render 'edit'
end
end
# Delete Auction
def destroy
#auction = Auction.find(params[:id])
#auction.destroy
redirect_to auctions_path, :notice => "Auction deleted"
end
private
# set required parameters for new created Auctions
def auction_params
params.require(:auction).permit(:condition, :product_name)
end
end
comments_controller
class CommentsController < ApplicationController
before_action :set_auction
before_action :all_comments, only: [:index, :create, :update, :destroy]
before_action :set_comment, only: [:edit, :update, :destroy]
respond_to :html, :js
# New Comment (Form)
def new
#comment = Comment.new
#auction.comments.build
end
# Create Comment
def create
#comment = #auction.comments.build(comment_params)
#comment.user_id = current_user.id
#comment.save
end
# Edit Comment
def update
#comment.update!(comment_params)
end
# Delete Comment
def destroy
#comment = Comment.find(params[:id])
#auction = #comment.auction
#comment.destroy
end
private
def set_auction
#auction = Auction.find(params[:auction_id])
end
def all_comments
#comments = #auction.comments.all
end
def set_comment
#comment = #auction.comments.find(params[:id])
end
def comment_params
params.require(:comment).permit(:body)
end
end
Normal Comments work. Only Comment Answers don't work.
The error happens in Auctions#show, the error clearly tells you that you are trying to call answers on a nil object. Therefore, it means #comment is nil in that view.
In fact, if you check the show action, you never fetch/assign any object to #comment.
# Show Auction by :id
def show
#auction = Auction.find(params[:id])
# Find Seller by ID
#seller = User.find(#auction.user_id)
# Find highest bid, by finding all related bids and ordering in descending and picking the first
#highest_bid = Bid.where(auction_id: params[:id]).order("amount DESC").first
# Find product
#product = Product.find(#auction.product_id)
end
In order to fix it, make sure #comment is properly assigned to a Comment instance.
There's another problem here:
def new
#answer = Answer.new
#comments.answers.build
end
You haven't got a variable called #comments, hence your form can't actually build answers off it. In fact, you're calling #comment in two other methods, where I can't even see it being declared:
def all_answers
#answers = #comment.answers.all
end
def set_answer
#answer = #comment.answers.find(params[:id])
end
The only time you've declared #commentis in the destroymethod:
def destroy
#answer = Comment.find(params[:id])
#comment = #answer.comment
#answer.destroy
end
Even then, it's weird.
Why are you calling the Comment model with the #answer variable? Surely you'd have an Answer model with comments attached by way of a has_many relationship?
I'd recommend you keeping it brain-dead simple:
#app/models/answer.rb
class Answer < ActiveRecord::Base
has_many :comments
end
#app/models/comment.rb
class Comment < ActiveRecord::Base
belongs_to :answer
end
This means when you call your actions controller, you'll be able to apply the following:
def show
#answer = Answer.find params[:id]
#comment = #answer.comments.build
end
If you wanted to have comments act as answers, you need to keep the models separate. Use a hierchy gem, like closure tree. This way, you'll be able to keep your Answers/Comments in hierarchy order, whilst keeping the Models consistent.
I am having challenges assigning a current user a role in a team the user is creating. I want to assign the user that creates the team the role of the captain which could be changed later.
I'm currently using the create_asociation method that comes with has_one relationship, as this instantiates the values of the associated model, which i want to be instantiated with the current user but get the error Can't mass assign protected attribute: captain. Captain is a self join model with user as i will like to use captain.teammates and team.captain.
Below are the models involved.
User and Captain Model
class User < ActiveRecord::Base
has_one :profile
has_many :teammates, :class_name => "User", :foreign_key => "captain_id"
belongs_to :captain, :class_name => "User"
belongs_to :team
# before_create :build_profile
after_create :build_default_profile
accepts_nested_attributes_for :profile
attr_accessible :email, :password, :password_confirmation, :profile_attributes, :captain_id
def build_default_profile
Profile.create(user_id: self.id)
end
has_secure_password
before_save { email.downcase! }
before_save :create_remember_token
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :password, presence: true, length: { minimum: 6 }
validates :password_confirmation, presence: true
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
end
Team Model
class Team < ActiveRecord::Base
has_many :profiles, through: :users
has_one :captain, :class_name => "User", foreign_key: :captain_id
has_one :result, as: :result_table
attr_accessible :teamname, :color, :result_attributes, :captain_attributes
after_create :build_result_table
after_create :build_default_captain
accepts_nested_attributes_for :profiles
accepts_nested_attributes_for :captain
accepts_nested_attributes_for :result
def build_result_table
Result.create(result_table_id: self.id, result_table_type: self.class.name)
end
def build_default_captain
# Team.captain = User
# Captain.create(team_id: self.id, captain_id: user.id)
end
end
User Controller
class UsersController < ApplicationController
before_filter :signed_in_user, only: [:index, :edit, :update, :destroy]
before_filter :correct_user, only: [:edit, :update]
before_filter :admin_user, only: :destroy
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save!
sign_in #user
flash[:success] = "Welcome to the JHDC Mini Olympics Web Application; Thanks for singing Up"
redirect_to user_profile_path(#user, #profile)
else
flash[:error_messages]
render 'new'
end
end
def show
#user = User.find(params[:id])
end
def index
#users = User.paginate(page: params[:page])
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
flash[:success] = "Profile Updated"
redirect_to user_profile_path(#user, #profile)
else
render 'edit'
end
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "User deleted."
redirect_to users_url
end
private
def signed_in_user
unless signed_in?
store_location
redirect_to signin_url, notice: "Please sign in."
end
def correct_user
#user = User.find(params[:id])
redirect_to(root_path) unless current_user?(#user)
end
def admin_user
redirect_to(root_path) unless current_user.admin?
end
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
end
end
Team Controller
class TeamsController < ApplicationController
def new
#team = Team.new
end
def create
#team = Team.new(params[:team])
#captain = #team.create_captain(captain: current_user)
if current_user.admin?
if #team.save!
flash[:success] = "Team created."
redirect_to #team
else
flash[:error_messages]
render 'new'
end
else
flash[:error] = "Sorry, you don't have the authority to create a Team"
redirect_to current_user
end
end
def index
#teams = Team.paginate(page: params[:page])
end
def show
#team = Team.find(params[:id])
end
def edit
if current_user.admin?
#team = Team.find(params[:id])
else
flash[:error] = "Sorry you dont have the authourity to edit a Team"
redirect_to current_user
end
end
def update
#team = Team.find(params[:id])
if #team.update_attributes(params[:team])
flash[:success] = "Team Updated"
redirect_to #team
else
render 'edit'
end
end
def destroy
Team.find(params[:id]).destroy
flash[:success] = "Team is deleted."
redirect_to teams_url
end
private
def team_params
params.require(:team).permit(:teamname, :color)
end
end
The admin is currently a way i'm using to restrict the user that can create a team but i plan to use gems like declarative authorization to create role based authorization. Thanks
The error you are getting is because the attribute :captain is not declared as attr_accessible
Either set the attribute :captain in your list of attr_accessible for the User model, or change the code form
Captain.create(team_id: self.id, captain_id: user.id)
to
captain = Captain.new
captain.team_id = self.id
captain.captain_id = user.id
captain.create
in this way, the attribute won't be set by mass-assignment and won't raise the error
Edited
After checking your code twice, just realized that you don't have a Captain model, actually :captain is a relation for the user and a relation from the Team to the User.
So on Team model, take off the build_default_captain stuff and the after_create :build_default_captain, I would say to replace with something like
after_save :set_default_captain
def set_default_captain
if captain_id_changed?
profiles.each do |user|
user.captain = captain
user.save
end
end
end
so every time the captain_id change for the model, you change the captain_id of all its profiles (users)
Then on the Team controller, on the action create, instead of
#team = Team.new(params[:team])
#captain = #team.create_captain(captain: current_user)
do something like
#team = Team.new(params[:team])
#team.captain = current_user
if current_user.admin?
if #team.save!
current_user.update_attribute(:team_id, #team.id)
flash[:success] = "Team created."
redirect_to #team
else
flash[:error_messages]
render 'new'
end
else
flash[:error] = "Sorry, you don't have the authority to create a Team"
redirect_to current_user
end
so on the last part of the code, you set the captain of the team to the current user and set the user team to the current team once its saved, you can also improve the code with current_user.build_team to avoid saving current_user.update_attribute
I would like to create relationships between three models: user, post and comment.
User have many posts and comments
Post have only one user and many comments
Comment have one user and one post
so i create next migrations:
class Users < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :name
t.string :email
t.timestamps
end
end
end
class Posts < ActiveRecord::Migration
def change
create_table :posts do |t|
t.string :content
t.integer :user_id
t.timestamps
end
end
end
class Comments < ActiveRecord::Migration
def change
create_table :comments do |t|
t.string :title
t.string :content
t.integer :user_id
t.integer :post_id
t.timestamps
end
end
end
=============================================
models are next:
user.rb
class User < ActiveRecord::Base
has_many :posts
has_many :comments
end
post.rb
class Post < ActiveRecord::Base
belongs_to :user
has_many :comments
end
comment.rb
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :post
end
===============================================
My users_controller.rb
class UsersController < ApplicationController
before_action :signed_in_user, only: [:index, :edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update]
before_action :admin_user, only: :destroy
def index
#users = User.paginate(page: params[:page])
end
def show
#user = User.find(params[:id])
#posts = #user.posts.paginate(page: params[:page])
#comments = #user.comments.paginate(page: params[:page])
end
def new
#user = User.new(params[:user])
end
def edit
##user = User.find(params[:id])
end
def update
##user = User.find(params[:id])
if #user.update_attributes(user_params)
flash[:success] = "Profile updated"
redirect_to #user
else
render 'edit'
end
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "User deleted."
redirect_to users_url
end
def create
#user = User.new(user_params)
if #user.save
flash[:success] = "Welcome to the Sample App!"
redirect_to #user
else
render 'new'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
# Before filters
def correct_user
#user = User.find(params[:id])
redirect_to(root_url) unless current_user?(#user)
end
def admin_user
redirect_to(root_url) unless current_user.admin?
end
end
now i want to create some actions for next tasks:
For posts_controller.rb
1.1 create a post by user
1.2 delete a post by user
1.3 show user post with all comments
1.4 show all user posts
class PostsController < ApplicationController
before_action :signed_in_user, only: [:create, :destroy]
before_action :correct_user, only: :destroy
def create
#post = user.post.build(post_params)
#post = post.save
end
def destroy
#post.destroy
end
def show_user_post_with_all_comments
???
end
def show_all_user_posts
???
end
private
def post_params
params.require(:post).permit(:title, :content)
end
def correct_user
#post = current_user.posts.find_by(id: params[:id])
redirect_to root_url if #post.nil?
end
end
For comments_controller.rb
2.1 create a comment by user in post
2.2 delete a comment by user in post
2.3 show all user comments
2.4 find and show a post by user comment
class CommentsController < ApplicationController
before_action :signed_in_user, only: [:create, :destroy]
before_action :correct_user, only: :destroy
def create
#comment = user.comment.build(comment_params)
#comment = comment.save
end
def destroy
#comment.destroy
end
def show_comment
???
end
def show_all_user_comments
???
end
def find_and_show_post_by_user_comment
???
end
private
def comment_params
params.require(:comment).permit(:content)
end
def correct_user
#comment = current_user.comments.find_by(id: params[:id])
redirect_to root_url if #comment.nil?
end
end
Pls check for correct my migrations and models and help me with creating of actions with "???" in bodies
Thank you much for your answers.
PostsController
def show_user_post_with_all_comments
#post = Post.find(params[:id]).eager_load(:comments)
end
def show_all_user_posts
#posts = current_user.posts
end
CommentsController
def show_comment
#comment = Comment.find(params[:id])
end
def show_all_user_comments
#comments = current_user.comments
end
def find_and_show_post_by_user_comment
#comment = Comment.find(params[:id]).eager_load(:post)
#post = #comment.post
end
What I've done in the past in a similar situation would be to put all this work in the UsersController and add a few new actions to it:
class UsersController < ApplicationController
...
def new_post
#user = User.find(params[:id])
end
def create_post
#user = User.find(params[:id])
if #user.update_attributes user_post_params
redirect_to somewhere_path
else
render 'new_post'
end
end
def show_post
#post = Post.find(params[:id])
# Not sure how you are implementing sessions, but say you have current_user defined
# for sessions, then your view could have a delete link conditional on
# #post.user_id == current_user.id
#comments = #post.comments
end
def show_all_posts
#user = User.find(params[:id])
#posts = #user.posts
end
def new_comment
#user = current_user
#post = Post.find(params[:id])
end
def create_comment
#user = current_user
#post = Post.find(params[:id])
#comment = Comment.new(comment_params)
if #post.update_attributes comment_params
#user.comments << #comment
if #user.save
redirect_to somewhere
else
render 'new_comment'
end
else
render 'new_comment'
end
end
def show_comments
#user = User.find(params[:id])
#comments = #user.comments
end
...
private
def user_post_params
params.require(:user).permit(:id, posts_attributes: [:title, :content])
end
def comment_params
params.require(:post).permit(:id, comments_attributes: [:content, :user_id])
end
In show_post.html.erb:
<% if #post.user_id == current_user.id %>
<%= link_to 'delete', post, method: :delete, data: { confirm: "you sure?" }
<% end %>
in your routes.rb:
get '/user/:id/new_post' => 'users#new_post', as: :user_new_post
put '/user/:id/create_post' => 'test_takers#create_post', as: :user_create_post
...and similar lines for the other actions.
Hopefully this can get you started...