rails, friendly_id, devise username not showing slug - ruby-on-rails

i feel like i am missing something obvious, but hopefully someone can help me out quickly.
i am trying to create a friendly_id slug for my User Model (using Devise). Here is my model:
class User < ActiveRecord::Base
extend FriendlyId
friendly_id :username, use: [:slugged, :finders]
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
validates :username, :uniqueness => {:case_sensitive => false},
:format => { with: /\A[a-zA-Z0-9]+\Z/ }
end
In my UserController:
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
end
end
When I load up the show method in my browser, it still has localhost:3000/user/3 - it does not show the slug. I know I have everything working because I have the same code in my Post Model and it works perfectly. Is there something I am missing with the User Validation / Devise that is causing it not to work?
Any help appreciated, thanks!

The reason is slug not saved. I think you are trying to find a legacy record which is saved before you initialized the friendly_id. But although you can generate slug and generate friendly-id for your record. Here is the process.
def should_generate_new_friendly_id?
new_record? || slug.nil? || slug.blank? # you can add more condition here
end
after adding this method, just trying save all the record again. Friendly will call the method and will update the slug if condition matched you defined inside that block.
User.find_each(&:save!)
In your view file write the url like:
<%= link_to "USER", current_user %>
or
<%= link_to "USER", user_path(current_user.to_param) %>
Please also make sure you don't have the username blank. If there is any chance that username can be blank you can use a dynamic method where you can set the column conditionally to use for generating slug. I think that should be all!
Thank you!

Related

Having trouble with an age check moethod in user model after over-riding devise registration controller

I'm trying to set up an age restriction when a user registers so that if they are too young they cannot register on an app i'm building.
I had to over-ride devise to allow me to pass through other values to the user (like :birth_date). However I also want to check the age of the user so that if they are too young, they cannot use the app.
What I have right here, in a rudimentary way it works, but it is not quite what I would like.
<%= f.input :birth_date, required: true, start_year:1999 %>
In my user model I created some methods that address the problem, however ultimately my problem is that none of this code is getting hit during the registration process, and that is what I need some help with. If someone could take a look at point me in the right direction, I would greatly appreciate it!
class User < ApplicationRecord
validate :age_restriction
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :cafes
def age_restriction
if (birth_date.to_date + 18.years) < Date.today # assuming dob format is mm/dd/yy
errors.add :birth_date, 'must be older than 18'
end
end
end
The controller I used to over-ride devise I called registration_controller and it is like so
class RegistrationsController < Devise::RegistrationsController
before_action :configure_sign_up_params, only: :create
before_action :configure_account_update_params, only: :update
protected
def configure_sign_up_params
devise_parameter_sanitizer.permit(:sign_up, keys: [:birth_date])
end
def configure_account_update_params
devise_parameter_sanitizer.permit(:account_update, keys: [:bith_date])
end
end
My initial controller was my user_controller. Initially I was hoping this would solve my issue, but after some more work realized I needed to over-ride devise (hence the other registrations_controller). I'll admit this may be what is causing me my issue, not sure though.
class UsersController < ActiveRecord::Base
def show
#user = User.find(params[:id])
end
def create
#user = current_user.build(user_params)
#user.save
end
private
def user_params
params.require(:user).permit(:birth_date)
end
end
Use validations.
There is a gem which adds some useful date validators:
https://github.com/adzap/validates_timeliness/
validates_date :date_of_birth, :before => lambda { 18.years.ago },
:before_message => "must be at least 18 years old"
You can use model validations to prevent a user instance from being created if the user does not meet the age restriction you have set:
User.rb
validate :age_restriction
def age_restriction
if (birth_date.to_date + 18.years) < Date.today # assuming dob format is mm/dd/yy
errors.add :birth_date, 'must be older than 18'
end
end

How to assign pre-existing roles to users upon sign up in Rails 4?

