Ruby - how to multiply variables within a method - ruby-on-rails

I'm using a method to try and multiply two variables as follows -
def total_amount
self.quantity.to_i * self.event.price.to_i
end
I'm building an events app using Ruby on Rails and the aim of the method is to allow one user to book multiple spaces for a paid event.
The method simply isn't working as when I click through to make a payment the amount simply shows as 0(zero). The method is in my booking model, in my bookings controller I have the following code -
class BookingsController < ApplicationController
before_action :authenticate_user!
def new
#event = Event.find(params[:event_id])
#booking = #event.bookings.new(quantity: params[:quantity])
#booking.user = current_user
end
def create
#event = Event.find(params[:event_id])
#booking = #event.bookings.new(booking_params)
#booking.user = current_user
Booking.transaction do
#event.reload
if #event.bookings.count > #event.number_of_spaces
flash[:warning] = "Sorry, this event is fully booked."
raise ActiveRecord::Rollback, "event is fully booked"
end
end
if #booking.save
# CHARGE THE USER WHO'S BOOKED
# #{} == puts a variable into a string
Stripe::Charge.create(
amount: #event.price_pennies,
currency: "gbp",
card: #booking.stripe_token,
description: "Booking number #{#booking.id}")
flash[:success] = "Your place on our event has been booked"
redirect_to event_path(#event)
else
flash[:error] = "Payment unsuccessful"
render "new"
end
if #event.is_free?
#booking.save!
flash[:success] = "Your place on our event has been booked"
redirect_to event_path(#event)
end
end
private
def booking_params
params.require(:booking).permit(:stripe_token, :quantity)
end
end
I have an input space on my events.show form which allows the user to input the number of spaces they require. The booking form has the following line of code which should reflect the total amount required -
<p>Total Amount<%= #booking.total_amount %></p>
I added .to_i to both variables as without this I received a NilClass error. How can I amend this so the method creates the correct output?

You can use regex to strip your currency symbol
def total_amount
quantity.to_i * strip_currency(event.price)
end
private
def strip_currency(amount = '')
amount.to_s.gsub(/[^\d\.]/, '').to_f
end

If you are calling booking.new that means you are creating an instance of the class where as self. means you are using a class variable.
Remove the self.

Related

How to display flash messages in rails service object

I want to add service objects in to my controller. Is there any chance to include flash messages in to this service object?
user_stocks_controller
class UserStocksController < ApplicationController
def create
#user_stock = UserStocksCreator.new(current_user, params).call
redirect_to my_portfolio_path
end
end
service objects user_stocks_creator
class UserStocksCreator
def initialize(current_user, params)
#current_user = current_user
#params = params[:stock_ticker]
end
def call
stock = Stock.find_by_ticker(params)
if stock.blank?
stock = Stock.new_from_lookup(params)
stock.save
end
#user_stock = UserStock.create(user: current_user, stock: stock)
flash[:success] = "Stock #{#user_stock.stock.name} was successfully added to portfolio"
end
private
attr_accessor :current_user, :params
end
With this code I have an error:
undefined local variable or method `flash'
The flash method is only available in the controller. When you want to set the flash in the service object then you need to pass the flash to the service object.
# in the controller
def create
#user_stock = UserStocksCreator.new(current_user, params, flash).call
redirect_to my_portfolio_path
end
# in the service
class UserStocksCreator
def initialize(current_user, params, flash)
#current_user = current_user
#params = params[:stock_ticker]
#flash = flash
end
def call
# unchanged...
end
private
attr_accessor :current_user, :params, :flash
end

Booking Process Issues - Stripe and Rails

OK, I have been trying to find the issue for the past 2 hours.. For some reason my bookings don't come through, not even notifications appear on the page.
When I click BOOK NOW, the page reloads and doesn't give me any errors, so not sure what am I doing wrong.
I'm adding the stripe to the booking path step by step. The BOOK NOW button was working fine on its own before, but now that I aded the card charges to it - it doesn't.
Any help is very welcome! Happy to provide additional information, if needed.
Thank you
Reservations Controller
def create
room = Room.find(params[:room_id])
if current_user.stripe_id.blank?
flash[:alert] = "Please update your payment method."
return redirect_to payment_method_path
#reservation = current_user.reservations.build
#reservation.room = room
#reservation.price = room.price
#reservation.total = room.price * days
if #reservation.Waiting!
if room.Request?
flash[:notice] = "Request sent succesfully"
else
charge(room, #reservation)
end
else
flash[:alert] = "Cannot make a reservation"
end
end
redirect_to room
end
def your_trips
#rooms = current_user.rooms
end
def aprove
charge(#reservation, room, #reservation)
redirect_to your_trips_path
end
def decline
#reservation.Declined!
redirect_to your_trips_path
end
Reservations Controller - private
private
def set_reservation
#reservation = Reservation.find(params[:id])
end
def charge(room, reservation)
if !reservation.user.stripe_id.blank?
customer = Stripe::Customer.retrieve(reservation.user.stripe_id)
charge = Stripe::Charge.create(
:customer => customer.id,
:amount => reservation.price,
:description => room.overview,
:currency => "usd"
)
if charge
reservation.Approved!
flash[:notice] = "Reservation created successfully!"
else
reservation.Declined!
flash[:alert] = "Cannot charge with this payment method!"
end
end
rescue Stripe::CardError => e
reservation.declined!
flash[:alert] = e.message
end
After checking current_user.stripe_id.blank? it looks like you are unintentionally returning all requests.
I would try this:
if current_user.stripe_id.blank?
flash[:alert] = "Please update your payment method."
redirect_to payment_method_path and return
end
If your model code depends on the record being saved before calling Waiting!, keep in mind that #reservation = current_user.reservations.build does not save the record.
The rescue block in your charge method also appears to be out of scope. That could be refactored into a begin-rescue-end block within the first if.
And if you aren't seeing notifications on the page, make sure your layout displays :notice and :alert somewhere.

Active Record: Adding "visit" counter on the model

I currently have a Subscriber model that takes in a "phone_number" and a "visit" attribute that is an integer. I want to set up a "check in" view form that will have a subscriber type in their phone_number and it will say if phone_number exists? add 1 to the visit attribute. So it will run a sql query and see if that number is in the database.
To be more clear I have to break the REST actions because the create action is already taken for the new subscribers. I'm pretty new to rails and I'm having a super hard time figuring this feature out. I'm curious if this is possible and how I should go about implementing this?
Here is my controller at the moment:
class SubscribersController < ApplicationController
def index
#subscriber = Subscriber.all
end
def new
#subscriber = Subscriber.new
end
def create
#subscriber = Subscriber.create(subscriber_params)
if #subscriber.save
flash[:success] = "Subscriber Has Been successfully Created"
redirect_to new_subscriber_path(:subscriber)
else
render "new"
end
end
def visit
end
private
def subscriber_params
params.require(:subscriber).permit(:first_name, :last_name, :email, :phone_number)
end
end
Something along those lines?
def visit
subscriber = Subscriber.find_by_phone_number(params[:phone_number])
if subscriber
subscriber.visit += 1
subscriber.save
end
end
Make sure that the default value (via DB/Migration) for visit is set to 0.
You don't need to break REST style controller though. You can create another controller which does that check. For example something like:
class Subscriber::VisitController < ApplicationController
def create
# code here
end
end

Redirect_to in model after error

I have a function in my model to import data from a CSV file and I'd like to have validations should there be any errors. For example, when I upload the file, I search for a User based on an ID in the file. If there is no User with that ID, I'd like to redirect_to a different page with an error.
def self.getUser(scale_id)
#user = User.find_by(scale_id: scale_id)
if #user == nil
redirect_to users_path
else
return #user
end
end
def self.bulk_upload_weigh_ins(file)
output = []
errors = []
CSV.foreach(file.path, headers: true, ) do |row|
row = row.to_hash
#scale_id = row["scale_id"]
#user = getUser(#scale_id)
row.merge!(user_id: #user_id)
WeighIn.create! row.to_hash
end
end
...and no matter what path I put there, I get the following: undefined local variable or method 'users_path' for #<Class:0x007fa06f466998> even when it is a valid path.
Is there something wrong with redirecting like this? If yes, how should I do it?
The cleanest way for custom validations is to do something like:
In your model:
User < ActiveRecord::Base
validate :get_user
def initialize(params={})
self.id = params[:id]
end
def get_user
#user = User.find_by(self.id)
if #user.nil?
errors.add(:base, "Invalid User")
return false
else
return #user
end
end
In your controller you'd then do:
def whatever_action_youre_using
#user = User.new(user_params)
unless #user.valid?
redirect_to users_path
end
end
def user_params
params.require(:user).permit(:first_name, :email, :etc, :whatever_your_atts)
end
I have a function in my model that is searching for a User based on an
ID
undefined local variable or method 'users_path' for
Class:0x007fa06f466998
As #Dave Newton mentioned in the comments, the path helpers are not available in models unless you specifically include them. Also you can't and never need to use redirect_to in a model. The application logic should be moved to the controller. Something like below should work
def get_user
#user = User.find_by(scale_id: params[:user][:scale_id])
if #user.nil?
redirect_to users_path, notice: 'Your error message here'
else
return #user
end
end

How do I to grab the number of words in a post before it was updated, and store the number in a variable in the update method

When a post is updated I want to grab the number of words before it is saved and subtract that from the newly saved post word count. I want to do this in the post update controller.
Here is the update method
def update
#guide = Guide.friendly.find(params[:guide_id])
#post = Post.friendly.find(params[:id])
post_word_count_before = #no idea how to get it
post_word_count_after = #post.post.scan(/\w+/).size
change_in_words = post_word_count_after - post_word_count_before
if #post.update post_params
PostContributer.create!(user_id: current_user.id, post_id: #post.id, no_of_contributions: 1, no_of_words_contributed: change_in_words)
redirect_to guide_post_path(#guide, #post)
flash[:success] = "Updated successfully"
else
render 'edit'
end
end
I'm using #post.post.scan(/\w+/).size to get the word count after the post is updated. But I dont know how to get the word count before it is updated and store it in the variable post_word_count_before
The #post object is not changed until the #post.update post_params line, so the way you have it, the post_word_count_after variable contains the word count before the update.
I think this is what you are after:
def update
#guide = Guide.friendly.find(params[:guide_id])
#post = Post.friendly.find(params[:id])
post_word_count_before = #post.post.scan(/\w+/).size
if #post.update post_params
post_word_count_after = #post.post.scan(/\w+/).size
change_in_words = post_word_count_after - post_word_count_before
PostContributer.create!(user_id: current_user.id, post_id: #post.id, no_of_contributions: 1, no_of_words_contributed: change_in_words)
redirect_to guide_post_path(#guide, #post)
flash[:success] = "Updated successfully"
else
render 'edit'
end
end
active_model which is mixed into active_record provides API for record changes. You could use record.changes which provides hash of all the attributes that got changed with before and after values.
So instead of putting all that business logic in the controller, I would that rather move that into model where it belongs and with the use of ActiveModel::Dirty API and the callbacks you could achieve what you are after.
For example: Following could be your updated controller logic, clean and simple
class PostsController < ApplicationController
before_action :load_guide, only [:update] # add other actions that require #guide
before_action :load_post, only [:update] # add other actions that require #guide
def update
if #post.update(post_params)
redirect_to(guide_post_path(#guide, #post), success: "Updated successfully")
else
render 'edit'
end
end
private
def load_guide
#guide = Guide.friendly.find(params[:guide_id])
end
def load_post
#post = Post.friendly.find(params[:id])
#post.contributer = current_user
end
end
And your updated model:
class Post < ActiveRecord::Base
attribute_accessor :contributer # to get the current_user info from controller
before_update :create_post_contributer!
def create_post_contributer!
before = self.changes[:post][0].scan(/\w+/).size
after = self.changes[:post][1].scan(/\w+/).size
change_in_words = after - before
PostContributer.create!(
user_id: self.contributer.id,
post_id: self.id,
no_of_contributions: 1,
no_of_words_contributed: change_in_words
)
end
end
Refer to ActiveModel::Dirty for more info.
Try using it through session.
Store the previous updated value in session variable and delete the session variable once you do all the calculation from older post and newer post.

Resources