Rails Type Mismatch Issue - ruby-on-rails

I'm following this answer: https://stackoverflow.com/a/8753998/561634 and trying to make a user_post_id based on the user just like described in the answer.
The end goal is to have urls like website.com/username/posts/4 where 4 is the 4th post from that specific user, instead of like now where website.com/username/posts/4 would show the 4th post in the database.
I'm getting this error:
ActiveRecord::AssociationTypeMismatch in PostsController#create User(#2171866060) expected, got String(#2152189000)
my controller create method looks like this:
def create
#post = Post.new(post_params)
#post.user = current_user.id
respond_to do |format|
if #post.save
format.html { redirect_to post_path(username: current_user.username, id: #post.id), notice: 'Post was successfully created.' }
format.json { render action: 'show', status: :created, location: #post }
else
format.html { render action: 'new' }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
my model looks like this:
before_save :set_next_user_post_id
validates :user_post_id, :uniqueness => {:scope => :user}
def to_param
self.user_post_id.to_s
end
belongs_to :user
private
def set_next_user_post_id
self.user_post_id ||= get_new_user_post_id
end
def get_new_user_post_id
user = self.user
max = user.posts.maximum('user_post_id') || 0
max + 1
end
Thanks for all help!

This line:
#post.user = current_user.id
Should either be #post.user = current_user or #post.user_id = current_user.id

Related

rails redirect in controller for a loop

I have create method like this in rails controller:
def create
#sale = Sale.new(sale_params)
#sale.user_id = current_user.id
#sale.total = #sale.total_all
params[:sale][:items_attributes].values.each do |p|
#product = Product.find(p['product_id'])
if #product.quantity.to_i - p['quantity'].to_i <= 0
flash.now[:notice] = "Quantity is higher then stock"
end
end
respond_to do |format|
if #sale.save
format.html { redirect_to #sale, notice: 'Sale was successfully created.' }
format.json { render :show, status: :created, location: #sale }
else
format.html { render :new }
format.json { render json: #sale.errors, status: :unprocessable_entity }
end
end
end
if #product.quantity.to_i - p['quantity'].to_i <= 0
flash.now[:notice] = "Quantity is higher then stock"
end
this condition fails it means the controller redirected to create page itself otherwise it needs to redirect to index page. If I puts render :new in loop rails says error. How to do this?
You can move it to the model as Harsh suggested. You'd do it something like this. Then you can get rid of the loop.
class Sale < ActiveRecord::Base
has_many :items
# Also make sure you validate children
validates_associated :items
validate :items_in_stock
def items_in_stock
items.each do |i|
if i.quantity > i.product.quantity
i.errors.add(:quantity) = "is not currently available"
end
end
end
end

Allow admin to post as user in Rails app

I'm building a rails app where users can log on and see a table of their SAT test scores. Users "has_many" scores and Scores "belongs_to" users. Currently it is set up so that the user can post their own scores. What I want is for an admin to post the scores and the user will just see the table on their show page. The "admin" is just a boolean field in users that I set to true for the admins.
Here is the scores controller:
class ScoresController < ApplicationController
def index
#scores = Score.all
end
def show
#score = Score.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #score }
format.js
end
end
def new
#score = Score.new
end
def create
#score = current_user.scores.new(params[:score])
#user = current_user
respond_to do |format|
if #score.save
format.html { redirect_to #score.user, notice: 'Score was successfully created.' }
format.json { render json: #score, status: :created, location: #score }
else
format.html { render action: 'new' }
format.json { render json: #score.errors, status: :unprocessable_entity }
end
end
end
def update
#score = Score.find(params[:id])
respond_to do |format|
if #score.update(params[:score])
format.html { redirect_to #score.user, notice: 'Score was successfully updated.' }
format.json { render action: 'show', status: :ok, location: #score }
else
format.html { render action: 'edit' }
format.json { render json: #score.errors, status: :unprocessable_entity }
end
end
end
def edit
#score = Score.find(params[:id])
end
def destroy
#score = Score.find(params[:id])
if #score.present?
#score.destroy
end
redirect_to #score.user
end
end
I know I'd have to change the scores controller so that it didn't rely on current_user to create and edit scores. I'm just not sure how to implement that. Let me know if you need more info! Thanks.
First, you'll need to add a select tag in your view to select which user you want to post as:
- if current_user.is_admin?
= f.select :user_id, options_for_select(User.all.map{ |u| [u.username, u.id] })
- else
= f.hidden_field :user_id, value: current_user.id
Then, on the server-side, we will double-check that current_user is an admin to allow the creation of a Score for another User:
def create
#score = Score.new(params[:score])
if current_user.id != #score.user_id # Someone is trying to create a Score for someone else!
#score.errors.add(:user_id, "You shall not create Score for other users than you, you evil hacker!") unless current_user.is_admin?
end
respond_to do |format|
if #score.save
format.html { redirect_to #score.user, notice: 'Score was successfully created.' }
format.json { render json: #score, status: :created, location: #score }
else
format.html { render action: 'new' }
format.json { render json: #score.errors, status: :unprocessable_entity }
end
end
end
I omitted the part #user = current_user because usually current_user is a helper method than can be accessed directly in the views, so instead of using #user in the create view, use current_user instead.

undefined method `destroy' on Public Activity

User's can comment on a Screen and it's tracked by PublicActivity :
#comment.create_activity :create, owner: current_user, recipient: #comment.screen.user
and the comments are dependent: :destroy on the screen model.
But when i delete a screen, while the comments are deleted, the Record from PublicActivity for that comment still exists.
here's my Screens Controller:
def destroy
#activity = PublicActivity::Activity.find_by_trackable_id(params[:id])
#activity.destroy #<-- Heres the Problem
#screen.destroy
respond_to do |format|
format.html { redirect_to root_path }
format.json { head :no_content }
end
end
But upon deleting a Screen, i'm getting undefined methoddestroy' for nil:NilClass`.
I read on Railscast:
it was due to calling the create_activity method after the object had
been destroyed.
According to the gem maintainers, you simply have to assume the record
will be destroyed, and call create_activity before the destroy
What am i missing?
Informations below
screen.rb
belongs_to :user
has_many :comments, :dependent => :destroy
comment.rb
belongs_to :user
belongs_to :screen
screens_contoller.rb
def create
#screen = current_user.screens.build(screen_params)
respond_to do |format|
if #screen.save
format.html { redirect_to #screen, notice: 'You successfully uploaded your Screenshot.' }
format.json { render action: 'show', status: :created, location: #screen }
current_user.add_points(2, 'Points for Uploading a Screenshot')
else
format.html { render action: 'new' }
format.json { render json: #screen.errors, status: :unprocessable_entity }
end
end
end
def destroy
#activity = PublicActivity::Activity.find_by_trackable_id(params[:id])
#activity.destroy
#screen.destroy
respond_to do |format|
format.html { redirect_to root_path }
format.json { head :no_content }
current_user.substract_points(1, "Substraction for Deleting a Screenshot")
end
end
comments_controller.rb
def create
#screen = Screen.find(params[:screen_id])
#comment = current_user.comments.build(comment_params)
#comment.screen_id = #screen.id
respond_to do |format|
if #comment.save
# Create Record for Public Activity
#comment.create_activity :create, owner: current_user, recipient: #comment.screen.user
format.html { redirect_to #screen, notice: 'Comment was successfully created.' }
format.json { render action: 'show', status: :created, location: #comment }
else
format.html { render action: 'new' }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
def destroy
#comment.destroy
respond_to do |format|
#activity = PublicActivity::Activity.find_by_trackable_id(params[:id])
#activity.destroy
format.html { redirect_to :back }
format.json { head :no_content }
end
end
That's how my Screen Controller Destroy Action looks like right now:
def destroy
#screen = current_user.screens.find(params[:id])
#activity = PublicActivity::Activity.find_by_trackable_id(params[:id])
#activity.destroy
#screen.destroy
current_user.substract_points(1, "Substraction for Deleting a Screenshot")
respond_to do |format|
format.html { redirect_to root_path }
end
end
Again the same error:
This isn't tested, but this is what I think you should do.
First you can remove the references to activities in your screens_controller#destroy
Then in your comments_controller#destroy
#comment = current_user.comments.find(params[:id])
#activity = PublicActivity::Activity.find_by(trackable_id: (params[:id]), trackable_type: controller_path.classify)
#activity.destroy
#comment.destroy
Should be outside of your respond to block
Next in your comments model you should do something like this:
#comment.rb
private
before_destroy :find_and_destroy_comments
def find_and_destroy_comments
activity = PublicActivity::Activity.find_by(trackable_id: self.id, trackable_type: self.class.name)
if activity.present?
activity.destroy
end
end
calling the before_destroy method overrides the default ruby destroy method that is called during dependent: :destroy
Let me know if this works, but It should.

No route matches [PATCH] "/admin/roles.4"

I made model Role with rolify gem.
But controller made to namespace :admin :
class Admin::RolesController < ApplicationController
def index
#roles = Role.all
end
def new
#role = Role.new
end
def create
#role = Role.new(role_params)
respond_to do |format|
if #role.save
format.html { redirect_to admin_role_path(#role), notice: 'Роль создана.' }
format.json { render action: 'show', status: :created, location: #role }
else
format.html { render action: 'new' }
format.json { render json: #role.errors, status: :unprocessable_entity }
end
end
end
def show
#role = Role.find(params[:id])
end
def edit
#role = Role.find(params[:id])
end
def update
respond_to do |format|
if #role.update(role_params)
format.html { redirect_to admin_role_path(#role), notice: 'Роль обновлена.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #role.errors, status: :unprocessable_entity }
end
end
end
def destroy
#role = Role.find(params[:id])
#role.destroy
respond_to do |format|
format.html { redirect_to admin_roles_url }
format.json { head :no_content }
end
end
private
def set_role
#role = Role.find(params[:id])
end
def role_params
params.require(:role).permit(:name)
end
end
When I want to update Role, I open form, edit, click submit and get error:
Routing Error
No route matches [PATCH] "/admin/roles.4"
Please help me.
Based on the form code you pasted above, you'll see that url is pointing to the path used for creates but not updates.
You should be able to update your call to simple_form like so:
= simple_form_for [:admin, #role], :html => { :class => 'form-horizontal' } do |f|
You'll see that you can pass an array with symbolized namespace names and the object instance, and it'll build the URL correctly for both POSTs and PATCHes.
Problem is solved.
In _form I fix url.
= simple_form_for #role, url: admin_role_path(#role), :html => { :class => 'form-horizontal' } do |f|

How do I access an associated model of an associated model?

I have a Topic which has_many Posts. Each Post belongs to a User, and each User has_one Profile.
In my "show" page for a specific Topic, I try to display profile information of the user who created the post:
<% #topic.posts.each do |post| %>
<%= post.user.profile.first_name %>
<% end %>
I get the following error:
undefined method `profile' for nil:NilClass
Any idea why it does not allow me to access the profile? Please advise.
My Topic controller is as follows:
class TopicsController < ApplicationController
# GET /topics
# GET /topics.json
add_breadcrumb :index, :topics_path
def index
if params[:tag]
#topics = Topic.tagged_with(params[:tag])
else
#topics = Topic.all
end
#newtopic = Topic.new
respond_to do |format|
format.html # index.html.erb
format.json { render json: #topics }
end
end
# GET /topics/1
# GET /topics/1.json
def show
#topic = Topic.find(params[:id])
#posts = #topic.posts
#newpost = #topic.posts.build
add_breadcrumb #topic.name
respond_to do |format|
format.html # show.html.erb
format.json { render json: #topic }
end
end
# GET /topics/new
# GET /topics/new.json
def new
add_breadcrumb :new, :topics_path
#topic = Topic.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #topic }
end
end
# GET /topics/1/edit
def edit
#topic = Topic.find(params[:id])
end
# POST /topics
# POST /topics.json
def create
#topic = Topic.new(params[:topic])
#topic.user_id = current_user.id
#topic.last_poster_id = current_user.id
#topic.last_post_at = Time.now
respond_to do |format|
if #topic.save
format.html { redirect_to #topic, notice: 'Topic was successfully created.' }
format.json { render json: #topic, status: :created, location: #topic }
else
format.html { render action: "new" }
format.json { render json: #topic.errors, status: :unprocessable_entity }
end
end
end
# PUT /topics/1
# PUT /topics/1.json
def update
#topic = Topic.find(params[:id])
respond_to do |format|
if #topic.update_attributes(params[:topic])
format.html { redirect_to #topic, notice: 'Topic was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #topic.errors, status: :unprocessable_entity }
end
end
end
# DELETE /topics/1
# DELETE /topics/1.json
def destroy
#topic = Topic.find(params[:id])
#topic.destroy
respond_to do |format|
format.html { redirect_to topics_url }
format.json { head :no_content }
end
end
end
Your error is caused by this line in the show action #topic.posts.build and this line in the view #topic.posts.each. Since you are building a new post in the controller, #topic.posts includes that new record which most probably have the user as nil. So the solution to your problem is to use #posts instead of #topic.posts in your view.
Check your database. Its very likely that in your database there is a post which corresponds to no user. Since the user for that post is none, the profile becomes undefined for nil:NilClass which is user(null).
This happens mostly when you creates the post that belongs to user but then you deletes the user that belongs to that post from database.
The correct way is to impose a constraint in your user model that should be-
class Post
belongs_to :user, :dependent => :destroy
end
So if the user gets deleted, the corresponding posts of that user also get deleted.
Please note that it is not a good practice to directly delete records from database after imposing the relationship between them using tables.

Resources