Add custom column to ruby collection - ruby-on-rails

Ok, here is what I have
columns in wines table -> ID, Name
columns in price table -> ID, wine_id, price
#query = Wine.find(:all)
now how do I add a custom price column to the #query hash? Can this be done?
This is what I want in the end
<% #query.each do |e| %>
<%= e.price %>
<% end %>
EDIT:
Actual table structure
Wines has many Inventories
Inventories has one Wine
Inventories has one Zone
Inventories has many Availabilities
Availabilities has one Inventory
Availabilities has one Markupprofile_id
Availabilities has one Discountprofile_id
This is the process I follow
#special offers
#q_array = Array.new
#q = SpecialOffers.find(:all, :conditions => ["client_id = ? AND application_id = ?", #client_id, #app_id])
#q.each do |e|
#q_array << e.availability_id
end
#filter, Special Offers, Client App Id, Zone, Available
#special_offers = Wine.find(:all, :include => [:availabilities, :zones], :conditions => ["availabilities.available = ? AND availabilities.id IN (?) AND availabilities.client_application_id = ? AND zones.id = ?", true, #q_array, #client_app_id, session[:zone_id]], :order => 'wines.name ASC')
#search page = 1, new arrivals = 2, special offers = 3, best sellers = 4, show page = 5
add_markup(#special_offers, 3)
Now I have the wines I want and I run them through add_markup()
def add_markup(collection, unique)
#price_array ||= [] #if null create blank array
collection.each do |e|
e.inventories.each do |f|
if session[:zone_id] == f.zone_id
#availability = Availability.find_by_inventory_id_and_client_application_id(f.id, #client_app_id)
#price = f.price
if #availability
if #availability.discount == true
price = Pricingmodel.find_by_discountprofile_id(#availability.discountprofile_id)
if price
was_price = Pricingmodel.find_by_markupprofile_id(#availability.markupprofile_id)
f.price = ((price.markup.to_f / 100) + 1) * #price * 6 #this is the normal price
f.stock = ((was_price.markup.to_f / 100) + 1) * #price * 6 #this is the discounted price
else
price = Pricingmodel.find_by_markupprofile_id(#availability.markupprofile_id)
f.price = ((price.markup.to_f / 100) + 1) * #price * 6 #this is the normal price
f.stock = ((price.markup.to_f / 100) + 1) * #price * 6 #this is the discounted price
end
else
price = Pricingmodel.find_by_markupprofile_id(#availability.markupprofile_id)
f.price = ((price.markup.to_f / 100) + 1) * #price * 6 #this is the normal price
f.stock = ((price.markup.to_f / 100) + 1) * #price * 6 #this is the discounted price
end
end
end
end
end
end
f.price is fine, its the normal price.
The problem is, I want to also display the discounted price somewhere
I used the stock column wich is of type int for this discounted price (f.stock = ((was_price.markup.to_f / 100) + 1) * #price * 6)
Is there any way I could "add" a of_type float column to this collection? Lets say discounted_price column of type float? Can this be done?

Let us assume you have has_one association between Wine and Price
Then
class Wine < ActiveRecord::Base
has_one :price
end
class Price < ActiveRecord::Base
belongs_to :wine
end
Then in your view display the price like below.
<% #query.each do |e| %>
<%= e.price.price %>
<% end %>

Do you have an association between both classes?
class Wine < ActiveRecord
has_one :price
end
class Price < ActiveRecord
belongs_to :wine
end
Then you could simply call:
<% #query.each do |e| %>
<%= e.price.price %>
<% end %>

I'd suggest you to use delegate.
class Wine < ActiveRecord::Base
has_one :price
delegate :rate, :to => :price # Notice 'rate' instead of 'price'. So that you could still get the associated price record.
end
class Price < ActiveRecord::Base
belongs_to :wine
end
You could simply do
<% #query.each do |e| %>
<%= e.rate %>
<% end %>

I think you can achieve this using attr_accessor!
Take a look at:
usage of attr_accessor in Rails
Why use Ruby's attr_accessor, attr_reader and attr_writer?

Related

Rails - filter by date range with 2 models

I have two models: space and booking. Space has_many bookings and Booking has two date attributes: check_in and check_out.
Given a valid date range, I want to show all spaces available during this range
This is the view:
<%= form_tag spaces_path, method: :get do %>
<%= date_field_tag :query1,
params[:query1],
class: "form-control" %>
<%= date_field_tag :query2,
params[:query2],
class: "form-control" %>
<%= submit_tag "Search", class: "btn" %>
<% end %>
This is the SpaceController:
(...)
def index
if params[:query1].present? && params[:query2].present?
query1 = DateTime.parse(params[:query1])
query2 = DateTime.parse(params[:query2])
search = query1..query2
bookings = Booking.all
# returns the bookings that overlaps with the search
overlapping_bookings = bookings.select do |booking|
check_in = booking[:check_in]
check_out = booking[:check_out]
period = check_in..check_out
search.overlaps?(booking.period)
end
# returns the spaces_id of the bookings that overlaps
overlapping_space_ids = overlapping_bookings.select do |overlapping_booking|
overlapping_booking[:space_id]
end
# remove the duplicates
overlapping_space_ids.uniq!
# remove the spaces with bookings that overlap with the search
#spaces = Space.all.reject do |space|
overlapping_space_ids.include? space[:id]
end
else
#spaces = Space.all
end
end
(...)
I assume the root cause of my problem is that I'm treating the Active Record Query Object as an array of hashes, not sure if it's correct. I made some researches on this but I haven't found any exhaustive answer.
Using an SQL subquery (in PostgreSQL for example) you would do this:
sql = <<SQL
SELECT *
FROM spaces
WHERE id in (
SELECT space_id
FROM bookings
WHERE
(check_in, check_out) OVERLAPS (:from, :to)
)
SQL;
Booking.find_by_sql([sql, {from: query1, to: query2})
Hope that helps :)
I would add a scope to the Booking model first:
# in app/models/booking.rb
scope :overlapping, ->(from, to) {
where(
"(check_in, check_out) OVERLAPS (?, ?)", from, to
)
}
and would then change the whole controller method to:
def index
#spaces = Space.all
if params[:query1].present? && params[:query2].present?
from = DateTime.parse(params[:query1])
to = DateTime.parse(params[:query2])
#space = #space.where.not(
id: Booking.select(:space_id).overlapping(from, to)
)
end
end

Rails fetch products based on minimum and maximum price

I have a rails app in which i have products and their variants.
product.rb
class Product < ActiveRecord::Base
has_many :variants
scope :with_min_range, lambda { |min|
where(" (variant_price) >= ?", "#{min.to_i}")
}
scope :with_max_range, lambda { |max|
where("(variant_price) <= ?", ["#{max.to_i}"])
}
def price_range
return #price_range if #price_range
return #price_range = ['N/A', 'N/A'] if active_variants.empty?
#price_range = active_variants.minmax {|a,b| a.price <=> b.price }.map(&:price)
end
def price_range?
!(price_range.first == price_range.last)
end
end
the way i fetch the price range of the product is
index.html.erb
<% #products.each do |product| %>
<figcaption>
<h4 class="aa-product-title"><%= link_to product.name, product_path(product) %></h4>
<span class="aa-product-price"><%= number_to_currency(product.price_range.first, :locale => :in ) %>
<% if product.price_range? %>
to
<%= number_to_currency(product.price_range.last, :locale => :in) %>
<% end %></span>
</figcaption>
<% end %>
Now you can see in the product.rb i want to fetch the product based on the price so that in the with_min_range the result will be the products whose variants' minimum price range will be greater than the value of min
and in the with_max_range the result will be the products whose variants' maximum price would be less than the max
P.S - I have given variant_price in the where query just to give you idea what I want
Please help me to figure out its solution
Anyways i figured it out myself i needed to make join in the Product model so that Product.joins(:variants) after that in the scope I written
scope :with_min_range, lambda { |min|
where(" variants.price >= ?", "#{min.to_i}")
}
scope :with_max_range, lambda { |max|
where("variants.price <= ?", "#{max.to_i}")
}
it now successfully fetch the record based on minimum and maximum price
If anyone can give a better answer then please !

How to add a size attribute to shopping cart item

I built a working shopping cart system the other day using Ruby on Rails, and with the guidance of following a tutorial. Now I would like to modify the existing shopping cart item to incorporate the size of a particular item being placed in the cart.
I created a migration file to add a size column to the Products table, and then I started modifying CartItem class, and the Cart class files respectively.
class CartItem
attr_reader :product_id, :quantity, :size
def initialize product_id, quantity = 1, size
#product_id = product_id
#quantity = quantity
#size = size
end
def increment
#quantity = #quantity + 1
end
def product
Product.find product_id
end
def total_price
# puts "Hello cart_item"
product.price * quantity
end
end
class Cart
attr_reader :items
def self.build_from_hash hash
items = if hash["cart"] then
hash["cart"]["items"].map do |item_data|
CartItem.new item_data["product_id"], item_data["quantity"], item_data["size"]
end
else
[]
end
new items
end
def initialize items = []
#items = items
end
def add_item product_id, size
item = #items.find { |item| item.product_id == product_id
item.size == size }
if item
item.increment
else
#items << CartItem.new(product_id, size)
end
end
def empty?
#items.empty?
end
def count
#items.length
end
def serialize
items = #items.map do |item|
{
"product_id" => item.product_id,
"quantity" => item.quantity,
"size" => item.size
}
end
{
"items" => items
}
end
def total_price(shipping_price = 0)
# puts "Hello cart"
#items.inject(0) { |sum, item| sum + item.total_price } + shipping_price
end
end
However, I'm getting the following error,
Because your add_item method should be into two parameter, but your params is hash can't use params[:id, :size] replace params[:id], params[:size] it work.

Validation by neighbor association attribute in rails

I have two models: Parent and Child, and i have a nested form for Parent, something like this:
= simple_form_for #parent do |form|
= form.input :name
= form.simple_fields_for :children do |children_form|
= children_form.input :money
= form.button :submit
What i need is a way to validate if sum of all children money equals to 100.
I've tried (in Parent):
validate do
total = children.inject(0) { |sum, child| child.money }
unless money == 100
children.each do |child|
child.errors.add(:money, :invalid)
end
end
end
but its not working for some reason. Thanks
Update:
Sorry, false alarm, i've used inject incorrectly.
total = children.inject(0) { |sum, child| sum + child.money }

Need to capture integers for hours minutes and save it to my db as a datetime

EDIT: I moved the time math into the model as a callback. I'm getting this error:
NoMethodError in OutstandingsController#create
undefined method `hours' for nil:NilClass
Is there some gem that I need to include to be able to do the time math? I thought it was included in ActiveSupport..?
class Outstanding < ActiveRecord::Base
belongs_to :user
before_validation(:on => :create) do
self.panic_deadline = Time.now
self.panic_deadline = self.panic_deadline + self.deadline_hours.hours
self.panic_deadline = self.panic_deadline + self.deadline_minutes.minutes
self.active = true
end
end
class OutstandingsController < ApplicationController
def create
#outstanding = Outstanding.new(params[:outstanding])
#user = current_user
#outstanding.user_id=#user.id
respond_to do |format|
if #outstanding.save ...
<%= form_for(#outstanding) do |f| %>
<div class="field">
<%= select_tag :deadline_hours, options_for_select(Array(0..99), 0) %><%= f.label :deadline_hours %>
<%= select_tag :deadline_mins, options_for_select(Array(1..59), 30) %><%= f.label :deadline_mins %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Maybe something like
panic_deadline = Time.now
panic_deadline = panic_deadline + #outstanding.deadline_hours.hours
panic_deadline = panic_deadline + #outstanding.deadline_minutes.minutes
Make sure that there is either a virtual attribute for deadline_hours / deadline_minutes or it's in the model schema.
class Outstanding < ACtiveRecord::Base
belongs_to :user
before_validation(:on => :create) do
self.panic_deadline = Time.now
self.panic_deadline = self.panic_deadline + self.deadline_hours.hours
self.panic_deadline = self.panic_deadline + self.deadline_minutes.minutes
self.active = true
end
attr_accessor :deadline_hours, :deadline_minutes
end
You can perform calculation on Time objects as shown in the Ruby docs for Time
time = Time.now
time += (60 * 60) # Time is now 1 hour from now
I think my math is right :P
This may be what you want:
#outstanding.deadline_hours.hours.since(#outstanding.deadline_mins.minutes.from_now)
And you should calculate this in a callback or an observer.

Resources