Chris Oliver

Joined

291,420 Experience
86 Lessons Completed
299 Questions Solved

Activity

Glad I can help, even if it's slow goin' for a bit. Do this a few times and you'll get really comfortable debugging these more complicated processes and be doing them often in no time! :)

And believe me...I've probably wasted months or more of my life to debugging all the various things over the years...

I guess one thing also to note is that since the record hasn't been saved, it won't be able to set the Booking ID in the Stripe description until after the record saves. Maybe initiate an update to store the booking ID reference on there after the save is a better plan.

Ah yup! That is probably it. Since you're inside the instance rather than the controller you can just reference it as stripe_token. And the same with all the other references.

Inspect all the steps in the reserve method and print out the calculations and such. There's probably a piece in there that's not correct and needs a little fixing. You can use byebug to play with those calculations interactively which I find to be super helpful for debugging.

Well, so you don't need that anymore because you said you've already got your database defaulting the column to 0. 👍 Free ones would be defaulted to 0, and your paid ones will always have a value because it'll be required in the form.

Gonna have to see your stacktrace regarding that error. I think maybe you're just having trouble wrapping your head around everything. I had trouble with it for a while the first time I was trying to register and purchase at the same time.

As far as making sure your quantity is required:

validates :quantity, presence: true, numericality: { greater_than: 0 }

Yeah exactly, this would take care of it on a database level which would mean that your could should always have a value. And then not having a default on the booking is probably the thing that triggered the nil error. It doesn't super matter if you have a default value there, but it probably does make sense to add a validation requiring you to always have 1+ quantity on the booking so that value is never nil.

Posted in Problems Viewing Videos

Hey Thomas, did you get a chance to poke around any more on the videos?

Probably a good plan. Yeah the thing is that most times "multi-tenancy" is more for when you truly want separate databases and everyone's stuff separated out. I think it's a common misconception and one that's kinda hard to make clear at times. Sounds like a decent plan and you can always go back and change things up later, it just may take a little longer with production data later on which isn't that bad.

I would probably add a validation for the quantity on the Booking, and then you may want to add a before_save callback for Event to set to 0 if it is free.

before_save :set_zero_price_pennies, if: :is_free?

def set_zero_price_pennies
  self.price_pennies = 0
end

And you can also validate that Event always has a value for price_pennies of greater than or equal to 0 so that that column is also never nil.

Posted in How do I build a beta invite system?

I haven't made an episode on this yet, but I need to. I use https://github.com/scambra/devise_invitable for sending invitations. Basically it lets you create user records without a password, it invites them with an email, they can click the link to set their password and get setup. It's really nice.

I use this for the team accounts on GoRails which work similar to beta invites, where a user can invite other people.

Yeah, so you'll probably want to make sure that the quantity on a booking is always 1 or more and your event's price_pennies should return 0 if the event is free, and greater than 0 if it is paid. That way you always have these numbers and you don't need to add any more if statements to these other calculations.

Pretty much! Stripe will require you to send over the price in cents as an integer, so it's usually good to keep that the same in your app. So for your examples, they'd be stored as 899 and 450 in the database as integers. Then you divide by 100.0 to get £8.99.

This way you don't have to go back and forth converting things and to and from cents and you would only do that for display purposes. Feels a little weird at first but dramatically reduces your risk of mistakes with money which is good.

Yep! That will also give you the benefit of making things more more cohesive from the controller perpsective because it only has to know if it worked or not, rather than knowing how to reserve different types of bookings.

So I actually forgot to include the total_amount bit. I would actually add a column in your database for it and set it at reservation.

class Booking < ActiveRecord::Base
  def reserve
    # Don't process this booking if it isn't valid
    return unless valid?

    # We can always set this, even for free events because their price will be 0.
    self.total_amount = quantity * event.price_pennies

    # Free events don't need to do anything special
    if event.is_free?
      save

    # Paid events should charge the customer's card
    else
      begin
        charge = Stripe::Charge.create(amount: total_amount, currency: "gbp", card: @booking.stripe_token, description: "Booking number #{@booking.id}", items: [{quantity: @booking.quantity}])
        self.stripe_charge_id = charge.id
        save
      rescue Stripe::CardError => e
        errors.add(:base, e.message)
        false
      end
  end
