ruby on rails - tutorial - Acceda a current_user en el modelo
simple login ruby (6)
Tengo 3 tablas
items (columns are: name , type)
history(columns are: date, username, item_id)
user(username, password)
Cuando un usuario dice "ABC" inicia sesión y crea un nuevo elemento, se crea un registro de historial con el siguiente filtro after_create. Cómo asignar este nombre de usuario ''ABC'' al campo de nombre de usuario en la tabla de historial a través de este filtro.
class Item < ActiveRecord::Base
has_many :histories
after_create :update_history
def update_history
histories.create(:date=>Time.now, username=> ?)
end
end
Mi método de inicio de sesión en session_controller
def login
if request.post?
user=User.authenticate(params[:username])
if user
session[:user_id] =user.id
redirect_to( :action=>''home'')
flash[:message] = "Successfully logged in "
else
flash[:notice] = "Incorrect user/password combination"
redirect_to(:action=>"login")
end
end
end
No estoy usando ningún plugin de autenticación. Agradecería que alguien me dijera cómo lograr esto sin usar un complemento (como userstamp, etc.) si es posible.
El controlador debe indicar la instancia del modelo
Trabajar con la base de datos es el trabajo del modelo. La gestión de las solicitudes web, incluido conocer al usuario para la solicitud actual, es el trabajo del controlador.
Por lo tanto, si una instancia modelo necesita conocer al usuario actual, un controlador debe contarla.
def create
@item = Item.new
@item.current_user = current_user # or whatever your controller method is
...
end
Esto supone que Item
tiene un attr_accessor
para current_user
.
Carriles 5
Declarar un módulo
module Current
thread_mattr_accessor :user
end
Asignar el usuario actual
class ApplicationController < ActionController::Base
around_action :set_current_user
def set_current_user
Current.user = current_user
yield
ensure
# to address the thread variable leak issues in Puma/Thin webserver
Current.user = nil
end
end
Ahora puede referirse al usuario actual como Current.user
Documentación sobre thread_mattr_accessor
Carriles 3,4
No es una práctica común acceder al current_user
dentro de un modelo. Dicho esto, aquí hay una solución:
class User < ActiveRecord::Base
def self.current
Thread.current[:current_user]
end
def self.current=(usr)
Thread.current[:current_user] = usr
end
end
Establezca el atributo current_user
en un around_filter
de ApplicationController
.
class ApplicationController < ActionController::Base
around_filter :set_current_user
def set_current_user
User.current = User.find_by_id(session[:user_id])
yield
ensure
# to address the thread variable leak issues in Puma/Thin webserver
User.current = nil
end
end
Establezca el current_user
después de la autenticación exitosa:
def login
if User.current=User.authenticate(params[:username], params[:password])
session[:user_id] = User.current.id
flash[:message] = "Successfully logged in "
redirect_to( :action=>''home'')
else
flash[:notice] = "Incorrect user/password combination"
redirect_to(:action=>"login")
end
end
Por último, consulte current_user
en update_history
of Item
.
class Item < ActiveRecord::Base
has_many :histories
after_create :update_history
def update_history
histories.create(:date=>Time.now, :username=> User.current.username)
end
end
El truco del Thread
no es seguro para los hilos, irónicamente.
Mi solución fue hacer retroceder la pila buscando un marco que responda a current_user
. Si no se encuentra ninguno, devuelve nil. Ejemplo:
def find_current_user
(1..Kernel.caller.length).each do |n|
RubyVM::DebugInspector.open do |i|
current_user = eval "current_user rescue nil", i.frame_binding(n)
return current_user unless current_user.nil?
end
end
return nil
end
Podría hacerse más robusto confirmando el tipo de retorno esperado, y posiblemente confirmando que el propietario del marco es un tipo de controlador ...
Esto se puede hacer fácilmente en pocos pasos implementando Thread.
Paso 1:
class User < ApplicationRecord
def self.current
Thread.current[:user]
end
def self.current=(user)
Thread.current[:user] = user
end
end
Paso 2:
class ApplicationController < ActionController::Base
before_filter :set_current_user
def set_current_user
User.current = current_user
end
end
Ahora puede obtener fácilmente al usuario actual como User.current
Podrías escribir un filtro around_ en ApplicationController
around_filter :apply_scope
def apply_scope
Document.where(:user_id => current_user.id).scoping do
yield
end
Si el usuario crea un artículo, ¿no debería el artículo tener una cláusula belongs_to :user
? Esto le permitiría en su after_update
hacer
History.create :username => self.user.username