Setting a basic entity while creating a new user in Devise - ruby-on-rails

Im using Devise to create my users in an App with Ruby on Rails.
I have a User model that has a Plan (hobby,premium, etc...)
When creating a new user, I want to add the basic plan to this new user (for business rules needs, I cant leave it blank).
The question is, how can I add this plan when creating this new user?
Here is my controller:
class RegistrationsController < Devise::RegistrationsController
clear_respond_to
respond_to :json
def save_user_type
session[:user_type] = params[:user_type]
end
private
def sign_up_params
params.require(:user).permit(:first_name, :last_name, :email, :password, :password_confirmation, :type, :provider )
end
end
In which method should I add something like this?
#user.plan = Plan.first
#user.save

class User < ActiveRecord::Base
has_one :plan
after_create :build_default_plan
private
def build_default_plan
plan.create(#paln_params)
#.. so on
end
end

Added this line to the user model
after_create do |user|
user.plan = Plan.first
user.save
end

Related

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.

Rails - How to add additional column value into Devise registration table?

User Model
class User < ApplicationRecord
belongs_to :tenant, dependent: :destroy
end
Tenant Model
class Tenant < ApplicationRecord
has_many :users
end
Controller
Workaround 1 (not working)
def create
super
#tenant = Tenant.new
#user = #tenant.build_user(params)
#tenant.save
end
Workaround 2 (not working)
def create
#tenant = Tenant.new
#user = User.build(params)
#tenant.save
super
end
Is there any possibilities to pass a parameter to devise super class?
Since Devise super method has its own functionality on user registration/password hashing/, I can not completely override the function.
I know the way I am saving is wrong, please suggest me the better approach.
Actual source code:
(with Controller, Model, Migrations and Routes files are added.)
https://repl.it/#aravin/HarmlessRepentantHarddrive
You can override the sign_up_params in your controller:
class RegistrationsController < Devise::RegistrationsController
private
def sign_up_params
params.require(:user).permit(:first_name, :last_name, :email, :password, :password_confirmation...).merge({tenant_id: Tenant.create!.id})
end
end
I wanted to provide a little more verbose answer than was provided by AbM.
You can generate the registrations_controller.rb file with the following command:
rails g devise:controllers users -c=registrations
Once you do this, you will want to modify it such that you have something like:
class RegistrationsController < Devise::RegistrationsController
private
def sign_up_params
params.require(:user).permit(:first_name, :last_name, :email, :password, :password_confirmation)
end
def account_update_params
params.require(:user).permit(:first_name, :last_name, :email, :password, :password_confirmation, :current_password)
end
end
Then in your routes.rb file you will want to change the devise_for line to tell devise that you want to override your registrations controller like:
devise_for :users, controllers: { registrations: 'users/registrations' }
Of course, you will want to replace the :user/:users references to the name of your devise authentication model if you are using something other than the standard User throughout my example.
Here is a reference to this in the official docs on GitHub.

Can Access data of associated model in console but not in app

In my app i have two types of users: students and companies.
During the registration process, i manage with the wicked gem, the user is asked to fill in data depending on the accounttype (student or company) he chose.
For example, a student have to fill in his university, a company have to fill in the amount of employers.
When the registration process is finished, everything is fine, but somehow i cant access the data of the user which chose accounttype 2 - company.
The data is stored in two seperated associated models:
For students: accountinfo (ye i know its against convention and should have the name account_info)
For companies: accountinfos_company (ye i know, weird pluralization .. but it does the trick and i am not confused by it)
Everything works fine for the students. I am able to fill in data and the data is properly saved within a nested form. Same goes for the companies but unfortunately i am not able to output the data of accountinfos_company in my app. What i can do is for example:
#user.accountinfo.description
For student-accounts. But if I try:
#user.accountinfos_company.description
The output fails with an error
undefined method `accountinfos_company' for #<User::ActiveRecord_Relation:0x007fc1851e6e20>
SOMEHOW I am able to output the data of the particular user via console like: User.last.accountinfos_company.description. I copied every step I made for the first associated model, the data gets properly saved but I am not able to access it inside the app.
I thought it might be something missing, some definition or sth like this but as far as i can see, everything is fine.
users_controller.rb (User association stuff gets created in another controller)
class UsersController < ApplicationController
before_action :authenticate_user!
def index
redirect_to root_path
end
def create
#user = User.create( user_params )
end
def show
#user = User.find(params[:id])
if #user.not_complete?
redirect_to user_steps_path
else
render 'show'
end
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
# might be the right way?
#user.update(user_params)
if #user.update(user_params)
redirect_to User.find(params[:id]), notice: 'Profil bearbeitet.'
else
render 'edit'
end
end
private
def user_params
params.require(:user).permit(:first_name, :last_name, :accounttype,
:accountinfo, :accountinfos_company, :profile_image, :active, accountinfo_attributes:[:id], accountinfos_company_attributes:[:id])
end
end
user_steps_controller.rb
class UserStepsController < ApplicationController
include Wicked::Wizard
steps :welcome, :info
before_action :authenticate_user!
def show
#user = current_user
render_wizard
end
def create
#user = current_user
if #user.accounttype == 1
#accountinfo = #user.accountinfo.create(user_params)
elsif #user.accounttype == 2
#accountinfos_company = #user.accountinfos_company.create(user_params)
end
end
def update
#user = current_user
#user.update(user_params)
render_wizard #user
end
private
def user_params
params.require(:user).permit(:first_name, :last_name, :accountinfo, :accountinfos_company,:profile_image, :active, accountinfo_attributes:[:id, :city, :competence, :description, :university], accountinfos_company_attributes:[:id, :city, :company_type, :description, :company_name, :employer_amount])
end
private
def redirect_to_finish_wizard_path
redirect_to root_path, notice: "Danke für deine Zeit!"
end
end
user.rb
class User < ApplicationRecord
has_one :accountinfo
has_one :accountinfos_company
has_many :offer_posts
has_many :search_posts
accepts_nested_attributes_for :accountinfo, update_only: true
accepts_nested_attributes_for :accountinfos_company, update_only: true
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
validates_presence_of :first_name, :last_name, :email, :accounttype
end
accountinfo.rb and accountinfos_company.rb
belongs_to :user
I dont know if i have something missed but yeah.
If you could help me i would be really glad.

One Model and two controller validation Ruby on Rails?

One thing that confuses me the most is when doing validation in one model with two controllers. I have a login system which register and logs users in. There both use the same model but both does not use the same amount of HTML widgets. One controller contains password, retype password, user name, first & second name and so on. The second controller uses only the user name and password fields. How would you do validation in the same model for this situation?
Thank you
here is the controller that register new users:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
session[:user_id] = #user.id
redirect_to '/cool'
else
#user = Newuser.new
#user.valid?
#user.errors.messages
render 'new'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password, :salt, :firstname, :secondname, :address, :postcode)
end
end
second controller:
class LoginsController < ApplicationController
before_filter :authorize
def index
#rentals = Rental.where(user_id: current_user.id).limit(5)
#buys = Buy.where(user_id: current_user.id).limit(5)
#users = User.where(id: current_user.id)
#buyGames = BuyGame.where(user_id: current_user.id).limit(5)
end
def destroy
#user = User.find(params[:id])
#user.destroy
redirect_to '/logout'
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update(account_params)
redirect_to '/cool'
else
render 'edit'
end
end
private
def account_params
params.require(:user).permit(:name, :email, :password, :salt, :firstname, :secondname, :address, :postcode)
end
end
Here is my model:
class User < ApplicationRecord
has_secure_password
end
One way to go is to remove validations from the model and put them in form objects. For this case, you'll have two form objects, each with its own set of validations. And you use the appropriate one in respective controllers. Something along these lines:
# logins_controller
def update
login_form = FormObjects::LoginForm.new(login_params)
if login_form.valid?
redirect_to '/cool'
else
render 'edit'
end
end
# users controller
def create
signup_form = FormObjects::SignupForm.new(user_params)
if signup_form.save
redirect_to '/cool'
else
render 'new'
end
end
# signup_form
module FormObjects
class SignupForm
include ::ActiveMode::Model
validate_presense_of :email, :password, :password_confirmation, :address, :whatever_else
def save
# create user here
end
end
end
# login_form
module FormObjects
class LoginForm
include ::ActiveMode::Model
validate_presense_of :email, :password
end
end
You can simply specify validations on actions, that is:
validates :first_name, presence: true, on: :create # which won't validate presence of first name on update or any other action
I believe the trick you are looking for is to define validation actions on create/update of the model. Something roughly along these lines:
class User < ActiveRecord::Base
# These are example validations only; replace with your actual rules.
validates :password, confirmation: true
validates_presence_of :username
validates :first_name, presence: true, format: {with: /.../}, on: create
validates :last_name, presence: true, format: {with: /.../}, on: create
end
...However, I am unclear why you would want to do this in your specific example. It would be advisable to always run all validation checks on fields like first_name, to help maintain data integrity.

Rails - validation inside decorator

I'm struggling with some kind of issue. I have a rails model (mongoid).
class User
include Mongoid::Document
include ActiveModel::SecurePassword
validate :password_presence,
:password_confirmation_match,
:email_presence,
field :email
field :password_digest
def password_presence
end
def email_presence
end
def password_confirmation_match
end
end
My goal is to call validations depends on which decorator I will use. Let's say I've got two decorators:
class PasswordDecorator < Draper::Decorator
def initialize(user)
#user = user
end
end
def RegistraionDecorator < Draper::Decorator
def initialize(user)
#user = user
end
end
So now when I create/save/update my user object inside RegistraionDecorator I would like to perform all validation methods.
RegistraionDecorator.new(User.new(attrbiutes))
But when I will do it inside PasswordDecorator I want to call for example only password_presence method.
PasswordDecorator.new(User.first)
When I move validations to decorator it won't work cuz its different class than my model.
How can I achieve that?
Try to use a Form Object pattern instead.
Here is an example (from a real project) of how it could be done with reform.
class PromocodesController < ApplicationController
def new
#form = PromocodeForm.new(Promocode.new)
end
def create
#form = PromocodeForm.new(Promocode.new)
if #form.validate(promo_params)
Promocode.create!(promo_params)
redirect_to promocodes_path
else
render :edit
end
end
private
def promo_params
params.require(:promocode).
permit(:token, :promo_type, :expires_at, :usage_limit, :reusable)
end
end
class PromocodeForm < Reform::Form
model :promocode
property :token
property :promo_type
property :expires_at
property :usage_limit
property :reusable
validates_presence_of :token, :promo_type, :expires_at, :usage_limit, :reusable
validates_uniqueness_of :token
validates :usage_limit, numericality: { greater_or_equal_to: -1 }
validates :promo_type, inclusion: { in: Promocode::TYPES }
end
Bonus: The model does not trigger validations and much easy to use in tests.

Resources