NoMethodError in Pages#profile - ruby-on-rails

I'm working on build a Twitter-like sample app, but I am stuck at creating a following method. I have created a Relationship model and the necessary classes to accomplish the task, but when I load the page, I receive the error "NoMethodError" on this line: <%= form_for(current_user.active_relationships.build, remote: true) do |f| %>
I render the form on the profile page like so:
<%= render '/components/follow_button', :user => User.find_by_username(params[:id]) %>
Here is the form:
<% if current_user.id != user.id %>
<div class="col s12">
<div class="panel panel-default">
<% if !current_user.following?(user) %>
<%= 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 %>
<% else %>
<%= form_for(current_user.active_relationships.find_by(followed_id: user.id),
html: { method: :delete }) do |f| %>
<%= f.submit "Unfollow", class: "btn" %>
<% end %>
<% end %>
</div>
</div>
<% 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
validates_uniqueness_of :email, :case_sensitive => false
validates_uniqueness_of :username, :case_sensitive => false
has_many :posts, dependent: :destroy
has_many :active_relationships, class_name: "Relationship", foreign_key: "follower_id", dependent: :destroy
has_many :passive_relationships, class_name: "Relationship", foreign_key: "followed_id", dependent: :destroy
has_many :following, through: :active_relationships, source: :followed
has_many :followers, through: :passive_relationships, source: :follower
def follow(other)
active_relationships.create(followed_id: other.id)
end
def unfollow(other)
active_relationships.find_by(followed_id: other.id).destroy
end
def following?(other)
following.include?(other)
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
RelationshipsController:
class RelationshipsController < ApplicationController
def create
user = User.find(params[:followed_id])
current_user.follow(user)
redirect_to(:back)
end
def destroy
user = Relationship.find(params[:id]).followed
current_user.unfollow(user)
redirect_to(:back)
end
end
Any help would be greatly appreciated.
EDIT: Following method used in Routes.rb
devise_for :users
resources :users do
member do
get :following, :followers
end
end

On the form_for documentation, it eventually talks about 'Resource-oriented style' forms
In the examples just shown, although not indicated explicitly, we still need to use the :url option in order to specify where the form is going to be sent. However, further simplification is possible if the record passed to form_for is a resource, i.e. it corresponds to a set of RESTful routes, e.g. defined using the resources method in config/routes.rb. In this case Rails will simply infer the appropriate URL from the record itself
<%= form_for(Post.new) do |f| %>
...
<% end %>
is equivalent to something like:
<%= form_for #post, as: :post, url: posts_path, html: { class: "new_post", id: "new_post" } do |f| %>
...
<% end %>
Looking at your code, since you are not passing a :url option to form_for and you are passing an instance of a model, it's assuming your model was configured in the routes file with resources :relationships, which generates some named route helpers such as relationships_path, the method that it's complaining is missing.
To fix your problem, you need to pass form_for a :url where your controller lives and where it should post to, or update your routes to use the resources :relationships. You can read more information about the resources routing here. If you add
resources :relationships, only: [:create, :destroy]
outside of your devise_for block, you'll end up with 2 new routes for
Prefix Verb URI Pattern Controller#Action
relationships POST /relationships(.:format) relationships#create
relationship DELETE /relationships/:id(.:format) relationships#destroy
and the named helpers relationships_path and relationship_path which your 2 form_for tags are going to be looking for.
Don't forget to restart your server after making changes to your config/routes.rb file to make sure rails picks them up.

Related

How to display which user created the post?

