Activity
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:
- 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
- If it's free, you simply just save the model and return the true or false
- If it's paid, you try to charge the card, if that fails, you return the error and false
- 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
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.
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.
Awesome! check out collection_select
for that dropdown btw. :)
Hmm, welp, there's something definitely wrong then. I did notice your Brand model was missing the through association on this: has_many :users, through: :user_brands
. Possibly that's it? I didn't think that was required, but maybe...
Hmm, that shouldn't be possible because of creating it through the association. Print out the brand before the save or byebug it to inspect and make sure it has the association setup in memory and the @brand.user_brands
record as well.
That looks correct. Everything working now?
Posted in Problems Viewing Videos
Thanks for the heads up Thomas. I wonder if this is a buffering issue or something.
The YouTube ones have always autoplayed just fine for me and testing just now it happily goes past the 42 second mark. However, the Wistia ones were getting stuck at 3 seconds before and seem to be loading fine for me now as well.
I also just tested this in Safari, Firefox, and Chrome and the YT videos autoplayed correctly past 50 seconds and more.
I'm curious, does this also pause at the 42 second mark for you? https://youtu.be/7B4wG7e9ayw?autoplay=1
Hey Brice, you might check your code because the Join button I believe needs to submit a POST request and your error is looking for the show action which implies it sent a GET request instead. The Join button should create a POST request in order to create the record to set you as a user inside the channel, so it should take you to the ChatroomUsersController's create with that POST. Make sure that link has a "method: :post" option on it?
Did you create your UserBrand model? or did you name it like I showed as BrandUser?
Haha you know I think we might have! Never hurts to talk about it again though. :)
Yep, if every user needs multiple brands, you'll need a BrandUser model (or UserBrand, whichever is clearer) in order to do that.
Then if you've got your associations setup, you can change your first line to the following which will automatically create the join record for you.
@brand = current_user.brands.new(brand_params)
That will require you to have these associations:
class User
has_many :brand_users
has_many :brands, through: :brand_users
end
class Brand
has_many :brand_users
has_many :users
end
class BrandUser
belongs_to :brand
belongs_to :user
end
This one is a pretty fun project too. :D
Posted in File Uploads with Refile Discussion
I don't use this anymore, but I would recommend just following the readme for the S3 example they have: https://github.com/refile/r...
Their Github would also be the best place to ask for questions on S3 as well.