In Rails, how do i keep before_create and using validates - ruby-on-rails

My code like this:
validates :user, presence: true
before_create :set_user
def set_user
self.user = current_user
end
I know before_create doesn't work but I need before action get user data.

You can use the before_action helper only on the actions you want.
before_action :set_user, only: :create
You can use an array to do this on more actions or use the except: :some_stage to exclude stages.

You can make it before_validation but to be honest I think that's the wrong solution too.
I would create whatever it is through the association.
class User
has_one :foo
has_many :bars
end
In the controller either
For has_one:
def new
#foo = current_user.build_foo
end
For has_many:
def new
#bar = current_user.bars.new
end

Related

How to limit the CRUD content of Rails' current_user

I'm making a task management system and building a user registration and login system without relying on the devise gem. Currently I am facing a challenge, I want each user (called current_user when logging in) can only see the tasks they created (listed on the homepage/index), including that they can only edit or delete their own tasks. To this end, I added a before action def find_user_task to the controller, but it is completely invalid. Please tell me, what am I missing in principle?
This is part of TasksController
before_action :find_task, only: [:show, :edit, :update, :destroy]
before_action :check_login!, except: [:index, :show]
before_action :find_user_task, only: [:edit, :update, :destroy, :publish]
private
def find_task
#task = Task.find(params[:id])
end
def task_params
p = params.require(:task).permit(:name, :due_date, :note, :priority)
p[:status] = params[:task][:status].to_i
return p
end
def find_user_task
#task = Task.find(params[:id])
#task = Task.find_by(id: params[:id], user_id: current_user.id)
# #task = current_user.tasks.find(params[:id])
end
end
This is part of Model Task
class Task < ApplicationRecord
validates :name, :note, :due_date, :status, :priority, presence: true
has_many :category_tasks
has_many :categories, through: :category_tasks
belongs_to :user, foreign_key: true, optional: true
This is part of Model User
require 'digest'
class User < ApplicationRecord
validates :email, presence: true, uniqueness: true
validates :password, confirmation: true, length: { minimum: 4 }, presence: true
validates :role, presence: true, uniqueness: true
has_many :tasks, dependent: :destroy
hope I can help you here...
first of all, here on this piece of code:
#task = Task.find(params[:id])
#task = Task.find_by(id: params[:id], user_id: current_user.id)
Your second line overrides the first, so you can remove the first one. About your problem, I would suggest you to do that based on your models and relationships... it's easier (and better) than to handle it in controller. The thing is you wanna make sure that a task belongs to a user (and which can have N tasks). Before you create a task, you make sure to set the user_id for that task as the current_user.id. It would be somthing like that:
class User < ApplicationRecord
has_many :tasks
end
and for your task model:
class Task < ApplicationRecord
belongs_to :user
end
Then, on your create action on your tasks controller (or wherever you do that), be sure to set the user_id for your current_user:
#task.user_id = current_user.id
If you still didn't created the foreign_key (user_id) for a task, just make a migration, something like this:
rails g migration AddUserIdToTask user_id:integer
rails db:migrate
Then, if you do it correctly, you can get the tasks for a specific user just acessing them by the relationship: user.tasks (or, in your case):
#tasks = current_user.tasks
For debugging and testing that, rails console is your friend... create a task for a user, then check on console if the user_id is set correctly. Then try to load that user.tasks and see if it brings them as you expected. Go checking it step by step.

Creating homes using nested routes

First this is all of my code
#models/user.rb
class User < ApplicationRecord
has_many :trips
has_many :homes, through: :trips
has_secure_password
accepts_nested_attributes_for :trips
accepts_nested_attributes_for :homes
validates :name, presence: true
validates :email, presence: true
validates :email, uniqueness: true
validates :password, presence: true
validates :password, confirmation: { case_sensitive: true }
end
#home.rb
class Home < ApplicationRecord
has_many :trips
has_many :users, through: :trips
validates :address, presence: true
end
class HomesController < ApplicationController
def show
#home = Home.find(params[:id])
end
def new
if params[:user_id]
#user = User.find_by(id: params[:user_id])
#home = #user.homes.build
end
end
def create
#user = User.find_by(id: params[:user_id])
binding.pry
#home = Home.new
end
private
def home_params
params.require(:home).permit(:address, :user_id)
end
end
I am trying to do something like this so that the home created is associated with the user that is creating it.
def create
#user = User.find_by(id: params[:user_id])
#home = Home.new(home_params)
if #home.save
#user.homes << #home
else
render :new
end
end
The problem is that the :user_id is not being passed into the params. So the #user comes out as nil. I can't find the reason why. Does this example make sense? Am I trying to set the associations correctly? Help or any insight would really be appreciated. Thanks in advance.
The way you would typically create resources as the current user is with an authentication such as Devise - not by nesting the resource. Instead you get the current user in the controller through the authentication system and build the resource off it:
resources :homes
class HomesController < ApplicationController
...
# GET /homes/new
def new
#home = current_user.homes.new
end
# POST /homes
def create
#home = current_user.homes.new(home_parameters)
if #home.save
redirect_to #home
else
render :new
end
end
...
end
This sets the user_id on the model (the Trip join model in this case) from the session or something like an access token when dealing with API's.
The reason you don't want to nest the resource when you're creating them as a specific user is that its trivial to pass another users id to create resources as another user. A session cookie is encrypted and thus much harder to tamper with and the same goes for authentication tokens.
by using if params[:user_id] and User.find_by(id: params[:user_id]) you are really just giving yourself potential nil errors and shooting yourself in the foot. If an action requires a user to be logged use a before_action callback to ensure they are authenticated and raise an error and bail (redirect the user to the sign in). Thats how authentication gems like Devise, Knock and Sorcery handle it.

