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
Related
I'm trying to convert boolean string like (Book OR Movie) AND SciFi to equivalent tagFilter query like [["Book", "Movie"], "SciFi"], Is there any helper method to convert boolean string to equivalent tagFilter in Algolia like below.
def to_tagfilter(str="(Book OR Movie) AND SciFi")
# conversion
[["Book", "Movie"], "SciFi"]
end
More read: https://www.algolia.com/doc/api-reference/api-parameters/tagFilters/
After discussing with the Algolia support team, ended up writing my own helper method. Assuming it may help for people who may encounter similar requirements.
Caveat: You can write your own validation wrapper on top of this based on the needs.
`
ALLOWED_BOOLEAN_OPERATORS = %w(and or not)
def generate_tag_filters(boolean_arr, tag_filter_arr=[])
return tag_filter_arr if tag_filter_arr.length >= 1 && (boolean_arr &
ALLOWED_BOOLEAN_OPERATORS).empty?
boolean_arr.each_with_index do |item , i|
if item == 'or'
tag_filter_arr.pop
if boolean_arr[i+1] == 'not'
tag_filter_arr << [boolean_arr[i-1], "!#{boolean_arr[i+2]}"]
boolean_arr.delete_if.with_index { |_, index| index <= i+2 }
else
tag_filter_arr << [boolean_arr[i-1], boolean_arr[i+1]]
boolean_arr.delete_if.with_index { |_, index| index <= i+1 }
end
refined_boolean_arr = tag_filter_arr + boolean_arr
return generate_tag_filters(refined_boolean_arr, tag_filter_arr)
elsif item == 'and'
tag_filter_arr.pop
tag_filter_arr << boolean_arr[i-1]
if boolean_arr[i+1] == 'not'
tag_filter_arr << "!#{boolean_arr[i+2]}"
boolean_arr.delete_if.with_index { |_, index| index <= i+2 }
else
tag_filter_arr << boolean_arr[i+1]
boolean_arr.delete_if.with_index { |_, index| index <= i+1 }
end
refined_boolean_arr = tag_filter_arr + boolean_arr
return generate_tag_filters(refined_boolean_arr, tag_filter_arr)
elsif item == 'not'
tag_filter_arr << "!#{boolean_arr[i+1]}"
boolean_arr.delete_if.with_index { |_, index| index <= i+1 }
refined_boolean_arr = tag_filter_arr + boolean_arr
return generate_tag_filters(refined_boolean_arr, tag_filter_arr)
end
end
end
## ['a','and', 'b', 'and', 'not', 'c'] => ["a", "b", "!c"]
puts generate_tag_filters(['a','and', 'b', 'and', 'not', 'c']).inspect
`
I've been working on this for a few days, at least. Testing seems to show the correct value is being returned. My problem is being able to grab the best_move value and have it print out. I set up the suggested_move method and try to use return suggested_move(best_move) but it triggers the method for every level back up the tree. Also it returns the wrong value, which I'm guessing is because it's stopping before depth is back to 0.
In my minimax I had a similar setup the difference being the depth was incremented (not decremented) on successive calls. Point being I was able to say if depth == 0 p best_move. So I'm scratching my head because using that same conditional I get a nil class error in this code.
#board = ["X","O","O","X","X","","","O",""]
def available_spaces
#board.map.with_index {|a, i| a == "" ? i+1 : nil}.compact
end
def suggested_move(move)
p "Suggested move: #{move}"
end
def full?
#board.all?{|token| token == "X" || token == "O"}
end
def suggested_move(move)
p "Move: #{move}"
end
def bestmove(board, depth=0, best_score = {})
return 0 if full?
return 1 if won?
best = -Float::INFINITY
available_spaces.each do |space|
#board[space-1] = current_player
best_score[space] = -bestmove(#board, depth-1, {})
#board[space-1] = ""
end
p best_score
if best_score.max_by {|key,value| value }[1] > best
best = best_score.max_by {|key,value| value }[1]
best_move = best_score.max_by {|key,value| value }[0]
end
return best_move
end
bestmove(#board)
I'm trying to use the minmax algorithm for a game of tic-tac-toe.
This is the code I'm working with for getting the best AI move in the game.
def get_best_move(board, next_player, depth = 0, best_score = {})
return 0 if tie(board)
return -1 if game_is_over(board)
available_spaces = []
best_move = nil
board.each do |s|
if s != "X" && s != "O"
available_spaces << s
end
end
available_spaces.each do |as|
board[as.to_i] = #com
if game_is_over(board)
best_score[as.to_i] = -1 * get_best_move(board, #com, depth + 1, {})
board[as.to_i] = as
return best_score
else
board[as.to_i] = #hum
if game_is_over(board)
best_score[as.to_i] = -1 * get_best_move(board, #com, depth + 1, {})
board[as.to_i] = as
return best_score
else
board[as.to_i] = as
end
end
end
best_move = best_score.max_by { |key, value| value }[0]
highest_minimax_score = best_score.max_by { |key, value| value }[1]
if depth == 0
return best_move
elsif depth > 0
return highest_minimax_score
end
end
I get this error when I run the game in the terminal, and I would like to know how to fix it.
rb:332:in get_best_move': undefined method `[]' for nil:NilClass (NoMethodError)
rb:332 is referring to these 2 lines in the example below
best_move = best_score.max_by { |key, value| value }[0]
highest_minimax_score = best_score.max_by { |key, value| value }[1]
The error message says pretty clearly what the problem is: you are attempting to call [] on nil, but nil doesn't respond to []. The only place where you call [] is on the result of max_by. max_by returns nil when called on an empty Enumerable. Ergo, best_score must be empty. And indeed: you always pass an empty Hash as the argument to best_score, and you never modify best_score, so it is and always will be empty.
As part of learning Ruby am trying to implement a basic interpreter which reads input and do basic arithmetic calculations. So far basic arithmetic operations are working but having problem in operator precedence. Which is not handled yet. This is the code. Am at a beginner level. Any mistakes in this code are due to my lack of knowledge. How this code can be modified to handle operator precedence.
Sample output
2+2+2 = 6 #correct
10+10/2 = 10 # incorrect as in irb answer must be 15
Github Repo of this interpreter
=begin
Basic calculator Interpreter
can add, substract, multiply , divide with any number of operands at a time
Drawback : Lacks operator precedence
=end
class Interpreter
attr_accessor :input
def initialize
#input = gets.chomp
end
def intepret
first_operand = []
f = []
operator = '+'
array = Array.new
lc = 0
#input.split.join.split("").each_with_index.map do |i, index|
if i.is_number?
first_operand.push(i)
if index == #input.length-1
array.push(first_operand.join("").to_i)
end
elsif i.is_plus?
f = first_operand
first_operand = nil
first_operand = []
array.push(f.join("").to_i)
array.push("+")
elsif i.is_minus?
f = first_operand
first_operand = nil
first_operand = []
operator = '-'
array.push(f.join("").to_i)
array.push("-")
elsif i.is_multi?
f = first_operand
first_operand = nil
first_operand = []
operator = '*'
array.push(f.join("").to_i)
array.push("*")
elsif i.is_divide?
f = first_operand
first_operand = nil
first_operand = []
operator = '/'
array.push(f.join("").to_i)
array.push("/")
else
puts "Illegal input exiting.."
exit
end
lc = lc+1
end
#apply the appropriate operation on the inputs based on the operand
#puts "=======TOKENS======"
#puts array.inspect
result = 0
array.each_with_index.map do |x, key|
result = x if key == 0
if x == '+'
if key == 0
result = add(result, array[key+1])
else
result = add(result, array [key+1])
end
elsif x == '-'
if key == 0
result = minus(result, array[key+1])
else
result = minus(result, array [key+1])
end
elsif x == '*'
if key == 0
result = multi(result, array[key+1])
else
result = multi(result, array [key+1])
end
elsif x == '/'
begin
if key == 0
result = divide(result, array[key+1])
else
result = divide(result, array [key+1])
end
rescue
puts "Zero Divsion error"
exit
end
end
end
puts "Result is: "+result.to_s
end
def print_token(type, value)
puts type + ' '+ value
end
def add(f,s)
return f.to_i + s.to_i
end
def minus(f,s)
return f.to_i - s.to_i
end
def multi(f,s)
return f.to_i * s.to_i
end
def divide(f,s)
return f.to_i / s.to_i
end
end
# Override the string class, to directly use methods like obj.is_number? rather than is_number?(obj)
class String
def is_number?
true if Float(self) rescue false
end
def is_plus?
true if self == '+' rescue false
end
def is_minus?
true if self == '-' rescue false
end
def is_multi?
true if self == '*' rescue false
end
def is_divide?
true if self == '/' rescue false
end
end
#continue accepting inputs until exit CTRL + D
while true
print 'pck>:'
i_obj = Interpreter.new
i_obj.intepret
end
First, process the input using the Shunting-yard algorithm. This should give a list of tokens in Reverse Polish notation (RPN). Then you can evaluate the RPN expression.
I've seen the solution and it more or less matches
Write a method that takes a string and returns the number of vowels
in the string. You may assume that all the letters are lower cased. You can treat "y" as a consonant.
Difficulty: easy.
def count_vowels(string)
vowel = 0
i = 0
while i < string.length
if (string[i]=="a" || string[i]=="e" || string[i]=="i" || string[i]=="o"|| string[i]=="u")
vowel +=1
end
i +=1
return vowel
end
puts("count_vowels(\"abcd\") == 1: #{count_vowels("abcd") == 1}")
puts("count_vowels(\"color\") == 2: #{count_vowels("color") == 2}")
puts("count_vowels(\"colour\") == 3: #{count_vowels("colour") == 3}")
puts("count_vowels(\"cecilia\") == 4: #{count_vowels("cecilia") == 4}")
def count_vowels(str)
str.scan(/[aeoui]/).count
end
/[aeoui]/ is a regular expression that basically means "Any of these characters: a, e, o, u, i". The String#scan method returns all matches of a regular expression in the string.
def count_vowels(str)
str.count("aeoui")
end
Your function is fine you are just missing a keyword end to close of your while loop
def count_vowels(string)
vowel = 0
i = 0
while i < string.length
if (string[i]=="a" || string[i]=="e" || string[i]=="i" || string[i]=="o"|| string[i]=="u")
vowel +=1
end
i +=1
end
return vowel
end
puts("count_vowels(\"abcd\") == 1: #{count_vowels("abcd") == 1}")
puts("count_vowels(\"color\") == 2: #{count_vowels("color") == 2}")
puts("count_vowels(\"colour\") == 3: #{count_vowels("colour") == 3}")
puts("count_vowels(\"cecilia\") == 4: #{count_vowels("cecilia") == 4}")
#=> count_vowels("abcd") == 1: true
#=> count_vowels("color") == 2: true
#=> count_vowels("colour") == 3: true
#=> count_vowels("cecilia") == 4: true
I think using HashTable data structure would be good way to go for this particular problem. Especially if you're required to output number of every single vowel separately.
Here is the code I'd use:
def vowels(string)
found_vowels = Hash.new(0)
string.split("").each do |char|
case char.downcase
when 'a'
found_vowels['a']+=1
when 'e'
found_vowels['e']+=1
when 'i'
found_vowels['i']+=1
when 'o'
found_vowels['o']+=1
when 'u'
found_vowels['u']+=1
end
end
found_vowels
end
p vowels("aeiou")
Or even this (elegant but not necessarily performant):
def elegant_vowels(string)
found_vowels = Hash.new(0)
string.split("").each do |char|
case char.downcase
when ->(n) { ['a','e','i','o','u'].include?(n) }
found_vowels[char]+=1
end
end
found_vowels
end
p elegant_vowels("aeiou")
which would output:
{"a"=>1, "e"=>1, "i"=>1, "o"=>1, "u"=>1}
So you don't have to turn the string into an array and worry about case sensitivity:
def getVowelCount(string)
string.downcase.count 'aeiou'
end