In Ruby, how can I define a string with interpolated variables before those variables are defined? - ruby

In Ruby, how can I define a string with interpolated variables before those variables are defined?
What do I need to do to perform the interpolation once those variables have values assigned?
tips = 'Hi,#{name},your order total price is #{price*count}'
name = 'roy'
price = 13.5
count = 10
tips.xxx = 'Hi,roy,your order total price is 135'

In order to evaluate all between #{...} as Ruby code you must use double quotes, otherwise it'd just be printed as a commong String. And I guess you want to return something like 'Hi,roy,your order total price is 135', so, you need to create the name, price and count variables before, because if you don't do it then you'll get an undefined local variable or method 'variable' for main:Object error, so you could try:
name = 'roy'
price = 13.5
count = 10
tips = "Hi,#{name},your order total price is #{price*count}"
puts tips
# Hi,roy,your order total price is 135.0

For your case you can use late interpolation:
tips = 'Hi, %{name},your order total price is %{price}'
name = 'roy'
price = 13.5
count = 10
tips % {name: name, price: price * count}
# => "Hi,roy,your order total price is 135"
Link to documentation: https://ruby-doc.org/core-2.4.1/String.html#method-i-25

Related

How to calculate original loan amount without year terms?

https://www.moneysmart.gov.au/tools-and-resources/calculators-and-apps/savings-goals-calculator
I want to get result like above calculator when I select:
I want to save: 6000
I want to spend it: As soon as possible
Starting balance: 0
Interest rate : 10%
Regular savings: 1000 Monthly
But I am getting not correct result using this code:
loan = 6000.0
interest = 10.0
monthly_payment = 1000.0
i =0.0
record = []
count = 1
add_interst = 0.0
while( loan>=0)
i = interest/(100*12)*loan
loan=i+(loan)-(monthly_payment);
add_interst = add_interst + i
end
puts add_interst
I am getting 181.42163384701658 which should be 168. I don't know where I am wrong.
The code doesn't work because you are doing the opposite of what the link you reference is doing. What they are calculating is saving interest, what you are calculating is loan interest.
Basically, this is how you should define the variables.Also, as others have pointed out, it is good to use BigDecimal to calculate money:
require 'bigdecimal'
balance = 0.to_d
interest = 10.to_d/1200.to_d
regular_saving = 1000;
goal =6000;
i = 0;
added_interest = 0
So, to correct things, you have to start from the starting balance (i.e 0) and start incrementing. Something like this:
while balance < goal
balance += regular_saving;
i = balance * (interest);
balance +=i;
added_interest+=i;
end
Note also, that in the last year you don't need to pay the full saving amount. You only need to pay to reach the goal. For that, you need to add a conditional statement to check goal - balance < regular_saving. If this was the case, the interest should be calculated in terms of the balance that should be paid (slightly less than the goal).

How should I change his script so that it works for collections and not just one product in Shopify?

