How to save historical data in Rails? - ruby-on-rails

What is the best way to save historical data?
One way would be just using what I already have - rails g scaffold value_history_datapoints item:references value:float - then just iterate through items and create ValueHistoryDatapoint for each of the item.
BUT
The database is already clunky, has lots of datapoints, all of which must be kept, but there are some data that are already obsolete and useless (old items that will never be used again, if nothing goes south).
Would it be better to somehow store all the data as json in the item's model? Or is it just better to stick to relations?

Have you checked the paper_trail gem? It creates a versions table that can store object data and changes for each CRUD operation.

Related

Ruby on Rails: ActiveRecord with list of data (like has_many for pieces of data, not objects)

If I want to have a record in Rails that has many options, such as a selection of times, then what is the best method for storing this in the record? I don't really want to setup the times as objects and use has_many, since 12:17pm doesn't really make sense as an object. What do I use to put a list of variable size into an ActiveRecord?
As an example, consider a DB of videos in a video collection. And for each video we want a list of all the dates that it was watched. We also have no need to search dates across all the videos, we just want to be able to list and maybe edit the dates for a given video object.
I understand why it seem unaesthetic to have the Times as their own database table but it might be the easiest option.
Database tables often get created which aren't pretty - join tables, for examples. Perhaps the model could be called TimeOption and it would have a foreign key. Seems like you already understand how to do, this though.
I'm also assuming the options are dynamic, otherwise they can just be stored in an application constant.
Another option is to use Postgres' array data type, which is new but I haven't used.
Another option is to serialize the array of Times into json, yaml, or csv and then store the serialized string in the database. Deserialize the data to read it and reserialize to save.

Creating a grouped text log of changed model attributes

I've recently started looking into Ruby on Rails, and I've set up a basic system to scan an parse and XML datasource, storing the elements in a MySQL database.
I'm intending to run the script as a rake task at set intervals, so want to track additions and updates, outputting the new, or changed, values to a text file.
I initially looked at using the before_save in order to write self.changes to a file, however the complexity arises as I'm retrieving data from two different pages and want to group the log output, e.g note each pricing row is a different record in the same table, ignore the variable names these are examples.
Item GUID
- Price US: #{old price} to #{new price}
- Price UK: #{old price} to #{new price}
The solution I'm currently looking to implement is appending a logged column to the table, if the data changes I can set this to changed, or new if the record has been added, and use this in a query to find records in which logged is not NULL, and group them by GUID. However as this will execute after the object has been saved I lose knowledge of the past values.
Is there a different approach I could take to achieve something like this?
Yes, there is a better way to do this. Take a look at these options you've got:
audited gem: https://github.com/collectiveidea/audited
paper_trail gem: https://github.com/airblade/paper_trail
espinita gem: https://github.com/continuum/espinita

Rails - Good way to support creation of drafts for several models