First of all, I'm new in Rails, so I apologize if this is long. I'm creating a jobs-board like website where it will have two types of users: companies that will post the jobs and job-hunters who will be able to apply for them. Since it's a one-to-many relationship, I tried to follow instructions from Railscasts' Embedded Association: http://railscasts.com/episodes/189-embedded-association where I did:
bash:
rails generate migration add_role_to_users role:string
rake db:migrate
models/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
ROLES = %w[admin company user]
def role_symbols
[role.to_sym]
end
end
users/new.html.erb:
<p>
<%= f.label :role %><br />
<%= f.collection_select :role, User::ROLES, :to_s, :humanize %>
</p>
For some reason f.collection_select is not adding the role to the role's column in the User table. When I go to the Rails console, the newly created user is there but role is "nil". I tried to use Rolify gem but it didn't give much instructions in how to add pre stablished roles using a form. My other thought is, if I create a Roles model and add the roles there manually wouldn't all the record(roles) be deleted when I upload in production? So, my question is:
Am I doing something wrong with f.collection_select? or is there a better way to accomplish this (without creating a table)? or just a better way?
I found the answer. Yes, I needed to permit the :role in the controller, but since I'm using devise, things are a bit different which is what I didn't know. So here it is:
controllers/application_controller.rb
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
before_action :configure_devise_permitted_parameters, if: :devise_controller?
protected
def configure_devise_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:role])
end
end

Rails - Devise - Add profile information to separate table

I am using Devise to build a registration/authentication system into my application.
Having looked at quite a few resources for adding information to the devise model (e.g. username, biography, avatar URL, et cetera..) [resources include Jaco Pretorius' website, this (badly formed) SO question, and this SO question.
That's all fine and well -- it works. But my problem is that it's saving to the User model, which, according to database normalizations (also referencing this SO question), it should in fact be saving to a sub-model of User which is connected via has_one and belongs_to.
Thus far, I have created a User model via Devise. I have also created a UserProfile model via the rails generate script.
user.rb (for reference)
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable, :confirmable, :recoverable, :rememberable, :trackable, :validatable
has_one :user_profile, dependent: :destroy
end
user_profile.rb
class UserProfile < ActiveRecord::Base
belongs_to :user
end
timestamp_create_user_profiles.rb
class CreateUserProfiles < ActiveRecord::Migration
def change
create_table :user_profiles do |t|
t.string :username, null: false
t.string :biography, default: ""
t.references :user, index: true, foreign_key: true
t.timestamps null: false
end
add_index :user_profiles, [:user_id, :username]
end
end
My question, now, is, how does one collect the information for both of these models and ensure, via the devise registration form, that it all ends up in the right places?
I've seen resources about creating state machines (AASM, and the answer to this SO question. I've also seen information about creating a wizard with WICKED, and an article on the same topic.
These all seem too complicated for my use-case. Is there some way to simply separate the inputs with devise and make sure the end up in the right place?
I think, instead of simply commenting on an answer that led me to the final answer, I'll archive the answer here in case someone in the future is trying to also find this answer:
I will be assuming that you have some sort of setup as I do above.
First step is you need to modify your User controller to accept_nested_attributes_for the profile reference as well as add a utility method to the model so when requested in code, the application can either retrieve the built profile model or build one.
The user model ends up looking like so:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable, :confirmable, :recoverable, :rememberable, :trackable, :validatable
has_one :user_profile, dependent: :destroy
accepts_nested_attributes_for :user_profile
def user_profile
super || build_user_profile
end
end
Secondly, you will need to modify your sign up/account_update form to be able to pass the attributes for this secondary model into the controller and eventually to be able to build the profile for the parent model.
You can do this by using f.fields_for.
Add something like this to your form:
<%= f.fields_for :user_profile do |user_profile_form| %>
<%= user_profile_form.text_field :attribute %>
<% end %>
An example of this in my specific case is:
<%= f.fields_for :user_profile do |user_profile_form| %>
<div class="form-group">
<%= user_profile_form.text_field :username, class: "form-control", placeholder: "Username" %>
</div>
<% end %>
Finally, you will need to tell Devise that it should accept this new hash of arguments and pass it to the model.
If you have created your own RegistrationsController and extended Devise's, it should look similar to this:
class RegistrationsController < Devise::RegistrationsController
private
def sign_up_params
params.require(:user).permit(:email, :password, user_profile_attributes: :username)
end
end
(Of course, make the proper changes for your specific use-case.)
If you have simply added the Devise sanitization methods to your application controller, it should look similar to this:
class ApplicationController < ActionController::Base
before_filter :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) {|u|
u.permit(:email, :password, user_profile_attributes: :username)}
end
end
(Again, make the proper changes for your specific use-case.)
A small note on user_profile_attributes: :username:
Note this is a hash, of course. If you have more than one attribute you are passing in, say, as an account_update (hint hint), you will need to pass them like so user_profile_attributes: [:attribute_1, :attribute_2, :attribute_3].
Please check out the RailsCasts.com web-site.
There are a couple of interesting railscasts about nested model forms:
http://railscasts.com/episodes/196-nested-model-form-part-1
http://railscasts.com/episodes/197-nested-model-form-part-2
http://railscasts.com/episodes/196-nested-model-form-revised
Also check out accepts_nested_attributes_for
Or check out this question:
Profile model for Devise users?
Also note that for Devise 4.2 the '.for' method for the devise_parameter_sanitizer is deprecated in favor of '.permit'
From the documentation:
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_in) do |user_params|
user_params.permit(:username, :email)
end
end