end

So you can set it up to always save the total_amount to store for later (like a receipt would do). Free events it should just be quantity * 0. Make sure that the price for free events returns 0 by default if it doesn't already, and then you can use that total amount to charge with Stripe. Save it in pennies so that you can make sure you don't have rounding errors or anything and just store as integers because they're safer. You'll have to do formatting on the UI to print out in decimal format, but that's totally fine.

Hey Mike,

So your code here creates a charge for the event price, but you need to actually multiply that by the number of seats in the booking. You've also got some potential issues here in that the booking might save but the payment fails letting the user get away with a free event.

It's probably good to add the total_amount so you can save that to the model in case the price changes later down the line for some reason.

So what I would do is refactor your code here into a method on the model as a start. This will simplify your controller and clean things up in a way that's a lot more manageable.

I would do something like this:

class Booking < ActiveRecord::Base
  def reserve
    # Don't process this booking if it isn't valid
    return unless valid?

    # Free events don't need to do anything special
    if event.is_free?
      save

    # Paid events should charge the customer's card
    else
      begin
        charge = Stripe::Charge.create(amount: @event.price_pennies, currency: "gbp", card: @booking.stripe_token, description: "Booking number #{@booking.id}", items: [{quantity: @booking.quantity}])
        self.stripe_charge_id = charge.id
        save
      rescue Stripe::CardError => e
        errors.add(:base, e.message)
        false
      end
  end
end
def create
    # actually process the booking
    @event = Event.find(params[:event_id])
    @booking = @event.bookings.new(booking_params)
    @booking.user = current_user

    if @booking.reserve
        flash[:success] = "Your place on our event has been booked"
        redirect_to event_path(@event)
    else
        flash[:error] = "Booking unsuccessful"
        render "new"
    end
end

Now this is mostly just psuedo code, nothing really tested by the gist is this:

  1. If you take all the logic and condense it into a function inside the booking, you can have it handle the free and paid events. You can have it decide what it needs to do to reserve
  2. If it's free, you simply just save the model and return the true or false
  3. If it's paid, you try to charge the card, if that fails, you return the error and false
  4. If the payment is successful you save a reference to the stripe charge and then save the record and return the result

You can still refactor this even further to separate out the free and paid reservations to simplify those a bit as well, but I'll leave that up to you. The main point is that by putting this into the model, you can create a new method called "reserve" that handles the logic for doing a reservation AND potentially payment all in one operation rather than trying to do them separately in your controller where things would get really messy.

This is just a start, and as things get more complicated you'll want to refactor this, probably into a service object of some kind, and retain the logic there, but it's should showcase the way you can begin to organize and group the work you need to do a bit better. Hope this helps!

Hey thanks! I did the whole thing together and just split up the video for consumption's sake and just posting one a day this week. I think I noticed that about halfway through that the tutorial didn't mention everything, so I started referencing the Github repo. :) I'm going to add that in the notes so people can check that out easily as well.

Had a lot of fun going through your tutorial. That was my first time checking out RethinkDB and I like it quite a lot. Please post some more! :D

Posted in Group Chat with ActionCable: Part 4 Discussion

jquery_ujs is used to trigger that. If you've got method post, then you'll want to see if you have JS errors that are causing it not to run that code to intercept the click and submit a POST instead.

Aside from that, I'm not sure. You might clone the repository and see if that works for you and compare your code with it. Probably something small, but super hard to debug over comments. :)

Posted in Problems Viewing Videos

Possibly, but I bet you probably aren't the only one. Can you try it in another browser? I'm wondering if these are stemming from changes YT and Wistia are doing internally? I know that Wistia pushed everyone to their new platform because Chrome 53 kills Flash support.

Posted in How do i save a field in a separate table?

I know right?! :)

Posted in User create Dynamic Role base

Check out Rolify. It can handle pretty much anything you want to do with roles and permissions.

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.