Peter Marcano

Joined

5,150 Experience
33 Lessons Completed
2 Questions Solved

Activity

Posted in Trouble with a lending library query

Tabish, you helped bring me to a solution!

I think you're right. Querying this information was rather complex and a state machine design is better.

I didn't want to add a gem since I avoid dependencies when I can, so this is what I came up with.

class Book < ApplicationRecord
  has_many :check_outs

  enum state: [:available, :checked_out, :damaged]

  def change_state
    damaged! if damage_info.present? # If there's a note about the poor condition, switch to damaged

    if book_checked_out?
      checked_out!
    else
      available!
    end
  end

  private

  def book_checked_out?
    check_outs.where(checked_out_at: nil).present?
  end
end

class CheckOut < ApplicationRecord
  belongs_to :patron
  belongs_to :book

  validates_uniqueness_of :book, scope: :checked_out_at # make sure no book can be checked out twice at the same time
  after_save :change_book_state

  private

  def change_book_state
    book.change_state
  end
end

Basically using ActiveRecord callbacks to change the state and enums to keep the state and add the available query.

Much simpler than the crazy join queries I was writing and failing at before.

Thanks for the tip!

Posted in Trouble with a lending library query

Let's say you operate a library.

You have so many patrons and so many books you need a Rails app and database to keep your sanity.

The basic schema looks something like this:

create_table :books do |t|
  t.bigint :id
  t.string :title
end

create_table :patrons do |t|
  t.bigint :id
  t.string :name
end

create_table :check_outs do |t|
  t.references :book, foreign_key: true
  t.references :patron, foreign_key: true
  t.datetime :checked_out_at, null: false
  t.datetime :checked_in_at
end

