Hey guys, I'm at a deadlock here after thinking about this for too long.
Context: Given the following models:
User
Item
Lock
Here's the scenario: A lock is basically like a 'hold'. A user can place a 'lock' on any given item to signal to the system that the item should not be deleted. Items wont be deleted until the lock is cleared.
Here's the tricky part. The lock is its own model because I want multiple users to be able to lock any given item. So let's say Bob locks an item, one didn't already exist so it creates a lock for that item, and information stating that Bob is currently associated with that lock. John comes and locks the same item, but a lock already exists, so John is simply 'added under' the same lock. The lock won't be removed until all users choose to 'unlock', or disassociate themselves with that lock.
My confusion is how I should model these relationships. A user can of course have many locks, each associated with a different item (since any given item can have at most one lock). The locks themselves can have many users. From the point of view of the item, each item can have one lock associated with many users.
So in other words, I would like to access the information a little something like this:
item.lock.users # get the users 'locking' the item
user.locks # get the items the user is currently 'locking
Perhaps the separate Lock model isn't required, but I figured it would be in order to signify that multiple users can be locking a particular item.
I think what further complicates things is that items are added by users, so I would want to have a way to access the items by a user for example user.items or item.user.
Right now I have:
user has and belongs to many locks
lock has and belongs to many users
user has many items
item belongs to user
item has one lock
lock belongs to item
Does this seem correct?
I think what you're doing will work though you may not have to use the habtm. What if an item can have many locks and can only be deleted when it has no locks. That way you could add a date/reason/comment for each lock by user.
user
has_many :locks
has_many :items
lock
belongs_to :user
belongs_to :item
item
belongs_to :user
has_many :locks
This will still allow you to do user.locks though item.lock.users won't work, but by looking at each lock you'll easily be able to get the users.
item.locks.each do |lock|
puts lock.user
end
You don't need the lock model. You can simply set up a habtm relationship between users and items:
class User < ActiveRecord::Base
has_many :items
has_and_belongs_to_many :locks, :class_name => "Item"
end
User.first.items # => [<#Item>, <#Item>, ...] # Items created by user
User.first.locks # => [<#Item>, <#Item>, ...] # Items locked by user
class Item < ActiveRecord::Base
belongs_to :user
has_and_belongs_to_many :lock_users, :class_name => "User"
end
Item.first.user # => <#User> # Creator of the item
Item.first.lock_users # => [<#User>, <#User>, ...] # "Lockers" of the item
You'll have to create a join table, of course, and be mindful of what Rails expect the join table to be named. You may be better off specifying the :join_table option for the habtm.
The key here is that relationships in Rails are very flexible. You can have multiple relationships between the two tables; you can have both the 'created by' and the lock relationships, independently of each other. All you have to do is use different names for the relationships.
I can relate to "thinking about this for too long". When that thought creeps into my mind I back away and work on other parts of the code. Relationships seem to reveal themselves over time as they're really a convenience to spare us writing a bunch of code. They're not essential, so, at least during the development phase, we can postpone the declaration of the relationships and see what we need a bit later.
Yeah, we're supposed to always know ahead of time according to the pragmatists, but in real life we're often having to use our common sense and experience and build a working prototype, then fine tune it. It's during that fine-tuning stage I tweak my relationships that weren't exactly clear before, and adjust my code.
Sniff... sniff... jeez, now my bosses know I'm not perfect... sniff....
Back to the question at hand: Normally, for locks to prevent accidental (or on purpose) deletion, I create a boolean field in my main table and if that record should be purged set it to true. For what you're doing I'd probably get rid of the flag field altogether and have a separate table that is the IDs of the records to lock, along with the user's IDs who want to keep the record. Delete those user's records if/when they think it's time to delete the record. When it's time to do some purging I'd check against that table. Something similar to:
delete from table1 where id not in (select distinct(table1_id) from table2)
The thing I don't like about it is there's potential to have another table full of "keep this record" records, and that's when I add another table for users to terminate who can't decide what things need to be deleted.
Related
My comments are placed by users. The Comment belongs_to :user and the User has_many :comments.
But users can be removed. If done, I'd rather not delete their comments, but instead associate their comments with one Dummy user.
I can think of several ways:
On load of a comment, if no associated user is found, create a User in memory with dummy data.
On load of a comment, if no associated user is found, pick a predefined one from the database.
On removal of a comment, associate all the comments with a predefined User in the database; through some post filter.
My feeling says that number one is cleanest; since the other two require a user in the database that will be hardwired in the code. If user 18394 will be that "special" user, I'd need all kinds of safetynets for that special user.
What about soft deleting users instead? Have a boolean field called User.active and set a default scope for User.active = t. When a user gets deleted, set the active field to false, and clear out any personal data.
I have a problem with the acts_as_soft_deletable plugin and a has_and_belongs_to_many relationship.
I have a model "Place" which has a couple of Categories (like restaurant, hotel, etc). This means that a table "places_categories" is created in the database, containing two columns "place", and "category".
When I destroy a place, it is placed in the table "deleted_places" by acts_as_soft_deletable. Then I try to restore it and the application crashes because a place cannot exist without categories. The entries in "places_categories" that stored which categories the place belonged to are deleted as the place is deleted.
How can I make sure that "places_categories" does not remove the relations when a place is moved to the "deleted_places" table?
Since there is no option to explicitly preserve those entries - you could do something crazy and stupid and just override the delete_sql option to an empty String or somethings thats not going to fail on the "database-side" like so:
class Place
has_and_belongs_to_many :categories, :delete_sql => "select true"
end
This is untested ! Just an idea.
You can read about all available options here.
I have a requirement that certain attribute changes to records are not reflected in the user interface until those changes are approved. Further, if a change is made to an approved record, the user will be presented with the record as it exists before approval.
My first try...
was to go to a versioning plugin such as paper_trail, acts_as_audited, etc. and add an approved attribute to their version model. Doing so would not only give me the ability to 'rollback' through versions of the record, but also SHOULD allow me to differentiate between whether a version has been approved or not.
I have been working down this train of thought for awhile now, and the problem I keep running into is on the user side. That is, how do I query for a collection of approved records? I could (and tried) writing some helper methods that get a collection of records, and then loop over them to find an "approved" version of the record. My primary gripe with this is how quickly the number of database hits can grow. My next attempt was to do something as follows:
Version.
where(:item_type => MyModel.name, :approved => true).
group(:item_type).collect do |v|
# like the 'reify' method of paper_trail
v.some_method_that_converts_the_version_to_a_record
end
So assuming that the some_method... call doesn't hit the database, we kind of end up with the data we're interested in. The main problem I ran into with this method is I can't use this "finder" as a scope. That is, I can't append additional scopes to this lookup to narrow my results further. For example, my records may also have a cool scope that only shows records where :cool => true. Ideally, I would want to look up my records as MyModel.approved.cool, but here I guess I would have to get my collection of approved models and then loop over them for cool ones would would result in the very least in having a bunch of records initialized in memory for no reason.
My next try...
involved creating a special type of "pending record" that basically help "potential" changes to a record. So on the user end you would lookup whatever you wanted as you normally would. Whenever a pending record is apply!(ed) it would simply makes those changes to the actual record, and alls well... Except about 30 minutes into it I realize that it all breaks down if an "admin" wishes to go back and contribute more to his change before approving it. I guess my only option would be either to:
Force the admin to approve all changes before making additional ones (that won't go over well... nor should it).
Try to read the changes out of the "pending record" model and apply them to the existing record without saving. Something about this idea just doesn't quite sound "right".
I would love someone's input on this issue. I have been wrestling with it for some time, and I just can't seem to find the way that feels right. I like to live by the "if its hard to get your head around it, you're probably doing it wrong" mantra.
And this is kicking my tail...
How about, create an association:
class MyModel < AR::Base
belongs_to :my_model
has_one :new_version, :class_name => MyModel
# ...
end
When an edit is made, you basically clone the existing object to a new one. Associate the existing object and the new one, and set a has_edits attribute on the existing object, the pending_approval attribute on the new one.
How you treat the objects once the admin approves it depends on whether you have other associations that depend on the id of the original model.
In any case, you can reduce your queries to:
objects_pending_edits = MyModel.where("has_edits = true").all
then with any given one, you can access the new edits with obj.new_version. If you're really wanting to reduce database traffic, eager-load that association.
Hi all
I have users and messages, messages can be deleted by both receiver and sender, without affecting the each-other view.
so when the sender deletes the message the receiver still sees it, hope I'm clear.
I wouldd just add two attributes, sender_archived_at and receiver_archived_at, but I rather manage it with rails_acts_as_paranoid, is it possible and how?
Thanks in advance
I had a better look at the plugin, and I'm confident that there is no way (yet) for doing that with acts_as_paranoid
"rails3_acts_as_paranoid" => hides records instead of deleting them, being able to recover them.
With the help of this gem can able to soft delete a record of many to many relationship also.
e.g. Product , Category a famous many to many relation consider
which has habtm's rich association like :through ,then at Product
model level can define 'acts_as_paranoid' and also at join_table's model
'ProductCategory' model.But remeber like :dependent => :destroy is mentioned.
then suppose any product got soft deleted because of dependent :destory 'ProductCategory' join_table 'deleted_at' also sets, and can handle both way.
I'm writing a quick app for a user to track their daily bills (for money tracking purposes). I want the user to be able to define their own categories that a bill can be applicable for. I'm trying however to decide the best way to model this and also validate categories as unique.
My initial thought was this:
class User
include Mongoid::Document
embeds_many :bills
field :categories, :type => Array
end
class Bill
include Mongoid::Document
embeded_in :user, :inverse_of => :bills
field :category
index :category
end
So a user can add categories, just as strings, and when they add a bill, they'll choose from their available categories for the bill.
So, a couple questions:
Does this seem like the proper design? I Don't think it's necessary to define an actual category model as it's literally just a string used to index bills on, but I'm not sure if there are other benefits to a separate model
How do I validate_uniqueness_of :categories in my user model. I don't think it works on array items like this, but I could be wrong. I don't want a user to create categories with the same name. I suppose this might be the advantage of a separate model, embedded in the User, but again it seems like more work than necessary.
Can someone tell me my best options here to validate that a user has unique categories (but users can have the same categories, i obviously don't care about that, just unique in the scope of a single user)
[Update]
The design seems proper. In a Rails specific way how would you validate the uniqueness? When adding a category pull the list and do an indexOf check to ensure it doesn't exist. If it does just bounce back an error.
I'm not a Rails guy, let me know if I'm off track or something.
I'm not sure MongoDB would be the best choice of storage engines for that. You would be better off using MySQL with a categories table.
Knocks against MongoDB:
Not ACID transactions
No single server durability
Not relational (you want relational for a bill tracking application)