self.method (params) Rails - ruby-on-rails

I'm newbie and I would like to know if is it possible to define model methods with params. I mean. I have this method to calculate distances using spherical coordinates
#in my model
#Haversin formula to calculate distance between spherical coordinates
def self.distance(b)
rad_per_deg = Math::PI/180 # PI / 180
rkm = 6371 # Earth radius in kilometers
#rm = rkm * 1000 # Radius in meters
a=[]
a.push(self.lat)
a.spuh(self.long)
dlon_rad = (b[1]-a[1]) * rad_per_deg # Delta, converted to rad
dlat_rad = (b[0]-a[0]) * rad_per_deg
lat1_rad, lon1_rad = a.map! {|i| i * rad_per_deg }
lat2_rad, lon2_rad = b.map! {|i| i * rad_per_deg }
a = Math.sin(dlat_rad/2)**2 + Math.cos(lat1_rad) * Math.cos(lat2_rad) * Math.sin(dlon_rad/2)**2
c = 2 * Math.asin(Math.sqrt(a))
distance=rkm * c
return distance
end
I want this to work like: obj.distance(b) where b is an array of latitude and longitude. But when I try this on irb I get:
NoMethodError: undefined method `distance' for #<Object:0x000000058854c8>
Probably I'm missing something.
class Meteo < ActiveRecord::Base
attr_accessible :date, :humidity, :lat, :long, :pressure, :temp, :town, :wind, :wind_direction
, :rain_quantity
#Haversin formula to calculate distance between spheric coordinates
def self.distance(b)
rad_per_deg = Math::PI/180 # PI / 180
rkm = 6371 # Earth radius in kilometers
#rm = rkm * 1000 # Radius in meters
a=[]
a.push(self.lat)
a.spuh(self.long)
dlon_rad = (b[1]-a[1]) * rad_per_deg # Delta, converted to rad
dlat_rad = (b[0]-a[0]) * rad_per_deg
lat1_rad, lon1_rad = a.map! {|i| i * rad_per_deg }
lat2_rad, lon2_rad = b.map! {|i| i * rad_per_deg }
a = Math.sin(dlat_rad/2)**2 + Math.cos(lat1_rad) * Math.cos(lat2_rad) * Math.sin(dlon_rad/2)
**2
c = 2 * Math.asin(Math.sqrt(a))
distance=rkm * c
return distance
end
end
And I call this on irb like:
irb> m=Meteo.last
irb> b=[86.43971008189519, 23.477053751481986]
irb> m.distance(b)

Just remove self.
When you write def self.distance, you mean that the method will be called on the model class. You should use def distance if you want the method to be called on a model instance.
Compare:
class SomeModel
def self.distance
# ...
end
end
SomeModel.distance
With:
class SomeModel
def distance
# ...
end
end
obj = SomeModel.new
obj.distance

If I get you right, you are defining a class method (with def self.distance) but are calling it on an instance of that class (obj.distance(array)).
You should call the method on obj's class e.g. Meteo.distance(array).
Or simply define it as an instance method, by just leaving the self in method definition.
Hope, that helps

Related

Rails survey calculating results and storing results in DB

Having trouble with the controller of my survey (Waterusage). It has 30+ variables collected from a form and those inputs need to be saved to the waterusage db and used to calculate a final score, also saved in the database.
class Waterusage < ApplicationRecord
belongs_to :user
end
class WaterusagesController < ApplicationController
def new
#waterusage = Waterusage.new
end
def create
#user = User.find(params[:user_id])
_showerTotal = :average_shower * :shower_flow_rate * :household_size
_bathTotal = :bath_rate * :bath_multiplier * 35
_bathroomSinkTotal = :bathroom_sink_usage * :bathroom_sink_flow_rate * :household_size
_toiletTotal = :mellow * :low_flow_toilet * :household_size
_kitchenTotal = :kitchen_sink_usage * :kitchen_sink_flow_rate
_dishwashingTotal = :dishwasher_rate * :dishwasher_multiplier * :dishwasher_method
_laundryTotal = :laundry_rate * :laundry_method * :laundry_multiplier
_homeUsage = _showerTotal + _bathTotal + _bathroomSinkTotal + _toiletTotal + _kitchenTotal + _dishwashingTotal + _laundryTotal + :greywater
_lawnTotal = :lawn_rate * :lawn_multiplier * :lawn_size * :xeriscaping
_swimmingTotal = (:swimming_pool / 365) + (:swimming_months * 1000 / 365
_carwashTotal = :carwash_rate * :carwash_multiplier * :carwash_method
_outsideUsage = _lawnTotal + _swimmingTotal + _carwashTotal
_drivingTotal = 0.735 * :miles
_powerTotal = :statewater * :percent_statewater / 100
_indirectTotal = :shopping + :paper_recycling + :plastic_recycling + :can_recycling + :textile_recycling + :diet + (200 * :pet_cost / 30)
:household_total = _homeUsage + _outsideUsage + _drivingTotal + _powerTotal + _indirectTotal
:individual_total = :household_total / :household_size
#waterusage = #user.waterusage.create(waterusage_params)
redirect_to user_path(#user)
end
def destroy
#user = User.find(params[:user_id])
#waterusage = #user.waterusage.find(params[:id])
#waterusage.destroy
redirect_to user_path(#user)
end
private
def waterusage_params
params.require(:waterusage).permit(:household_size, :average_shower,
:shower_flow_rate, :bath_rate, :bath_multiplier, :bathroom_sink_usage,
:bathroom_sink_flow_rate, :mellow, :low_flow_toilet, :kitchen_sink_usage,
:kitchen_sink_flow_rate, :dishwasher_rate, :dishwasher_multiplier,
:dishwasher_method, :laundry_rate, :laundry_multiplier, :laundry_method,
:greywater, :lawn_rate, :lawn_multiplier, :lawn_size, :xeriscaping,
:swimming_pool, :swimming_months, :carwash_rate, :carwash_multiplier,
:carwash_method, :miles, :statewater, :percent_statewater, :shopping,
:paper_recycling, :plastic_recycling, :can_recycling, :textile_recycling,
:diet, :pet_cost, :individual_total, :household_total)
end
end
Is there a better way I can be doing this? Currently there are errors on the lines that are working to sum subtotals. (ie. :household_total = _homeUsage + _outsideUsage + _drivingTotal + _powerTotal + _indirectTotal
)
Also I'm not sure if I am properly connecting the user info to the survey schema
You don't want to do that math in the controller. Skinny controllers, fat models. Aside from that, one reason it's failing is that the syntax is incorrect. A symbol (:hello_world) can't be assigned a value nor does it contain one. Less importantly, while it's not illegal to have an underscore prefixed local variable, that is not the convention in Ruby. Neither is camelcase. You want hello_world rather than helloWorld. Anyway...
Assumption: You have a requirement that the totals must be persisted. They cannot be calculated values.
You want to move those calculations to the model. And instead of assigning a ton of variables, use methods. That way you can easily unit test them.
What's missing here: Validations in the model that ensure that all expected attribute values are present. The controller should handle an invalid Waterusage instance on create, too. This code is untested and is just for illustrative purposes.
class Waterusage < ApplicationRecord
belongs_to user
before_validation :calculate_totals
def calculate_totals
self.household_total = get_household_total
self.individual_total = get_individual_total
end
def get_household_total
home_usage + outside_usage + driving_total + power_total + indirect_total
end
def get_individual_total
household_total / household_size
end
def home_usage
shower_total + bath_total + bathroom_sink_total + toilet_total + kitchen_total + dishwashing_total + laundry_total + greywater
end
def outside_usage
lawn_total + swimming_total + carwash_total
end
def driving_total
0.735 * miles
end
def power_total
statewater * percent_statewater / 100
end
def indirect_total
shopping + paper_recycling + plastic_recycling + can_recycling + textile_recycling + diet + (200 * pet_cost / 30)
end
def shower_total
average_shower * shower_flow_rate * household_size
end
def bath_total
bath_rate * bath_multiplier * 35
end
def bathroom_sink_total
bathroom_sink_usage * bathroom_sink_flow_rate * household_size
end
def toilet_total
mellow * low_flow_toilet * household_size
end
def kitchen_total
kitchen_sink_usage * kitchen_sink_flow_rate
end
def dishwashing_total
dishwasher_rate * dishwasher_multiplier * dishwasher_method
end
def laundry_total
laundry_rate * laundry_method * laundry_multiplier
end
def lawn_total
lawn_rate * lawn_multiplier * lawn_size * xeriscaping
end
def swimming_total
(swimming_pool / 365) + (swimming_months * 1000 / 365)
end
def carwash_total
carwash_rate * carwash_multiplier * carwash_method
end
end
class WaterusagesController < ApplicationController
...
def create
#user = User.find(params[:user_id])
#waterusage = #user.waterusage.create(waterusage_params)
redirect_to user_path(#user)
end
...
end
First of all prefix every ':' inside of create with a 'params[' and suffix ']', then change every '_' with a '#'.
It would be like this:
_powerTotal = :statewater * :percent_statewater / 100
turns into
#powerTotal = params[:statewater].to_i * params[:percent_statewater].to_i /100
Like that,
:individual_total = :household_total / :household_size
turns into
#individual_total = params[:household_total].to_i / params[:household_size].to_i
Also you're doing nothing with your calculations, they are just floating around, as it is, you can't even invoke them from your view.
If you want it to save on your waterusage object that relates to an user the individual_total attributes it would be;
#waterusage = #user.waterusage.create(waterusage_params, individual_total: #individual_total).

how to compare complex decimal number with integer value

I'm going to get all clinics that are near to my latitude and longitude. i did that with following method. the result of dist is a long value that i need to compare it with a integer value. i don't know why i get this error during the compare dist and distance which is a integer value.
this is my error:
NoMethodError (undefined method `<=' for (-2.693846638591123+0.0i):Complex):
and this is what everyhting that i did for this:
def get_clinic_list
ulat=params[:lat]
ulang=params[:lang]
distance=params[:distance]
#clinic=[]
Clinic.all.each do |clinic|
if clinic_distance(ulat,ulang,distance,clinic.id)
#doctor=DoctorProfile.find_by(user_id: clinic.user_id)
end
end
end
def clinic_distance(ulat, ulang,distance,clinic)
#clinic=Clinic.find(clinic)
diff_lat= ulat.to_f - #clinic.latitude.to_f
diff_lang= ulang.to_f - #clinic.longitude.to_f
#disc=Math.sqrt(((diff_lat*119.574)**2)+(diff_lang * Math.cos(diff_lat) * 111.320))
a=(diff_lat * 119.574) ** 2
b= diff_lang * Math.cos(diff_lat) * 111.320
c=a+b
logger.info "the c parameter is #{c}"
dist=Math.sqrt(c)
dist = dist ** 2
if dist <= distance
return true
else
return false
end
end
Complex numbers don't support <= or >= (although they do support ==)
Simplest solution is to get the absolute part of the number
if dist.abs <= distance
There was a mistake is pretty much every line of your clinic_distance method. I tried my best at correcting it, but I cannot test it without your data.
The problem isn't about Complex numbers. I don't know where this Complex number comes from, possibly from a negative c in your Math.sqrt(c).
EarthRadius = 6371 # km
OneDegree = EarthRadius * 2 * Math::PI / 360 # 1° latitude in km
def get_clinic_list
lat = params[:lat]
lon = params[:lang] # :lang???
max_distance = params[:distance] # :distance should probably be :max_distance
#clinic = [] # What do you do with this empty array?
Clinic.all.each do |clinic|
if distance_in_km(lat, lon, clinic.latitude, clinic.longitude) < max_distance
# Do you really want to keep overriding #doctor every time a clinic is found?
#doctor = DoctorProfile.find_by(user_id: clinic.user_id)
end
end
# You return every clinic, even ones far away...
end
def distance_in_km(lat1, lon1, lat2, lon2)
diff_lat = lat1.to_f - lat2.to_f
diff_lon = lon1.to_f - lon2.to_f
lat_km = diff_lat * OneDegree
lon_km = diff_lon * OneDegree * Math.cos(lat1.to_f * Math::PI / 180) # Math.cos expects a radian angle
Math.sqrt(lat_km**2 + lon_km**2)
end

