Rails 3 validation in model failure - ruby

I have a validate_completion method defined in my task.rb model. The Pseudo logic should be
if no hazards exist
date_completed = Date.today
elsif any hazard exists with a risk_total > 1000
date_completed = nil
else any hazard that exists has a risk_total =< 1000
date_complete = Date.today
end
The code looks like as follows.
def validate_completion
if self.date_completed.nil?
if self.Biohaz_exist == "No"
if self.Hazmat_exist == "No"
if self.Fire_exist == "No"
if self.Hazengy_exist == "No"
if self.Tool_exist == "No"
if self.Rad_exist == "No"
if self.Laz_exist == "No"
if self.Mag_exist == "No"
if self.Ergo_exist == "No"
if self.Mechand_exist == "No"
if self.Road_exist == "No"
if self.Fall_exist == "No"
if self.Hazatm_exist == "No"
if self.Noise_exist == "No"
if self.Ovrhead_exist == "No"
if self.Cut_exist == "No"
if self.Temp_exist == "No"
if self.Access_exist == "No"
if self.Cowrkr_exist == "No"
if self.Lonewrkr_exist == "No"
self.date_completed = Date.today
elsif self.Biohaz_exist == "Yes" and self.Biohaz.risk_total > 1000
self.date_completed = nil
elsif self.Hazmat_exist == "Yes" and self.Hazmat.risk_total > 1000
self.date_completed = nil
elsif self.Fire_exist == "Yes" and self.Fire.risk_total > 1000
self.date_completed = nil
elsif self.Hazengy_exist == "Yes" and self.Hazengy.risk_total > 1000
self.date_completed = nil
elsif self.Tool_exist == "Yes" and self.Tool.risk_total > 1000
self.date_completed = nil
elsif self.Rad_exist == "Yes" and self.Rad.risk_total > 1000
self.date_completed = nil
elsif self.Laz_exist == "Yes" and self.Laz.risk_total > 1000
self.date_completed = nil
elsif self.Mag_exist == "Yes" and self.Mag.risk_total > 1000
self.date_completed = nil
elsif self.Ergo_exist == "Yes" and self.Ergo.risk_total > 1000
self.date_completed = nil
elsif self.Mechand_exist == "Yes" and self.Ergo.risk_total > 1000
self.date_completed = nil
elsif self.Road_exist == "Yes" and self.Ergo.risk_total > 1000
self.date_completed = nil
elsif self.Fall_exist == "Yes" and self.Fall.risk_total > 1000
self.date_completed = nil
elsif self.Hazatm_exist == "Yes" and self.Hazatm.risk_total > 1000
self.date_completed = nil
elsif self.Noise_exist == "Yes" and self.Noise.risk_total > 1000
self.date_completed = nil
elsif self.Ovrhead_exist == "Yes" and self.Ovrhead.risk_total > 1000
self.date_completed = nil
elsif self.Cut_exist == "Yes" and self.Cut.risk_total > 1000
self.date_completed = nil
elsif self.Temp_exist == "Yes" and self.Temp.risk_total > 1000
self.date_completed = nil
elsif self.Access_exist == "Yes" and self.Access.risk_total > 1000
self.date_completed = nil
elsif self.Cowrkr_exist == "Yes" and self.Cowrk.risk_total > 1000
self.date_completed = nil
elsif self.Lonewrkr_exist == "Yes" and self.Lonewrkr.risk_total > 1000
self.date_completed = nil
else
self.date_completed = Date.today
end
end
end
end
end
end
end
end
end
end
end
end
end
end
end
end
end
end
end
end
end
end
As is right now if no hazards exist, it successfully sets the date to today. And if I then add a hazard it effectively switches the date back to nil. However, If one hazard exists and it has a risk_total below 1000 the date remains nil.

"Well, given that your hazards are statically defined and not in the database, you could do something like this:
#definition of all the hazard
hazards = [:Fire, :Road, :Fall]
def validate_completion
# find all the hazards that exist
exists = hazards.select { |hazard| self.send("#{hazard.to_s}_exist") }
# find if at least one high risk hazard exists
high_risk_total = exists.detect {|hazard| self.send("#{hazard.to_s}_risk_total") > 1000 }
# find if at least one low rick hazard exists
low_risk_total = exists.detect {|hazard| self.send("#{hazard.to_s}_risk_total") <= 1000 }
# validation logic here
end
You can get by with this logic, but it might be significantly easier to make your hazards a database table and use ActiveRecord associations and queries to simplify this.

