All threads / How do I calculate a computed value in a model when showing or indexing over that model in its controller?
Ask A Question

Notifications

You’re not receiving notifications from this thread.

How do I calculate a computed value in a model when showing or indexing over that model in its controller?

Jordan T-H asked in Rails

I'm new to Rails but not new to programming or webdev.

I have a Student model that uses a service object to pull data from Google Cloud Firestore for its level attribute, an integer. I have hooked the service object's method into an update_level method on the Student model using the before_save ActiveRecord callback. This method checks the last time the student's level was updated from a column in the database and if it's been more than 10 minutes it will fire the method to check Cloud Firestore. This works fine when creating or updating these objects, but one of the reasons for this Rails app is to present up-to-date information to teachers about their students. (I promise this isn't creepy learning analytics, it's for a bespoke game for a special curriculum.)

What is the best, most Rails-like way to hook this functionality into the index and show controller methods, such that if I'm showing a Student record or indexing over a list of Students, fire the same update_level method (or one like it) for each student. Now, my mind immediately starts thinking about some kind of job queue or ActionCable solution to this, but that seems far too complicated. I'm trying to minimize unnecessary calls to Firestore, but I want to make sure the data I'm presenting is relatively up to date.

What is the Rails way of doing this?

If it helps, here's the gist of the code fire off to update a Student's level. FirestoreGetter is my service object that wraps Google's Cloud Firestore gem with some formatting niceties.

  def firestore_getter
    @firestore_getter ||= FirestoreGetter.new
  end

  def update_level
    if self.last_level_checked.nil? || self.last_level_checked < 10.minutes.ago # FIXME: abstract this to some setting
      retrieved_level = firestore_getter.retrieve_student_level username
      self.level = retrieved_level
      self.last_level_checked = DateTime.now
    end
  end

After sleeping on it, I believe the best way (and the way that requires the fewest queries to Cloud Firestore) is to just download the whole Firestore DB and update from that object every 10 or so minutes with a scheduler of some kind.

I believe the answer to my original question is something like: do it in the controller's index and show methods, but it will be bad and inefficient and slow and violate RESTful practices and smells bad.

Join the discussion

Want to stay up-to-date with Ruby on Rails?

Join 66,029+ developers who get early access to new tutorials, screencasts, articles, and more.

    We care about the protection of your data. Read our Privacy Policy.

    logo Created with Sketch.

    Screencast tutorials to help you learn Ruby on Rails, Javascript, Hotwire, Turbo, Stimulus.js, PostgreSQL, MySQL, Ubuntu, and more. Icons by Icons8

    © 2022 GoRails, LLC. All rights reserved.