Using Ruby on Rails, what is an efficient method of ordering separate users post_id's sequentially? - ruby-on-rails

My domain structure is similar to this:
www.domain.com/:user_id/post/:post_id
i.e. www.domain.com/user1/post/1
I would like it to work where each user's post increments up the user's last :post_id, disregarding the actual ID of the database row, as multiple users posts will be stored there, like so:
www.domain.com/user1/post/1
www.domain.com/user1/post/2
www.domain.com/user2/post/1
www.domain.com/user2/post/2
I understand I will need a "secondary_id" column in the Post database and before committing a post to the database I will need to query the last post of the user to obtain that secondary_id to increment but I'm unsure where this logic would best reside in my app structure and what can I do to simplify/automate the process?
Also, how can I avoid race conditions on the secondary_id if this where implemented in a team environment where users could be submitting posts in between when another user has queried for the last secondary_id to increment and the second user would error out since the other user got to that secondary_id first?

Related

Sort by timestamp on has_many after scope

I have a User object, a Package object (User has_many packages) and then a LocationTracker (User has_many location_trackers), which acts as a join table between User and Package, but just tracks details such as the most recent package delivery.
I'd like to sort my Users based on the most recent package they sent. The LocationTracker has an attribute last_received_from_user
I can easily sort the users from a certain location by ordering by the last_received_from_user attribute, however I'd also like to have a global index page that shows all of the Users, sorted by the last package they delivered.
I'm having trouble grouping the users. I'm attempting to use a DISTINCT ON(last_received_from_user), but then it complains that the attribute isn't in the group, and when I add it to the group, it groups by that timestamp, which is obviously pretty unique, so I get duplicate users showing up.
My current code is as follows:
User.includes(:location_trackers)
.group("location_trackers.user_id, users.id")
.order("location_trackers.last_received_from_user #{order} NULLS LAST")
Any help is greatly appreciated!
EDIT:
I've got the last_received_from_user which allows me to sort users from a SINGLE location well. However, I need to be able to scope based on what could be a number of different options. For example, only show users in a certain area (Which could be compromised of a few locations), or order by ALL users for ALL locations. The attribute works great for a single user-location relationship, but fails when it comes to attempting to perform the search on more than 1 location.
I'd like to sort my Users based on the most recent package they sent
Wouldn't it be easier (and way more efficient) having an attribute like latest_delivery_location and using a callback on the User model like:
class User < ApplicationRecord
after_update :update_latest_delivery_location
private
def update_latest_delivery_location
update_attributes(
latest_delivery_location: location_trackers.last.last_received_from_user
)
end
end
Or updating such attribute after an order has been placed / dispatched. I'd go for this approach because is easier to maintain and, if you want it more performing you could always add an index on users.latest_delivery_location for sorting operations.

Octopus gem dynamically select shard for an associated model based on shard_id

We have a users table. Users have many listings.
We'd like to shard the association model Listing such that all users stay on database "master" shard. Users will get a shard_id column and listings will be split into different databases "shard1", "shard2".
We can augment our code to access the listings on the correct shard using the using method:
Listing.where(user: current_user).using(current_user.shard_id)
However that is a big code change. Ideally we want to just keep using our existing association statements like this:
current_user.listings
And have it automatically use current_user.shard_id beneath the hood.
Any suggestions for doing this?
According to the documentation, current_user.listings should work out of the box.
Octopus also handles associations. When you try to get a object that is associated to another object, you could use normal ActiveRecord syntax to get the objects
https://github.com/thiagopradi/octopus/wiki/How-Octopus-Works

Generate random human understandable ID for users in rails

I want to generate a unique (human recognisable) id for each user that changes when they comment on different posts in my app. (so doesn't need to be stored in db as is constantly changing). The ID only needs to be used within the scope of a thread.
What I mean by this is identical to what YikYak does. In YikYak, anonymity is essential, so in each thread, users are assigned a randomly generated avatar which is the only way they and other people recognise which posts are posted from the same user.
I'm building an app where I too need anonymity, but I need users to be able to recognise when other people are commenting more than once.
What is the best thing to generate? A string of 2 random words like "Brocolli umbrella"? Or is there a way to randomly generate a simple avatar?
Any advice much appreciated
You might try ffaker gem and use
FFaker::Name.name
#=> "Christophe Bartell"

"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

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