Undefined Method when .build new Object

I'm new to Ruby on Rails and currently trying to make a little test website for me.I've got an issue in my code that states an "undefined method `service_providers' for #"
The line of code which produces the error is the following:
def new
#service_provider = current_user.service_providers.build(serviceprovider_params)
end
My Database Model is Table "User" has_one "ServiceProvider" has_many "Services".
I use the rubygem "devise" for the user-model.
I've tried to transfer the idea of the micropost of the "Ruby on Rails Tutorial" (https://www.railstutorial.org/book/user_microposts) in my example app. In Listing 13.36 there's also this code because with this ruby knows the reference between the current_user and the micrrpost.
I don't have an idea why it isn't working with my code:
Model
class ServiceProvider < ApplicationRecord
belongs_to :user
has_many :service
validates :name, presence: true
validates :street, presence: true
validates :plz, presence: true
validates :location, presence: true
validates :user_id, presence: true
end
Controller
class ServiceProvidersController < ApplicationController
before_action :set_serviceprovider, only: [:show, :edit, :update]
before_action :authenticate_user!
def index
end
def show
end
def new
#service_provider = current_user.service_providers.build(serviceprovider_params)
end
def create
#service_provider = current_user.service_provider.build(serviceprovider_params)
if #service_provider.save
redirect_to #service_provider, notice: "Gespeichert"
else
render :new
end
end
def edit
end
def update
end
private
def set_serviceprovider
#service_provider = Service_Provider.find(params [:id])
end
def serviceprovider_params
params.require(:service_provider).permit(:name, :street, :plz, :location)
end
end
ServiceProvider-Helper
module ServiceProvidersHelper
def current_service_provider
#current_service_provider = service_provider.user_id.find(current_user.id)
end
end
If there's some coding missing which you need for your help, please ask. I'm a newbie in coding with ruby, but i think taht must be the relevant parts of the code which is involved.
Thanks for help.
If you have has_one association then you need to use build_ASSOCIATION_NAME so in this case, it will be build_service_provider
#service_provider = current_user.build_service_provider(serviceprovider_params)
Also, I think you need to take a look at this association
class ServiceProvider < ApplicationRecord
belongs_to :user
has_many :service
If you are building an association with has_many then the class name should be plural.
Change it to
has_many :services

Rails: callback in controller to change attribute from true to false in database

so I'm testing how to make a multiplayer Tic Tac Toe game, and for this I made a User model and a Game model, and by a has_many_through association they have various game_users.
Each game has an attribute: "seeking_players", that by default is true. When I create a new game_user, I check if they're exists a game with the seeking_players attribute set to true. If such a game exists, I make a new game_user for this game and I want to set this attribute to false.
But whatever I try, I can't seem to change this attribute. So, my question: what's wrong with this code: EDIT: this is the new working code after suggestions from #Малъ Скрылевъ
class GamesController < ApplicationController
before_action :logged_in_user
before_action :assign_game, only: [:new]
def new
#game.game_users.create(user: current_user)
update_seeking_players
redirect_to game_url(id: #game.id)
end
def game
#game = Game.find_by_id(params[:id])
end
private
def assign_game
#game = Game.find_by_seeking_players(true) || Game.create
end
def update_seeking_players
if #game.users.size == 2
#game.update_attributes(seeking_players: false)
end
end
end
PS: I also tried changing this "seeking players" attribute in the Game model (with a callback "after_add"), which is maybe a more appropriate place? But I really can't figure out how to do this...
UPDATE:
these are the Game & GameUser model
class Game < ActiveRecord::Base
has_many :game_users
has_many :users, :through => :game_users
end
class GameUser < ActiveRecord::Base
belongs_to :game
belongs_to :user
validates :game_id, presence: true
validates :user_id, presence: true
end
UPDATE 2 the migration for seeking_players
class AddSeekingPlayersWithIndexToGames < ActiveRecord::Migration
def change
add_column :games, :seeking_players, :boolean, :default => true
add_index :games, :seeking_players
end
end
I see some issues with the contoller.
It seems that gameRequest isn't real action name like new, so replace it with real one.
Try not using camel case (or similar) in names of variables and method, since it descreace understability of the code.
So I propose you to change the contoller as follows:
class GamesController < ApplicationController
before_action :logged_in_user
before_action :assign_game, only: [:new]
def new
#game.game_users.create(user: current_user)
update_seeking_players
redirect_to game_url(#game)
end
def game
end
private
def assign_game
#game = Game.find_by_seeking_players(true) || Game.create
end
def update_seeking_players
if #game.users.size == 2
#game.update(seeking_players: false)
end
end
end
so and if seeking_players is a real column, and it must be, so remove attr_accessor, since it block access to db column:
class Game < ActiveRecord::Base
has_many :game_users
has_many :users, :through => :game_users
end

load_and_authorize_resource with collection and custom model

I have controller with many custom actions:
class FooController < ApplicationController
def fizz
end
def buzz
end
...
end
And I want to load and authorize Bar collection (without repeating #bars = Bar.all) into these two methods.
Something like that:
load_and_authorize_resource :bar, :collection, :only => [:fizz, :buzz, ...]
But this simply doesn't work, #bars variable is nil in the all actions. Please, help me to understand what is going wrong and how can I make it work. Thanks!
The following worked for me (source):
load_and_authorize_resource :bar, :parent => false
You could use a before_filter in your controller:
before_filter :load_and_authorize_resource, :only => [:fizz, :buzz]
private
def load_and_authorize_resource
#bars = Bar.all
end

Resources