Iterating through only persisted objects - ruby-on-rails

This is the code for my create display:
def create
#display = #department.displays.new(display_params)
#token = #display.build_token(value: (max_token + 1) , status: 0)
if #display.save
....
end
max_token is the method called to find the largest number of token in the display tokens.
def max_token
#tokens = #department.displays.map do |display|
display.token.value
end
#tokens.max
end
Problem
I've created a new display for the department with the code in the create method.
#display = #department.displays.new(display_params)
But it is not saved yet, as the #display.save is called only after the max_token method.
But when the max_token method is called, the code
#tokens = #department.displays.map do |display|
is also displaying the unsaved display of the department.
And since the token of the display has not been set yet, as it is not saved, throws a nil value error.
My Solution
This is what i've tried upto now, but I want to know if there's a better method.
def max_token
#tokens = #department.displays.map do |display|
if display.token.nil?
display.token.value
else
0
end
end
#tokens.max
end

If you're not worried about the uniqueness of value at the DB layer, you can simply filter out displays with a nil value for token:
def max_token
#department.displays.where.not(token: nil).map do |display|
display.token.value
end.max
end
(This is also assuming you don't actually need to assign #tokens as a side effect of max_token.)

Try to create a new separated Display first then assign it to the #department after max_token get called so the new Display won't be included in #department.displays.map
def create
#display = Displays.new(display_params)
#token = #display.build_token(value: (max_token + 1) , status: 0)
#department.displays << #display
if #display.save
....
end

Related

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

what var type to dynamically access Model's attribute from another controller? (Rails 4.2)

Goal: dynamically update another Model's properties (Tracker) from Controller (cards_controller.rb), when cards_controller is running the def update action.
Error I receive : NameError in CardsController#update, and it calls out the 2nd last line in the
def update_tracker(card_attribute) :
updated_array = #tracker.instance_variable_get("#{string_tracker_column}")[Time.zone.now, #card.(eval(card_attribute.to_s))]
Perceived problem: I have everything working except that I don't know the appropriate way to 'call' the attribute of Tracker correctly, when using dynamic attributes.
The attribute of the Tracker is an array (using PG as db works fine), I want to
figure out what property has been changed (works)
read the corresponding property array from Tracker's model, and make a local var from it. (works I think, )
push() a new array to the local var. This new array contains the datetime (of now) and, a string (with the value of the updated string of the Card) (works)
updated the Tracker with the correct attribute.
With the following code from the cards_controller.rb
it's the if #card.deck.tracked in the update method that makes the process start
cards_controller.rb
...
def update
#card = Card.find(params[:id])
if #card.deck.tracked
detect_changes
end
if #card.update_attributes(card_params)
if #card.deck.tracked
prop_changed?
end
flash[:success] = "Card info updated."
respond_to do |format|
format.html { render 'show' }
end
else
render 'edit'
end
end
...
private
def detect_changes
#changed = []
#changed << :front if #card.front != params[:card][:front]
#changed << :hint if #card.hint != params[:card][:hint]
#changed << :back if #card.back != params[:card][:back]
end
def prop_changed?
#changed.each do |check|
#changed.include? check
puts "Following property has been changed : #{check}"
update_tracker(check)
end
end
def update_tracker(card_attribute)
tracker_attribute = case card_attribute
when :front; :front_changed
when :back; :back_changed
when :hint; :hint_changed
end
string_tracker_column = tracker_attribute.to_s
#tracker ||= Tracker.find_by(card_id: #card.id)
updated_array = #tracker.instance_variable_get("#{string_tracker_column}")[Time.zone.now, #card.(eval(card_attribute.to_s))]
#tracker.update_attribute(tracker_attribute, updated_array)
end
Edit: For clarity here's the app/models/tracker.rb:
class Tracker < ActiveRecord::Base
belongs_to :card
end
Your use of instance_variable_get has been corrected, however this approach is destined to fail because ActiveRecord column values aren't stored as individual instance variables.
You can use
#tracker[string_column_changed]
#card[card_attribute]
To retrieve attribute values by name. If you want to get an association, use public_send. The latter is also useful if there is some accessor wrapping the column value (eg carrierwave)
From your error it seem your issue is this:
#tracker.instance_variable_get("#{string_tracker_column}")
evaluates to this after string interpolation:
#tracker.instance_variable_get("front_changed")
which is incorrect use of instance_variable_get. It needs an # prepended:
#tracker.instance_variable_get("#front_changed")
Seems like using instance_variable_get is unnecessary, though, if you set attr_reader :front_changed on the Tracker model.

Calculating and inserting value into table column using rails

Hi I'm a newbie to Rails. So pardon if this is a silly question.
I'm working on a project where I need to calculate the Bank Balance and Cashbox balance in a Transaction. So this depends upon the type of transaction "Debit/Credit" and type of payment "Online Payment/Cheque/Cash". There is only one model involved and that is Transaction Model and transactions table. So this is what I'm doing,
transactions_controller
def create
#transaction = Transaction.create(transaction_params)
#amount = transaction_params[:amount].to_f
#cashbox = transaction_params[:cashbox].to_f
#bank = transaction_params[:bank].to_f
if transaction_params[:t_type] == "Debit"
if transaction_params[:t_method] == "Cash"
#cashbox -= #amount
transaction_params[:cashbox] = #cashbox.to_s
else
#bank -= #amount
transaction_params[:bank] = #bank.to_s
end
elsif transaction_params[:t_type] == "Credit"
if transaction_params[:t_method] == "Cash"
#cashbox += #amount
transaction_params[:cashbox] = #cashbox.to_s
else
#bank += #amount
transaction_params[:bank] = #bank.to_s
end
end
if #transaction.save
redirect_to #transaction
else
render 'new'
end
end
def transaction_params
params.require(:transaction).permit(:date, :notes, :t_type, :t_method, :amount, :paid_by, :paid_to, :cashbox, :bank, :house_id, :expense_for)
end`
Transaction Model
class Transaction < ActiveRecord::Base
end
But when I submit the form containing all the values, the calculation part does't happen and rails inserts only the values submitted in the form.
How to calculate cashbox balance and bank balance on submit from the form and store the updated values in the table?
Also, kindly suggest if there are any better ways to do this.
You need to put this functionality to the model and update object before rendering (if i understood you right). Now you never save the changes into the object.
Your logic is confuse, let read more resful of rails.
- First: you can't save, after that create Transaction again.
- Second: you created transaction and below don't see updating that transaction should of course it be not changed.
Let try yourself:
def create
#amount = transaction_params[:amount].to_f
#cashbox = transaction_params[:cashbox].to_f
#bank = transaction_params[:bank].to_f
if transaction_params[:t_type] == "Debit"
if transaction_params[:t_method] == "Cash"
#cashbox -= #amount
transaction_params[:cashbox] = #cashbox.to_s
else
#bank -= #amount
transaction_params[:bank] = #bank.to_s
end
elsif transaction_params[:t_type] == "Credit"
if transaction_params[:t_method] == "Cash"
#cashbox += #amount
transaction_params[:cashbox] = #cashbox.to_s
else
#bank += #amount
transaction_params[:bank] = #bank.to_s
end
end
#transaction = Transaction.new transaction_params
if #transaction.save
redirect_to #transaction
else
render 'new'
end
end
def transaction_params
params.require(:transaction).permit(:date, :notes, :t_type, :t_method, :amount, :paid_by, :paid_to, :cashbox, :bank, :house_id, :expense_for)
end`
You've got a few things in a bit of a muddle I think:
1) if #transaction.save
Here you're trying to save the transaction and only continuing if you do so successfully. Where is #transaction getting set up though? Presumably in some kind of before_filter but I suspect you don't need that in this case.
2) #transaction = Transaction.create(transaction_params)
Now you're creating, and saving to the database, a new Transaction object base on the parameters that are being submitted from the form (but only if it passes validation).
3) Then you do a load of work to calculate some parameters that appear to be transaction parameters. At this point though you've already created the transaction object with the parameters that were submitted. You add all these to the params hash which is predominantly used to pass web request parameters in (yes, you can use it for other things but I'd avoid doing that just yet).
Now, the most simple fix is probably to move your Transaction.create line to after all your params changes because then you'll be creating a transaction with the parameters you want. I wouldn't advocate that though. It seems like this code would be better moved to the Transaction model and have a method to apply the values to a transaction after you've built it.
(I'd also question if you've got your modelling quite right. You seem to be passing in a #bank value as part of the web request and then applying the value of the transaction your creating to it to give your bank value on the transaction. Of course, I'm not sure of your exact requirements but this seems like a potentially problematic approach. One for a different question perhaps).

Rails Check If Record Is First

I am iterating through a list of records. I need to check that if a record is first do XYZ and if not do ABC. Unfortunately I cant do this:
user = User.first
or
user = User.find(:id)
user.first?
Solution posted below
1. Make method to grab next and previous records
def next
[Model].where("id > ?", id).first
end
def prev
[Model].where("id < ?", id).last
end
2. Make method to check if record is first
def first?(record)
[Model].first == record
end
3. check if record is first
records.each do |record|
if record.first?(record)
record.update_attributes(attr: record.attr + record.attr)
else
prev_rec = [Model].find(record.id).prev
record.update_attributes(attr: prev_rec.attr + record.attr )
end
end
returns true or false
One improvement i would make sure that [Model].first is persistent so that it doesn't make a call to the database each time the loop is run.

How to delete a single instance from a collection

I'm trying to delete a single instance from a database query. "l.remove" represents what i want to do but i know its wrong. I have tried delete and destroy. destroy didn't work and delete actually removed the data from the database. I just want the data removed from the variable. Can anyone help me?
<%
#owner = User.find(params[:id])
#job_list = ShoppingList.where(:user_id=>#user.user_id)
#job_list.each do |l|
#temp = FlaggedCandidate.where(:flagged_user_id=>#owner.user_id, :list_id=>l.list_id)
if !#temp.nil?
l.remove
end
end
#candidate = FlaggedCandidate.new
%>
based on the code i assume that User has many ShoppingList.
You can do something like:
#job_list = #owner.shopping_lists.where( list_id: FlaggedCandidate.where( flagged_user_id: #owner.user_id ).pluck(:list_id) )
That could save the trouble of looping around.
You are trying to remove record from db. In order to just modify collection #job_list you need reject some unsatisfied elements. You can do it with select method (to select job_lists that flagged), or reject in opposite. This is how you code should looks like:
#owner = User.find(params[:id])
#job_list = ShoppingList.where(:user_id=>#user.user_id)
#job_list.select! do |job_list|
FlaggedCandidate.where(
:flagged_user_id => #owner.user_id,
:list_id => job_list.list_id
).any?
end
#candidate = FlaggedCandidate.new
select! simply change the original collection, instead of doing #job_list = #job_list.select { ... }

Resources