The script is a template that comes with the script editor app in Shopify. I need to make it work so that if you buy one product from a collection, you get another one free from that collection. This script works only for buying the same product. Here is the script:
PAID_ITEM_COUNT = 2
DISCOUNTED_ITEM_COUNT = 1
# Returns the integer amount of items that must be discounted next
# given the amount of items seen
#
def discounted_items_to_find(total_items_seen, discounted_items_seen)
Integer(total_items_seen / (PAID_ITEM_COUNT + DISCOUNTED_ITEM_COUNT) * DISCOUNTED_ITEM_COUNT) - discounted_items_seen
end
# Partitions the items and returns the items that are to be discounted.
#
# Arguments
# ---------
#
# * cart
# The cart to which split items will be added (typically Input.cart).
#
# * line_items
# The selected items that are applicable for the campaign.
#
def partition(cart, line_items)
# Sort the items by price from high to low
sorted_items = line_items.sort_by{|line_item| line_item.variant.price}.reverse
# Create an array of items to return
discounted_items = []
# Keep counters of items seen and discounted, to avoid having to recalculate on each iteration
total_items_seen = 0
discounted_items_seen = 0
# Loop over all the items and find those to be discounted
sorted_items.each do |line_item|
total_items_seen += line_item.quantity
# After incrementing total_items_seen, see if any items must be discounted
count = discounted_items_to_find(total_items_seen, discounted_items_seen)
# If there are none, skip to the next item
next if count <= 0
if count >= line_item.quantity
# If the full item quantity must be discounted, add it to the items to return
# and increment the count of discounted items
discounted_items.push(line_item)
discounted_items_seen += line_item.quantity
else
# If only part of the item must be discounted, split the item
discounted_item = line_item.split(take: count)
# Insert the newly-created item in the cart, right after the original item
position = cart.line_items.find_index(line_item)
cart.line_items.insert(position + 1, discounted_item)
# Add it to the list of items to return
discounted_items.push(discounted_item)
discounted_items_seen += discounted_item.quantity
end
end
# Return the items to be discounted
discounted_items
end
eligible_items = Input.cart.line_items.select do |line_item|
product = line_item.variant.product
!product.gift_card? && product.id == 11380899340
end
discounted_line_items = partition(Input.cart, eligible_items)
discounted_line_items.each do |line_item|
line_item.change_line_price(Money.zero, message: "Buy one Bolur, get one Bolur free")
end
Output.cart = Input.cart
I tried changing, what seems to be the relevant code:
eligible_items = Input.cart.line_items.select do |line_item|
product = line_item.variant.product
!product.gift_card? && product.id == 11380899340
end
to this:
eligible_items = Input.cart.line_items.select do |line_item|
product = line_item.variant.product
!product.gift_card? && **collection.id** == 123
end
but I get an error:
undefined method 'collection' for main (Your Cart)
undefined method'collection' for main (No Customer)
Two things here:
line_item.variant.product does not have the property collections. For that, you want to use line_item.product (docs) – which (should...see point two) expose all of the methods and properties of the product object.
However, in my attempt to do something similar to you (discount based on product) I tried iterating over line_item.variant – and am always hitting the error of undefined method 'product' for #<LineItem:0x7f9c97f9fff0>. Which I interpret as "line_items accessed in cart scripts can only be at the variant level".
So, I wonder if this is because the cart only contains variants (product/color/size) – so we aren't actually able to access the line_items by product, and only by variant.
I tired iterating over line_item.product_id, which also throws a similar error. I think we just have to try to do some hacky thing at the variant level.
I am going to see if I can access the product by the variant ID...back to the docs!
You actually can't do a collection, so you'd need to modify the script to work with a product type or tags. That script will need to be heavily modified to work for a number of products and not multiples of the same

How to write this domain to be sure at 100 % that we will get the right stock pack operation for each invoice line?

This post should be a little more complex than usual.
We have created a new field for an account.invoice.line : pack_operation. With this field, we can print serial/lot number for each line on the PDF invoice (this part works well).
Many hours passed trying to write the domain to select the EXACT and ONLY stock pack operation for each invoice line.
In the code below, we used the domain [('id','=', 31)] to make our tests printing the PDF.
Ho to write this domain to be sure at 100 % that we will get the right stock pack operation for each invoice line?
I really need your help here... Too complex for my brain.
Our code :
class AccountInvoiceLine(models.Model):
_inherit = "account.invoice.line"
pack_operation = fields.Many2one(comodel_name='stock.pack.operation', compute='compute_stock_pack_operation_id')
def compute_stock_pack_operation_id(self):
stock_operation_obj = self.env['stock.pack.operation']
stock_operation = stock_operation_obj.search( [('id','=', 31)] )
self.pack_operation = stock_operation[0]
EDIT#1
I know that you won't like my code. But, this one seems to work. I take any comments and improvements with pleasure.
class AccountInvoiceLine(models.Model):
_inherit = "account.invoice.line"
pack_operation = fields.Many2one(comodel_name='stock.pack.operation', compute='compute_stock_pack_operation_id')#api.one
def compute_stock_pack_operation_id(self):
procurement_order_obj = self.env['procurement.order']
stock_operation_obj = self.env['stock.pack.operation']
all_picking_ids_for_this_invoice_line = []
for saleorderline in self.sale_line_ids:
for procurement in saleorderline.procurement_ids:
for stockmove in procurement.move_ids:
if stockmove.picking_id.id not in all_picking_ids_for_this_invoice_line
all_picking_ids_for_this_invoice_line.append(stockmove.picking_id.id)
all_picking_ids_for_this_invoice_line))
stock_operation = stock_operation_obj.search(
[ '&',
('picking_id','in',all_picking_ids_for_this_invoice_line),
('product_id','=',self.product_id.id)
]
)
self.pack_operation = stock_operation[0]
The pack_operation field is a computed field, that be default means that the field will not be saved on the database unless you set store=True when you define your field.
So, what you can do here is change:
pack_operation = fields.Many2one(comodel_name='stock.pack.operation', compute='compute_stock_pack_operation_id')
to:
pack_operation = fields.Many2one(comodel_name='stock.pack.operation', compute='compute_stock_pack_operation_id', store=True)
And try running your query again.