Related

Rails ActionController::Parameters sanitize

I need to sanitize the strong parameters in Rails5 in Rails 4 I used this:
def forest_hash(hash)
new_hash = hash.deep_dup
new_hash.each do |k, v|
new_hash[k] =
if v.is_a?(Hash)
forest_hash(v)
# elsif v.respond_to?(:to_unsafe_h)
# forest_hash(v.to_unsafe_h)
elsif v.is_a?(String) && DATE_TIME_REGEXP === v
v = Time.zone.parse(v)
elsif v == ''
nil
elsif v == ['']
[]
elsif v == '<p> </p>'
nil
elsif v == 'true'
true
elsif v == 'false'
false
elsif v.is_a?(String) && v.to_i.to_s == v
v.to_i
elsif v.is_a?(Array) && v.count > 1
v.delete_if { |x| x == '' }
elsif v.is_a?(String) && base_helpers.strip_tags(v) != v
Foresttrees::TagSanitizer.new(v).sanitize
elsif v.is_a?(String)
v.squish
else
v
end
if new_hash[k].is_a?(Array) && new_hash[k].all? { |vv| vv.is_a?(Hash) }
binding.pry
new_hash[k].map! { |vv| forest_hash(vv) }
end
end
end
<ActionController::Parameters {"utf8"=>"✓", "authenticity_token"=>"XXXXXXX", "commit"=>"Save", "activism_tree"=><ActionController::Parameters {"name"=>"qewrqewr", "reason"=>"", "affected_locations"=>["", "global"], "risk"=>"3", "description"=>"<p>qerqewr</p>", "published_at"=>"", "published_internet_at"=>"", "related_tree_ids"=>[""], "target_customer_ids"=>["", "5f75952427e4984019f6e9d4"], "monitored_source"=>"false", "cuter"=>"qerqewrqewr", "cut_duration"=>"", "expected_date"=>"", "cut_probability"=>"", "place"=>"", "hour"=>"", "followers"=>"0", "simplified_evidences_attributes"=>{"0"=>{"name"=>"qerqewrq", "description"=>"", "type"=>"url", "url"=>"qwerqwer", "sanitized"=>"false"}}} permitted: false>, "type"=>"activism", "controller"=>"foresttrees/trees", "action"=>"create", "locale"=>"en"} permitted: false>
The problem is that:
after doing that params.each {​​ |k,v| params[k] = v }​​.class
I get a hash so I loose the permit, required methods,
I tried to use slice and fetch but slice is not really working properly. And I want not loose de ActionController::Parameter Properties in my new object.
so how I can proceed
Thera are many difference between Rails 4 and Rails 5 and one is in Strong parameters:
But with Rails 5, ActionController::Parameters will no longer inherit
from HashWithIndifferentAccess
As pointed here
But if you look at the documentation, you can use other methods that can perform better your task more functionally, without the deep_dup and without using map! and work excusively in the values of the object that you want in this case.
in this case you should use transform_values also alternatively exists with !.
One thing important is that v can be a scalar (String, number ...), Array or Hash or another (ActiveController::Parameters). so you can process like this:
def forest_hash(hash)
hash.transform_values do |v|
if v.is_a?(Hash) || v.is_a?(ActionController::Parameters)
forest_hash(v)
elsif v.is_a?(String) && DATE_TIME_REGEXP === v
Time.zone.parse(v)
elsif v == ''
nil
elsif v == ['']
[]
elsif v == '<p> </p>'
nil
elsif v == 'true'
true
elsif v == 'false'
false
elsif v.is_a?(String) && v.to_i.to_s == v
v.to_i
elsif v.is_a?(Array)
v.delete_if { |x| x == '' }.map do |vv|
vv.is_a?(Hash) || vv.is_a?(ActionController::Parameters) ? forest_hash(vv) : vv
end
elsif v.is_a?(String) && base_helpers.strip_tags(v) != v
Foresttrees::TagSanitizer.new(v).sanitize
elsif v.is_a?(String)
v.squish
else
v
end
end
end
You should also, maybe encapsulate some value manipulation the scalar String and Array (hash ActiveController::Parameters always recursive) in a separate method, to made this code more ruby like and no need of the infinite if else chain.
Whith this code you return a new ActionController::Parameters that applies the sanitazions or modifications on the values

Ruby For loop through JSON response from API

