Model to implement users (Ruby on Rails 3) - ruby-on-rails

I am implementing a User that is not actually deleted from the system with destroy but only marked with :active = false.
The problem here is that such an inactivate user will show up in all User.find, User.all, ... calls. I don't want to pollute the code with all kinds of 'if-else's or overwriting the behavior of .find, .all etc.
I just want to know whether I can nicely define it within the User's model so that inactive users will virtually disappear unless I explicitly want to extract such a user.
If there is no way to do it in the model then what are my options?

Use a scope, or a class method with a where clause.

I think you may want to check acts_as_paranoid Here is a link for one of the implementations: https://github.com/technoweenie/acts_as_paranoid
From the wiki:
Now whenever destroy is called on that model, it is just removed from view and the deleted_at column set to the current date time. All the finder methods ignore “deleted” records.

Related

Force filter on user_id

I made a new Rails project and added Devise for user management. I also made tables like 'posts' and 'tags' and they have a field 'user_id' because the data is per user.
Now I can make queries where I always include user_id as filter. This works fine, but I'm afraid that someday I will forget to filter on user_id and a user can see data of other users.
Is there a way in models to force a certain filter?
For some models like 'posts' and 'tags' I would like to always filter on the current_user. Is there a way to automatically do this or maybe raise an exception if I forget to filter on user?
Any tips are welcome.
(I could use something like Apartment, but I prefer a single database/schema for now)
In your proxy method for params, you can use the require method to require the user_id field. Thus if you restrain from using params directly, as everybody should, your contraint will be enforced.
Another way is to have a before_action filter, where you check your condition. That way, unless you purposefully exclude a method from this filter, your check will always be enforced (or 422 will be returned).
Putting a condition on the model itself seems wrong to me: the model should not know what the access conditions are because access control is an orthogonal feature and should not be entangled with the model.

Make acts_as_paranoid finders exclude "deleted" objects conditionally

Normally, with acts_as_paranoid, when you use a finder such as find or where, it excludes entries that have a deleted_at (or whatever you named your delete column) value other than null. However, I have created a user permission that allows users to view and manipulate even "deleted" (see: hidden) entities in the database. Thing is, as it stands, I will need to check for the permission every time I want to run find, or where, or what-have-you, and if you have that permission use with_deleted as opposed to having one place that checks to see if the acts_as_paranoid logic should even work to begin with, and always adding with_deleted when that logic shouldn't.
Is there any way to accomplish this, or something similar to it, to make it all DRY?
You can use cancan. This allows you to define abilities.
You can then define:
if user.admin?
can :manage, User.with_deleted
else
can :manage, User
end
Then you can select in the controller, using:
User.accessible_by(current_ability)

Delete all in rails console

i have an association for a user as user has_many agents and agent belongs_to user.
in rails console,i am trying to use different users to test a particular scenario and i want a user with no agents,hence i want to delete the user.agents.
i tried user.agents.map(&:destroy),but it gives error as ActiveRecord::StaleObjectError: Attempted to delete a stale object.i even tried user.agents.delete_all but it too does not work.can i delete the users agents with a single command in rails console.
You better use destroy because it goes through all the Rails magic (callbacks and such)
user.destroy #For a single record
user.agents.destroy_all #For a collection
You are looking for a .destroy_all method. It destroys all records of a given collection.
So user.agents.destroy_all, would return an empty array for user.agents.
You could not have used .delete_all because it is a class method and it deletes records that match a given condition. Like this, Agent.delete_all(condition). If used without a condition it deletes all records from a matched table.
Keep in mind that .destroy methods are instance methods. They instantiate an object and perform callbacks before erasing it. .delete methods are class methods and they directly erase an object.
This works for me
user.agents.find_each(&:destroy)
ActiveRecord::StaleObjectError
Is for Optimistic locking, remove any locks you have on it before trying to delete again. Check if anyone else is using the system or submit any forms you have open.

What is the best way to override Rails ActiveRecord destroy behavior?