I am trying to do arithmetic on table values and keep getting an error. here is my code

I am trying to perform arithmetic on table values and keep getting an error. Here is my total code. I am basically trying to generate simplex noise. I have created a multidimensional array (table) and am trying to perform operations on the values but i keep getting an error that says I cannot perform arithmetic on a table value. I don't know If I have to convert it to something or what. Please Help.
totalNoiseMap = {}
function simplex_noise(width, height)
simplexnoise = {}
for i = 1,512 do
simplexnoise[i] = {}
for j = 1, 512 do
simplexnoise[i][j] = 0
end
end
frequency = 5.0 / width
for x = 1, width do
for y = 1, height do
simplexnoise[x][y] = noise(x * frequency,y * frequency)
simplexnoise[x][y] = (simplexnoise[x][y] + 1) / 2
end
end
return simplexnoise
end
function noise(x, y, frequency)
return simplex_noise(x / frequency, y / frequency)
end
function generateOctavedSimplexNoise(width,height,octaves,roughness,scale)
totalnoise = {}
for i = 1,512 do
totalnoise[i] = {}
for j = 1, 512 do
totalnoise[i][j] = 0
end
end
layerFrequency = scale
layerWeight = 1
weightSum = 0
for octave = 1, octaves do
for x = 1, width do
for y = 1, height do
totalnoise[x][y] = (totalnoise[x][y] + noise(x * layerFrequency,y * layerFrequency, 2) * layerWeight)
end
end
--Increase variables with each incrementing octave
layerFrequency = layerFrequency * 2
weightSum = weightSum + layerWeight
layerWeight = layerWeight * roughness
end
return totalnoise
end
totalNoiseMap = generateOctavedSimplexNoise(512, 512, 3, 0.4, 0.005)
totalnoise[x][y] + noise(x * layerFrequency,y * layerFrequency, 2) * layerWeight
Here you get table noise(x * layerFrequency,y * layerFrequency, 2), mult it by scalar layerWeight and than add it to scalar totalnoise[x][y].
I can think of how to multiply table by scalar - it should be something like
for i = 1,512 do
for j = 1,512 do
a[i][j] = t[i][j] * scalar
end
end
But I'm unable to get what you're trying to do by adding. Suppose it should be addition of two tables
for i = 1,512 do
for j = 1,512 do
a[i][j] = b[i][j] + c[i][j]
end
end
But it works only with same-sized tables