Ruby: Convert String to Symbol during String Interpolation

I am looping over a block of code, each time increasing variable "filter_index", from 1, to 2, to 3... etc.
I'd like to use this variable to access the different symbols:
filter1_field_name,
filter2_field_name,
filter3_field_name
#filter1_field_name = "Region"
#filter2_field_name = "Country"
#filter3_field_name = "City"
SELECT_STATEMENT = "GROUP BY "
numberOfFilters = 3
filter_index = 1
numberOfFilters.times do #Number of iterations
filter_field_name = "#filter#{filter_index.to_s}_field_name"
SELECT_STATEMENT.sub! "GROUP BY", "AND #{filter_field_name.to_sym} GROUP BY"
filter_index += 1
end
puts SELECT_STATEMENT
This results in
AND #filter1_field_name AND #filter2_field_name AND
#filter3_field_name GROUP BY
But the desired result is
AND Region AND Country AND City GROUP BY
I'm wondering why filter_field_name.to_sym is not working (or rather, what I'm doing wrong)?
You can use instance_variable_get function to get variable value.
Replace the line
filter_field_name = "#filter#{filter_index.to_s}_field_name"
with
filter_field_name = instance_variable_get("#filter#{filter_index.to_s}_field_name")
EDIT:
I think you can put the field names into an array and concat the values with join method.
fields = ['Region','Country','City']
fields.join(' AND ')
you can use instance_variable_get or eval
filter_field_name = instance_variable_get("#filter#{filter_index.to_s}_field_name")
or
filter_field_name = eval("#filter#{filter_index.to_s}_field_name")

Elixir/Phoenix sum of the column

I'm trying to get the sum of the particular column.
I have a schema of orders, with the field total, that stores the total price.
Now I'm trying to created a query that will sum total value of all the orders, however not sure if I'm doing it right.
Here is what i have so far:
def create(conn, %{"statistic" => %{"date_from" => %{"day" => day_from, "month" => month_from, "year" => year_from}}}) do
date_from = Ecto.DateTime.cast!({{year_from, month_from, day_from}, {0, 0, 0, 0}})
revenue = Repo.all(from p in Order, where: p.inserted_at >= ^date_from, select: sum(p.total))
render(conn, "result.html", revenue: revenue)
end
And just calling it like <%= #revenue %> in the html.eex.
As of right now, it doesn't return errors, just renders random symbol on the page, instead of the total revenue.
I think my query is wrong, but couldn't find good information about how to make it work properly. Any help appreciated, thanks!
Your query returns just 1 value, and Repo.all wraps it in a list. When you print a list using <%= ... %>, it treats integers inside the list as Unicode codepoints, and you get the character with that codepoint as output on the page. The fix is to use Repo.one instead, which will return the value directly, which in this case is an integer.
revenue = Repo.one(from p in Order, where: p.inserted_at >= ^date_from, select: sum(p.total))
#Dogbert's answer is correct. It is worth noting that if you are using Ecto 2.0 (currently in release candidate) then you can use Repo.aggregate/4:
revenue = Repo.aggregate(from p in Order, where: p.inserted_at >= ^date_from, :sum, :total)

Resources