I have an application where I would like to override the behavior of destroy for many of my models. The use case is that users may have a legitimate need to delete a particular record, but actually deleting the row from the database would destroy referential integrity that affects other related models. For example, a user of the system may want to delete a customer with whom they no longer do business, but transactions with that customer need to be maintained.
It seems I have at least two options:
Duplicate data into the necessarily models effectively denormalizing my data model so that deleted records won't affect related data.
Override the "destroy" behavior of ActiveRecord to do something like set a flag indicating the user "deleted" the record and use this flag to hide the record.
Am I missing a better way?
Option 1 seems like a horrible idea to me, though I'd love to hear arguments to the contrary.
Option 2 seems somewhat Rails-ish but I'm wondering the best way to handle it. Should I create my own parent class that inherits from ActiveRecord::Base, override the destroy method there, then inherit from that class in the models where I want this behavior? Should I also override finder behavior so records marked as deleted aren't returned by default?
If I did this, how would I handle dynamic finders? What about named scopes?
If you're not actually interested in seeing those records again, but only care that the children still exist when the parent is destroyed, the job is simple: add :dependent => :nullify to the has_many call to set references to the parent to NULL automatically upon destruction, and teach the view to deal with that reference being missing. However, this only works if you're okay with not ever seeing the row again, i.e. viewing those transactions shows "[NO LONGER EXISTS]" under company name.
If you do want to see that data again, it sounds like what you want has nothing to do with actually destroying records, which means that you will never need to refer to them again. Hiding seems to be the way to go.
Instead of overriding destroy, since you're not actually destroying the record, it seems significantly simpler to put your behavior in a hide method that triggers a flag, as you suggested.
From there, whenever you want to list these records and only include visible records, one simple solution is to include a visible scope that doesn't include hidden records, and not include it when you want to find that specific, hidden record again. Another path is to use default_scope to hide hidden records and use Model.with_exclusive_scope { find(id) } to pull up a hidden record, but I'd recommend against it, since it could be a serious gotcha for an incoming developer, and fundamentally changes what Model.all returns to not at all reflect what the method call suggests.
I understand the desire to make the controllers look like they're doing things the Rails way, but when you're not really doing things the Rails way, it's best to be explicit about it, especially when it's really not that much of a pain to do so.
I wrote a plugin for this exact purpose, called paranoia. I "borrowed" the idea from acts_as_paranoid and basically re-wrote AAP using much less code.
When you call destroy on a record, it doesn't actually delete it. Instead, it will set a deleted_at column in your database to the current time.
The README on the GitHub page should be helpful for installation & usage. If it isn't, then let me know and I'll see if I can fix that for you.

Triggers/Callbacks in Ruby on Rails

We are creating a system in Ruby on Rails and we want to be able to offer our users a bit of control about notifications and actions that can take place when some pre-defined trigger occurs. In addition, we plan on iterating through imported data and allowing our users to configure some actions and triggers based on that data.
Let me give you a few examples to better clarify:
Trigger - Action
------------------------------------------------------------------------
New Ticket is Created - User receives an e-mail
New Ticket Parsed for Keyword 'evil' - Ticket gets auto-assigned to a
particular group
User Missed 3 Meetings - A ticket is automatically created
Ideally, we would like some of the triggers to be configurable. For instance, the last example would possibly let you configure how many meetings were missed before the action took place.
I was wondering what patterns might help me in doing this event/callback situation in Ruby on Rails. Also, the triggers and actions may be configurable, but they will be predefined; so, should they be hard coded or stored in the database?
Any thoughts would be greatly appreciated. Thanks!
Update 1: After looking at it, I noticed that the badges system on SO is somewhat similar, based on these criteria, I want to do this action. It's slightly different, but I want to be able to easily add new criteria and actions and present them to the users. Any thoughts relating to this?
I think that what you are looking for are the Observers.
In your examples the Observers could handle the first and the third example (but not the second one, since an Observer only observes the object, not interact with it, even though it is technically possible).
Some code to show how I mean:
class TicketObserver < ActiveRecord::Observer
def after_create(ticket)
UserMailer.deliver_new_ticket_notification
end
end
class UserObserver < ActiveRecord::Observer
def after_update(user)
Ticket.new if user.recently_missed_a_meeting and user.missed_meetings > 3
end
end
And then add the observers to environment.rb
config.active_record.observers = :user_observer, :ticket_observer
Of course you will have to fill in the logic for the missed_meetings, but one detail to mention.
Since the after_update will trigger after every time that the user is being updated, the recently_missed_a_meeting attribute is useful. I usually follow the thinking of restful-authentication and have an instance variable that is being set to true everytime I want to trigger that row. That can be done in a callback or in some custom logic depends on how you track the meetings.
And for the second example, I would put it in a before_update callback, perhaps having the keywords in a lookup table to let users update which words that should trigger the move to a specific group.
You should look at the "callback" methods in Rails
For docs see - Callbacks
Your first rule would be implemented via the after_create method.
If you want them to be configurable, I would suggest using a model / table to store the possible actions and doing a lookup within the callback.
If this is high volume, be sure to consider caching the configuration since it would end up doing a db lookup on each callback.
Maybe something like a state-machine can help. Try AASM gem for RoR.

Resources