On Rails 4, I am using Devise login. On first sign_in user gets directed to new_profile_path(#user) or profile_path(#profile = #user.profile_id). I am building the Profile ActiveRecord during User Registration. I'd like to pass the profile.id that is created during registration back to the User ActiveRecord before_save.
I am able to test with success "after_create", the build is working fine. Then "before_save" I'm able to get it to work by hard-coding a profile_id (see working code below). However, I cannot figure out after an entire day on the proper method to pass profile_id. I obviously do not want a hard-coded value. Here is my model code.
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable
has_one :profile, :dependent => :destroy, autosave: true
accepts_nested_attributes_for :profile
after_create :build_profile
before_save :set_profile_id
def build_profile
Profile.create(user: self) # Associations must be defined correctly for this syntax, avoids using ID's directly.
end
def set_profile_id
self.profile_id = 30 # Hard-coding tested and works. Profile built with user_id = user.id and user.profile_id = 30
end
end
class Profile < ActiveRecord::Base
belongs_to :user
#validates :username, presence: true, length: {maximum: 255}, uniqueness: { case_sensitive: false }, format: { with: /\A[a-zA-Z0-9]*\z/, message: "deve contar apenas letras e nĂºmeros" }
#validates :firstname, presence: true, length: {maximum: 255}
#validates :lastname, presence: true, length: {maximum: 255}
#validates :gender, presence: true, inclusion: %w(m f)
#validates :birthday, presence: true
has_many :offers, dependent: :delete_all
before_save :set_address
geocoded_by :address # can also be an IP address
def address
address = "#{street_address}, #{city}, #{state}, #{zipcode}"
end
after_validation :geocode # auto-fetch coordinates
reverse_geocoded_by :latitude, :longitude
after_validation :reverse_geocode # auto-fetch address
has_attached_file :logo_image, :styles => { :medium => "250x250>", :small => "175x175>", :thumb => "100x100>" }, :default_url => "/images/:style/missing.png"
# Validate content type
validates_attachment_content_type :logo_image, :content_type => /\Aimage/
# Validate filename
validates_attachment_file_name :logo_image, :matches => [/png\Z/, /jpe?g\Z/, /JPG?\Z/]
validates_attachment_size :logo_image, :less_than => 2.megabytes
#validates_attachment_presence :avatar
# Explicitly do not validate
#do_not_validate_attachment_file_type :avatar
# This may not be necessary anymore. With 'def address' above. Keeping for now as reference
def set_address
self.address = "#{street_address}, #{city}, #{state}, #{zipcode}"
end
end
class RegistrationsController < Devise::RegistrationsController
# GET /resource/sign_up
def new
build_resource({})
#validatable = devise_mapping.validatable?
if #validatable
#minimum_password_length = resource_class.password_length.min
end
respond_with self.resource
end
# POST /resource
def create
build_resource(sign_up_params)
resource_saved = resource.save
yield resource if block_given?
if resource_saved
if resource.active_for_authentication?
set_flash_message :notice, :signed_up if is_flashing_format?
sign_up(resource_name, resource)
respond_with resource, location: after_sign_up_path_for(resource)
else
set_flash_message :notice, :"signed_up_but_#{resource.inactive_message}" if is_flashing_format?
expire_data_after_sign_in!
respond_with resource, location: after_inactive_sign_up_path_for(resource)
end
else
clean_up_passwords resource
#validatable = devise_mapping.validatable?
if #validatable
#minimum_password_length = resource_class.password_length.min
end
respond_with resource
end
end
private
def sign_up_params
params.require(:user).permit(:email, :password, :password_confirmation, :user_id, :profile_id)
end
def account_update_params
params.require(:user).permit(:email, :password, :password_confirmation, :current_password, :user_id, :profile_id)
end
end
class ProfilesController < ApplicationController
before_action :set_profile, only: [:show, :edit, :update, :destroy]
def index
#profiles = Profile.all
#hash = Gmaps4rails.build_markers(#profiles) do |profile, marker|
marker.lat profile.latitude
marker.lng profile.longitude
marker.infowindow profile.address
end
end
def address
#profile = Profile.find(params[:address])
Geocode.search("#profile")
end
def show
#profiles = Profile.all
#offers = Offer.where(user_id: current_user.id)
#hash = Gmaps4rails.build_markers(#profiles) do |profile, marker|
marker.lat profile.latitude
marker.lng profile.longitude
marker.infowindow profile.address
end
end
def new
#user = current_user # added to solve for session re-direct on sign-in
#profile = Profile.new
end
def create
#profile = Profile.new(profile_params)
##user.profile_id = #profile.id # added to solve for session re-direct on sign-in
if #profile.save
flash[:notice] = "Profile has been created."
redirect_to #profile
else
flash[:alert] = "Profile has not been created."
render "new"
end
end
def edit
#hash = Gmaps4rails.build_markers(#profiles) do |profile, marker|
marker.lat profile.latitude
marker.lng profile.longitude
marker.infowindow profile.address
end
end
def update
if #profile.update(profile_params)
flash[:notice] = "Profile has been updated."
redirect_to #profile
else
flash[:alert] = "Profile has not been updated."
render "edit"
end
end
def destroy
#profile.destroy
flash[:notice] = "Profile has been destroyed."
redirect_to profiles_path
end
private
def profile_params
params.require(:profile).permit(:id, :firstname, :lastname, :company, :website, :street_address, :city, :state, :zipcode, :phone, :logo_image, :address, :latitude, :longitude, :user_id, :user_email_id)
end
def set_profile
#profile = Profile.find(params[:id])
rescue ActiveRecord::RecordNotFound
flash[:alert] = "The profile you were looking for could not be found."
redirect_to profiles_path
end
end
Related
I have a user model with a nested model for user's informations, and i have already a working country field with country_select gem, but it doesn't offer states and cities .
After some research i found this gem ruby geocoder and as it says in the documentation :
In Any Rack-Based Framework
Detect Location of HTTP Request
Get current user's city and country (using IP address). A location method is added to the standard Rack::Request which returns a Geocoder::Result object:
# Rails controller or Sinatra app
city = request.location.city
country = request.location.country_code
Basically i want to get off using country_select gem and use ruby geocoder
I have two models :
models/user.rb
class User < ApplicationRecord
extend FriendlyId
friendly_id :username, use: :slugged
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_one_attached :avatar, :dependent => :destroy
# User Information
has_one :user_information, :dependent => :destroy
accepts_nested_attributes_for :user_information, :allow_destroy => true
def with_user_information
build_user_information if user_information.nil?
self
end
# Login with username or email
attr_accessor :login
validates :username, uniqueness: true, presence: true
def login
#login || self.username || self.email
end
def self.find_for_database_authentication(warden_conditions)
conditions = warden_conditions.dup
if login = conditions.delete(:login)
where(conditions.to_h).where(["lower(username) = :value OR lower(email) = :value", { :value => login.downcase }]).first
elsif conditions.has_key?(:username) || conditions.has_key?(:email)
where(conditions.to_h).first
end
end
end
and a nested model :
models/user_information.rb
class UserInformation < ApplicationRecord
belongs_to :user
has_one :gender, :dependent => :destroy
accepts_nested_attributes_for :gender, :allow_destroy => true
has_one :relationship, :dependent => :destroy
accepts_nested_attributes_for :relationship, :allow_destroy => true
def age
now = Time.current
dob = self.born_in
now.year - dob.year - ((now.month > dob.month || (now.month == dob.month && now.day >= dob.day)) ? 0 : 1)
end
def country_name
country = ISO3166::Country[country_code]
country.translations[I18n.locale.to_s] || country.name
end
end
this my devise controller
controllers/accounts_controller.rb
class AccountsController < Devise::RegistrationsController
def update
self.resource = resource_class.to_adapter.get!(send(:"current_#{resource_name}").to_key)
prev_unconfirmed_email = resource.unconfirmed_email if resource.respond_to?(:unconfirmed_email)
resource_updated = update_resource(resource, account_update_params)
yield resource if block_given?
if resource_updated
set_flash_message_for_update(resource, prev_unconfirmed_email)
bypass_sign_in resource, scope: resource_name if sign_in_after_change_password?
session[:return_to] ||= request.referer
redirect_to session.delete(:return_to)
else
clean_up_passwords resource
set_minimum_password_length
session[:return_to] ||= request.referer
redirect_to session.delete(:return_to), alert: resource.errors.full_messages[0]
end
end
def settings
#user = current_user
if #user
render "devise/accounts/settings"
else
render file: 'public/404', status: 404, formats: [:html]
end
end
def passwords
#user = current_user
if #user
render "devise/accounts/passwords"
else
render file: 'public/404', status: 404, formats: [:html]
end
end
def security
#user = current_user
if #user
render "devise/accounts/security"
else
render file: 'public/404', status: 404, formats: [:html]
end
end
protected
def update_resource(resource, params)
if params[:current_password].blank? && params[:password].blank? && params[:password_confirmation].blank? && params[:email].blank?
resource.update_without_password(params.except(:current_password, :password, :password_confirmation, :email))
else
resource.update_with_password(params)
end
end
end
If you want to replace country_select with geocoder, you just need to take this values in controller that you need.
class UsersController < ApplicationController
...
def create
#user = User.new(user_params)
#user.user_information.country = country_name(request.location.country_code)
#user.user_information.city = request.location.city
#user.save
end
end
If you're using Devise...
# app/controllers/registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
def new
super
end
def create
# put above logic here
end
def update
super
end
end
You need to implement helper/model methods needed.
I have problems with my Rails Block. After I implemented a comment-section I am not able to create posts anymore. The console gives me a rollback transaction. So I did
p = Post.new
p.valid? # false
p.errors.messages
It seems I have some validation problems with user :user=>["must exist"]. But before I implemented comments it did work. Can someone help me out?
User.rb
class User < ApplicationRecord
has_many :posts
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
Post.rb
class Post < ApplicationRecord
belongs_to :user
has_many :comments, dependent: :destroy
validates :title, presence: true, length: {minimum: 5}
validates :body, presence: true
has_attached_file :image #, :styles => { :medium => "300x300>", :thumb => "100x100>" }
validates_attachment_content_type :image, :content_type => /\Aimage\/.*\Z/
end
Post-migrate
class CreatePosts < ActiveRecord::Migration[5.1]
def change
create_table :posts do |t|
t.string :title
t.text :body
t.timestamps
end
end
end
Post_controller
class PostsController < ApplicationController
def index
#posts = Post.all.order("created_at DESC")
end
def new
#post = Post.new
end
def create
#post = Post.new(post_params)
if #post.save
redirect_to #post
else
render 'new'
end
end
def show
#post = Post.find(params[:id])
end
def edit
#post = Post.find(params[:id])
end
def update
#post = Post.find(params[:id])
if #post.update(post_params)
redirect_to #post
else
render 'edit'
end
end
def destroy
#post = Post.find(params[:id])
#post.destroy
redirect_to posts_path
end
private
def post_params
params.require(:post).permit(:title, :body, :theme)
end
end
When you are creating a post you need to assign a user to that post in the create method under your posts controller. You could try something like this.
def create
if current_user
#post.user_id = current_user.id
end
## More create method stuff
end
By default, in a belongs_to association a user is required to create the post otherwise you will not be able to create the post. Since, from the looks of it, you do not have anything that assigns the user to that post in the create method.
I have set up users with devise and each user can select a role. What I am trying to do is allow admins to be able to edit any user on the site if they have role admin. I currently have a UsersController setup like this:
class UsersController < ApplicationController
before_filter :authenticate_user!, only: [:index, :new, :edit, :update, :destroy]
skip_before_filter
def index
#users = User.order('created_at DESC').all
end
def show
#user = User.friendly.find(params[:id])
#users_authors = User.all_authors
end
# get authors index in here
def authors
end
def create
#user = User.create(user_params)
end
def edit
#user = User.find(params[:id])
end
def update
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
def destroy
#user = User.find(params[:id])
#user.destroy
if #user.destroy
redirect_to users_url, notice: "User deleted."
end
end
private
def user_params
params.require(:user).permit(:avatar, :email, :name, :biography, :role_id, :book_id, :username, :password, :password_confirmation)
end
end
This is trying to create a CRUD to edit users which works but I need to be able to populate the forms in the users/edit view wityh the correct selected users details. I my devise controller I have this setup:
class Admin::UsersController < Admin::BaseController
helper_method :sort_column, :sort_direction
before_filter :find_user, :only => [:edit, :update, :show, :destroy]
def index
#q = User.search(params[:q])
#users = find_users
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
redirect_to admin_users_path, :notice => "Successfully created user."
else
render :new
end
end
def show
end
def edit
#user = User.find(params[:id])
end
def update
if #user.update_attributes(user_params)
redirect_to admin_users_path, :notice => "Successfully updated user."
else
render :edit
end
end
def destroy
#user.destroy
redirect_to admin_users_path, :notice => "User deleted."
end
protected
def find_user
#user = User.find(params[:id])
end
def find_users
search_relation = #q.result
#users = search_relation.order(sort_column + " " + sort_direction).references(:user).page params[:page]
end
def sort_column
User.column_names.include?(params[:sort]) ? params[:sort] : "created_at"
end
def sort_direction
%w[asc desc].include?(params[:direction]) ? params[:direction] : "desc"
end
private
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user).permit(:email,:username,:name,:biography,:role_id,:book_id,:role_name,:password,:password_confirmation,:encrypted_password,:reset_password_token,:reset_password_sent_at,:remember_created_at,:sign_in_count,:current_sign_in_at,:last_sign_in_at,:current_sign_in_ip,:last_sign_in_ip)
end
end
For clarity here is the user model:
class User < ActiveRecord::Base
belongs_to :role
has_many :books, dependent: :destroy
has_many :ideas, dependent: :destroy
accepts_nested_attributes_for :books
accepts_nested_attributes_for :ideas
def confirmation_required?
false
end
extend FriendlyId
friendly_id :username, use: [:slugged, :finders]
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable
has_attached_file :avatar, styles: {
large: "600x450#",
medium: "250x250#",
small: "100x100#"
}, :default_url => "/images/:style/filler.png"
#validates_attachment_content_type :avatar, :content_type => ["image/jpg", "image/jpeg", "image/png", "image/gif"]
validates_attachment_content_type :avatar, :content_type => /\Aimage\/.*\Z/
validates :avatar, :email, :username, :password, presence: true
def self.all_authors
User.select('users.id, users.username, users.role_id AS USER_ROLE')
.joins(:role).where(users: {role_id: '2'})
end
before_create :set_default_role
private
def set_default_role
self.role ||= Role.find_by_name('Admin')
end
end
In my routes I added a new route for users below the devise users resource as suggested on devise wiki like so:
devise_for :users, :path_prefix => 'my', :path_names => { :sign_up => "register" }
namespace :admin do
resources :users
end
Can anyone help with adding the ability of admins being able to edit all users here, I think its right but I cannot get the correct data into the forms in edit, it uses the current logged users details only.
A first draft for your ability.rb would be:
class Ability
include CanCan::Ability
def initialize(user)
# ...
if user.admin?
can :manage, User
end
# ...
end
end
And then in your user's controller remove the before_filter :find_user, :only => [:edit, :update, :show, :destroy] and related method, and use
load_and_authorize_resource :user
That would load the user from the URL and authorize! it using CanCan. You'll also need to handle the CanCan::AccessDenied exception for non-admin users visiting those pages, but that is another question that you can check in the CanCan docs.
When you visit admin_users_path routes you'll be able to CRUD them if you have the views ready and working.
I am trying to setup a site wide search that searches for users, as well as items belonging to users.
Basically each user has many "cereals", and I want to be able to keep the cereal hidden from the search results, and show the owner of the cereal in its place.
Right now I have sunspot functional, but on my index page in users, it lists all the users before a search is performed. If now search is performed I want the results to be empty.
I am still a noob to rails, and am trying to figure this out. Any help would be greatly appreciated. Here is my source.
App/models/user.rb
class User < ActiveRecord::Base
include Amistad::FriendModel
mount_uploader :avatar, AvatarUploader
has_many :cereals, dependent: :destroy
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :authentication_keys => [:login]
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me, :username, :login, :avatar, :avatar_cache, :remove_avatar, :firstname, :lastname, :phone, :urlname
# attr_accessible :title, :body
attr_accessor :login
def self.find_first_by_auth_conditions(warden_conditions)
conditions = warden_conditions.dup
if login = conditions.delete(:login)
where(conditions).where(["lower(username) = :value OR lower(email) = :value", { :value => login.downcase }]).first
else
where(conditions).first
end
end
def update_with_password(params={})
current_password = params.delete(:current_password)
if params[:password].blank?
params.delete(:password)
params.delete(:password_confirmation) if params[:password_confirmation].blank?
end
result = if params[:password].blank? || valid_password?(current_password)
update_attributes(params)
else
self.attributes = params
self.valid?
self.errors.add(:current_password, current_password.blank? ? :blank : :invalid)
false
end
clean_up_passwords
result
end
searchable do
text :firstname, :lastname, :username
end
end
app/controllers/users_controller.rb
class UsersController < ApplicationController
before_filter :authenticate_user!
def index
#search = User.search do
fulltext params[:search]
end
#users = #search.results
end
def show
if params[:id].nil? && current_user
#user = current_user
else
#user = User.find(params[:id])
end
#cereal = current_user.cereals.build if signed_in?
#cereals = #user.cereals.paginate(page: params[:page])
end
def first_time
if params[:id].nil? && current_user
#user = current_user
else
#user = User.find(params[:id])
end
end
def edit
if params[:id].nil? && current_user
#user = current_user
else
#user = User.find(params[:id])
end
end
def profile
#username = params[:id]
#friends = current_user.friends || []
#title = "User Profile for #{#username}"
#user = User.find_by_username(#username)
#users = User.all :conditions => ["id != ?", current_user.id]
end
end
views/users/index.html.haml
= form_tag #users_path, :method => :get do
%p
= text_field_tag :search, params[:search]
= submit_tag "Search", name: 'nil'
- #users.each do |user|
%p
= user.username
I have a bit of code here where users can login via their twitter account. the problem here is, how can i skip email confirmation for user the sign up from external services like twitter. i am using devise and i do not know how to skip the email confirmation for this type of users. my code sample is as follows
class AuthenticationsController < ApplicationController
# GET /authentications
# GET /authentications.json
def index
#authentications = current_user.authentications if current_user
end
# POST /authentications
# POST /authentications.json
def create
omniauth = request.env["omniauth.auth"]
authentication = Authentication.find_by_provider_and_uid(omniauth['provider'], omniauth['uid'])
if authentication
flash[:notice] = "Signed in successfully"
sign_in_and_redirect(:user, authentication.user)
elsif current_user
current_user.authentications.create!(:provider => omniauth['provider'], :uid => ['uid'])
flash[:notice] = "Authentication successful"
redirect_to authentication_url
else
user = User.new
user.apply_omniauth(omniauth)
if user.save
flash[:notice] = "Signed in successfully"
sign_in_and_redirect(:user, user)
else
session[:omniauth] = omniauth.except('extra')
redirect_to new_user_registration_url
end
end
rescue Exception => e
# Just spit out the error message and a backtrace.
render :text => "<html><body><pre>" + e.to_s + "</pre><hr /><pre>" + e.backtrace.join("\n") + "</pre></body></html>"
end
# DELETE /authentications/1
# DELETE /authentications/1.json
def destroy
#authentication = current_user.authentications.find(params[:id])
#authentication.destroy
respond_to do |format|
format.html { redirect_to authentications_url }
format.json { head :ok }
end
end
end
my registration controller is as follows
class RegistrationsController < Devise::RegistrationsController
def create
super
session[:omniauth] = nil unless #user.new_record?
end
private
def build_resource(*args)
super
if session[:omniauth]
#user.apply_omniauth(session[:omniauth])
#user.valid?
end
end
end
and my user model is below
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :encryptable, :lockable, :timeoutable and
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me, :username, :about, :facebook_username, :twitter_username, :icon, :admin
validates_uniqueness_of :username
has_attached_file :icon, :styles => {:thumb => "64x64#"}, :default_url => 'icon_:style.png'
validates_attachment_content_type :icon, :content_type => ['image/jpeg', 'image/png', 'image/gif']
validates_attachment_size :icon, :less_than => 1.megabyte
ajaxful_rater
has_many :authentications
validates_presence_of :username
def apply_omniauth(omniauth)
self.email = omniauth['user_info']['email'] if email.blank?
self.name = omniauth['user_info']['name'] if name.blank?
self.image = omniauth['user_info']['image'] if image.blank?
authentications.build(:provider => omniauth['provider'], :uid => omniauth['uid'])
end
def password_required?
(authentications.empty? || !password.blank?) && super
end
end
my like to the auth url is below
<a href="/auth/twitter" class="auth_provider">
<%= image_tag "twitter_64.png", :size => "64x64", :alt => "Twitter" %>
</a>
my routh is like this
match 'auth/:provider/callback' => "authentications#create"
Whenever you want to skip confirmation for Devise period, just use the following before the user.save...
user.skip_confirmation!
So basically, in your create controller action, if it detects omniauth logic, then call that.
This could be achieved by setting the confirmed_at attribute, which Devise sets internally to mark an account as confirmed:
user.update(
confirmed_at: Time.now.utc,
confirmation_token: nil
)