I have a perfectly functional piece of code that does what I want but it is really heavy and i am sure it could be greatly improved by using a nice For loop somewhere but I'm not sure how to go about it
My code is:
def helper
response = RestClient.get API_RESPONSE
check_X_0 = JSON.parse(response.body)['EXP'][0]['X']
check_Y_0 = JSON.parse(response.body)['EXP'][0]['Y']
check_X_1 = JSON.parse(response.body)['EXP'][1]['X']
check_Y_1 = JSON.parse(response.body)['EXP'][1]['Y']
check_X_2 = JSON.parse(response.body)['EXP'][2]['X']
check_Y_2 = JSON.parse(response.body)['EXP'][2]['Y']
check_X_3 = JSON.parse(response.body)['EXP'][3]['X']
check_Y_3 = JSON.parse(response.body)['EXP'][3]['Y']
check_X_4 = JSON.parse(response.body)['EXP'][4]['X']
check_Y_4 = JSON.parse(response.body)['EXP'][4]['Y']
if check_X_0 == false && check_Y_0 == true
exp_id = JSON.parse(response.body)['EXP'][0]['ABC']
elsif check_X_1 == false && check_Y_1 == true
exp_id = JSON.parse(response.body)['EXP'][1]['ABC']
elsif check_X_2 == false && check_Y_2 == true
exp_id = JSON.parse(response.body)['EXP'][2]['ABC']
elsif check_X_3 == false && check_Y_3 == true
exp_id = JSON.parse(response.body)['EXP'][3]['ABC']
elsif check_X_4 == false && check_Y_4 == true
exp_id = JSON.parse(response.body)['EXP'][4]['ABC']
else
puts 'Nothing valid - use default'
exp_id = JSON.parse(response.body)['EXP'][1]['ABC']
end
This is fairly cumbersome so can anyone help me to trim this down?
You should not parse your json 10 times in the first place. Parse it once and use the result.
rb = RestClient.get(API_RESPONSE).body['EXP']
checks = (0..4).map { |i, s| [i, rb[i]['X'], rb[i]['Y']] }
exp_id =
if found = checks.detect { |_i, f, t| !f && t }
rb[found.first]['ABC']
else
puts 'Nothing valid - use default'
rb[1]['ABC']
end

How to break out of a loop by writing the same word 3 times in a row (word+return key ,3 times)?

I have to break out of a loop by typing the same word "BYE" 3 times in a row and i'm out of ideas, this is how i tried but its not working:
the test:
elsif var == 'BYE' * 3
break
the original code:
while true
timeVar = rand(1930..1950)
inputVar = gets.chomp
if inputVar == inputVar.downcase
puts ' HUH?! SPEAK UP SONNY!!! '
elsif inputVar == inputVar.upcase && inputVar != 'BYE'
puts 'NO, NOT SINCE ' + timeVar.to_s + ' !'
elsif inputVar == 'BYE'
break
end
end
and the working solution implemented :
bye_count = 0
while bye_count < 3 do
time_grandma = rand(1930..1950)
input_var = gets.chomp
if input_var == "BYE" then
bye_count += 1
else
bye_count = 0
end
if input_var == input_var.downcase
puts ' HUH?!? SPEAK UP SONNY!!'
elsif input_var == input_var.upcase
puts ' NO, NOT SINCE ' + time_grandma.to_s + ' !'
end
end
Adjust your code to look something like this.
bye_count = 0
while bye_count < 3 do
input_var = gets.chomp
# ...
if input_var == "BYE" then
bye_count += 1
else
bye_count = 0
end
end

Rubocop:method has too many lines

Hello I am new at ruby programming.
Ran rubocop inspection in my project and it says:
Method has too many lines. [13/10] def refresh_status
Here is my methods:
def refresh_status
lost = false
in_progress = false
won = false
#bets.each do |bet|
lost = true if bet.result == :lost
if bet.result == :canceled
#to_return /= bet.odd
won = true
end
in_progress = true if bet.result == :in_progress
won = true if bet.result == :won
end
def_result_after_refresh(lost, in_progress, won)
end
def def_result_after_refresh(lost, in_progress, won)
if lost
#result = :lost
elsif in_progress
#result = :in_progress
elsif won
#result = :won
end
end
Can't find a way to make that method shorter, maybe you could help?
You can use some the Enumerable methods.
def refresh_status
#to_return /= #bets.select { |bet| bet.result == :canceled }.map(&:odd).reduce(1, :*)
results = #bets.map { |bet| bet.result == :cancelled ? :won : bet.result }.uniq
#result = case
when results.include?(:lost) then :lost
when results.include?(:in_progress ) then :in_progress
when results.include?(:won) then :won
end
end

