param is missing or the value is empty: user - Rails - ruby-on-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 %>

Related

How to add id_user_created and id_user_edited to a Plans table in rails

I am new in rails and programming at all. I have to add a kind of id_user_created and id_user_edited in a table called Plan. These ids will help me to know which user created and edited a plan, but I have no idea how to do it. On my db schema, there is no relation between User and Plan but now that I have to add theses ids, I assume that I will have to create a relation, right? Thanks a lot.
Models
class Plan < ApplicationRecord
has_many :users
end
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
belongs_to :plan
end
Controller
class PlansController < ApplicationController
def new
#plan = Plan.new
end
def create
#user = current_user
#plan = Plan.new(plan_params)
#plan.user = #user
if #plan.save
redirect_to plan_path(#plan), notice: 'O plano foi criado com sucesso.'
else
render :new
end
end
def edit
#plan = Plan.find(params[:id])
end
def update
#plan = Plan.find(params[:id])
if #plan.update(plan_params)
redirect_to #plan, notice: 'O plano foi editado com sucesso.'
else
render :edit
end
end
private
def plan_params
params.require(:plan).permit(:name, :duration, :price, :status, :default)
end
end
Route
Rails.application.routes.draw do
devise_for :users
root to: 'pages#home'
resources :plans do
resources :accounts, only: %i[new create] do
end
end
resources :payments, only: %i[index]
resources :accounts, only: %i[index show edit update destroy] do
resources :users, only: %i[new create] do
resources :roles
end
end
resources :users, only: %i[index show edit update destroy]
Plans Form
<%= simple_form_for [#user, #plan] do |f| %>
<%= f.input :name, label: 'Nome' %>
<%= f.input :duration, label: 'Duração' %>
<%= f.input :price, label: 'Preço' %>
<%= f.input :status %>
<%= f.input :default %>
<%= f.button :submit, class:"btn-outline-secondary" %>
<% end %>
In Rails you generate migrations to create foreign keys by using the references (aka belongs_to) type:
rails g migration add_user_to_plans user:references
Which generates the following migration:
class AddUserToPlans < ActiveRecord::Migration[6.0]
def change
add_reference :plans, :user, null: false, foreign_key: true
end
end
When you run the migration it creates a plans.user_id column which points to the users table.
If you want to call the column/association something else like creator_id you need to explicitly tell rails which table you are referencing. Just don't call your columns id_user_created unless you want to come off as a complete snowflake.
class AddCreatorToPlans < ActiveRecord::Migration[6.0]
def change
add_reference :plans, :creator,
null: false,
foreign_key: { to_table: :users }
end
end
And you also have to explicitly set up your association:
class Plan
belongs_to :creator,
class_name: 'User',
inverse_of: :plans
end
class User
has_many :plans,
foreign_key: :creator_id,
inverse_of: :plans
end
Your form is also off. When you're dealing with creating resources as the logged in user you don't want/need to nest the route.
<%= simple_form_for #plan do |f| %>
<%= f.input :name, label: 'Nome' %>
<%= f.input :duration, label: 'Duração' %>
<%= f.input :price, label: 'Preço' %>
<%= f.input :status %>
<%= f.input :default %>
<%= f.button :submit, class:"btn-outline-secondary" %>
<% end %>
And you can also trim that create method down by building the resource off the association on the current user:
def create
#plan = current_user.plans.new(plan_params)
if #plan.save
redirect_to #plan,
notice: 'O plano foi criado com sucesso.'
else
render :new
end
end
While you could do the same thing and add an editor_id column to plans its probably not what you want as it will only let you record a single id and not something more useful like a history of who edited the record and when which requires a join table and this is really an entire question on its own.
**Try add 's' to model in database relationship**
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
belongs_to :plans
end
<%= simple_form_for [#user, #user.plans] do |f| %>
<%= f.input :name, label: 'Nome' %>
<%= f.input :duration, label: 'Duração' %>
<%= f.input :price, label: 'Preço' %>
<%= f.input :status %>
<%= f.input :default %>
<%= f.button :submit, class:"btn-outline-secondary" %>
<% end %>

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

NoMethodError in Pages#profile

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.

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