I want to allow users to create drafts of several models (such as article, blog post etc). I am thinking of implementing this by creating a draft model for each of my current models (such as articleDraft, blogpostDraft etc.). Is there a better way to do this? Creating a new model for every existing model that should support drafts seems messy and is a lot of work.
I think the better was is to have a flag in the table (ex: int column called draft), to identify if the record is a draft or not.
Advantages of having such a column with out a separate table, as I can see:
It's easy to make your record non-draft (just change the flag)
you will not duplicate data (because practically you will have the same in draft and non-draft records)
coding will be easy, no complex login
all the data will be in one place and hence less room for error
I've been working on Draftsman, a Ruby gem for creating a draft state of your ActiveRecord data.
Draftsman's default approach is to store draft data for all drafted models in a single drafts table via a polymorphic relationship. It stores the object state as JSON in an object column and optionally stores JSON data representing changes in an object_changes column.
Draftsman allows for you to create a separate draft model for each model (e.g., article_drafts, blog_post_drafts) if you want. I agree that this approach is fairly cumbersome and error-prone.
The real advantage to splitting the draft data out into separate models (or to just use a boolean draft flag on the main table, per sameera207's answer) is that you don't end up with a gigantic drafts table with tons of records. I'd offer that that only becomes a real problem when your application has a ton of usage though.
All that to say that my ultimate recommendation is to store all of your draft data in the main model (blog) or a single drafts table, then separate out as needed if your application needs to scale up.
Check out the Active Record Versioning category at The Ruby Toolbox. The current leader is Paper Trail.
I'd go down the state machine route. You can validate each attribute when the model's in a certain state only. Far easier than multiple checkboxes and each state change can have an action (or actions) associated with it.
Having a flag in the model has some disadvantages:
You can not save as draft unless the data is valid. Sure, you can skip validations in the Rails model, but think about the "NOT NULL" columns defined in the database
To find the "real" records, you have to use a filter (like "WHERE draft = FALSE"). This can slow down query performance.
As an alternative, check out my gem drafting. It stores drafts for different models in a separate table.

CoreData - Is there a good way to upsert items?

Just wondering whether there is a good way to upsert items in a CoreData db?
Or is there a way for me to consider a CoreData db as a set?
I mean, if I insert an item into the db and if there is already an identical redundant item there, the db ignores it. Any way to conveniently do it? or I have to query each time when I insert in order to avoid redundancy?
I recently read this article
https://www.upbeat.it/upsert-in-core-data/
Basically, first create a constraint on the name, then you specify the TrumpMergePolicy in the Core Data context.
managedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
No - Core Data doesn't have a way of knowing how you consider an item "identical" or "redundant." The definitions of those words can change with almost every entity you create - for example, departments in a business might have unique names, but multiple people can have the same name (and frequently do).
You can take advantage of Core Data's querying power, however, and do a quick query with an NSPredicate to find out whether a record with your chosen identifier already exists. You might factor this query out to its own method (perhaps on your managed object subclass) so that you can call it conveniently.

Tracking changes on instances of a class and their associations - thoughts?

I have a class Question which has a lot of assocated models. On one page on my app i list a summary of all the current questions, with various info from associated records. Ultimately this is a hash of values that i then just print out into a csv-style row (i'll call this the 'row hash' from hereon)
I now have a requirement to only show the changes to questions (or their associated data) over a given period. I'm currently deliberating the best way to do this. Here's some ideas i've had so far, i'd welcome any feedback, thoughts, suggestions etc.
1) Approach 1 - acts_as_audited
This was my first thought as i've used this before in other apps. The porblem with aaa though is that it only tracks changes to the record's data (ie it doesn't care if the associations change). So, i could audit all of the associated records as well but then trying to piece together what had changed by tying different audit records together sounds like a nightmare.
2) Save the old and new hash out into serialized fields: ie
- when someone goes to the question/edit page, i calculate the current row hash and save it in a serialized field "old_data" in the question table. Then after they save the question i calculate the new current row hash and save it into a serialized field "new_data" in the question table. Also, i compare the two serialized hashes and save the differences into another serialized hash field 'changes'. Now to do my report i just look for questions updated in the last x days and output their changes data.
3) make a view
- i make a view which corresponds to the data that i want to output (ie that amalgamates all the data that i pull into my report). Then i track changes to the view - somehow. I'm not sure how exactly i would do that.
I'm leaning towards option 2 right now.
Any other thoughts/comments? grateful for any suggestions - max.
So, like you said, you only want to show changes to the records between time x and time y, right? This would seem perfect to me using the acts_as_audited plugin because you end up with a table of changes, right? So make a has_many_through association from Question to all these related tables, then search it for related changes, where date created is after time X. This would return a list of changes. From there, you could connect this list back to the parent object if you need to, or whatever - but it in the end seems like a more reasonable thing to search. You're not looking for a list of related objects, after all, you're looking for a list of changes, so having a table of changes seems a reasonable way to accomplish that?
Hey I had a similar problem, check this out. If you can, go with Mongoid or Mongomapper, embedded versioned documents are sweet.
Thanks guys. I ended up rolling my own solution because what i really needed to do was to capture changes in the results of various methods called on the object, some of which involved associated objects. I wasn't so much interested in the associated objects as (for example) a text string generated as a result of looking at a few different associated objects. I had methods to do all of this already so i really just needed to track changes in the results of calling these methods.
None of the plugins i saw could really do that simply and effectively, so i ended making a table called states which just holds a serialized hash with results of all of these method calls. This gets saved when the record is altered and saved or when any of the relevant associated objects get altered and saved. Then i have some methods to return the differences between different saved state records. It works well for my needs. Thanks very much for your advice anyway.

Resources