All links work well except for devise. If I try to click sign up or log in it takes me to the sign up/log in page, right?
From the URL it looks normal. localhost:3000/users/sign_up but the view is wrong. It's displaying the profile view instead of the devise sign up/log in view.
Routes code:
Rails.application.routes.draw do
resources :questions
get '/users/:id' => 'profile#profile'
devise_for :users
root 'home#index'
end
Profile controller:
class ProfileController < ApplicationController
def profile
end
end
User model:
class User < ActiveRecord::Base
has_many :questions
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
Question model
class Question < ActiveRecord::Base
belongs_to :user
end
It didn't do that at first, I think this bug happened when I edited the show.html.erb view in the questions folder.
Show code (question):
<% #questions.each do |q| %>
<% if user_signed_in? %>
<% if q.id == current_user.id %>
<%= link_to 'Edit', edit_question_path(#question) %>
<% end %>
<% end %>
<% end %>
Question controller:
class QuestionsController < ApplicationController
before_action :set_question, only: [:show, :edit, :update, :destroy]
respond_to :html
def index
#questions = Question.all
respond_with(#questions)
end
def show
#questions = Question.all
respond_with(#question)
end
def new
#question = Question.new
respond_with(#question)
end
def edit
end
def create
#question = Question.new(question_params)
#question.save
respond_with(#question)
end
def update
#question.update(question_params)
respond_with(#question)
end
def destroy
#question.destroy
respond_with(#question)
end
private
def set_question
#question = Question.find(params[:id])
end
def question_params
params.require(:question).permit(:title, :description, :image_url)
end
end
Try putting that get '/users/:id' => 'profile#profile' below the devise_for :users line.
Related
I'm trying to setup a simple rails app with job board functionality. I was able to add jobs to the database, until I added an association between my Job model and devise User model. Now it won't update the database when I fill out the form.
jobs_controller
class JobsController < ApplicationController
def index
#jobs = Job.all
end
def new
#job = Job.new
end
def listing
end
def listings
end
def create
#job = Job.new(params.require(:job).permit(:title, :description, :url, :user_id))
if #job.save
redirect_to root_path
else
render "new"
end
end
end
new.html.erb
<%= simple_form_for #job do |form| %>
<%= form.input :title, label: "Job title" %>
<%= form.input :description, label: "Description" %>
<%= form.input :url, label: "URL" %>
<%= form.button :submit %>
<% end %>
index.html.erb
<% #jobs.each do |job| %>
<div class="job">
<h2><%= link_to job.title, job.url %></h2>
<p><%= job.description %></p>
</div>
<% end %>
<p><%= link_to "Add a job", new_job_path %></p>
user.rb
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :jobs
end
job.rb
class Job < ApplicationRecord
belongs_to :user
end
There isn't an error in the console, but the database doesn't seem to be updated or it's not updating the view.
I also ran a migration:
class AddUserToJob < ActiveRecord::Migration[5.2]
def change
add_reference :jobs, :user, foreign_key: true
end
end
You can get the user with current_user in Devise.
class JobsController < ApplicationController
# This restricts the actions to authenticated users and prevents a nil error
before_action :authenticate_user, except: [:show, :index]
# ...
def create
# this sets the user_id column
#job = current_user.jobs.new(job_params)
if #job.save
# you really should set a flash message or something to notify the user
# and possibly redirect to the show or index action instead
redirect_to root_path
else
render "new"
end
end
private
def job_params
params.require(:job)
.permit(:title, :description, :url, :user_id)
end
end
If you don't want to associate the job immediately to a user, you need to change the association to be optional, like:
class Job < ApplicationRecord
belongs_to :user, optional: true
end
Else you need to supply user_id in your form or set it in the controller action.
You should also delegate this part to a separate method
def job_params
params.require(:job).permit(:title, :description, :url, :user_id)
end
Job.new(job_params)
Ruby on Rails. Getting various errors while trying to add a voting feature via the acts_as_votable gem on nested comments.
The acts_as_votable works with the posts, but not with the nested comments under the posts.
With :
<%= link_to "Like", like_post_comment_path(#post, #comment), method: :put do %>
<i class="fa fa-arrow-up" aria-hidden="true">Yes</i>
<%= #p.get_upvotes.size %>
<% end %>
I get the error:
No route matches {:action=>"upvote", :controller=>"comments", :id=>nil, :post_id=>"1"}, missing required keys: [:id]
With:
<%= link_to "Like", like_post_comment_path(#post, #comment.id), method: :put do %>
<i class="fa fa-arrow-up" aria-hidden="true">Yes</i>
<%= #p.get_upvotes.size %>
<% end %>
I get the error:
undefined method `id' for nil:NilClass
I have tried various combinations as the parameters. All produce an error.
My Comments controller:
class CommentsController < ApplicationController
before_action :find_post, only: [:upvote, :downvote, :create, :destroy, :edit, :update]
before_action :find_comment, only: [:upvote, :downvote :destroy, :update, :edit, :comment_owner ]
before_action :comment_owner, only: [:destroy, :edit, :update]
def new
#comment = Comment.new
#post = Post.new
end
def create
#comment = #post.comments.create(params[:comment].permit(:content))
#comment.user_id = current_user.id
#comment.save
if #comment.save
redirect_back(fallback_location: root_path)
else
render 'new'
end
end
def destroy
#comment.destroy
redirect_back(fallback_location: root_path)
end
def edit
#comment = Comment.find(params[:id])
#p = Post.find(params[:post_id])
end
def update
if #comment.update(params[:comment].permit(:content))
respond_to do |f|
if (#comment.save)
f.html { redirect_to "/explore", notice: "Comment edited!" }
elsif
f.html { redirect_to "/explore", notice: "Error: Your Comment is the same!." }
else
render 'edit'
#f.html { render 'edit'} ## Specify the format in which you are rendering "new" page
end
end
end
end
def upvote
#comment.upvote_from current_user
redirect_back(fallback_location: root_path)
end
def downvote
#comment.downvote_from current_user
redirect_back(fallback_location: root_path)
end
private
def find_post
#post = Post.find(params[:post_id])
end
def find_comment
#comment = #post.comments.find(params[:id])
end
def comment_owner
unless current_user.id == #comment.user_id
flash[:notice] = "You can't do that!"
redirect_back(fallback_location: root_path)
end
end
end
Comment model:
class Comment < ApplicationRecord
belongs_to :commentable, :polymorphic => true
belongs_to :user
acts_as_votable
# scope :desc
default_scope { order(created_at: :desc) }
end
Post model:
class Post < ApplicationRecord
belongs_to :user
has_many :comments, :as => :commentable
attr_accessor :post_id
acts_as_votable
validates :user_id, presence: true
validates :content, presence: true, length: { maximum: 100} # Questions are capped at 100 chars.
default_scope { order(cached_votes_score: :DESC) }
end
User model:
class User < ApplicationRecord
has_many :posts
has_many :comments, :as => :commentable
acts_as_voter
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :posts, dependent: :destroy
def user
user = User.find_by(params[:id])
end
Routes:
like_post_comment PUT /posts/:post_id/comments/:id/like(.:format) comments#upvote
dislike_post_comment PUT /posts/:post_id/comments/:id/dislike(.:format) comments#downvote
I have comments underneath a bunch of questions, right now the code I have shows the delete comment link under everyone's comments not just the user who made the comment. How do I fix this so that users can only delete comments they made themselves? I am using devise gem to authenticate users.
<% commentable.comments.each do |comment| %>
<h6 style="text-align:left; margin-bottom: 0px;"><strong><%= comment.user.profile.first_name %> <%= comment.user.profile.last_name %>: </strong></h6>
<p style="text-align:left">
<%= comment.body %>
</p>
<% if current_user %>
<p style="text-align:left; font-size: 12px; margin-top: -10px"><%= link_to 'Delete', [comment.user, comment],
method: :delete,
data: { confirm: 'Are you sure?' } %></p>
<% end %>
<% end %>
comments_controller.rb
class CommentsController < ApplicationController
before_action :authenticate_user!
def create
#comment = #commentable.comments.new(comment_params)
#comment.user = current_user
if #comment.save
redirect_back(fallback_location: root_path)
end
end
def update
#comment.update(comment_params)
end
def destroy
#comment = Comment.find(params[:id])
#comment.destroy
redirect_back(fallback_location: root_path)
end
private
def comment_params
params.require(:comment).permit(:body)
end
end
comment.rb
class Comment < ApplicationRecord
belongs_to :commentable, polymorphic: true
belongs_to :user
end
user.rb
class User < ApplicationRecord
before_create :add_role_to_user
ROLES = %w[admin member].freeze
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_one :profile
has_many :comments, dependent: :destroy
def add_role_to_user
self.role = 'member'
end
end
Change your view:
<% if current_user && current_user == comment.user %>
Change your controller:
def destroy
# ensure user only can find owner comment.
#comment = current_user.comments.find(params[:id])
#comment.destroy
redirect_back(fallback_location: root_path)
end
If you change <% if current_user %> in your view to <% if current_user && current_user == comment.user %> then the Delete link will only appear for the owner of that comment.
You should also check that the current_user matches the #comment.user in your destroy controller method.
I am a complete beginner in Rails as such and I am trying to build a page to add extra profile data once the user logs in.
I am using Devise for authentication purposes and that works fine. I get this error and I have been stuck here.
undefined method `profiles'
Can you please help?
Codes
profiles_controller.rb
class ProfilesController < ApplicationController
before_action :authenticate_user!, only: [:new, :create, :show]
def new
#profile = current_user.profiles.build
end
def create
#profile = current_user.profiles.build(profile_params)
if #profile.save
format.html {redirect_to #profile, notice: 'Post was successfully created.'}
else
format.html {render 'new'}
end
end
def show
#profile = current_user.profiles
end
private
def profile_params
params.require(:profile).permit(:content)
end
end
The error seems to be coming from these lines in particular
def new
#profile = current_user.profiles.build
end
Other codes for reference:
/views/profiles/new.html.erb
<h1>Profiles#new</h1>
<p>Find me in app/views/profiles/new.html.erb</p>
<h3>Welcome <%= current_user.email %></h3>
<%= form_for(#profile) do |f| %>
<div class="field">
<%= f.label :content %><br />
<%= f.text_field :text, autofocus: true %>
</div>
<div class="actions">
<%= f.submit "Sign up" %>
</div>
<%end%>
routes.rb
Rails.application.routes.draw do
get 'profiles/new'
get 'profiles/create'
get 'profiles/show'
get 'profiles/update'
get 'pages/home'
get 'pages/dashboard'
devise_for :users, controllers: { registrations: "registrations" }
resources :profiles
root 'pages#home'
devise_scope :user do
get "user_root", to: "page#dashboard"
end
end
models/user.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_one :profile, dependent: :destroy
end
models/profile.rb
class Profile < ActiveRecord::Base
belongs_to :user
end
I just figured it out!
As the relationship is has_one, we should be using
def new
#profile = current_user.build_profile
end
instead of
def new
#profile = current_user.profiles.build
end
according to the documentation -
http://guides.rubyonrails.org/association_basics.html#has-one-association-reference
1) If your user must have many profiles. Set in your app/models/user.rb has_many :profiles
2) In your ProfilesController in new method instead of #profile = current_user.profiles use #profile = Profile.new
3) In your routes.rb delete
get 'profiles/new'
get 'profiles/create'
get 'profiles/show'
get 'profiles/update'
because you have already used resources :profiles
4) To stay with rules of DRY you can render form from a partial. Just add in views/profiles/_form.html.erb with the same content in your new.html.erb and after this you can delete everything im new.htm.erb and paste <%= render "form" %>. In future it will help you to render edit form if you want.
5) In your ProfilesController you can add method index with all profiles
def index
#profiles = Profile.all
end
You are trying to call an undefined relationship:
def new
#profile = current_user.profiles.build
end
has_one :profile
You should be calling:
def new
#profile = current_user.build_profile
end
name
metric
date_value
result_value
None of the above attributes are showing when I load the index page.
I know its because of this line: <% if averaged.user == current_user %>
More specifically it's because of the nested attributes of date_value and result_value (because if I take out those nested attributes the name & metric will show)
What do I need to add to the controller to allow the nested attributes to show on the index page, and in effect all the attributes?
I am using the cocoon gem and the devise gem.
Thanks in advance for your service!
<div id="values" class="panel panel-default">
<div class="panel-heading"><h4><b>AVERAGE</b></h4></div>
<!-- Table -->
<table>
<% #averaged_quantifieds.each do |averaged| %>
<% if averaged.user == current_user %>
<th class="value">
<%= link_to edit_quantified_path(averaged) do %>
<%= averaged.name %>
<% end %>
(<%= averaged.metric %>)
</th>
<tbody class="value">
<td><%= averaged.date_value.strftime("%m-%Y") %></td>
<td><%= averaged.result_value %></td>
</tbody>
<% end %>
<% end %>
</table>
</div>
controller
class QuantifiedsController < ApplicationController
before_action :set_quantified, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
def index
#averaged_quantifieds = current_user.quantifieds.averaged
#instance_quantifieds = current_user.quantifieds.instance
#averaged_quantifieds = Result.all.order("date_value")
#instance_quantifieds = Result.all.order("date_value")
end
def show
end
def new
#quantified = current_user.quantifieds.build
#quantified = Quantified.new
end
def edit
end
def create
#quantified = current_user.quantifieds.build(quantified_params)
if #quantified.save
redirect_to quantifieds_url, notice: 'Quantified was successfully created'
else
render action: 'new'
end
end
def update
if #quantified.update(quantified_params)
redirect_to quantifieds_url, notice: 'Goal was successfully updated'
else
render action: 'edit'
end
end
def destroy
#quantified.destroy
redirect_to quantifieds_url
end
private
def set_quantified
#quantified = Quantified.find(params[:id])
end
def correct_user
#quantified = current_user.quantifieds.find_by(id: params[:id])
redirect_to quantifieds_path, notice: "Not authorized to edit this goal" if #quantified.nil?
end
def quantified_params
params.require(:quantified).permit(:categories, :name, :metric, :result, :date, results_attributes: [:id, :result_value, :date_value, :_destroy])
end
end
quantified.rb
class Quantified < ActiveRecord::Base
belongs_to :user
scope :averaged, -> { where(categories: 'averaged') }
scope :instance, -> { where(categories: 'instance') }
has_many :results
accepts_nested_attributes_for :results, :reject_if => :all_blank, :allow_destroy => true
CATEGORIES = ['averaged', 'instance']
end
result.rb
class Result < ActiveRecord::Base
belongs_to :quantified
belongs_to :user
end
user.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :goals
has_many :values
has_many :quantifieds
has_many :results
end
Please let me know if you need any more code or comments to help bring this question to a resolution.