A simple "check out" keeps track of where a book is at any given time (checked out to someone or in the library's possession).

In this application you want to be see which books are available for patrons to check out.

I have thought of two situations that would make a book 'available'.

  1. The book is new and has never been checked out.
  2. The book was checked out but has since been checked in.

The first is situation is easy. Look for books that don't have any check_outs...

class Book < ApplicationRecord
  has_many :check_outs

  scope :never_checked_out, -> { left_joins(:check_outs).where(check_outs: { book_id: nil }) }
end

The second situation is stumping me for some reason.

I can query the books and join the check outs table to see which books are currently checked out... (pretty much the opposite of what I want)

...
  scope :currently_checked_out, -> { left_joins(:check_outs).where(check_outs: { checked_in_at: nil }) }
...

... and the logical opposite query lets me know which books have been returned in the past...

...
  scope :books_returned, -> { left_ joins(:check_outs).where.not(check_outs { checked_in_at: nil }) }
...

... but... this books_returned query will also return any book that had been previously checked in, even if it is currently checked out.

How would I put together a query that can return only the books in my possession?

Once I get these queries to work, I could also use some help figuring out how to merge them into a single database query. I've gotten as far as this (bonus if you have a better solution!):

...
  scope :available, -> { books_returned + never_checked_out }
...

Haha oh yes please for a screen cast...

I found this tutorial that seems to be useful https://www.railsagency.com/blog/2016/10/14/simple-polymorphic-selects-with-global-ids/

For the sake of this question, lets assume I run a hotel and I want to make a system that manages the bookings of my hotel rooms.

While most of the time I book Guests in my hotel rooms, Employees have the ability to book a room too.

The basic relationships begin to look like this.

class Guest
  has_many :bookings, as: :bookable
end

class Employee
  has_many :bookings, as: :bookable
end

class Booking
  belongs_to :bookable, polymorphic: true
  belongs_to :room
end

class Room
  has_many :bookings
end

I want to create a form to create bookings. How would you go about creating a form element that can select a guest or an employee?

Posted in Do you measure your app's uptime?

I was leaning towards HoneyBadger I just didn't know if consensus of the crowd was different! Thanks, I'll set that up.

Posted in Do you measure your app's uptime?

If so, than how do you measure it? Any libraries, tools, services, etc worth investigating?

I think I finally figured it out. Took long enough but I figured I'd share it.

class Student < ApplicationRecord

  has_many :enrollments
  has_many :klasses, through: :enrollments

  scope :all_in_klass, ->(klasses) { 
        joins(:enrollments)
            .where(enrollments: {klass_id: klasses} )
            .having('COUNT(*) = ?', klasses.size)
            .group(:id)
    }
end

Student.all_in_klass([@sci.id, @math.id])

So the trick I have here is to query the join table for matching ID's (does with WHERE IN when passing arrays), then group them by the student ID and avoid duplicates. Before grouping, the HAVING clause filters out results which don't share the amount of classes in the array (two classes in this case).

Further testing required as I am worried about that last HAVING clause. Not entirely sure I have a good understanding of how the SQL works but RSpec is all green so I am going with it.

Ugh... turns out this isn't solved! I am getting all kids enrolled in science even if they don't take math!

I added more data and tested a few more scenarios and found out it wasn't working properly.

I will return with an update, but until then, if anyone has any recommendations I'd love to hear them!

HEY I GOT BOTH TESTS TO PASS!! THANKS SO MUCH ASHLEY!

Current solution until I find a better way to refactor it (and hopefully into one query since this method will likely be called a lot)...

class Student < ApplicationRecord

  has_many :enrollments
  has_many :klasses, through: :enrollments

  scope :all_in_klass, ->(k){ joins(:enrollments).where(enrollments: {klass: k} )}
end
def find_students_who_share_classes
    in_math = Student.all_in_klass(@math)
    in_sci = Student.all_in_klass(@sci)
    in_math.merge(in_sci)
    return in_math.distinct
end

Well well well! That passed one of my Rspec tests but not the other! (So much further than I got before so thank you so much!)

Test 1 basically finds Charlie by passing in Phys Ed and Science eliminating the other two students.

Test 2 finds Alice and Bob as described above. But only Alice gets put out because of what I imagine is the distinct?

Sorry, this is a little confusing because I am translating my client's problem to a different domain of school children so may be unclear when testing your approach and returning the results!

I think I've been at this problem for too long and the obvious answer has evaded me.

Say you have students, classes (klass for ruby's sake), and enrollements as the join between them.

class Student < ApplicationRecord
    has_many :enrollments
    has_many :klasses, through: enrollments
end

class Enrollment < ApplicationRecord
    belongs_to :student
    belongs_to :klass
end

class Klass < ApplicationRecord
    has_many :enrollments
    has_many :students, through: enrollments
end

So far just a simple many-to-many.

Let's give some data and a scenario.

Students:

  • Alice
  • Bob
  • Charlie

Classes:

  • Math
  • Science
  • Literature
  • Phys Ed.

Alice is enrolled in Math, Science, and Phys Ed.
Bob is enrolled in Math, Science, and Literature.
Charlie is enrolled in Science and Phys Ed.

How could I construct a query to say "Who is enrolled in Math & Science?" and have the query return unique records "Alice & Bob".

I started going down the path of something like this, but keep getting tripped up somewhere:

Student.joins(:enrollments).joins(:klasses).where(klass: { id: [math.id, science.id] }).uniq

But since Charlie is enrolled in Science as well, he gets thrown into my results.

Again, I think I've overthought it and I am doing something insanely stupid. At this point I am assuming the answer is probably clear to everyone but me 🤣

Thanks for your help!

That could work? I'll test it later and let you know.

I'm surprised that Basecamp could develop a frameworks like Rails and Turbolinks and not account for SJR.

I did try to pass an index.js.erb file to do this work as well but it didn't render the modal properly... may be a problem with bootstrap dependencies?... I just don't know anymore 🙄

I like your thinking, unfortunately it doesn't work :(

My temporary solution is now:

<% if @show_modal %>
        <% content_for :head do %>
            <meta name="turbolinks-visit-control" content="reload">
        <% end %>

    <script>
            document.addEventListener("turbolinks:load", function() {
                $('#emailModal').modal('show');
            }
    </script>
<% end %>

Which isn't ideal as it make forces a full page reload upon navigating back to the page, but at least it doesn't reload every visit.

Here's to hoping I can be clear and concise about my problem. I am willing to venmo/paypal a beer's worth of cash to anyone who can help me 🍻

I currently have a bootstrap (3.3.7) modal that is, by default, inactive on the index page of a controller. It's just sitting there, waiting to pop up when I click a link.

In a scenario, I want that modal to automatically pop up (no link clicking required) when aformentioned index action is navigated to and a set of conditions are met.

On the index action of a controller (let's call it the "LearnController" for filename's sake), I perform a little bit of logic that compares cookie and database data to determine whether an instance variable holds a value of true or false in the index.html.erb view and conditionally runs some javascript to force open the modal.

# app/controllers/learn.rb
class LearnController < ApplicationController
  def index
      ...
      @show_modal = do_logic
    end

    private

    def do_logic
      # ... looks at cookies and database to 
        # do some logic and return true or false...
    end
end

# app/views/learn/index.html.erb
<% if @show_modal %>
    <script>
        document.addEventListener("turbolinks:load", function() {
            $('#emailModal').modal('show');
        }
    </script>
<% end %>

I am able to test out the do_logic I had written. It successfully compares the database & cookie data in the ways I want, and returns a true or false at appropriate times. Domain logic is good.

My issue now lies somewhere between my javascript architecture and Turbolinks.

When the controller makes the @show_modal = true, the modal shows up, I fill out the email form or dismiss it entirely. In either event, the cookie is updated to make sure the @show_modal returns false and won't return true again until other conditions are met in the future.

But, when I navigate around the app and back to the learn#index page, @show_modal returns false but the turbolinks:load event still fires and displays the modal when no javascript on the page asked it to.

If I refresh the page in the browser it seems to reload the turbolinks cache and everything works as designed until the next time @show_modal returns true and the browser caches that turbolinks:load behavior again and fires when it shouldn't.

Some interesting things I've found:

  1. Inspecting the page after the modal opens when it shouldn't, my <script> tags aren't present in the DOM. The Rails learn#index doesn't render that text again, yet the behavior stays until the cache is reloaded via a refresh on the browser.
  2. Not only does triggering a reload in the browser fix the issue, but adding <meta name="turbolinks-visit-control" content="reload"> to that specific page also seems to ensure that my code behaves properly. I don't like this solution because this index page is visited more than any page on the app and having turbolinks disabled on the most visited page makes the user experience slow and strange where it shouldn't be.

Do you see an obvious answer to this problem, or maybe you've had a similar struggle but implemented this one-off javascript differently?

Thanks!

Posted in How do I install bootstrap-v4 via yarn?

Nice! I think it's worth noting now that with webpacker 3.0 you no longer need to run ./bin/webpack-dev-server unless you want features like hot module reloading.

Running rails s will also compile webpack assets.

Posted in How do I install bootstrap-v4 via yarn?

Hi Drilon,

Sorry nobody got back to you. I had some trouble doing this myself a while back. As Chris informed me, the webpacker and the asset pipeline are two totally seperate things with similar goals. You don't use them together.

The webpacker documentation may be the best place to look for tutorials. But as a TL;DR I would do the following:

  1. Add bootstrap through yarn: yarn add bootstrap@4.0.0-alpha.6
  2. Create a file to be transpiled by webpacker: app/javascript/bootstrap.sass (or app.sass... whichever name suits you best.)
  3. Within bootstrap.sass import your newly added boostrap library: @import '~bootstrap/dist/css/bootstrap'
  4. In the head of your app/views/layouts/application.html.erb add <%= stylesheet_pack_tag 'bootstrap' %>.
  5. Run rails s in one terminal window tab and ./bin/webpack-dev-server in the other so webpacker can compile your assets and serve them in your app.

Note that in the webpacker documentation you can import the bootstrap sass using the javascript pack tag, but I don't mix my scripts and stylesheets. Personal preference.

Yes sir, you are absolutely right.

Once I realized that stylesheet_pack_tag was a thing I realized I was doing it all wrong fundementally.

Thanks, we're cooking now!

Learning more rails 5.1 specific stuff!

Apparently @import "bulma/bulma/utilities/utilities"; works a lot better if you add:

Rails.application.config.assets.paths << Rails.root.join('node_modules')
to
config/initializers/assets.rb!

Maybe it's included there by default on new projects but I upgraded and forgot that important step!

Yeah that doesn't seem to do it either. I think I have more learning to do with webpack & loaders.

For now, as long as the crazy long path works works in heroku, I am going to just run with it.

Thanks!

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

© 2024 GoRails, LLC. All rights reserved.