Couldn't find User without an ID - Ruby 2.1.4 / Rails 4.1.6

I am facing an error while trying to link the :username in my User table and my Room table. I made a custom auth with devise and added :username.
I would like the username to be the link between the User table from devise and my Room table.
I am trying to build this app to recreate a kind of airbnb but mainly as an exercise as I started programming in ruby few months ago.
I get the error:
ActiveRecord::RecordNotFound in RoomsController#new
Couldn't find User without an ID
line #19 #room.username = User.find(params[:username])
Thank you very much for your help. I am stuck in here for hours now :-(
rooms_controller
def new
#room = Room.new
#room.username = User.find(params[:username]) #error seems to come from here
end
routes.rb
Rails.application.routes.draw do
devise_for :users
get "home/info"
root :to => "home#info"
resources :rooms
resources :users do
resources :rooms
end
room.rb
class Room < ActiveRecord::Base
mount_uploader :photo, PictureUploader
belongs_to :user
validates_presence_of :username, :location, :description, :capacity, :price_day, :photo
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_presence_of :username
validates_uniqueness_of :username
has_many :rooms
end
It should be something like this
def new
#room = Room.new
#room.username = User.find_by_username(params[:username])
end
If you just use .find() it expects the id of the user. Also see http://guides.rubyonrails.org/active_record_querying.html#dynamic-finders
There is a logic error in that you are saving #room.username to the User Object. You should be setting #room.user = User.find_by(...) OR #room.user_id = User.find_by(...).id
Active record will automagically make a method for you that will be #room.user.username if you want to get the username.
Now, here are the ways to find a user.
#room = Room.new #Then either of the following
#room.user = User.find_by(username: params[:username]) #Returns only one value
#room.user = User.find_by_username(params[:username]) #Returns only one value
#room.user = User.where(username: params[:username]) #Returns all users which meet condition.
As already mentioned in the answers, User.find() takes an ID. One thing to know is that all methods that start with .find for active record return a single record even if many meet the condition.
If you are having any problems still, then show us your Database Schema, and we can help further.
I found a solution. The Room is created with the the right :username and nothing is seen by the user.
In my Rooms controller, I kept
def new
#room = Room.new end
And I added this line in the "def create" part :
def create
#room = Room.new(room_params)
#room.username = current_user.username
Thank you for your help, this help me to understand better the relations in rails.
Have a nice day !

Ruby on Rails cannot show paperclip avatar within the app

I recently started to learn ruby on rails and I was able to successfully create an app and add users with devise, also add an avatar to the user with paperclip.
Now I'm having a problem on how to display the avatar throughout the app. The avatar only displays in http:localhost:3000/users/... (within the devise folders) for exemple, but if I try to create a new page, model, controller http://localhost:3000/profile/ for exemple, using the the tag
<%= image_tag #user.avatar.url(:thumb) %>
the page will not load and will return this error
undefined method 'avatar?' for nil:NilClass
It's probably something really simple, but I can't figure out how to fix it.
My model user.rb looks like this:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
validates_uniqueness_of :username
has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "100x100>" }
attr_accessible :name, :username, :email, :password, :password_confirmation, :remember_me, :avatar
attr_accessor :current_password
end
And my controller looks like this:
class UserController < ApplicationController
def profile
end
end
Thanks!
On routes.rb, you should have something like this:
match "profile" => "user#profile"
On your UserController, you should have something like this:
class UserController < ApplicationController
def profile
#user = current_user
end
end
And then you'll be able to use #user.avatar.url. Also, pay attention that if you don't have a logged in user, current_user will be nil, and then you will have the error you described, so please add something like this on your controller:
class UserController < ApplicationController
before_filter :authenticate_user!
def profile
#user = current_user
end
end
And then, when a unauthenticated account tries to access /profile, it'll be redirected to the login form.
I am still new to Rails so correct me if I am wrong but I think this might work for you.
class UserController < ApplicationController
def profile
#user = User.find(current_user.username)
end
end

Resources