Trying to learn ruby on rails by creating a reddit clone.
So now I'm trying display the username of the user who created the post, but I'm only getting it displayed as u/username everywhere.
Also when I try to click on u/username to see if I can get data about user by going to his page, all I'm getting is an error saying:
undefined method `username' for nil:NilClass
This is my partial _list class that displays data about post:
<% posts.each do |post| %>
<%= link_to post.community.name %> | Posted by <%= link_to post.account.username, profile_path(:username) %> <%= time_ago_in_words post.created_at%> ago. </p> </small>
<%= link_to post.title, community_post_path(post.community_id, post) %>
<%= truncate post.body, length: 200 %>
<% end %>
profile.html.erb just to display username for now:
<%= #profile.username %>
account.rb:
class Account < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :posts
has_many :communities
validates_presence_of :username
end
public_controller:
class PublicController < ApplicationController
def index
#communities = Community.all.limit(5)
#posts = Post.order(id: :desc).limit(20)
end
def profile
#profile = Account.find_by_username params[:username]
end
end
post.rb:
class Post < ApplicationRecord
belongs_to :account
belongs_to :community
validates_presence_of :title, :body, :account_id, :community_id
end
and routes.rb:
Rails.application.routes.draw do
devise_for :accounts
get "u/:username" => "public#profile", as: :profile
resources :communities do
resources :posts
end
root to: "public#index"
end
Full repo here

How to access the edit method of a nested resource with simple_form in rails?

I have 3 models: Book, UserBook, and User (from devise). User and Book are linked though UserBook with a has_many:users through: :user_books.
I've been trying to access the edit and destroy methods in user_books using a simple form in books#show to change a boolean value have_or_want from false to true. These are the models:
class Book < ApplicationRecord
has_many :user_books
has_many :users, through: :user_books
validates :title, :author, presence: true
validates :rating, inclusion: {in: [1,2,3,4,5], allow_nill: false }
end
class UserBook < ApplicationRecord
belongs_to :user
belongs_to :book
end
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :user_books
has_many :books, through: :user_books
end
The idea is that you can look a book in books#show and an if statement decides which of three simple_forms is shown:
If there is no UserBook linking the current_user to the book there will be a radio button which if you want to add it to your own book collection (#user_book.have_or_want: true) or add it to your reading list (#user_book.have_or_want: false). This bit work and a new UserBook is created correctly using user_books#update.
I am now trying to have a form for when the book is on the users reading list(#user_book.have_or_want) to give the option marking the book as read (change #user_book.have_or_want to true with user_books#update) or to remove the book (user_books#destroy).
Also a third form for if it is in the book collection (#user_book.have_or_want: true) to remove it by deleting the #user_book.
In the routes UserBook is nested in Book.
The if statement and simple forms in books#show looks like this and the first part works.
<% if current_user.books.exclude? #book %>
<%= simple_form_for [#book, #user_book] do |f| %>
<%= f.input :have_or_want,
:collection => [[true, 'Add to Bookshelf'], [false, 'Add to resding
list']],
:label_method => :last,
:value_method => :first,
:as => :radio_buttons %>
<%= f.submit "Submit" %>
<% end %>
<% elsif #book.user_books[0].have_or_want == false %>
<!-- If is is on the reading list you can add to bookshelf(changes
have_or_want to true) or can remove from reading list (delete
user_book instance) -->
<%= simple_form_for [#book, #user_book], url:
book_user_book_path(#book, #user_book), method: :patch do |f| %>
<%= f.input :have_or_want %>
<%= f.submit "Submit" %>
<% end %>
<% else %>
<!-- It is on the bookshelf so option to remove - user_books#destroy?
-->
<% end %>
I have tried several ways to do the second part and this is my best guess at the moment. There error I get is:
No route matches {:action=>"show", :book_id=>"1", :controller=>"user_books", :id=>nil}, missing required keys: [:id]
Also #user_book is a user_book with all values of nil.

param is missing or the value is empty: user - Rails

I have got the following rails app, which allows users to subscribe to widgets. i.e. Many-to-Many through model with Users-to-Widgets through Subscriptions.
My Models:
class Subscription < ActiveRecord::Base
belongs_to :user
belongs_to :widget
validates_uniqueness_of :user_id, scope: :widget_id
end
class User < ActiveRecord::Base
has_many :subscriptions
has_many :widgets, through: :subscriptions
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
class Widget < ActiveRecord::Base
has_many :subscriptions
has_many :users, through: :subscriptions
end
My Controller:
class SubscriptionsController
def edit
#user = current_user
end
def update
#user = current_user
if #user.update(user_subscription_params)
redirect_to #user, notice: "Subscriptions updated"
else
render :edit
end
end
private
def user_subscription_params
params.require(:user).permit(:widget_ids)
end
end
and this is being rendered in views/subscriptions/_forms.html.erb like this:
<%= form_for #user, url: user_subscription_path, method: :patch do |f| %>
<%= f.collection_check_boxes :widget_ids, Widget.all, :id, :name %>
<%= f.submit %>
<% end %>
I am getting the error:
No route matches {:action=>"show", :controller=>"subscriptions"} missing required keys: [:id]
I would be really grateful of any ideas how to fix this.
Edit:
Routes.rb:
Rails.application.routes.draw do
root 'home#index'
resources :widgets
resources :subscriptions
devise_for :users
devise_scope :user do
get '/users/sign_out' => 'devise/sessions#destroy'
end
authenticated :user do
root to: 'home#index', as: :authenticated_root
end
You can try
<%= form_for #user, url: user_subscription_path(#user), method: :patch do |f| %>
<%= f.collection_check_boxes :widget_ids, Widget.all, :id, :name %>
<%= f.submit %>
<% end %>

Devise: Could not find a valid mapping for User id

The following form in a partial called locationpicker:
<%= simple_form_for(current_user, url: registration_path(current_user), method: :put ) do |f| %>
<% f.association :location, collection: Location.where(category: 'Country'), label: false, input_html: {onchange: "this.form.submit()"} %>
<% end %>
brings up this error:
Could not find a valid mapping for #<User id:...
It's the registration_path(current_user) which is causing it. My suspicions were that something has changed in the routes or the users model, but I can't for the life of me think what or know where to start looking. I thought it might be the recent inclusion of ActiveModel::Dirty but removing that doesn't solve the problem.
Routes.rb:
scope ":locale", locale: /#{I18n.available_locales.join("|")}/ do
devise_for :users, :controllers => {:registrations => "registrations"}
...
end
User.rb
class User < ActiveRecord::Base
include ActiveModel::Dirty
devise :database_authenticatable, :registerable, #:encryptable,
:recoverable, :rememberable, :trackable, :validatable, :lockable
has_one :assignment
has_one :role, :through => :assignment
has_many :changerequests, inverse_of: :created_by, foreign_key: "created_by_id"
belongs_to :location, required: true
belongs_to :person, inverse_of: :user, required: true
has_many :people_details, through: :person
scope :inclusive, -> {includes(:person).includes(:people_details).includes(:location).includes(:assignment).includes(:role)}
after_update :update_locale
after_save :expire_caches
def role?(role_sym)
role_name.to_sym == role_sym
end
def role_group?(role_sym)
role_group_name.to_sym == role_sym
end
def send_on_create_confirmation_instructions
true
end
def update_locale
if locale_changed?
I18n.locale = self.locale || "en"
self.expire_caches
end
end
def country
if self.location.category == "Country"
self.location
elsif self.location.ancestors.where(category: "Country")
self.location.ancestors.where(category: "Country").first
elsif self.location.children.where(category: "Country")
self.location.children.where(category: "Country").first
end
end
def expire_caches
#Admin users can change their location so the caches need expiring
if location_id_changed? or locale_changed?
ctrlr = ActionController::Base.new
#Clear fragments
ctrlr.expire_fragment("#{id}_#{locale}_location_picker")
etc...
end
if location_id_changed?
#Clear other caches
Rails.cache.delete("#{id}_locations_scope")
.... etc
end
end
Try adding as: in your simple_form_for call:
<%= simple_form_for(current_user, as: :user, url: registration_path(current_user), method: :put ) do |f| %>
<% f.association :location, collection: Location.where(category: 'Country'), label: false, input_html: {onchange: "this.form.submit()"} %>
<% end %>
Normally, the devise registrations edit form looks like:
= simple_form_for(resource, as: resource_name, url: user_registration_path, html: { method: :patch }) do |f|
In other words, using "resource" not "current_user". You might try that as well, if your form partial is being rendered within the devise controller context.

'Like/Unlike' button with Rails 4

I'm trying to create a simple Like/unlike button with Rails 4. I tried to do this with socialization gem, but after one day of struggling I gave up and decided to modify M. Hartl's 'foollow' mechanism from Rails Tutorial. Here is what i got so far:
User.rb
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable, :validatable
has_many :questions
has_many :answers
has_many :likes, foreign_key: "liker_id", dependent: :destroy
has_many :liked_answers, through: :likes, source: :liked, source_type: "Answer"
def like?(answer)
likes.find_by(liked_id: answer.id)
end
def like!(answer)
likes.create!(liked_id: answer.id)
end
def unlike!(answer)
likes.find_by(liked_id: answer.id).destroy
end
end
Answer.rb:
class Answer < ActiveRecord::Base
belongs_to :question
belongs_to :user
has_many :likes, foreign_key: "liked_id",
class_name: "Like",
dependent: :destroy
has_many :likers, through: :likes, source: :liker
end
Like.rb:
class Like < ActiveRecord::Base
belongs_to :liker, class_name: "User"
belongs_to :liked, class_name: "Answer"
validates :liker_id, presence: true
validates :liked_id, presence: true
end
likes_controller.rb:
class LikesController < ApplicationController
skip_before_action :verify_authenticity_token
before_action :authenticate_user!
respond_to :html, :js
def create
#answer = Answer.find(params[:like][:liked_id])
current_user.like!(#answer)
respond_with #answer.question
end
def destroy
#answer = Like.find(params[:id]).liked
current_user.unlike!(#answer)
respond_with #answer.question
end
end
_like.html.erb:
<%= form_for(current_user.likes.build(liked_id: #answer.id), remote: true) do |f| %>
<div><%= f.hidden_field :liked_id %></div>
<%= f.submit "Like!", class: "btn btn-large btn-primary" %>
<% end %>
_unlike.html.erb:
<%= form_for(current_user.likes.find_by(liked_id: #answer.id),
html: { method: :delete }, remote: true) do |f| %>
<%= f.submit "Unlike.", class: "btn btn-large" %>
<% end %>
routes.rb:
Rails.application.routes.draw do
devise_for :users
scope "/admin" do
resources :users
end
resources :questions do
resources :answers do
get :likes
end
end
resources :likes, only: [:create, :destroy]
root to: 'questions#index'
end
I also have jQuery and jQuery_ujs required in application.js, and relevant js.erb files ("create" and "destroy") in views.
The 'like/unlike' mechanism itself seems to work pretty well - in the console, with my 'like!' and 'unlike!' methods, I'm able to create and destroy "Like" objets with id, liker_id and liked_id.
The problem begins with the button itself.
I can see the "Like!" button next to each answer, but when I click it - I get this error:
ActiveRecord::RecordNotFound in LikesController#create
Couldn't find Answer with 'id'=
The error points on this line in LikesController:
#answer = Answer.find(params[:like][:liked_id])
so I suppose my #answer.id results to be 'nil', but I have no idea where did I make mistake. My first guess would be routes file - I'm still not sure if everything is correct there.
I've spent whole day looking for solution, I also found some similar questions on SO, but none of the answers could help me.
Any ideas would be greatly appreciated.
EDIT: Params from the error
Parameters:
{"utf8"=>"✓",
"like"=>{"liked_id"=>""},
"commit"=>"Like!"}
You're using the hidden field tag wrong. http://api.rubyonrails.org/v4.1.1/classes/ActionView/Helpers/FormHelper.html#method-i-hidden_field shows you need to supply two values into your tag. Change your like ERB file to this:
_like.html.erb:
<%= form_for(current_user.likes.build(liked_id: #answer.id), remote: true) do |f| %>
<div><%= f.hidden_field :liked_id, :value => #answer.id %></div>
<%= f.submit "Like!", class: "btn btn-large btn-primary" %>
<% end %>
and that should get you what you want.

Resources