How/where to temporarily store ActiveRecord objects if not in session? - ruby-on-rails

I'm refactoring a Rails-based event registration application that has a checkout process that hits several ActiveRecord models. Ideally, the objects should not be saved unless checkout is complete (payment is successfully processed). I'm not entirely certain why it would be a bad thing to serialize these objects into the session temporarily, but I've read over and over that its bad mojo. At least there's no risk of getting out of sync with existing records, as there won't be any existing records.
My questions are:
A) Is it still problematic to store records in the session even though they don't exist elsewhere? Even if I modify a model, can't I kill all existing sessions?
B) If it may cause problems, how should I temporarily store objects? Should I save them and use a boolean flag to indicate permanent vs. temporary status? Then cron a script to weed out stale temporary objects?
Thoughts?

Why don't you store them in the database, but in a way you know they are "incomplete"?
For example, you can add a added_to_cart_at datetime field. When the product is added to the cart, you save the record and you set the value for that field. Then, if the user completes the purchase, you clear the field and you associate the product to an order.
To clear stale record, you can setup a daily cron to delete all records where added_to_cart_at is older than 1.day.ago.

The problem of storing them in the session is that the session has limited space. ActiveRecord objects bring a lot of overhead in terms of space when stored in the session. If you're storing records with densely populated has_many relationships you will run into trouble.
I believe Simone Carletti's description of storing partial records in the database to be the best solution.
But, if you really want to store objects in the session, store the minimum amount of information possible. For example ids and updated fields only. Also store the information as a hash, instead of storing the object in the session directly.

Related

Rails: working on temporary instance between requests and then commit changes to database

I have already read Rails - How do I temporarily store a rails model instance? and similar questions but I cannot find a successful answer.
Imagine I have the model Customer, which may contain a huge amount of information attached (simple attributes, data in other tables through has_many relation, etc...). I want the application's user to access all data in a single page with a single Save button on it. As the user makes changes in the data (i.e. he changes simple attributes, adds or deletes has_many items,...) I want the application to update the model, but without committing changes to the database. Only when the user clicks on Save, the model must be committed.
For achieving this I need the model to be kept by Rails between HTTP requests. Furthermore, two different users may be changing the model's data at the same time, so these temporary instances should be bound to the Rails session.
Is there any way to achieve this? Is it actually a good idea? And, if not, how can one design a web application in which changes in a model cannot be retained in the browser but in the server until the user wants to commit them?
EDIT
Based on user smallbutton.com's proposal, I wonder if serializing the model instance to a temporary file (whose path would be stored in the session hash), and then reloading it each time a new request arrives, would do the trick. Would it work in all cases? Is there any piece of information that would be lost during serialization/deserialization?
As HTTP requests are stateless you need some kind of storeage between requests. The session is the easiest way to store data between requests. As for you the session will not be enough because you need it to be accessed by multiple users.
I see two ways to achive your goal:
1) Get some fast external data storage like a key-value server (redis, or anything you prefer http://nosql-database.org/) where you put your objects via serializing/deserializing (eg. JSON).
This may be fast depending on your design choices and data model but this is the harder approach.
2) Just store your Objects in the DB as you would regularly do and get them versioned: (https://github.com/airblade/paper_trail). Then you can just store a timestamp when people hit the save-button and you can always go back to this state. This would be the easier approach i guess but may be a bit slower depending on the size of your data model changes ( but I think it'll do )
EDIT: If you need real-time collaboration between users you should probably have a look at something like Firebase
EDIT2: Anwer to your second question, whether you can put the data into a file:
Sure you can do that. But you would need some kind of locking to prevent data loss if more than one person is editing. You will need that aswell if you go for 1) but tools like redis already include locks to achive your goal (eg. redis-semaphore). Depending on your data you may need to build some logic for merging different changes of different users.
3) Another aproach that came to my mind would be doing all editing with Javascript and save it in one db-transaction. This would go well with synchronization tools like firebase (or your own synchronization via Rails streaming API)

Whats the best way to store a Global/Class Variable in Rails thats updated through a dashboard?

