Adding and Accessing Multiple Session Variables in Rails - ruby-on-rails

I need a way to change the status of products in my store to sold once a purchase is completed...
I use the following code:
if #order.purchase #I use active merchant's purchase method on compiled order
update_product_status #method explained below
end
Before a product is copied as a line_item this code is run:
#product = Product.find(params[:product_id]) #line_item belongs to product and cart
session[:pending_purchase] = #product.id #stores ID number into session
So that later I can pull the ID, for the update_product_status code:
def update_product_status #used to update boolean 'sold' status on product
product = session[:pending_purchase]
#sold_product = Product.find(product)
#sold_product.update(:sold = true) #not sure if this syntax is correct
end
A problem may present itself if someone buys two items. Will the ID in :pending_purchase get over written if a second line_item is created and added to the cart? How would I access both variables and make sure both items now have a 'sold = true' attribute?

the simplest way is that stores product's id as an array.
#product = Product.find(params[:product_id])
(session[:pending_purchase] ||= []) && (session[:pending_purchase] << #product.id )
and your will find more products when you use it:
#products = Product.find(session[:pending_purchase])
#products.each do |p|
.........
end

Related

How to refactor this code to make it more readable and efficient?

I need help refactoring the code.I ve tried by best landed with the following code. Is there anything That I can do
class OrdersController < ApplicationController
before_action :get_cart
before_action :set_credit_details, only: [:create]
# process order
def create
#order = Order.new(order_params)
# Add items from cart to order's ordered_items association
#cart.ordered_items.each do |item|
#order.ordered_items << item
end
# Add shipping and tax to order total
#order.total = case params[:order][:shipping_method]
when 'ground'
(#order.taxed_total).round(2)
when 'two-day'
#order.taxed_total + (15.75).round(2)
when "overnight"
#order.taxed_total + (25).round(2)
end
# Process credit card
# Check if card is valid
if #credit_card.valid?
billing_address = {
name: "#{params[:billing_first_name]} # .
{params[:billing_last_name]}",
address1: params[:billing_address_line_1],
city: params[:billing_city], state: params[:billing_state],
country: 'US',zip: params[:billing_zip],
phone: params[:billing_phone]
}
options = { address: {}, billing_address: billing_address }
# Make the purchase through ActiveMerchant
charge_amount = (#order.total.to_f * 100).to_i
response = ActiveMerchant::Billing::AuthorizeNetGateway.new(
login: ENV["AUTHORIZE_LOGIN"],
password: ENV["AUTHORIZE_PASSWORD"]
).purchase(charge_amount, #credit_card, options)
unless response.success?
#order.errors.add(:error, "We couldn't process your credit
card")
end
else
#order.errors.add(:error, "Your credit card seems to be invalid")
flash[:error] = "There was a problem processing your order. Please try again."
render :new && return
end
#order.order_status = 'processed'
if #order.save
# get rid of cart
Cart.destroy(session[:cart_id])
# send order confirmation email
OrderMailer.order_confirmation(order_params[:billing_email], session[:order_id]).deliver
flash[:success] = "You successfully ordered!"
redirect_to confirmation_orders_path
else
flash[:error] = "There was a problem processing your order. Please try again."
render :new
end
end
private
def order_params
params.require(:order).permit!
end
def get_cart
#cart = Cart.find(session[:cart_id])
rescue ActiveRecord::RecordNotFound
end
def set_credit_details
# Get credit card object from ActiveMerchant
#credit_card = ActiveMerchant::Billing::CreditCard.new(
number: params[:card_info][:card_number],
month: params[:card_info][:card_expiration_month],
year: params[:card_info][:card_expiration_year],
verification_value: params[:card_info][:cvv],
first_name: params[:card_info][:card_first_name],
last_name: params[:card_info][:card_last_name],
type: get_card_type # Get the card type
)
end
def get_card_type
length, number = params[:card_info][:card_number].size, params[:card_info][:card_number]
case
when length == 15 && number =~ /^(34|37)/
"AMEX"
when length == 16 && number =~ /^6011/
"Discover"
when length == 16 && number =~ /^5[1-5]/
"MasterCard"
when (length == 13 || length == 16) && number =~ /^4/
"Visa"
else
"Unknown"
end
end
end
Products with a price attribute. We have shopping Carts that have many Products through the OrderedItems join table. An OrderedItem belongs_to a Cart and a Product. It has a quantity attribute to keep track of the number of products ordered.
The OrderedItem also belongs_to an Order
I wanted to know if it can be refactored further.
First of all you should move all that business logic from the controller into models and services (OrderProcessService, PaymentService). All the controller's private methods belong to a PaymentService.
Split the code into smaller methods.
If doing that on the model level some things that come into my mind when reading your code are the following:
#order.add_items_from_cart(#cart)
#order.add_shipping_and_tax(shipping_method)
Orders should be first saved (persisted in DB), then processed (purchased with changing their status).
#order.save might fail after a successful payment, so a client will lose the money and not get their order.
the purchasing is an important and critical process, so you should make sure everything is ready for it (the order is valid and saved)
a client should be able to purchase later or after the payment page is accidentally reloaded without filling the form again
normally when a payment is performed you should send an order ID to the payment system. The payment system will store the ID and you will always know which order the payment belongs to.
There are a lot of other things to consider. You have a lot of work to do.

Rails saving arrays to separate rows in the DB

Could someone take a look at my code and let me know if there is a better way to do this, or even correct where I'm going wrong please? I am trying to create a new row for each venue and variant.
Example:
venue_ids => ["1","2"], variant_ids=>["10"]
So, I would want to add in a row which has a venue_id of 1, with variant_id of 10. And a venue_id of 2, with variant_id of 10
I got this working, and it's now passing in my two arrays. I think I am almost there I'm not sure the .each is the right way to do it, but I think that I'm on the right track haha. I have it submitting, however, where would I put my #back_bar.save? because this might cause issues as it won't redirect
Thanks in advance.
def create
#back_bar = BackBar.new
#venues = params[:venue_ids]
#productid = params[:product_id]
#variants = params[:variant_ids]
# For each venue we have in the array, grab the ID.
#venues.each do |v|
#back_bar.venue_id = v
# Then for each variant we associate the variant ID with that venue.
#variants.each do |pv|
#back_bar.product_variant_id = pv
# Add in our product_id
#back_bar.product_id = #productid
# Save the venue and variant to the DB.
if #back_bar.save
flash[:success] = "#{#back_bar.product.name} has been added to #{#back_bar.venue.name}'s back bar."
# Redirect to the back bar page
redirect_to back_bars_path
else
flash[:alert] = "A selected variant for #{#back_bar.product.name} is already in #{#back_bar.venue.name}'s back bar."
# Redirect to the product page
redirect_to discoveries_product_path(#back_bar.product_id)
end
end # Variants end
end # Venues end
end
private
def back_bar_params
params.require(:back_bar).permit(:venue_id,
:product_id,
:product_variant_id)
end
as i said in comments
this is untested code and just showing you how it's possible to do with ease.
class BackBar
def self.add_set(vanue_ids, variant_ids)
values = vanue_ids.map{|ven|
variant_ids.map{|var|
"(#{ven},#{var})"
}
}.flatten.join(",")
ActiveRecord::Base.connection.execute("INSERT INTO back_bars VALUES #{values}")
end
end
def create
# use in controller
BackBar.add_set(params[:venue_ids], params[:variant_ids])
# ...
end

Rails saving array with the duplicated elements

I'm populating an array with multiple elements (some of then are equals, thats what I want). My code is something like this:
def create
#order = Order.create()
#order.table = Table.find_by(:number => params['table'][0])
#products ||= []
#qtd = []
Product.all.each do |product|
params['order'].each_pair do |ordered|
if(product.id.to_s == ordered.first)
for i in 0..ordered.second[0].to_i
#order.products << product
#order.save
end
end
end
end
binding.pry #here the #order.products is the way I want to
if #order.save
flash[:success] = "Pedido criado com sucesso."
redirect_to tables_path
else
flash[:danger] = "Erro ao criar pedido."
render :new
end
end
But When I go to rails console and do Order.last.products he dosen't show me de duplicated elements like I saved on my controller. Whats happening?
Well in your case you should be sending order information from client to server like
"order"=>{"line_items_attributes"=>{"0"=>{"quantity"=>"4", "id"=>"127"}}}
Instead of repeating the product id in the list, your should implement a concept of line items in your system. Line Items are the objects representing items that are added to your shopping cart and instead of repeating the product_id you can use term quantity.
Now you can have separate model called LineItem. Order can have many LineItems. LineItem has many products.
For more info see What is a "order line"?
For current Implementation:
<< method does not allow duplicate entries. It filters out the duplicates. Its basically relates products with order since order can have multiple products via some_table. It cannot relate same product to the order twice.
My suggestion would be, create a string field(column) called products and add the serialized array of product ids.
order.products = [1, 1, 3, 4,4,4].to_s
while accessing you de-serialize.

Best way to reindex objects in Ruby after jQuery.sortable()

I have objects with a 'position' defined. I 'm moving one object (using jQuery Sortable), then I need to reindex the position of each object in that list (I want to keep them in order 0,1,2...n)
Basically we have a ProductLine model that contains Products (using act_as_list for convinience).
First, swap the position of the previous object and the new object (the one we just dropped here):
def sort
#product = Product.find(params[:id])
if(params.has_key?(:next_id))
#next_product = Product.find(params[:next_id])
end
if(params.has_key?(:prev_id))
#prev_product = Product.find(params[:prev_id])
end
if #prev_product.blank?
#product.move_to_top
elsif #next_product.blank?
#product.move_to_bottom
else
#product.insert_at(#prev_product.position)
end
#product.save
reindex #product.product_line_id
render json: #products
end
This is the reindex function that ensures they are sequential:
def reindex(product_line_id)
#products = Product.where(product_line_id: product_line_id).order(position: :asc)
#products.each_with_index { |p, index|
p.position = index
p.save
}
end
I'm quite new to the Ruby language and I'm sure there are several cleaner, more efficent ways of doing this?

Take random ids, then store those random ids into the db

so I'm working on a code snippet that essentially takes out 35 random ids from the table List.
What I would like to do to find the ids that got randomly generated, store them into a database called Status.
The purpose is to avoid duplication the next time I get a new 35 random ids from the List. So I never get the same random id twice.
Here's what I've tried, but been unsuccessful to get working.
#schedule = current_user.schedules.new
if #schedule.save
#user = User.find(params[:id])
Resque.enqueue(ScheduleTweets, #user.token)
#schedule.update_attribute(:trial, true)
flash[:notice] = "success"
redirect_to :back
else
flash[:alert] = "Try again."
redirect_to :back
end
and the worker:
def self.perform(user_token)
list = List.first(6)
#status = list.statuses.create
list.each do |list|
Status.create(list_id: "#{list}")
if list.avatar.present?
client.create_update(body: {text: "#{list.text}", profile_ids: profile_ids, media: { 'thumbnail' => 'http://png-1.findicons.com/files/icons/85/kids/128/thumbnail.png', 'photo' => 'http://png-1.findicons.com/files/icons/85/kids/128/thumbnail.png' } })
end
end
end
however the Status.create(list_id: #list) doesn't work.
Does anybody have any idea what is going on, and how I can make the list_ids get saved successfully to Status?
It's also associated:
list has many statuses, and status belongs to list
The following line of code is wrong:
Status.create(list_id: "#{list}") # WRONG
In your case the list variable is a List instance. And you're passing its string version to list_id which expects an integer.
Do this:
Status.create(list_id: list.id)
Or this:
list.statuses.create
I think the following will also work:
Status.create(list: list)

Resources