How to break logic and avoid coupling in this scenerio - ruby-on-rails

I have a item review system where people vote on an item.
The UI also displays how they voted, up/down.
The user can get points when they vote.
The item object that they vote on, has to reflect the # of voters and other stats.
So there is allot of things going on here, like if someone voted previously, I have to change their vote and therefore update their statistics and the statistics on the object voted on.
How can I possible break these different tasks out, since they all seem to be highly coupled with each other and I cant' figure out a way how to avoid having my code have all these different interactions mixed together.
Example method:
Set_Vote
1. is this an old vote?
-> if old vote, change old vote to new vote value
-> otherwise, insert new vote
2. update user stats
3. update item voted on stats
4. return a json response to the UI with information to update the screen via ajax etc.
Now it may seem easy to de-couple until you get to coding. Like for example, I can't really set the user stats unless I know the outcome of the logic from #1 since if its an old vote, I have to reverse what happened earlier, and then apply the new values (same with #3).
Should the method in #1 have a very rich return object that tells me things like 1) it was an old vote, or it wasn't? what was the outcome of the attempt to vote? (maybe the vote was rejected for whatever reason in my logic).

Related

Most elegant way to keep track of "last active" time

I have a website where I need to be able to display on each user's profile the last time they were "active" on the site. In this case, "active" is defined by browsing content, interacting with other users and completing courses.
My plan is to have a last_active column on the Users table, which I can update with Time.now. The question is, how do I do this without hitting up the database during every single controller action? That seems... expensive. For example, I want to avoid doing this:
# In each controller
def index
current_user.activity
end
# In the User model
def activity
self.update_attribute(:last_active, Time.now)
end
Because then every time a user gets the content listing, I have to make a database call.
The other option would be to have an Activity table, which I update with various user actions (kind of like audits). That would allow me to store and display more relevant information about what users are doing. But that goes back to the same question: how do I update these tables without massive overhead?
It's really a rather moot question – No, there is no way to update the database without updating the database.
If you wanted to get complicated, you could try to do some client-side scripting to store that information in a session variable or a cookie, and only commit it to the db once in a while, but that seems like a lot of work for a small feature.
Maybe if you add an index to your :last_active column you make it marginally less expensive? But otherwise, I would just go for it, and try to be conservative about how often it's updated.
You could also check if Time.now > #user.last_active+10.minutes before updating to make sure that you aren't constantly writing to the db, but then your just querying instead which may not be better...
I don't think there is a way around the overhead without restricting when you update a particular user's 'last active' attribute.
So as Charlie Egan alluded to, you have two options:
Only update the 'last active' attribute when the user logs in. You'll still get a decent sense of a user's general activity on the site just by doing this.
Or
Only update the 'last active' attribute on certain activity. For example, you mentioned in your question that users can complete courses. That seems like a fairly significant 'activity', so update the 'last active' attribute. Less significant activities, like browsing content can be ignored.
Don't you think about redis|memcache|any_in_memory_storage for such data?
If you're fine with async updates, you could set up a delayed resque job, deleting previous jobs.
Resque.remove_delayed_selection RecordLastUserActivity, {
|args| args[0]['user_id'] == current_user.id
}
Resque.enqueue_at(10.minutes.from_now, RecordLastUserActivity,
user_id: current_user.id, last_seen_at: Time.now)
Not sure if this will provide greater perfomance though, it will require some testing.

"Archive" a table on rails

long time reader first time poster.
I recently started using ruby on rails so I am still very new to the environment (even though I have completed a few guides) so be gentle please.
What I want to do is create a sort of archive table of another table that the user can access at any time(via a different link on the website).
So for example, if I have the "users" table, I want to be able to archive old users but still give the option for someone to go and view them.
Basically, it will sort of have to delete the user from the initial table, and save his/her info in to the archived_users table.
Thank you for your time.
I figured my comment was more of an answer, so posting it here and adding more info
In this situation you're better off adding some sort if "active" flag to the users table, which you can flip on or off as needed. That way you don't need to worry about dealing with yet another model class, and you can reuse all the same view and controller structures. In your views, you can then simply "hide" any inactive users (and maybe only show inactive folks if the logged in user is an admin...etc).
You also have the freedom to include other meta data such as "deactivated on" (time stamp) for example.
Long story short, if you're concerned about performance, with proper indexing (and partitioning if necessary), you shouldn't really need to create a separate archive table.
The only reason I can think of to do this is if you're dealing with billions upon billions of records, and/or growing by an insane amount (which is probably not your case).
The best way to do this is probably to add a column called deleted on the original Users table. You can then filter out the old users in normal circumstances (preferably using a default scope) but allow them to be seen/queried when needed.
Papertrail might work for you.
It creates a "versions" table and logs create/update/destroy events for any class which includes has_paper_trail. For example:
class User < ActiveRecord::Base
has_paper_trail
end
deleted_users = Papertrail::Version.where(item_type: User, event: "destroy")
deleted_users.last.reify.name # assuming the users table has a 'name' column