each block create a Nil class

I want calculate components of an average
Controller
def set_average_course
#course = #group.courses.find(params[:course_id])
#evaluations = #course.evaluations
#evaluations.each do |evaluation|
#numerator += evaluation.average * evaluation.coefficient
#denumerator += evaluation.coefficient
end
#average = #numerator / #denumerator
#course.update(average: average)
end
Logs
NoMethodError (undefined method `+' for nil:NilClass)
But
#numerator += evaluation.average * evaluation.coefficient
is well defined outside the .each block
It would be better and more efficient to compute the values using SQL:
The code will look like this:
def set_average_course
#course = #group.courses.find(params[:course_id])
res = Evaluation.select("SUM(Evaluations.average * Evaluations.coefficient) AS numerator, SUM(Evaluations.coefficient) AS denumerator").
where(course_id: #course.id)
average = res.first['numerator'] / res.first['denominator'] # Raise an error if 0
#course.update(average: average)
end
Hope this leads you to the right direction
When you enter the each loop for the first time, #numerator and #denumerator are not initialized yet. Instance variables are different from local variables in that they evaluate to nil if not initialized yet, whereas a reference to an uninitialized local variable will thrown an error. The solution is to initialize the variables to the neutral element of addition, which is 0, before entering the loop:
def set_average_course
#course = #group.courses.find(params[:course_id])
#evaluations = #course.evaluations
#numerator = 0
#denumerator = 0
#evaluations.each do |evaluation|
#numerator += evaluation.average * evaluation.coefficient
#denumerator += evaluation.coefficient
end
#average = #numerator / #denumerator
#course.update(average: average)
end
Although, as a side note, I think you mean "denominator" instead of "denumerator". You will also have to account for the case where #denumerator is equal to 0, for example, when #evaluations is empty:
def set_average_course
#course = #group.courses.find(params[:course_id])
#evaluations = #course.evaluations
return if #evaluations.blank? # or raise an error
#numerator = 0
#denumerator = 0
#evaluations.each do |evaluation|
#numerator += evaluation.average * evaluation.coefficient
#denumerator += evaluation.coefficient
end
#average = #numerator / #denumerator
#course.update(average: average)
end
Try to initialize numerator and denumerator to number like #p11y's answer.
My suggestion is to calculate average more ruby way like this:
#numerator = #evaluations.map { |e| e.average * e.coefficient }.reduce(&:+)
#denumerator = #evaluations.map { |e| e.coefficient }.reduce(&:+)
#average = #numerator / #denumerator

Number of Percentage in Ruby on Rails

I have a coupon model and i am trying to calculate how much you save from the original deal.
Coupons.rb
def original_deal
original_price
end
def our_deal
deal_price
end
def percentage_off
original_deal / our_deal * 100.ceil
end
Show.html
<%= number_to_percentage(#coupon.percentage_off, :precision => 2) %>
Coupon Info
original_price = £100.00
deal_price = £90.00
Results.
I get 111.11% as the answer. What am i missing?
Your discount percentage calculation is off. You need to find the difference between the original price and the new price. And divide it by the original price to get the discount percentage:
def percentage_off
(Float(original_deal - our_deal) / original_deal * 100).ceil
end
Your input example would now return (100 - 90) / 100 * 100 = 10
Here's an alternative, step-by-step calculation :)
original = 100.0
our = 90.0
our_relative = our / original # => 0.9
you_save = (1.0 - our_relative).round(2) # => 0.1
you_save_percents = (you_save * 100).round # => 10
The calculation is coming out correct. If you look at the values you provided and do the math you come up with the following equation:
100.00 / 90.00 = 1.11
then you do
1.11 * 100 = 111.11
If you want to calculate your savings percentage, try this:
(original_price - deal_price) / original_price * 100.ceil
Hopefully that helps you out.

Resources