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.
Related
There is something I don't really get with ruby-on-rails (I'm very new to it).
If my understanding is correct we use the model objects in the views.
The model object is the exact representation of the database. But in a lot of cases what we want to show in the view isn't the exact representation of the database.
Let's say we have an object line in the database:
line [line_id, quantity, category_id]
So if I want to show a list of lines there is no problem I can use the model object "line". But what if I want to show one line by category with a sum of the quantity for that category ?
Should I use the line object ? I feel bad about that because each line will not reflect a line in the database.
Should I create another kind of object ? Some sort of ViewModel that doesn't exist in the database but is usefull for rendering.
I'm not sure this is very clear... Thanks in advance for any help.
Always displaying data exactly as it is in the database happens only in tutorials :)
In real-world apps it is often necessary to transform data before presenting. This has many names: ViewModel (as you mentioned), Decorator, Presenter and others. So yes, make new objects for this, there's no reason not to.
If you display categories with a column in the view that shows the total quantity for each category, it would make sense to use the Category class in your application. This should be an ActiveRecord model.
On this model, you can define a method that reads the lines and sums the quantity. It could look like this:
def total_quantity
lines.map(&:quantity).sum
end
This method will read your lines (assuming you have set up a has_many :lines relationship in the Category class. Then it will read the quantity method on each lines and put the result in an array. Finally it will add the values together.
Note that this approach is a starting point and not very fast for larger sets of data. The approach can be improved through either lazy loading or specialized queries.
As your application grows in size, the number of methods such as the one above may grow in size to the point where the Category class becomes hard to understand. At this point, you may want to start looking for an intuitive ways to extract these methods into separate classes.
I am building a ruby on rails application where a user can learn words from a story (having many stories on his list of stories to learn from), and conversely, a story can belong to many users. Although the story is not owned by the user (it's owned by the author), the user can track certain personal things about each story that relate to him and only to him, such as how many words are left to learn in each of his stories (which will obviously differ from user to user).
Currently, I have a has_many :through relationship set up through a third table called users_stories. My concern/question has to do with "calculated fields": is it really necessary to store things like words_learnt_in_this_story (or conversely, words_not_yet_learnt_in_this_story) in the database? It seems to me that things like this could be calculated by simply looking at a list of all the words that the user has already learnt (present on his learnt_words_list), and then simply contrast/compare that master list with the list of words in the story in order to calculate how many words are unlearnt.
The dilemma here is that if this is the case, if all these fields can simply be calculated, then there seems to be no reason to have a separate model. If this is the case, then there should just be a join model in the middle and have it be a has_and_belongs_to_many relationship, no? Furthermore, in such a scenario, where do calculated attributes such as words_to_learn get stored? Or maybe they don't need to get stored at all, and rather just get calculated on the fly every time the user loads his homepage?
Any thoughts on this would be much appreciated! Thanks, Michael.
If you're asking "is it really necessary to store calculated values in the DB" I answer you. No, it's not necessary.
But it can give you some pros. For example if you have lots of users and the users call those values calculating a lot then it could be more winnable strategy to calculate them once in a while. It will save your server resources.
Your real question now is "What will be more effective for you? Calculate values each time or calculate them once in a while and store in DB?"
In a true relational data model you don't need to store anything that can be calculated from the existing data.
If I understand you correctly you just want to have a master word list (table) and just reference those words in a relation. That is exactly how it should be modelled in a relational database and I suggest you stick with it for consistency reason. Just make sure you set the indices right in the database.
If further down the road you run into performance issue (usually you don't) you can solve that problems then by caching/views etc.
It is not necessary to store calculated values in the DB, but if the values are often used in logic or views its good idea to store it in Database once(calculate again on change) and use from there rather then calculating in views or model.
Fast questions:
Is possible to build multiple level nested objects? How to do this?
#main_object = Object.new
#main_object.build_anotherobject
THe questions are: how to build more objects inside theese children objects? And is that a correct thing to do in Rails?
#main_object.another.others.build??
I don't know how to do that and if this is a good way to achieve multiple level nested objects.
Another question is about handling foreign keys in these nested objects. Some light how to get the correct FK?
I see that i would need to save the objects in a correct order because i need to validate the existence of a row in a parent table and then insert the valid FK inside a child with parent_id.
I could forget a big part of this problem and put many informations inside 1 table instead, but i would like to learn in a professional way, avoiding repeated informations in DB isn't a good point? For example, many people/user lives at same states, same cities, so i'm thinking that i shouldn't save the same strings over and over.
THanks for all attention!
You will need to assign it to a variable:
another_object = #main_object.build_anotherobject
And then:
another_object.even_more_objects.build
Repeat as you need to.
In Rails, when a record is to be deleted, I want to maintain a separate table for such deleted records (that in structure would be analogous to the former).
One way to achieve this would be to obviously copy the structure, validations and associations from the first model and paste it into the deleted items model. This would, however, result in a lot of code redundancy and is not a scalable solution.
Is there a way to achieve this in Rails without much (or any) code redundancy
or a solution that might be more scalable than the one mentioned
above?
I am using Ruby 1.9.3-p125 and Rails 3.2.
UPDATE
I did consider using an additional is_deleted column in the table, however, I decided against it because I didn't want this table to get too big and messy with deleted posts. I don't intend to really access these deleted posts - these are merely stored for record-keeping or archival purposes. Adding this column would also make accessing this table slower and more importantly, I am afraid that I may miss the check is_deleted == false in some SQL condition somewhere - even if I include this check in the default_scope of the model.
It is good idea to move them to separate table so that your primary table have less number of records and performance is not decreased by time.
Use Rails ActiveRecord Callback for deletion i.e.
before_destroy :move_to_trash
.
.
def move_to_trash
Trash.create!(self)
end
In this way, when a record is deleted, its copy will be created in Trash table.
Well, basically you want to keep the records and not throw them away. So you may want to just mark them "deleted" and tweak the logic in your code to not consider those records while retrieving them.
Add a 'deleted' column in your original table. Set the default scope of the model to exclude deleted records.
sorry if it's duplicate, I've been looking for something similar before posting, but no success.
So basically I need an up/down rating system ( it's very similar to the one here on stackoverflow ).
I have 6 rateable models, so polymorphic association seem to be the best option.
But since this table will probably hold a solid number of records, won't it take too much time to get the item rating ( upvotes_count - downvotes_count )?
I actually though about adding a new row to each and every rateable model, something like current_rating_value. So that this value will be changed every time the rating object will be created/destroyed.
Could you please advice, what might be a better option in this case? Extra UPDATE call to change the current_rating_value or an extra SELECT(SUM...) call to calculate it?
Depending on how much traffic you get loading these models it could tax resources more then desired. I've ended up using acts_as_votable gem in my projects. Its very easy to make a model votable and it also caches the data which is also awesome.
for the long-term performance, you should add something like current_rating_value into every ratable model. Everytime there is new rate, just update this field.
Otherwise, it will be very harmful to performance when you have to calculate the rating everytime.