How to display a user's posts only. Not others - ruby-on-rails

This app is for tutors. When a tutor finishes a class, they fill out a class_report. Then the index page should display only their class_reports.
Here's my problem: I've made two accounts, test1 and test2. test1 can see both test1 and test2's class_reports but test2 cannot see any posts, not even their own. It is even saying a post was created by test1 when test2 created it.
I'm pretty sure something is off in the index part or create part of the class_reports_controller but I am not entirely sure tbh. I think it could also be in the models as well.
class_reports_controller.rb
class ClassReportsController < ApplicationController
before_action :require_login
before_action :set_class_report, only: [:show, :edit, :update, :destroy]
# GET /class_reports
# GET /class_reports.json
def index
#class_report = current_user.class_reports
end
def create
#class_report = ClassReport.new(class_report_params)
#class_report.user = User.first
respond_to do |format|
if #class_report.save
format.html { redirect_to #class_report, notice: 'Class report was successfully created.' }
format.json { render :show, status: :created, location: #class_report }
else
format.html { render :new }
format.json { render json: #class_report.errors, status: :unprocessable_entity }
end
end
end
Models:
class_report.rb
class ClassReport < ApplicationRecord
belongs_to :user
end
user.rb
class User < ApplicationRecord
include Clearance::User
has_many :class_reports
before_save { self.email = email.downcase }
end

There is something wrong in your create action, on this line:
#class_report.user = User.first
and change it to:
#class_report.user = current_user
So the problem with first line is that all the report is created and link to first user (always), that's why other user doesn't have the reports. By changing to second line, we create a report and link to the logged-in user (current_user), therefore the report is created and assigned to whoever logged-in and create report.
Hope it help.

Related

Confused about default behavior of model initializer

Given the following...
Model:
class Equipment < ApplicationRecord
belongs_to :user
end
Controller:
class EquipmentController < ApplicationController
before_action :set_equipment, only: %i[ show edit update destroy ]
# GET /equipment/new
def new
#equipment = Equipment.new
end
# POST /equipment or /equipment.json
def create
#equipment = Equipment.new(equipment_params)
respond_to do |format|
if #equipment.save
format.html { redirect_to [#equipment.user, #equipment], notice: "Equipment was successfully created." }
format.json { render :show, status: :created, location: #equipment }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: #equipment.errors, status: :unprocessable_entity }
end
end
end
Test:
class EquipmentControllerTest < ActionDispatch::IntegrationTest
setup do
#equipment = equipment(:one)
sign_in_as(#equipment.user)
end
test "should get new" do
get new_user_equipment_url(#equipment.user.id)
assert_response :success
end
My test fails here, because equipment.user is null.
<%= form_with(model: [equipment.user, equipment]) do |form| %>
However, when I visit the corresponding URL in the development environment in the browser, it works. Debugging the development environment, the Equipment.new sets the user on the new equipment object. However, when running the test, the user is not set.
Making this change in the controller fixes the issue, but I don't understand the difference in behavior between the integration test and using the browser. Can someone explain?
#equipment = Equipment.new user: Current.user
I think this would be a lot less confusing if you wrote the test properly:
class EquipmentControllerTest < ActionDispatch::IntegrationTest
setup do
#user = users(:one)
sign_in_as(#user)
end
test "should get new" do
get new_user_equipment_url(#user)
assert_response :success
end
end
You should not use the equipment fixture when you're testing the new/create action - after all what your test is supposed to cover is the form for a completely new resource. The equipment fixture would be used when showing and updating an existing equipment.
Then I think you're falling for a classic trap and confusing the #equipment instance variable in your test and the instance variable in your controller with the same name.
Remember that your test and the controller are completely separate instances of different classes - sometimes even running in a completely different processes. Data doesn't just magically teleport itself from the test to the controller - it has to be passed through parameters or be loaded from the database.
#equipment in your controller is just a new Equipment instance that you have created there which doesn't have a user assigned. Thats why you need to assign the user.
Note that unless your users need to be able to create equipment for other users you don't actually need to have a nested route like this. You could just use GET /equipment/new and fetch the user from the session (or a token) instead of a parameter.
For example given a typical Devise setup you would do:
class User < ApplicationRecord
has_many :equipment
end
class EquipmentController < ApplicationController
before_action :set_equipment, only: %i[ show edit update destroy ]
before_action :authenticate_user! # ensure that a user is logged in.
# POST /equipment or /equipment.json
def create
#equipment = current_user.equipment.new(equipment_params)
respond_to do |format|
if #equipment.save
format.html { redirect_to #equipment, notice: "Equipment was successfully created." }
format.json { render :show, status: :created, location: #equipment }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: #equipment.errors, status: :unprocessable_entity }
end
end
end

add automatically value to column database in rails

notes use rails 5.2 and postgresql
I have Foluser model contains name,email,password,id_watch
I need when admin add new foluser
generate password
when admin create new foluser generate password like Secure Password Generator
get id_watch from admin model and put it to id_watch from Foluser model
Adminwhen register enterusername,email,password,id_watch`
in point 2 need take this id_watch and save it in user model .
admin only create foluser
`
class FolusersController < ApplicationController
before_action :set_foluser, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show, :new , :create, :edit]
# GET /folusers
# GET /folusers.json
def index
#folusers = current_master.foluser.all
#render json: #folusers
end
# GET /folusers/1
# GET /folusers/1.json
def show
##folusers = Foluser.where(master_id: #master.id).order("created_at DESC")
##foluser = Foluser.find(params[:id])
#render json: #foluser
end
# GET /folusers/new
def new
#foluser = current_master.foluser.build
end
# GET /folusers/1/edit
def edit
#render json: #foluser
end
# POST /folusers
# POST /folusers.json
def create
#foluser = current_master.foluser.build(foluser_params)
respond_to do |format|
if #foluser.save
format.html { redirect_to #foluser, notice: 'Foluser was successfully created.' }
format.json { render :show, status: :created, location: #foluser }
else
format.html { render :new }
format.json { render json: #foluser.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /folusers/1
# PATCH/PUT /folusers/1.json
def update
respond_to do |format|
if #foluser.update(foluser_params)
format.html { redirect_to #foluser, notice: 'Foluser was successfully updated.' }
format.json { render :show, status: :ok, location: #foluser }
else
format.html { render :edit }
format.json { render json: #foluser.errors, status: :unprocessable_entity }
end
end
end
# DELETE /folusers/1
# DELETE /folusers/1.json
def destroy
#foluser.destroy
respond_to do |format|
format.html { redirect_to folusers_url, notice: 'Foluser was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_foluser
#foluser = Foluser.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def foluser_params
params.require(:foluser).permit(:name, :email, :numberphone, :password)
end
end
foluser model
class Foluser < ApplicationRecord
belongs_to :admin, :optional => true
end
admin model
class Master < ApplicationRecord
has_many :foluser
end
Using your current code, setting the id_watch can be done here in the controller:
class FolusersController < ApplicationController
def create
#foluser = current_master.folusers.build(foluser_params)
#foluser.id_watch = current_master.id_watch # <-- !!!
respond_to do |format|
if #foluser.save
# ...
end
end
end
end
Despite our extended conversation above, I'm still unclear what you're trying to achieve with the "password generation".
(Should it be generated in the front-end, or the back-end? Should it be stored encrypted, or in plain text? If encrypted, do you need to be able to reverse this encryption? Is it a "permanent" password, or a "temporary" password? ...)
Therefore, the following code should be taken with a big pinch of salt - since I still don't really know what the desired/correct behaviour is.
In the FolusersController, you've defined the following method:
def foluser_params
params.require(:foluser).permit(:name, :email, :numberphone, :password)
end
However, if you want the password to be generated by the server then you shouldn't be allowing the admin to set the password through the controller. Therefore, remove this parameter:
def foluser_params
params.require(:foluser).permit(:name, :email, :numberphone)
end
And then somewhere - perhaps in the controller, or as a hook in the model - set this password to something random:
class FolusersController < ApplicationController
def create
#foluser = current_master.folusers.build(foluser_params)
#foluser.password = SecureRandom.hex(10 + rand(6))
# ...
end
end
# or
class Foluser < ApplicationRecord
after_initialize :default_password
def default_password
self.password ||= SecureRandom.hex(10 + rand(6))
end
end
I think you found the solution, use rails callbacks in your model to extract this kind of logic from the controller.
But I'd rather use after_initialize than before_save so that you won't set a default password before each save(so possibly even update action)
Then use things like SecureRandom (ActiveSupport concern) (already bundled by rails, no requires required)
after_initialize :defaultpassword
...
def default_password
self.password ||= SecureRandom.hex(10 + rand(6))
end
not the best way to do random I know but feel free to customize it.
secure_random output examples:
=>bf8d42b174d297f6460eef
=>efd28869171a1ec89c3438
=>3855c61fb6b90ed549d777

Redirect back with errors

I have a controller:
controller/streets_controller.rb
class StreetsController < ApplicationController
before_action :set_street, only: [:show, :edit, :update, :destroy]
def show
#house = #street.houses.build
end
.......
And a view for that show action. There I also display a form to create new street houses:
views/streets/show.html.erb
<h1>Street</h1>
<p>#street.name</p>
<%= render '/houses/form', house: #house %>
When somebody submits the houses/form the request goes to the houses_controller.rb
class HousesController < ApplicationController
def create
#house = House.new(house_params)
respond_to do |format|
if #house.save
format.html { redirect_back(fallback_location: streets_path) }
format.json { render :show, status: :created, location: #house }
else
format.html { redirect_back(fallback_location: streets_path) }
format.json { render json: #house.errors, status: :unprocessable_entity }
end
end
end
So far it works that when somebody inserts correct house_params it redirects back and creates the house correctly
But when somebody inserts wrong house_params it redirects_back but it doesn't show the house errors in the form, it shows the form for a new house #house = #street.houses.build
How can I redirect to the StreetsController with the #house object, so that the errors are shown and also the house form is filled?
Thanks
Generally when there is an error in the create action, you would render the "new" view instead of redirecting back. In your case, you could try redirecting to the streets show path, and passing the #house attributes as query params. Then in StreetsController#show, pass the house params as an argument to build.

How to use POST with ruby on rails?

I created a blog application using rails. I'm trying use that as an API for a mobile app. I'm trying to write JSON to POST the content into the application.
#model/post.rb
class Post < ActiveRecord::Base
has_many :comments
belongs_to :category
end
#model/comment.rb
class Comment < ActiveRecord::Base
belongs_to :post
end
#model/category.rb
has_many :posts
#controllers/comments_controller
class CommentsController < ApplicationController
before_action :set_comment, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
# GET /comments/new
def new
#comment = Comment.new
end
# GET /comments/1/edit
def edit
end
# POST /comments
# POST /comments.json
def create
#comment = Comment.new(comment_params)
respond_to do |format|
if #comment.save
format.html { redirect_to #comment, notice: 'comment was successfully created.' }
format.json { render :show, status: :created, location: #comment }
else
format.html { render :new }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_comment
#comment = Comment.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def comment_params
params.require(:comment).permit(:rating, :comment, :post_id, :user_id)
end
end
What will be the JSON for the POST request for posting a blog post and a comment?
How should I pass association through JSON?
Here is the route for that action.
POST /comments(.:format) comments#create
If you want to be able to create a post and comment in the same request you will need to use accepts_nested_attributes_for.
class Post
has_many :comments
accepts_nested_attributes_for :comments
end
The params to create a post and a comment would look something like this:
{
"post" : {
"title": "Hello World",
"comments_attributes": [
{
"rating": 3,
"comment": "Pretty mediocre if you ask me."
}
]
}
}
Rails does a pretty good job at abstracting out the difference between parameters sent as application/x-www-form-urlencoded, multipart or JSON. Dealing with JSON input is just like dealing with regular form input.
Then in your PostsController you would need to whitelist the nested attributes:
class PostsController < ApplicationController
# POST /posts
def create
#post = Post.new(post_params)
respond_to do |format|
if #comment.save
format.html { redirect_to #post, notice: 'comment was successfully created.' }
format.json { render :show, status: :created, location: #post }
else
format.html { render :new }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
private
def post_params
params.require(:post).allow(:title, comments_attributes: [:rating, :comment])
end
end
If you want your blog app to provide an API for external apps to use, you'll have to create an API layer within your application. This exposes endpoints that other apps can use to retrieve/post information to/from your site.
One of the first steps would be to scope out an api layer in your routes.rb:
YourBloggingApp::Application.routes.draw do
namespace '/api', defaults: {format: 'json'} do
scope '/v1' do
scope '/blog_posts' do
get '/' => 'api_blog_posts#index'
post '/' => 'api_blog_posts#create'
...
end
end
end
end
Then you can build the appropriate actions within your api controllers:
class Api::BlogPostsController < ApplicationController
def index
#blog_posts = BlogPost.all
end
def create
#blog_post = BlogPost.create(title: params[:title], description: params[:description])
end
...
end
And when a user from your mobile app fills out a form, the form can send a POST request to https://yourappdomain.com/api/v1/blog_posts/, the blog_posts_controller#create action is triggered, which allows you to then pass in the given params sent through the request to create blog posts, comments, or whatever you set up in your project.
This is a very generic example, and there are a lot of specific details to cover on this topic, but this AirPair article provides a decent starting point for building a RESTful API.
{"comment":{"comment":"blah blah blah", "post_id":1}}
I have to pass the post_id in that POST request.

Devise Custom Controller not creating record in nested form

This was working, but I've made a change somewhere and now cannot get it to work. I have a nested form to create a company along with the first contact that uses Devise. The company record is being created but the contact (devise model) isn't being created. If I create the company and then add a contact to the company, the contact is created so I'm pretty sure the controller works. Need another set of eyes to spot my mistake.
Error:
RuntimeError in CompaniesController#create
Could not find a valid mapping for nil
CompaniesController (relevant methods):
class CompaniesController < ApplicationController
before_action :set_company, only: [:show, :edit, :update, :destroy]
before_action :authenticate_contact!, only: [:index, :show, :edit, :update, :destroy]
# GET /companies/new
def new
#company = Company.new
#company.contacts.build
end
# POST /companies
# POST /companies.json
def create
#company = Company.new(company_params)
respond_to do |format|
if #company.save
sign_in(#company.contacts.first)
format.html { redirect_to #company, notice: 'Company was successfully created.' }
format.json { render action: 'show', status: :created, location: #company }
else
format.html { render action: 'new' }
format.json { render json: #company.errors, status: :unprocessable_entity }
end
end
end
# Never trust parameters from the scary internet, only allow the white list through.
def company_params
params.require(:company).permit(:name, :legal_entity, :KVK_number, :VAT_number, :KVKdoc, contacts_attributes:[:first_name, :last_name, :address, :phone, :email, :postcode, :password] )
end
end
Contacts Registration Controller:
class Contacts::RegistrationsController < Devise::RegistrationsController
# This is a custom controller that inherits from the standard Devise Registration Controller.
# This controller is used as the user is added to the existing account not it's own account.
# Skips the standard Devise authentication filter to allow for an active user session to create a user recorod.
skip_before_filter :require_no_authentication
# Custom method to ensure that the user on the page has an active session to ensure that the account is accessable.
before_filter :has_current_contact
# Create method is just a call to the standard Devise create method
def create
super
end
protected
# Stop the user sessisons from being switched to the new user
def sign_up(resource_name, resource)
true
end
# Test to see that the person on the page has an active session
def has_current_contact
if current_contact.nil?
redirect_to root_path, alert: 'You need to be logged in to do this!'
end
end
# Sets the permited params for the registration. Inherits the standard devise parameters defined in the
# application controller and merges the account id of the current user.
def sign_up_params
devise_parameter_sanitizer.sanitize(:sign_up).merge(company_id: current_contact.company_id)
end
end
I can see that the code throws the error on the attempt to find a contact for the company and sign in since it can't find a contact given that none has been created, but can't figure out why it's not being created.
Help?

Resources