# Terminal
bundle add activerecord-session_store
bundle add useragent
rails g model user_device user:references ip_address session_id:string:unique data:text properties:json
# config/initializers/session_store.rb
Rails.application.config.session_store :active_record_store, key: '_template_session'
ActiveRecord::SessionStore::Session.table_name = 'user_devices'
# db/migrate/20210620023327_create_user_devices.rb
class CreateUserDevices < ActiveRecord::Migration[6.1]
def change
create_table :user_devices do |t|
t.references :user
t.string :ip_address
t.string :session_id
t.text :data
t.json :properties
t.timestamps
end
end
end
# models/user.rb
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :user_devices, dependent: :destroy
end
# models/user_device.rb
class UserDevice < ApplicationRecord
belongs_to :user
store :properties, accessors: [:browser_name, :browser_version, :os_name], coder: JSON
end
# application_controller.rb
class ApplicationController < ActionController::Base
before_action :update_user_device
private
def update_user_device
return unless user_signed_in?
user_device = UserDevice.find_by(session_id: rack_session_id)
return unless user_device
user_device.user = current_user if user_device.user.nil?
user_device.ip_address = request.remote_ip
user_agent = UserAgent.parse(request.user_agent)
user_device.properties[:browser_name] = user_agent.browser
user_device.properties[:browser_version] = user_agent.version
user_device.properties[:os_name] = user_agent.os
user_device.save
end
def rack_session_id
request.env['rack.session.record'].session_id
end
helper_method :rack_session_id
end
# views/devise/registrations/edit.html.erb
<h1>Devices</h1>
<% resource.user_devices.each do |device| %>
<p>
<%= device.properties['browser_name'] %>
(<%= device.properties['browser_version'] %>)<br>
<%= device.properties['os_name'] %>
<%= device.updated_at %>
<% if device.session_id == rack_session_id %>
<strong><em>active</em></strong>
<% else %>
<%= link_to 'delete', device, method: :delete %>
<% end %>
</p>
<% end %>
# config/routes.rb
Rails.application.routes.draw do
devise_for :users
resources :users
resources :user_devices, only: :destroy
root to: 'welcome#index'
end
# user_devices_controller.rb
class UserDevicesController < ApplicationController
def destroy
device = current_user.user_devices.find(params[:id])
session_record = ActiveRecord::SessionStore::Session.find_by(session_id: device.session_id)
session_record.destroy
redirect_to edit_user_registration_path
end
end
# views/layouts/application.html.erb
<%= link_to 'My Account', edit_user_registration_path %>