I was following the Michael Hart book and wanted to and add the follow and unfollow button to the users show page. While implementing this I encountered this error undefined method 'relationships_path'.
Any help would be helpful and appreciated.
relationships controller.rb
class RelationshipsController < ApplicationController
before_action :logged_in_user
def create
#user = User.find(params[:followed_id])
current_user.follow(#user)
respond_to do |format|
format.html { redirect_to #user }
format.js
end
end
def destroy
#user = Relationship.find(params[:id]).followed
current_user.unfollow(#user)
respond_to do |format|
format.html { redirect_to #user }
format.js
end
end
end
users controller.rb
class UsersController < ApplicationController
before_action :authenticate_user!
before_action :set_user
def create
#user = User.friendly.find(params[:followed_id])
current_user.follow(#user)
respond_to do |format|
format.html { redirect_to #user }
format.js
end
end
def destroy
#user = Relationship.find(params[:id]).followed
current_user.unfollow(#user)
respond_to do |format|
format.html { redirect_to #user }
format.js
end
end
def following
#title = "Following"
#user = User.find(params[:id])
#users = #user.following.paginate(page: params[:page])
render 'show_follow'
end
def followers
#title = "Followers"
#user = User.find(params[:id])
#users = #user.followers.paginate(page: params[:page])
render 'show_follow'
end
end
Relationship.rb
class Relationship < ActiveRecord::Base
belongs_to :follower, class_name: "User"
belongs_to :followed, class_name: "User"
validates :follower_id, presence: true
validates :followed_id, presence: true
end
User.rb
class User < ActiveRecord::Base
extend FriendlyId
friendly_id :name, use: :slugged
has_many :active_relationships, class_name: "Relationship",
foreign_key: "follower_id",
dependent: :destroy
has_many :following, through: :active_relationships, source: :followed
has_many :passive_relationships, class_name: "Relationship",
foreign_key: "followed_id",
dependent: :destroy
has_many :followers, through: :passive_relationships, source: :follower
# Follows a user.
def follow(other_user)
active_relationships.create(followed_id: other_user.id)
end
# Unfollows a user.
def unfollow(other_user)
active_relationships.find_by(followed_id: other_user.id).destroy
end
# Returns true if the current user is following the other user.
def following?(other_user)
following.include?(other_user)
end
end
_follow.html.erb
<%= form_for(current_user.active_relationships.build, remote: true) do |f| %>
<div><%= hidden_field_tag :followed_id, #user.id %></div>
<%= f.submit "Follow", class: "btn btn-primary" %>
<% end %>
_unfollow.html.erb
<%= form_for(current_user.active_relationships.find_by(followed_id: #user.id),
html: { method: :delete },
remote: true) do |f| %>
<%= f.submit "Unfollow", class: "btn" %>
<% end %>
show.html.erb
<% provide(:title, #user.name) %>
<div class="row">
<aside class="col-md-4">
<section class="stats">
<% #user ||= current_user %>
<div class="stats">
<strong id="following" class="stat">
<%= #user.following.count %>
</strong>
following
</a>
<strong id="followers" class="stat">
<%= #user.followers.count %>
</strong>
followers
</a>
<%= render 'follow' %>
</div>
</section>
</aside>
</div>
Routes.rb
Rails.application.routes.draw do
get 'users/index'
get 'users/show'
devise_for :users do
member do
get :following, :followers
end
end
get 'users/:id' => 'users#show', as: :user
end
Relationships | Migration
class CreateRelationships < ActiveRecord::Migration
def change
create_table :relationships do |t|
t.integer :follower_id, index: true
t.integer :followed_id, index: true
t.timestamps null: false
end
add_index :relationships, [:follower_id, :followed_id], unique: true
end
end
As per your route.rb, you don't have the required route i.e. POST /relationships. Add it into your route.rb:
resources :relationships
Related
Albums have many reviews and users through reviews, Reviews belong to album and belong to user, and Users have many reviews and have many albums through reviews.
On my albums show page, Reviews do not display. But Reviews display on the reviews index page. I can't seem to figure out what I'm missing. I'm not getting any errors.
Here is my Albums controller:
class AlbumsController < ApplicationController
before_action :set_album, only: [:show, :edit, :update]
def index
#albums = Album.all
#current_user
end
def show
#review = #album.reviews.build
end
def new
#album = Album.new
end
def create
#album = Album.new(album_params)
if #album.save
redirect_to album_path(#album)
else
render :new
end
end
def edit
end
def update
if #album.update(album_params)
redirect_to album_path(#album), notice: "Your album has been updated."
else
render 'edit'
end
end
private
def set_album
#album = Album.find(params[:id])
end
def album_params
params.require(:album).permit(:artist, :title, :avatar)
end
end
Reviews Controller:
class ReviewsController < ApplicationController
def index
#reviews = Review.all
end
def show
#review = Review.find(params[:id])
##reviews = Review.where("album_id = ?", params[:album_id])
end
def new
#review = Review.new
end
def create
#review = current_user.reviews.build(review_params)
if #review.save
redirect_to reviews_path
else
render :new
end
end
def update
end
private
def review_params
params.require(:review).permit(:title, :date, :content, :user_id, :album_id, album_attributes:[:artist, :title])
end
end
Here is my albums show page:
<% if #album.avatar.attached? %>
<image src="<%=(url_for(#album.avatar))%>%" style="width:350px;height:350px;">
<% end %>
<br>
<%= #album.artist %> -
<%= #album.title %>
<br>
<%= link_to "Edit Album", edit_album_path %>
<br><br><br>
<%= render '/reviews/form' if logged_in? %>
<h3>Album Reviews</h3>
<% #album.reviews.each do |review| %>
<%= review.title %>
<%= review.content %>
<% end %>
<br><br><br>
<%= link_to "All Albums", albums_path %><br>
<%= link_to "Upload a New Album", new_album_path %>
Here is my routes file:
Rails.application.routes.draw do
get '/signup' => 'users#new', as: 'signup'
post '/signup' => 'users#create'
get '/signin' => 'sessions#new'
post '/signin' => 'sessions#create'
get '/signout' => 'sessions#destroy'
resources :albums do
resources :reviews, except: [:index]
end
resources :users
resources :reviews, only: [:index]
root to: "albums#index"
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end
Here are my Models:
class Album < ApplicationRecord
has_many :reviews
has_many :users, through: :reviews
has_one_attached :avatar
end
class Review < ApplicationRecord
belongs_to :album, optional: true
belongs_to :user
accepts_nested_attributes_for :album
end
class User < ApplicationRecord
has_secure_password
has_many :reviews
has_many :albums, through: :reviews
accepts_nested_attributes_for :reviews
end
Review form:
<%= form_for :review, method: "POST",:url => { :action => :create } do |f| %>
<!-- <%= f.collection_select :album_id, Album.order(:artist),:id,:artist %> -->
<br>
<%= f.label :title %>
<%= f.text_field :title%>
<br><br>
<%= f.label :date %>
<%= f.datetime_field :date%>
<br><br>
<%= f.label :content %>
<%= f.text_area :content %>
<br><br>
<%= f.submit "Submit" %>
<% end %>
The problem is the iteration, because you use the instance variable coming from the controller instead of the block variable, it should be:
<% #album.reviews.each do |review| %>
<%= review.title %>
<%= review.content %>
<% end %>
#review is a new instance of a review (empty) which you created for the form I guess.
So instead of showing each review's title and content you showed the one from the empty review saved in #review in each iteration.
NoMethodError in PostsController#create
undefined method `posts' for nil:NilClass
Posts#controller
class PostsController < ApplicationController
# before_action :logged_in_user, only: [:create, :destroy]
before_action :correct_user, only: :destroy
before_action :find_postable, except: [:index]
def index
#posts = Post.all.order('created_at DESC')
end
def new
#post = Post.new
end
def create
#post = #postable.posts.new post_params
if #post.save
redirect_to :back, notice: 'Your comment was successfully posted!'
else
redirect_to :back, notice: "Your comment wasn't posted!"
end
end
def destroy
Post.find(params[:id]).destroy
redirect_to posts_path#, notice: "Post successfully deleted"
end
def edit
#post = Post.find(params[:id])
end
def update
#post = Post.find(params[:id])
if #post.update_attributes(student_params)
redirect_to post_path
else
redirect_to edit_post_path
end
end
def show
#post = Post.find (params[:id])
end
private
def post_params
params.require(:post).permit(:content, :image, :document, :audio, :video, :classroom_id)
end
def correct_user
#post = current_user.posts.find_by(id: params[:id])
redirect_to root_url if #post.nil?
end
def find_postable
#postable = Post.find_by_id(params[:post_id]) if params[:post_id]
#postable = Classroom.find_by_id(params[:classroom_id]) if params[:classroom_id]
end
Here is the post model
class Post < ActiveRecord::Base
belongs_to :user
belongs_to :classroom
belongs_to :postable, polymorphic: true
has_many :posts, as: :postable
end
Here is the classroom model (it belongs to the class and this is selected in the new post form"
class Classroom < ActiveRecord::Base
has_many :posts, as: :postable
belongs_to :teachers
# belongs_to :students
Posts in Schema.rb
create_table "posts", force: :cascade do |t|
t.text "content"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "classroom_id"
t.integer "postable_id"
t.string "postable_type"
end
I've tried many things with no success, I'm not quite sure why I'm getting this error. I'm following a video and trying to apply it to my existing post model but am still having trouble. Thanks
New Post Form:
<%= form_for :post, html: { multipart: true, class: "post-form" }, url: posts_path do |f| %>
<h4>Choose a Classroom</h4>
<div class="form-group">
<%= f.collection_select :classroom_id, current_user.classrooms, :id, :name %>
</div>
<h4>Write a post</h4>
<div class="form-group">
<%= f.text_area :content, class: "form-control" %>
</div>
<h4>Attach Files</h4>
<div class="form-group">
<%= f.file_field :image %>
</div>
<%= f.submit "Add Post", class: "btn btn-primary" %>
<% end %>
For a post reply
<li>
<%= post.content %> -
<%= form_for [post, Post.new] do |f| %>
<%= f.text_area :content, placeholder: "Add a Reply" %><br/>
<%= f.submit "Reply" %>
<% end %>
</li>
The error is in find_postable method, specifically in this line:
#postable = Classroom.find_by_id(params[:classroom_id]) if params[:classroom_id]
You are trying to fetch classroom_id from params (i.e. params[:classroom_id]) but that key (classroom_id) exists within post key in your parameters hash:
Parameters: {
"utf7"=>"✓",
"authenticity_token"=>"bunchofletters==",
"post"=>{
"classroom_id"=>"1",
"content"=>"hello"
},
"commit"=>"Add Post"
}
To fix this, change params[:classroom_id] to params[:post][:classroom_id] in find_postable method:
def find_postable
#postable = Post.find_by_id(params[:post_id]) if params[:post_id]
#postable = Classroom.find_by_id(params[:post][:classroom_id]) if params[:classroom_id]
end
I'm a beginner and have a serious problem with making two steps signing up.
I keep getting that error:
First argument in form cannot contain nil or be empty
<%= form_for(#Profile) do |f| %>
Users can sign up with their user information which are on the user table(DB). And all is fine with them. But I really want them to have profiles. So they can put their bio for example.
I tried to create MVC structure for the profiles based on users' MVC but it doesn't work.
I have been trying to find the answer for days. Tried hundreds of variations but nothing worked out. Please help!
views/users/new.html.erb (This is working)( I just wanted to create profile for the users and give them the ability to fill out their information as the second steps of the signing up)
<div class="container">
<% provide(:title, 'Sign up') %>
<h1 class="center">Sign up</h1>
<div class="row">
<div class="col s12 m10 l8 offset-m1 offset-l2">
<%= form_for(#user) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :first_name %>
<%= f.text_field :first_name %>
<%= f.label :genre %>
<%= f.text_field :genre %>
<%= f.label :middle_name %>
<%= f.text_field :middle_name %>
<%= f.label :last_name %>
<%= f.text_field :last_name %>
<%= f.label :preferred_name %>
<%= f.text_field :preferred_name %>
<%= f.label :email %>
<%= f.text_field :email %>
<%= f.label :password %>
<%= f.password_field :password %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation %>
<div class="row center">
<%= f.submit "Create my account", class: "btn btn-large btn-primary" %>
</div>
<% end %>
</div>
</div>
</div>
models/user.rb
class User < ActiveRecord::Base
has_one :profile
has_many :microposts, dependent: :destroy
has_many :relationships, foreign_key: "follower_id", dependent: :destroy
has_many :followed_users, through: :relationships, source: :followed
has_many :reverse_relationships, foreign_key: "followed_id",
class_name: "Relationship",
dependent: :destroy
has_many :followers, through: :reverse_relationships, source: :follower
before_save { self.email = email.downcase }
before_create :create_remember_token
validates :first_name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
has_secure_password
validates :password, length: { minimum: 6 }
def User.new_remember_token
SecureRandom.urlsafe_base64
end
def User.digest(token)
Digest::SHA1.hexdigest(token.to_s)
end
def feed
Micropost.from_users_followed_by(self)
end
def following?(other_user)
relationships.find_by(followed_id: other_user.id)
end
def follow!(other_user)
relationships.create!(followed_id: other_user.id)
end
def unfollow!(other_user)
relationships.find_by(followed_id: other_user.id).destroy
end
private
def create_remember_token
self.remember_token = User.digest(User.new_remember_token)
end
end
controllers/users_controller.rb
class UsersController < ApplicationController
before_action :signed_in_user,
only: [:index, :edit, :update, :destroy, :following, :followers]
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])
#microposts = #user.microposts.paginate(page: params[:page])
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
sign_in #user
flash[:success] = "Welcome to the my app"
redirect_to #user
else
render 'new'
end
end
def edit
end
def update
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] = "just got destroyed."
redirect_to users_url
end
def following
#title = "Connections"
#user = User.find(params[:id])
#users = #user.followed_users.paginate(page: params[:page])
render 'show_follow'
end
def followers
#title = "known by"
#user = User.find(params[:id])
#users = #user.followers.paginate(page: params[:page])
render 'show_follow'
end
private
def user_params
params.require(:user).permit(:first_name, :middle_name, :last_name, :preferred_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
second signup page
views/users/signup2.html.erb
<div class="container">
<% provide(:title, 'Sign up2') %>
<h1 class="center">Sign up2</h1>
<div class="row">
<div class="col s12 m10 l8 offset-m1 offset-l2">
<%= form_for(#Profile) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :primary_instrument %>
<%= f.text_field :primary_instrument %>
<%= f.label :bio %>
<%= f.text_field :bio %>
<div class="row center">
<%= f.submit "Create my account2", class: "btn btn-large btn-primary" %>
</div>
<% end %>
</div>
</div>
</div>
models/profile.rb
class Profile < ActiveRecord::Base
belongs_to :user
end
controllers/profiles_controller.rb
class ProfilesController < ApplicationController
before_filter :get_user
def get_user
#profile = User.find(params[:user_id])
end
# generate new-profile form
def new
#user.profile = Profile.new
#profile = #user.profile
end
# process new-profile-form post
def create
#user.profile = Profile.new(params[:profile])
#profile = #user.profile
respond_to do |format|
if #profile.save
flash[:notice] = 'Profile was successfully created.'
format.html { redirect_to(#profile) }
format.xml { render :xml => #profile, :status => :created, :location => #profile }
...
end
end
end
# generate edit-profile form
def edit
#profile = Profile.find(params[:id])
end
# generate edit-profile-form post
def update
#profile = #user.profile
respond_to do |format|
if #profile.update_attributes(params[:profile])
flash[:notice] = 'Profile was successfully updated.'
# format.html { redirect_to(#profile) }
format.html { redirect_to(user_profile(#user)) }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #profile.errors, :status => :unprocessable_entity }
end
end
end
Routes.rb
root to: 'static_pages#home'
match '/signup', to: 'users#new', via: 'get'
match '/signup2', to: 'profiles#new', via: 'get'
match '/signin', to: 'sessions#new', via: 'get'
match '/signout', to: 'sessions#destroy', via: 'delete'
match '/help', to: 'static_pages#help', via: 'get'
match '/about', to: 'static_pages#about', via: 'get'
match '/contact', to: 'static_pages#contact', via: 'get'
end
Your #Profile variable is capitalized in the markup, while the variable set in your controller is downcased. Rewrite the variable in the markup like so:
<%= form_for(#profile) do |f| %>
Hope it helps!
Apart from #Zoran's answer you can use the following gem which would make your life easy with multi step forms.
Wicked gem
I am implementing a follow feature for my users. For this, I followed instruction of micheal hartl tutorial link needed, yet I am getting this error:
undefined method id' for nil:NilClass <% if current_user.following?(#user) %>
this is my user model
has_many :relationships, foreign_key: "follower_id", dependent: :destroy
has_many :followed_users, through: :relationships, source: :followed
has_many :reverse_relationships, foreign_key: "followed_id",
class_name: "Relationship",
dependent: :destroy
has_many :followers, through: :reverse_relationships, source: :follower
def following?(user)
relationships.find_by(followed_id: user.id)
end
def follow!(user)
relationships.create!(followed_id: user.id)
end
def unfollow!(user)
relationships.find_by(followed_id: user.id).destroy
end
this is my relationship model
class Relationship < ActiveRecord::Base
belongs_to :follower, class_name: "User"
belongs_to :followed, class_name: "User"
validates :follower_id, presence: true
validates :followed_id, presence: true
end
and this is my view
<% if current_user.id && current_user.id != user.id %>
<div id="follow_form">
<% if current_user.following?(#user) %>
<%= form_for(current_user.relationships.find_by(followed_id: #user.id),
html: { method: :delete }) do |f| %>
<%= f.submit "Unfollow", class:"unfollow-button" %>
<% end %>
<% else %>
<%= form_for(current_user.relationships.build(followed_id: #user.id)) do |f| %>
<div><%= f.hidden_field :followed_id %></div>
<%= f.submit "Follow", class:"follow-button" %>
<% end %>
<% end %>
this is the user controller
def show
#user= User.find_by_slug(params[:id])
if #user
#posts= Post.all
render action: :show
else
render file: 'public/404', status: 404, formats: [:html]
end
end
def index
#users = (current_user.blank? ? User.all : User.find(:all, :conditions => ["id != ?", current_user.id]))
end
This means #user is nil. Make sure that #user object is not null by doing
= debug #user
That should print all the details about #user object.
New to rails and this one has been troubling me for a while now. All the tutorials on polymorphic associations seem to be only two levels and never a polymorphic model nested in a polymorphic model. really need help on this.
I have a app that has post, comment and link models. Both comments and links are polymorphic as I want to add link urls to both comments and posts, as well as comments to other things, similar to how facebook works. I want to nest the links model so can send as one form. Post and links work but comments and links has an MassAssignmentSecurity error.
Erorr
ActiveModel::MassAssignmentSecurity::Error in CommentsController#create
Can't mass-assign protected attributes: link
Request Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"
J1JfIINrtvax77M3JcbDDWvHFpyGG1ciK1DisGOLu6M=",
"comment"=>{"content"=>"z",
"link"=>{"link_url"=>"z"}},
"commit"=>"Add comment",
"forum_post_id"=>"16"}
Routes
resources :forum_posts do
resources :comments
resources :links
end
resources :comments do
resources :links
end
Forum_Post Model
class ForumPost < ActiveRecord::Base
attr_accessible :content, :links_attributes, :comments_attributes , :link_url
has_many :links, :as => :linkable
has_many :comments, :as => :commentable
accepts_nested_attributes_for :links, :allow_destroy => true #, :reject_if => lambda { |t| t[:link].nil?}
end
Comment Model
class Comment < ActiveRecord::Base
attr_accessible :commentable_id, :commentable_type, :content,:links_attributes, :link_url
belongs_to :commentable, :polymorphic => true
has_many :links, :as => :linkable
accepts_nested_attributes_for :links, :allow_destroy => true #, :reject_if => lambda { |t| t[:link].nil?}
end
Link Model
class Link < ActiveRecord::Base
attr_accessible :description, :image_url, :link_url, :linkable_id, :linkable_type, :title, :link_id
belongs_to :linkable, :polymorphic => true
end
Forum_Post Controller
class ForumPostsController < ApplicationController
...............
def new
#forum_post = ForumPost.new
#link = #forum_post.links.build
respond_to do |format|
format.html # new.html.erb
format.json { render json: #forum_post }
end
end
........
end
Comments Controller
class CommentsController < ApplicationController
def find_commentable
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
nil
end
def index
#commentable = find_commentable
#comments = #commentable.comments
end
def create
#commentable = find_commentable
#comment = #commentable.comments.build(params[:comment])
#comment.links.build
if #comment.save
flash[:notice] = "Successfully saved comment."
redirect_to :id => nil
else
render :action => 'new'
end
end
end
Links Controller
class LinksController < ApplicationController
def find_linkable
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
nil
end
def index
#linkable = find_linkable
#links = #linkable.links
end
def create
#linkable = find_linkable
#link = #linkable.link.build(params[:link])
if #link.save
flash[:notice] = "Successfully saved link."
redirect_to :id => nil
else
render :action => 'new'
end
end
end
Comment Partial View
<h2>Comments</h2>
<% if commentable.comments.empty? %>
No comments to display.
<% else %>
<% for comment in commentable.comments %>
<%= comment.content %>
<% end %>
<% end %>
<% :link_url %>
<h2>New Comment</h2>
<%= form_for [#commentable, Comment.new] do |f| %>
<div class="field">
<%= f.label :content %><br />
<%= f.text_area :content, :rows => 5 %>
</div>
<%= f.fields_for [#linkable, Link.new] do |link| %>
<%= render :partial => 'links/link', :locals => { :f => link } %>
<% end%>
<div class="actions">
<%= submit_tag "Add comment" %>
</div>
<% end %>
Link Partial View
<h2>Link Previews</h2>
<div class="field">
<%= f.label :link_url %><br />
<%= f.text_field :link_url, :id => "url_field" %>
</div>