Rails and arrays - ruby-on-rails

Here is problem :
Order object has two attributes which defined as array of string (list_of_products, quantity_of_product). Also Order has_many line_items and has_many products through line_items.
This is show_receipt action in Public controller:
def show_receipt
#order = Order.find(params[:id])
#order.sum = 0.0
#order.quantity = 0
#order.quantity_of_product = []
#order.list_of_products = []
#order.line_items.each do |item|
#product = Product.find(item.product_id)
#order.list_of_products << #product.name
#order.sum += (item.quantity*item.price).to_d
#order.quantity += item.quantity
#order.quantity_of_product << item.quantity.to_s
#product.unite = #product.unite - item.quantity
#product.save
end
#order.save
end
it works fine, when I debug(#order) in the view of show_receipt action I can see those two arrays (list_of_products and quantity_of_product). Both are NOT empty. But when I debug(#order) in the view of ../orders/#order there are two arrays but one of them (list_of_products) is empty and the other one (quantity_of_product) is NOT empty?
Here is Order class:
class Order < ActiveRecord::Base
belongs_to :customer
has_many :line_items
has_many :products, :through => :line_items
accepts_nested_attributes_for :line_items
accepts_nested_attributes_for :products
end
Any idea?

Related

Get id, name from master table - Ruby

The association is has follows
Company has_many company_commodities
CompanyCommodity belongs to Company
CompanyCommodity belongs to Commodity
Consider that company1 has an entry in the company_commodities table.
Now in the decorator file, i need to get the commodity name and id of that record.
I have implemented as follows.
company1 = Company.find(1)
arr = co.company_commodities.map(&:commodity).pluck(:name, :id)
arr.map { |a| { name: a[0], id: a[1] } }
This produces the output as
[{:name=>"Pharmaceuticals", :id=>25},
{:name=>"Medical Devices", :id=>26}]
Is there a cleaner way to do this? 
class Company < ApplicationRecord
has_many :company_commodities
has_many :commodities, through: :company_commodities
end
class CompanyCommodity < ApplicationRecord
belongs_to :commodity
belongs_to :company
end
company = Company.find(1)
# this returns the object you can access by arr.first.id, arr.first.name
arr = company.commodities.select(:name, :id)
# if you want to access as hash, usage: arr.first['id'], arr.first['name']
arr = company.commodities.select(:name, :id).as_json
You can get the commodities association by using through, then you can use select to filter the attributes.
Change the associations as below
class Company
has_many :company_commodities
has_many :commodities, through: :company_commodities
end
class CompanyCommodity
belongs_to :company
belongs_to :commodity
end
And query the records as
company1 = Company.find(1)
arr = co.commodities.pluck(:name, :id)
arr.reduce([]) { |array, el| array << [[:name, :id], el].to_h}

Rails select has_many though get the right Id

I'm trying to pass the size of a product to a order with select. the product has many sizes with has_many though.
the size.id on the orger page did not match with the size.id that i choose on the product page.
someone know why?
for exemple if choose product size.id(2) on the order page show size.id(4)
Size Model
attr_accessible :size
has_many :orders
has_many :sizeship
has_many :products, :through => :sizeship
Product Model
has_many :orders
has_many :sizeships
has_many :sizes, through: :sizeships
Order Model
belongs_to :cart
belongs_to :product, touch: true
belongs_to :size
Cart Model
belongs_to :user
has_many :orders
Product Controller
def show
#product = Product.cached_find(params[:id])
#sizes_for_dropdown = #product.sizes.collect { |s| [s.size, s.id] }
end
Cart Controller
def add
product = Product.find(params[:id])
if product
if product.buyable?(current_user)
current_user.cart = Cart.new #if current_user.cart.nil?
size = product.sizes.find_by_id(params[:size_id])
quantity = params[:quantity].to_i > 0 ? params[:quantity].to_i : 1
order = current_user.cart.orders.find_by_product_id_and_status_and_size_id(product.id, nil, product.sizes.nil? ? nil : product.sizes.find { |l| l.id } )
if order.nil? # create new order
order = Order.new
order.product = product
order.size = product.sizes.find { |k| k.id }
current_user.cart.orders << order
else # increase quantity
order.quantity += quantity
order.save
end
redirect_to product_path(product)
flash[:success] = "Success"
else
redirect_to product_path(product)
flash[:error] = " Error"
end
else
redirect_to :shop, flash[:error] = 'Error'
end
end
Log
Started POST "/carts/add/yy" for 127.0.0.1 at 2016-11-17 00:11:16 -0200
Processing by CartsController#add as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"Hf7eLJPojw+u/HCGuQtAVzADC7v0jpkw5mJ0sCmx0u4=", "score"=>"", "quantity"=>"1", "size_id"=>"1", "commit"=>"add to basket", "id"=>"yy"}
Redirected to http://localhost:3000/products/yy
Completed 302 Found in 3471.4ms

How do I update Nested Attributes in Rails without accepts_nested_attributes_for?

I'm working on a project for a class where I have a very large form with Nested Models. Here are the models that are important to the form, as well as their associations:
Course: has_many :time_blocks, has_many :tags, through: :taggings, belongs_to :institution, has_many :roles, has_many :users, through: :roles
TimeBlock: belongs_to :course
Tag: has_many :taggings
Tagging: belongs_to :tag, belongs_to :taggable_type
Institution: has_many :courses, has_many :users
Role: belongs_to :course, belongs_to :user
I am able to create the Nested Form correctly, but I can't get the Nested Models to update correctly. Here is the controller, the form is very long, but I have provided the params for the Nested Models. Note, I cleared out the values from the params, but some of the params have ID values because they exist in the db. I've also included the CoursesHelper to show the helper methods I'm using in the controller.
app/controllers/courses_controller.rb
def new
#course = current_user.courses.new
#course.institution = Institution.new
4.times { #course.tags.build }
7.times { #course.time_blocks.build }
end
def create
#course = Course.new(params[:course])
#course.institution = Institution.new(params[:institution])
filled_tags = set_tags(params[:tag])
#course.tags.build(filled_tags)
filled_time_blocks = set_time_blocks(params[:time_block])
#course.time_blocks.build(filled_time_blocks)
if #course.save
Role.create!(
user_id: current_user.id,
course_id: #course.id,
title: 'instructor'
)
redirect_to #course
else
(4 - filled_tags.count).times { #course.tags.build }
(7 - filled_time_blocks.count).times { #course.time_blocks.build }
flash.now[:errors] = #course.errors.full_messages
render :new
end
end
def edit
end
def update
filled_time_blocks = set_time_blocks(params[:time_block])
filled_time_blocks.each do |time_block|
#course.time_blocks.update_attributes(time_block)
end
filled_tags = set_tags(params[:tag])
filled_tags.each { |tag| #course.tags.update_attributes(tag) }
# #course.tags.update_attributes(filled_tags)
# #course.time_blocks.update_attributes(filled_time_blocks)
fail
if #course.update_attributes(params[:course])
redirect_to #course
else
flash.now[:errors] = #course.errors.full_messages
render :edit
end
end
app/helpers/courses_helper.rb
def set_time_blocks(entries)
result = []
days = entries[:day_of_week].reject! { |day| day.blank? }
days.each do |day|
time_block = {}
time_block[:day_of_week] = day
time_block[:start_time] = entries[day][:start_time]
time_block[:end_time] = entries[day][:end_time]
time_block[:id] = entries[day][:id]
result << time_block
end
result
end
def set_tags(entries)
[].tap do |tags|
entries.each do |entry|
tags << entry unless entry.values.all?(&:blank?)
end
end
end
def find_course
if params.include?(:id)
#course = Course.find(params[:id])
else
flash[:notice] = "Sorry, Could Not Find Course."
redirect_to current_user
end
end
TimeBlock Params
{"sun"=>{"start_time"=>"", "end_time"=>"", "id"=>""}, "mon"=>{"start_time"=>"", "end_time"=>"", "id"=>"3"}, "tue"=>{"start_time"=>"", "end_time"=>"", "id"=>"4"}, "wed"=>{"start_time"=>"", "end_time"=>"", "id"=>"5"}, "thu"=>{"start_time"=>"", "end_time"=>"", "id"=>"6"}, "fri"=>{"start_time"=>"", "end_time"=>"", "id"=>"7"}, "sat"=>{"start_time"=>"", "end_time"=>"", "id"=>""}, "day_of_week"=>[]}
Tag Params
[{"name"=>"", "id"=>"4"}, {"name"=>"", "id"=>""}, {"name"=>"", "id"=>""}, {"name"=>"", "id"=>""}]
If you cant make it work with accepts_nested_attributes_for then you'll have to write your own setter method(s) manually. Something like:
class Course < ActiveRecord::Base
def tag_attributes=(tags)
tags.each do |tag|
self.tags.build(tag)
end
end
end
The method name (tag_attributes= in my example) needs to match the key name that the tag params are listed under

Rails `has_many through` with distinct tags

I have the following active record classes. I am trying to make a tagging similar to twitter without using gem. Can save_tags and create_tags be optimised further?
class Opinion < ActiveRecord::Base
belongs_to :user
has_many :taggings
has_many :tags, through: :taggings
after_create :save_tags
def self.tagged_with(name)
Tag.find_by!(name: name).opinions
end
private
def save_tags
tags = create_tags
tags.each do |tag|
t = tag.downcase
oldTag = Tag.find_by(name: t)
if oldTag
tagging = Tagging.new(opinion: self, tag: oldTag)
tagging.save
else
self.tags.create(name: t)
end
end
end
def create_tags
tags = self.about.scan(/(\#{1}[a-zA-Z1-9]*\b)/).flatten
if tags
tags.uniq.reject! { |tag| tag.length < 2 }
end
return tags
end
end
class Tag < ActiveRecord::Base
has_many :taggings
has_many :opinions, through: :taggings
validates_uniqueness_of :name
end
class Tagging < ActiveRecord::Base
belongs_to :tag
belongs_to :opinion
end
This code invokes tags.count DB queries:
tags.each do |tag|
t = tag.downcase
oldTag = Tag.find_by(name: t)
...
end
You could preload them:
old_tags = Tag.where(name: tags)

Method to merge objects which are similar (differing by quantity)

I need help with a cart object which has_many line_items. If a line_item in the cart is created and it has the same exact attributes as a line_item which is already in the cart I just want to update the pre existing line_items quantity rather than create duplicate objects with separate quantities.
I wrote a few methods in my models to try and make this work but it isn't working. Below is my code:
models
class LineItem < ActiveRecord::Base
attr_accessible :cart_id, :product_id, :quantity, :unit_price, :product, :cart, :color_id, :size_id, :extra_id
belongs_to :cart
belongs_to :product
belongs_to :color
belongs_to :size
belongs_to :extra
validates :quantity, :presence => true
def update_quantity(qty)
quantity += qty
quantity.save
end
def exists_in_collect?(items)
if items.include?(product)
if color == items.color && size == items.sizes && extra == items.extra
return true
end
else
return false
end
end
end
class Cart < ActiveRecord::Base
attr_accessible :purchased_at
has_many :line_items
has_one :order
def where_line_item_with(prod_id)
line_items.where(:product_id => prod_id)
end
end
controller
class LineItemsController < ApplicationController
def new
#line_item = LineItem.new
end
def create
#line_item = LineItem.new(params[:line_item].merge(:cart => current_cart))
if #line_item.exists_in_collect?(current_cart.line_items)
current_cart.where_line_item_with(product.id).update_quantity(#line_item.quantity)
#line_item.destroy!
else
#line_item.save!
#line_item.update_attributes!(:unit_price => #line_item.item_price)
end
redirect_to current_cart_url
end
def update
#line_item = LineItem.find(params[:id])
#line_item.update_attributes(params[:line_item])
redirect_to current_cart_url
end
Any insight is fully appreciated.
1.You should change your where_line_item_with(prod_id) to the following:
def where_line_item_with(prod_id)
line_items.where(:product_id => prod_id).first
end
As the where returns an array and you cannot do update_quantity(#line_item.quantity) on an array.
2.In exists_in_collect?(items) - Here your aim is to find if the items of the cart include the item similar to new item. it should be updated as following:
def exists_in_collect?(items)
items.each do |item|
if color == item.color && size == item.sizes && extra == item.extra && product == item.product
return true
end
end
return false
end

Resources