undefined method `keys' for nil:NilClass in tic tac toe game RUBY

I'm trying to build a simple 2 player tic tac toe game in Ruby.
Here is the code:
class Morpion
def init
create_grid
get_player
show_grid
end
def get_player
puts "Let play some Tic Tac Toe"
puts ""
#player1 ='X'
#player2='O'
puts ""
puts "Where would you like to move? (check out the grid below and type any number 1-9 to place your symbol): "
puts " 1 | 2 | 3 "
puts "---+---+---"
puts " 4 | 5 | 6 "
puts "---+---+---"
puts " 7 | 8 | 9 "
end
def create_grid
#grid = {
'1' => ' ',
'2' => ' ',
'3' => ' ',
'4' => ' ',
'5' => ' ',
'6' => ' ',
'7' => ' ',
'8' => ' ',
'9' => ' '
}
end
def show_grid
puts ""
puts "#{#grid['1']}|#{#grid['2']}|#{#grid['3']}"
puts "-----"
puts "#{#grid['4']}|#{#grid['5']}|#{#grid['6']}"
puts "-----"
puts "#{#grid['7']}|#{#grid['8']}|#{#grid['9']}"
puts ""
end
def play
number_turns=1
while number_turns < 10
number_turns.odd? ? player_turn(#player1) : player_turn(#player2)
game_checker
if game_checker
break
end
number_turns+=1
end
end
def player_turn(player)
puts player == 'X' ? "It's X's turn!" : "It's O's turn!"
puts ""
cell = gets.chomp
unless #grid.keys.include?(cell) #check if the user entered a number corresponding to the grid
puts ""
puts "it has to be a number from 1 to 9"
player_turn(player)
end
if #grid[cell] == ' ' #check if cell in grid is empty for user input
#grid[cell] = player
else
puts ""
puts "That cell is occupied. Choose again!"
player_turn(player)
end
show_grid
end
def game_checker
end_game = false
if #grid['1'] != ' ' && #grid['5'] != ' ' && #grid['9'] != ' '
if (#grid['1'] == #grid['2'] && #grid['1'] == #grid['3'])
end_game = true
victory = #grid['1']
elsif (#grid['4'] == #grid['5'] && #grid['4'] ==#grid['6'])
end_game = true
victory = #grid['4']
elsif (#grid['7'] == #grid['8'] && #grid['7'] == #grid['9'])
end_game = true
victory = #grid['7']
elsif (#grid['1'] == #grid['4'] && #grid['1'] == #grid['7'])
end_game = true
victory = #grid['1']
elsif (#grid['2'] == #grid['5'] && #grid['2'] == #grid['8'])
end_game= true
victory = #grid['2']
elsif (#grid['3'] == #grid['6'] && #grid['3'] == #grid['9'])
end_game = true
victory = #grid['3']
elsif (#grid['1'] == #grid['5'] && #grid['1'] == #grid['9'])
end_game = true
victory = #grid['1']
elsif (#grid['3'] == #grid['5'] && #grid['3'] == #grid['7'])
end_game = true
victory = #grid['3']
else
end_game = false
end
end
if end_game
puts "the winner of this game is #{victory}"
return true
end
end
end
m=Morpion.new
m.play
So my issue is this:
1. I am asking a player to add his symbol (X or O) in the grid that ranges from 1 to 9 (because there is 9 cells)
if I enter 1 for example, which is the upper left cell I get this error:
(eval):187: undefined method `keys' for nil:NilClass (NoMethodError)
from (eval):168:in `play'
from (eval):245
If you want to run this program I suggest using THIS LINK
EDIT:
The problem as #Paul and #August stated was that I used an incorrect constructor method init instead of using the correct one: initialize.
Now my program works. Thanks to them.
You initialize the #grid hash in a method called init. Ruby won't call this method when you construct a new instance of Game. You should instead rename the init method to initialize, which will be called automatically by Ruby.
The problem is that your #grid variable is never being created; it is nil. Hence the error message, you're attempting to invoke a method on a nil object.
The cause of your woes is because you've misnamed the constructor method. In Ruby, constructors are named initialize, however you named it init. Rename it to the correct name, and it should work.

Resources