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'.
- The book is new and has never been checked out.
- 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!
Posted in How do I add a conditional snippet of page specific javascript (likely a turbolinks issue)?
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 🙄
Posted in How do I add a conditional snippet of page specific javascript (likely a turbolinks issue)?
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.
Posted in How do I add a conditional snippet of page specific javascript (likely a turbolinks issue)?
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:
- Inspecting the page after the modal opens when it shouldn't, my
<script>
tags aren't present in the DOM. The Railslearn#index
doesn't render that text again, yet the behavior stays until the cache is reloaded via a refresh on the browser. - 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:
- Add bootstrap through yarn:
yarn add bootstrap@4.0.0-alpha.6
- Create a file to be transpiled by webpacker:
app/javascript/bootstrap.sass
(orapp.sass
... whichever name suits you best.) - Within
bootstrap.sass
import your newly added boostrap library:@import '~bootstrap/dist/css/bootstrap'
- In the head of your
app/views/layouts/application.html.erb
add<%= stylesheet_pack_tag 'bootstrap' %>
. - 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!