I need to store a global/class variable that is updated by managers from a web dashboard. The variable will be an array, lets call it car_types. About once a week managers need to go in and change the value. So maybe they'll update from ['suv', 'convertible', 'sedan'] to ['suv', 'convertible'].
What I'm not sure on is where to store this variable.
I could certainly create a database table with one record in it that gets updated, but that seems like overkill.
We use memecached, so I could send the variable there, though I'm not sure if thats persistent enough.
I was thinking of having the dashboard update a class variable, but we have dozens of servers running the same app, and I'm unclear if the change would be replicated to all boxes or just stay on one box.
thanks
Global variables are prefixed by $, example: $cars
But What if your application goes down? The global var is reinitialized to its default value.
I would recommend a database, eventually with caching if you want to save on performances.
You could cache your database values in you $cars variable
That's my personal approach: database + cache for records that being updated not often. cache is cleared when a change is made in the table, and cache is created (with a db request) during first fetch of the record.
As a result its all good cause you have the flexibility to change the records sometimes, no problem arise when the server goes down, or with multi-threading, and the cache permit not to kill performances

MVC design - handle file upload before saving the record

We've an MVC web app which has a Claim management wizard (similar to a typical Order entry stuff). Claim can have multiple Items and each Item can have multiple files related to it.
Claim --> Items --> Files
While adding a new Claim - we want to allow the user to add one or more items to it and also allow file upload for those items. But we want to keep everything in memory until the Claim is actually saved - so that if the user doesn't complete the Claim entry or discards it, no database interaction is done.
We're able to handle data level in-memory management via session. We serialize the Claim object (which also includes a Claim.Items property) in session. But how to manage files?
We store files in < ClaimID >\< ItemID > folder but while creating a new
claim in memory we don't have any IDs until the record is being
saved in the database (both are auto-increment int).
For now, we've to restrict the user from uploading files until a Claim is saved.
Why not interact with the database? It sounds like you're intending to persist data between multiple requests to the application, and databases are good for that.
You don't have to persist it in the same tables or even in the same database instance as the more long-term persisted data. Maybe create tables or a database for "transient" data, or data that isn't intended to persist long-term until it reaches a certain state. Or perhaps store it in the same tables as the long-term data but otherwise track state to mark it as "incomplete" or in some other way transient.
You could have an off-line process which cleans up the old data from time to time. If deletes are costly on the long-term data tables then that would be a good reason to move the transient data to their own tables, optimized for lots of writes/deletes vs. lots of reads over time.
Alternatively, you could use a temporary ID for the in-memory objects to associate them with the files on the disk prior to be persisted to the database. Perhaps even separate the file-associating ID from the record's primary ID. (I'm assuming that you're using an auto-incrementing integer for the primary key and that's the ID you need to get from the database.)
For example, you could have another identifier on the record which is a Guid (uniqueidentifier in SQL) for the purpose of linking the record to a file on the disk. That way you can create the identifier in-memory and persist it to the database without needing to initially interact with the database to generate the ID. This also has the added benefit of being able to re-associate with different files or otherwise change that identifier as needed without messing with keys.
A Guid shouldn't be used as a primary key in many cases, so you probably don't want to go that route. But having a separate ID could do the trick.

Persisting ActiveRecord objects across requests in ruby on rails

