I would like to have the list of currently logged in users.
This code doesn't work :
<% UserSession.all.each do |user_session| %>
<% end %>
#syed-aslam has a good solution, but you could just let Authlogic do the work. Check out the module Authlogic::ActsAsAuthentic::LoggedInStatus which defines two scopes: logged_in, logged_out
Your code becomes:
<% User.logged_in.each do |user| %>
<% end %>
P.S. I would normally link to the RDoc instead of source code, but the RDoc seems to have problems at the moment.
Authlogic gives you all kind of automatic columns that you don’t really need to update or maintain on your own, they are maintained by the actual code flow of Authlogic itself.
Those fields can contain some basic functionality related issues like the number of login attempts made, the ip address from which the attempt was made an or even what was the ip address the last time that user logged in. fun.
The magic column that will help us find who is probably online is the one called last_request_on, which basically indicates when was the last time that user made a request to your application.
The second parameter we’ll need in order to make a more accurate selection, is the configuration option named logged_in_timeout, which sets the timeout after which a stale session will be expired, by default it will expire after 10 minutes.
so if you set your session expiry to 30 minutes:
class User << ActiveRecord::Base
acts_as_authentic do |c|
c.logged_in_timeout 30.minutes
end
end
searching for those users is pretty easy:
module OnlineUsers
def count_online_users
User.count(:conditions => ["last_request_at > ?", 30.minutes.ago])
end
end
Why not creating a field called currently_active in the user model and update it to true once a session is created, and update to false once the session is destroy.
You can then call User.where(currently_active: true) gives you the users that online.
You cannot get UserSession for all user, UserSession is created every time user sends request and is not remembered between requests.
However you can show users which logged in some period of time (if you have last_logged_in column updated on every signin)
Logged in last 15 minutes:
<% User.find("last_logged_in < ?", 15.minutes.ago ).each do |user| %>
I wrote after_create and before_destroy callbacks in UserSession model.
In after_create callback, I wrote the user id of the user logging in to a text file (self.user.id) and in before_create callback I deleted the same. To check the activity of the user, I read the text file and checked the presence of the user id in that file.
Related
`I have recently started working on a rails app and using devise as authentication. but I have ran into a wall. I would like to know if there's a way to set a period limit on how often a user may update their username. For example, if a User update their username today, they shouldn't be able to update it again until a 30day period has passed.
I have looked through devise docs, but nothing address that functionality, I have also search SO and the web but to no avail.
Any help would be very much be appreciated on how to go about it, or at least be pointed in the right direction. Thanks in advance!
I have added to the user model
after_save :name_last_updated
def name_last_updated
if self.username_changed?
self.name_last_updated_at = Time.now
end
but this does not update the colunm name_last_updated_at. any clues of what i am doing wrong will be helpful ^^ thanks!
so, after messing around with a few codes i figured an alternative way to go about this. I created a new column to track new time the user should be able to see the form to update his username.
def username_next_update
self.username_next_update_at = self.username_last_update_at + 2.minutes
end
for learning and testing purposes i added +2.minutes
and i did a before_save on it.
In my view i wrapped it around an if and else statement although i am quite confuse with the logic.
<% if current_user.username_next_update_at < time.zone.now %>
********
<% end %>
but i expected to it to work only if it was > sign instead of <. any tips will be helpful or any better alternatives :)
You need to create it from scratch.
Create a new column in the users table, call it name_last_updated, when the user updates their name for the first time, set that column to today's date. then every time a user wants to update their name, check that column and compare with today's date and see if 30 days have passed:
if Date.today - user.name_last_update < 30
#display error
end
We can use user updated_at column created by the devise gem and add a callback method in the user model to make sure that we call this method every time the user model is updated.
before_update { |user| user.write_attribute if user.is_permitted? }
def write_attribute
self.user_name = params[:user][:user_name]
end
def is_permitted?
if self.username_changed?
Date.today - updated_at < 30
end
end
You should use the before_update and specify which record has to activate the callback instead of using after_save.
I would do something like this:
before_update :name_last_updated, if: :username_changed?
def name_last_updated
if (self.name_last_updated_at.to_date + 30.days) < Date.today
self.update(name_last_updated_at: Time.now)
end
end
I have a rails project using cancan for authorization. When a record is updated I want to notify users who are authorized to read it that the record is updated. Cancan easily lets me check if a specific user is authorized to perform an action on a record or get retrieve records that the specific user is allowed to operate on. But can I do the reverse through cancan? I.e. given a specific record retrieve all users authorized to perform an action on it?
I am averagely familiar with Cancan. Having said that, this is what I could think of:
#myrecord = MyRecord.find(1)
authorized_user_ids = User.find_each do |user|
user.id if Ability.new(user).can? :read, #myrecord
end.compact
puts authorized_user_ids
# => [1, 2, 5, 10, 78]
Note however, this is extremely inefficient because it will loop through all users. You might want to perform caching this with a separate table/model that you would implement.
given a specific record retrieve all users authorized to perform an action on it?
No.
That's like asking "can Devise show me all the users who are currently logged in?" - it doesn't have the capacity to do it because the scope is not there.
There are several ways to achieve what you want.
The simplest is to push the notification (I'm not sure how you're doing this) to the front-end of your application (so that, potentially, anyone could read it).
Then, as CallmeSurge said, you'll be able to use can? read on it to make it so that only the users who were eligible could actually invoke the data:
#app/views/layouts/application.html.erb
<%= [[notification]] if can? :read, [[notification]] %>
--
The other way is to use ActiveRecord to return the users eligible to read the notification with the criteria you use already. This can be done using the following:
#app/models/user.rb
class User < ActiveRecord::Base
def self.notify
where(x: y)
end
end
Then you'd be able to set the notifications as follows:
User.notify.each do |user|
user.notification.create .....
end
I've searched through the site on how to accomplish this and many answers point towards using the changed? method. I'm trying to notify users of an update after persisting the DB so unfortunately this won't work.
I then found the previous_changes method but this also triggers when a post is first created. The goal for is to do this only edit actions. How can this be done?
<% if #post.previous_changes %>
<span>Updated:</span> <span><%= time_ago_in_words(#post.updated_at) %> ago</span>
<% end %>
One possible solution is to set a flag whenever the element is updated.
class Model
after_update :flag_update
def updated?
!!#updated
end
private
def flag_update
#updated = true
end
end
Then in your code simply check #post.updated?.
You have an updated record when
#post.previous_changes.present? && #post.previous_changes["id"].nil?
Sounds like a good candidate for using after_update callback and in it calling a job to send an e-mail, text message or whatever your means of notifying a user is.
Using the callback to flag the model instance basically adds a state for the model and only works once because it is set after the first update. Any subsequent updates might go unnoticed depending on what and how you want to notify your users, unless you could somehow reset the state (flag), which may be a lot more work than what it is worth.
Relying on the updated_at timestamp to be greater than the created_at suffers from the same one-time opportunity than flagging.
We used an approach where we checked what a model's changes returns (you could also use changed?) before saving it, and then sent a notification after a successful save.
If you're able to call previous_changes, why not just add a conditional to make sure that id was not changed (as per this answer):
<% if #post.previous_changes && !#post.id_changed? %>
...
<% end %>
I have helpers that are used to determine whether a given user is an administrator or a vendor. From within the helpers, I query the database for their respective role objects (administrator, vendor, etc.) for comparison against roles associated with the user but have a feeling that this is an ass-backward way to go about determine a user's role.
Am I doing something wrong here? What could I do better? I should probably mention that I'm using/learning Pundit, so maybe it contains a better means by which to accomplish this.
Here's my code:
users_helper.rb
1 module UsersHelper
2 def admin?
3 # Determine whether the user has administrator status within a given event
4 #admin_role = Role.find(1)
5 return true if #user.roles.include? #admin_role
6 end
7
8 def vendor?
9 # Determine whether the user is an approved vendor within a given event
10 #vendor_role = Role.find(2)
11 return true if #user.roles.include? #vendor_role
12 end
13 end
Here's how I use the helpers from within my template:
show.html.erb
1 <% provide(:title, #user.username) %>
2
3 <% if admin? %>
4 <p>Admin</p>
5 <% elsif vendor? %>
6 <p>Vendor</p>
7 <% else %>
8 Something else.
9 <% end %>
The associations of User model with roles imply that your User model may have more then one role such as admin or vendor at the same time.
I would suggest to refactor User model to use has_one association with role, so that users will have only one role.
Then the helper in user_helper.rb might be something like:
def is_a?(user)
roles = { Roles.find(1) => "Admin", Roles.find(2) => "Vendor"}
roles.fetch(user.role) do
"Something else."
end
end
This will return string with correct string definition of role. You can make comparison with that or use it in view as follows:
<p><%= is_a?(#role) %></p>
Which does same thing as your code above in one line.
This might be better over at Code Review.
1) Your role implementation is a bit clunky (what with the magic numbers) - do you do anything with the Role class that warrants its being an ActiveRecord class?
If not, you could use the role_model gem (as mentioned by Sontya), which saves the user roles as a bitmask in the users table.
In case you need a more sophisticated role class, you may want to have a look at rolify, which is similar in concept to your current solution.
Either way, user roles in Rails applications are a solved problem, you do not need to write your own solution (of course, don't let that discourage you, building your own solution is at the very least a good learning exercise).
2) Checking for roles in a view is a pretty bad idea, especially if you're already using pundit.
If you ever add a new role, you need to go through all the views and change the if conditions to support the new new role.
This is a problem that explodes when both your number of views and your number of roles grow.
Instead, use the pundit policies in your view, and check for the users role(s) in the corresponding policy object.
Example:
class CompanyPolicy < ApplicationPolicy
def delete?
user.is?(:admin)
end
end
in the view:
<% if policy(#company).delete? %>
delete link here
<% end %>
Don't forget to authorize in the controller as well!
That way, if you add a new role, you simply have to change a few policy objects and you're good to go.
First off all, include? method returns boolean, so:
return true if #user.roles.include? #admin_role
is equal to:
#user.roles.include? #admin_role
Unless you need nil instead of false.
Second, your method names and the code itself should be so simple and self-descriptive, to not require comments. Unless you need automated documentation, when writing public gem or something.
Regarding you question, IMHO you should do this in your User model:
def admin?
roles.where(id: 1).any?
end
Then you can do #user.admin? in the view.
I have an application with set limits on subscription attributes i/e a user can have five projects for subscription A but have ten for subscription B
At present I run a check on there current usage when linking to the create action and if they are over there limit I do not display the link to create a new project. On the view (for the new project ) I again run the check (in a helper) to see if they can create a new project and if they are not I display a message stating so and a little upgrade link.
Is this a secure method of stopping a user bypassing there subscription attribute limitations ?
What about direct PUT requests etc ?
You can also validate that the user's subscription allows starting a new project when a new project is created. This guarantee that even if they posted directly to the new_project_path they would get an error.
class Project
belongs_to :user
validate_on_create :subscription_allows_new_project
def subscription_allows_new_project
unless self.user.subscription.max_projects > self.user.projects.count
errors.add_to_base("Project limit reached, please upgrade today!")
end
end
end
If you're really cautious about the put requests, you could simply create a helper method that you call in all of the pages.
<% if has_user_hit_project_limits %>
Upgrade Now!
<% else %>
Add project
<% end %>
def has_user_hit_project_limits
if #logic
true
else
false
end
end