Implementing a delayed association in Rails

Here's a programming challenge I'm facing. I'm new at this...
I'd like to do this the Rails Way, with elegant code, and learn something in the process.
In my app, Players track an Action (25 minutes of focus starting on the half-hour). The action#new form loads at the beginning, but is not saved until the end of the 25 minutes. The last 2 minutes are a retrospective and include some required "how did it go" questions.
I now want to add an Insights table. I'm picturing a separate, small "new Insight" form that blends into the larger "new Action" form. I want to save the Insight at whatever time it comes to me.
I also want the Insight to be associated with that particular Action... even though the Action hasn't been created yet, and I don't know what id it will have (somebody else may record their Action before I do). If an Insight was created but the Action fails, I'm not yet sure what to do... I could allow Action-less Insights.
How do I implement this? A temp variable? Where would that go?
Again, I'm brand new at this, and I appreciate your support.
Mihai
TheActionGame.com
I'm note sure about what your action represent, but even if you have some time-based principle, nothing prevent your for saving the Action before (for a browser-based application, 25 minutes is a lot of time during which a lot of bad things can happen, like a refresh or a connection failure).
My advice would be to create (save) the Action immediately, thus allowing your Insight to be linked to it. I would add a "status" column to Action, with something like "new" (the base status), "confirmed" and other if needed.
You can then work only on the "confirmed" Actions, and eventually delete the "new" one on a regular basis (like all that are older than 30 minutes, with your thresold of 25).
Hope this make sense (more design than Rails, anyway).
You could add a column to your actions "insight_ids" as an array and implement it as a has_and_belongs_to_many.
Then when you have an insight, record the insight's id in the array

How to design a database in rails where all tables can be filtered by a global setting? Default global scope?

I am building a rails app and the data should be reset every "season" but still kept. In other words, the only data retrieved from any table should be for the current season but if you want to access previous seasons, you can.
We basically need to have multiple instances of the entire database, one for each season.
The clients idea was to export the database at the end of the season and save it, then start fresh. The problem with this is that we can't look at all of the data at once.
The only idea I have is to add a season_id column to every model. But in this scenario, every query would need to have where(season_id: CURRENT_SEASON). Should I just make this a default scope for every model?
Is there a good way to do this?
If you want all the data in a single database, then you'll have to filter it, so you're on the right track. This is totally fine, as data is filtered all the time anyway so it's not a big deal. Also, what you're describing sounds very similar to marking data as archived (where anything not in the current season is essentially archived), something that is very commonly done and usually accomplished (I believe) via setting a boolean flag on every record to true or false in order to hide it, or some equivalent method.
You'll probably want a scope or default_scope, where the main downside of a default_scope is that you must use .unscoped in all places where you want to access data outside of the current season, whereas not using a default scope means you must specify the scope on every call. Default scopes can also seem to get applied in funny places from time to time, and in my experience I prefer to always be explicit about the scopes I'm using (i.e. I therefore never use default_scope), but this is more of a personal preference.
In terms of how to design the database you can either add the boolean flag for every record that tells whether or not that data is in the current season, or as you noted you can include a season_id that will be checked against the current season ID and filter it that way. Either way, a scope of some sort would be a good way to do it.
If using a simple boolean, then either at the end of the current season or the start of the new season, you would have to go and mark any current season records as no longer current. This may require a rake task or something similar to make this convenient, but adds a small amount of maintenance.
If using a season_id plus a constant in the code to indicate which season is current (perhaps via a config file) it would be easier to mark things as the current season since no DB updates will be required from season to season.
[Disclaimer: I'm not familiar with Ruby so I'll just comment from the database perspective.]
The problem with this is that we can't look at all of the data at once.
If you need to keep the old versions accessible, then you should keep them in the same database.
Designing "versioned" (or "temporal" or "historized") data model is something of a black art - let me know how your model looks like now and I might have some suggestions how to "version" it. Things can get especially complicated when handling connections between versioned objects.
In the meantime, take a look at this post, for an example of one such model (unrelated to your domain, but hopefully providing some ideas).
Alternatively, you could try using a DBMS-specific mechanism such as Oracle's flashback query, but this is obviously not available to everybody and may not be suitable for keeping the permanent history...

Record changes pend approval by a privileged user; Its like versioning combined with approvals

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.

Resources