I am trying to figure out a way to persist ActiveRecord objects across requests in the most efficient way possible.
The use case is as follows: a user provides some parameters and clicks "show preview". When that happens, I am doing a lot of background computation to generate the preview. A lot of ActiveRecord objects are created in the process. After seeing the preview, the user clicks "submit". Instead of recomputing everything here, I would like to simply save the ActiveRecord objects created by the previous request. There is no guarantee that these two requests always happen (e.g. the user may opt out after seeing the preview, in which case I would like to remove these objects from the persistence layer).
Are there any proven efficient ways to achieve the above? Seems like it should be a common scenario. And I can't use sessions since the data can exceed the space allotted to session data. Moreover, I'd rather not save these objects to the DB because the user hasn't technically "submitted" the data. So what I am looking for is more of an in-memory persistence layer that can guarantee the existence of these objects upon executing the second request.
Thanks.
You can save you a lot of unnecessary work by just saving it to the DB and not add other not-really-persistent-layers to your app.
A possible approach: Use a state attribute to tell, in what state your record is (e.g. "draft", "commited"). Then have a garbage collector run to delete drafts (and their adjactent records) which haven't been commited within a specific timeframe.
Im not sure if not saving the object in a dirty state would be the best option as you could manage this with some sort of control attribute like state or status.
Having this would also be pretty great as you could validate data along the way and not do it until the user decides to submit everything. I know Ryan Bates has a screencast to create this sorts of complex forms (http://railscasts.com/episodes/217-multistep-forms).
Hopefully it can help.
Is the reason the data can exceed the space allotted to session data because you're using cookie based sessions? If you need more space, why not use active record based sessions? It's trivial to make the change from cookie based sessions and is actually the recommended way (so why it's not the default I don't know)

Allow users to remove their account

I am developing a gallery which allows users to post photos, comments, vote and do many other tasks.
Now I think that it is correct to allow users to unsubscribe and remove all their data if they want to. However it is difficult to allow such a thing because you run the risk to break your application (e.g. what should I do when a comment has many replies? what should I do with pages that have many revisions by different users?).
Photos can be easily removed, but for other data (i.e. comments, revisions...) I thought that there are three possibilities:
assign it to the admin
assign it to a user called "removed-user"
mantain the current associations (i.e. the user ID) and only rename user's data (e.g. assign a new username such as "removed-user-24" and a non-existent e-mail such as "noreply-removed-user-24#mysite.com"
What are the best practices to follow when we allow users to remove their accounts? How do you implement them (particularly in Rails)?
I've typically solved this type of problem by having an active flag on user, and simply setting active to false when the user is deleted. That way I maintain referential integrity throughout the system even if a user is "deleted". In the business layer I always validate a user is active before allowing them to perform operations. I also filter inactive users when retrieving data.
The usual thing to do is instead of deleting them from a database, add a boolean flag field and have it be true for valid users and false for invalid users. You will have to add code to filter on the flag. You should also remove all relevant data from the user that you can. The primary purpose of this flag is to keep the links intact. It is a variant of the renaming the user's data, but the flag will be easier to check.
Ideally in a system you would not want to "hard delete" data. The best way I know of and that we have implemented in past is "soft delete". Maintain a status column in all your data tables which ideally refers to the fact whether the row is active or not. Any row when created is "Active" by default; however as entries are deleted; they are made inactive.
All select queries which display data on screen filter results for only "active records". This way you get following advantages:
1. Data Recovery is possible.
2. You can have a scheduled task on database level, which can take care of hard deletes of once in a way; if really needed. (Like a SQL procedure or something)
3. You can have an admin screen to be able to decide which accounts, entries etc you'd really want to mark for deletion
4. A temperory disabling of account can also be implemented with same solution.
In prod environments where I have worked on, a hard delete is a strict No-No. Infact audits are maintained for deletes also. But if application is really small; it'd be upto user.
I would still suggest a "virtual delete" or a "soft delete" with periodic cleanup on db level; which will be faster efficient and optimized way of cleaning up.
I generally don't like to delete anything and instead opt to mark records as deleted/unpublished using states (with AASM i.e. acts as state machine).
I prefer states and events to just using flags as you can use events to update attributes and send emails etc. in one foul swoop. Then check states to decide what to do later on.
HTH.
I would recommend putting in a delete date field that contains the date/time the user unsubscribed - not only to the user record, but to all information related to that user. The app should check the field prior to displaying anything. You can then run a hard delete for all records 30 days (your choice of time) after the delete date. This will allow the information not to be shown (you will probably need to update the app in a few places), time to allow the user to re-subscribe (accidental or rethinking) and a scheduled process to delete old data. I would remove ALL information about the member and any related comments about the member or their prior published data (photos, etc.)
I am sure it changing lot since update with Data Protection and GDPR, etc.
the reason I found this page as I was looking for advice because of new Apply policy on account deletion requirements extended https://developer.apple.com/news/?id=i71db0mv
We are using Ruby on Rails right now. Your answers seem a little outdated? or not or still useful right now
I was thinking something like that
create a new table “old_user_table” with old user_id , First name, Second name, email, and booking slug.
It will allow keep all users who did previous booking. And deleted their user ID in the app. We need to keep all records for booking for audit purpose in the last 5 years in the app.
the user setup with this app, the user but never booking, then the user will not transfer to “old_user_table” cos the user never booking.
Does it make sense? something like that?

Resources