so i have this code that and my aim was to convert any empty string to null
def convert_empty_strings_to_null
if request.patch? || request.post?
convert_empty_strings_to_null_rec(request.params)
end
end
def convert_empty_strings_to_null_rec(param)
param = nil if param.empty? if param.is_a?(String)
param.all?{|v| convert_empty_strings_to_null_rec v} if param.is_a?(Array)
param.all?{|k,v| convert_empty_strings_to_null_rec v} if param.is_a?(Hash)
end
But i'm new to ruby on rails and i found it that it sends params by value and not by reference, so no change in params is made, how do i fix this ?
I assume that by "empty" you mean zero-with strings, meaning that strings consisting only of whitespace should be left intact. (Otherwise blank? and strip would be your friends.)
def convert_empty_strings_to_nil
if request.patch? || request.post?
request.params.each do |key, value|
request.params[key] = convert_empty_strings_to_nil_rec(value)
end
end
end
def convert_empty_strings_to_nil_rec(param)
case param
when String
param.empty? ? nil : param
when Array
param.map{ |v| convert_empty_strings_to_nil_rec(v) }
when Hash
param.map{ |k,v| [k, convert_empty_strings_to_nil_rec(v)] }.to_h
else
param
end
end
First of all, this is how your convert_empty_strings_to_null_rec method should be, for keeping the changes persistent:
def convert_empty_strings_to_null_rec(param)
if param == ""
updated_param == nil
elsif param.is_a?(Array)
updated_param == param.map{|x| nil if x.empty? }
elsif param.is_a?(Hash)
updated_param = {}
param.each do |k, v|
if v.empty?
updated_param[k] = nil
else
updated_param[k] = v
end
end
end
return updated_param
end
Further, I am assuming from your question that convert_empty_strings_to_null is a action method. It should be updated to catch what convert_empty_strings_to_null_rec method is returning.
def convert_empty_strings_to_null
if request.patch? || request.post?
updated_params = convert_empty_strings_to_null_rec(request.params)
end
# you can use the updated_params here on in this action method
end
Hope it helps : )
Related
I have a class method inside my model that I use as a scope
class Foo < ApplicationRecord
def self.bar(params)
if !params['some-param'].blank?
return Foo.where(....)
elsif !params['another-param'].blank?
return Foo.where(....)
end
self
end
end
# so in the controller I do something like this
my_var = Foo.bar(params).another_scope.all
which works fine. If I change the code to this though
def self.bar(params)
return self if params.empty?
if !params['some-param'].blank?
return Foo.where(....)
end
if !params['another-param'].blank?
return Foo.where(....)
end
end
this throws an error: undefined method 'another_scope' for nil:NilClass.
Lets assume that params are not empty. Both of params['some-param'] and params['another-param'] are empty. And your method wouldn't rentrun anything.
It's better:
def self.bar(params)
if params['some-param'].present?
Foo.where(....)
elsif params['another-param'].present?
Foo.where(....)
end
self
end
Try this:
def self.bar(params = {}) # with a default value
params.delete_if { |_, v| v.blank? } # Clean up keys with blank values
if params['some-param']
Foo.where(....)
elsif params['another-param']
Foo.where(....)
else # if some unknown key present in params OR the params are blank
self
end
end
When an new order_preview is created, I call USPS for shipping options. If a user updates their zip, I would like the ship_option to reset
Edit: I am no longer calling the intial API call from the view, rather I do an after_create method in the controller.
def get_ship_options
ship_options = {}
#order_preview.fedex_rates.each do |k, v|
if k.service_name == "FedEx Ground Home Delivery" || k.service_name == "FedEx 2 Day" || k.service_name == "FedEx Standard Overnight"
ship_options["#{k.service_name}"] = "#{number_to_currency(k.price.to_f / 100)}"
end
end
#order_preview.usps_rates.each do |k, v|
if k.service_name == "USPS Priority Mail 1-Day"
ship_options["#{k.service_name}"] = "#{number_to_currency(k.price.to_f / 100)}"
end
end
#order_preview.ship_option_hash = ship_options.map { |k,v| ["#{k} - #{v}","#{k} - #{v}" ] }
#order_preview.save
end
I tried using the answers you guys provided, but the before_save didn't actually save the shiphash the way #order_preview.save does at the end of the above method.
I tried using the same idea, but zip_changed? doesn't work in the controller.
How can I save the new hash that is pulled from the model directly over to the #order_preview ?
From the model I now have
Model.rb
def clear_hash
if zip_changed?
get_shipping_rates
end
end
and
ship_options = {}
fedex_rates.each do |k, v|
if k.service_name == "FedEx Ground Home Delivery" || k.service_name == "FedEx 2 Day" || k.service_name == "FedEx Standard Overnight"
ship_options["#{k.service_name}"] = "#{number_to_currency(k.price.to_f / 100)}"
end
end
usps_rates.each do |k, v|
if k.service_name == "USPS Priority Mail 1-Day"
ship_options["#{k.service_name}"] = "#{number_to_currency(k.price.to_f / 100)}"
end
end
ship_option_hash = ship_options.map { |k,v| ["#{k} - #{v}","#{k} - #{v}" ] }
**save ship_option_hash to #order_preview.ship_option_hash**
class OrderPreview
before_save :check_state
def check_state
if zip_changed?
ship_option_hash = nil
end
end
...
end
class OrderPreviewController
def update
#order_preview.update(order_preview_params)
end
...
end
Try changing your callback from after_save to before_save. Record considered changed until the changes are not persisted. Changes are lost when you save your object, that's why your record is unchanged when you check for changes in after_save callback.
It should work this way:
before_save :clear_hash, if: :zip_changed?
def clear_hash
ship_option_hash = nil
end
This way the changes will be saved, because you use before_save. In your code, changes were not saved, because you used after_save callback
You controller:
def update
respond_to do |format|
if #order_preview.update(order_preview_params)
flash[:notice] = "Record was successfully updated"
else
flash[:alert] = "Record was not updated"
end
end
end
How can I remove all values from ruby has. I don't want to remove keys just values.
For example:
here is my hash: {'a'=>{'b'=>'c'},'d'=>'e','f'=>{'g'=>''}}
I want this: {'a'=>{'b'=>nil},'d'=>nil,'f'=>{'g'=>nil}}
I don't want to delete the nested hashes. The nesting level varies from one to six levels
thanx
You can write custom delete_values! method, like this:
class Hash
def delete_values!
each_key do |key|
self[key].is_a?(Hash) ? self[key].delete_values! : self[key] = nil
end
end
end
{'a'=>{'b'=>'c'},'d'=>'e','f'=>{'g'=>''}}.delete_values!
# => {"a"=>{"b"=>nil}, "d"=>nil, "f"=>{"g"=>nil}}
h = {'a'=>{'b'=>'c'},'d'=>'e','f'=>{'g'=>''}}
def clean_hash h
h.each do |key, value|
if value.instance_of? Hash
clean_hash value
else
h[key] = nil
end
end
end
clean_hash h
#{"a"=>{"b"=>nil}, "d"=>nil, "f"=>{"g"=>nil}}
h = {'a'=>{'b'=>'c'},'d'=>'e','f'=>{'g'=>''}}
def cleaned_hash(h)
h.reduce({}) do |memo, (key, val)|
memo[key] = if val.is_a? Hash
cleaned_hash(val)
else
nil
end
memo
end
end
cleaned_hash h
# => {"a"=>{"b"=>nil}, "d"=>nil, "f"=>{"g"=>nil}}
This will not modify your hash but instead give you cleaned copy
I want to write a method that loops through all the params to make sure they aren't all blank.
My params are:
params[:search][:company]
params[:search][:phone]
params[:search][:city]
params[:search][:state]
params[:search][:email]
I have this method:
def all_blank_check(params)
array=[]
params[:search].each do |key, value|
array << value unless value.blank?
end
if array.count < 1
return true
else
return false
end
end
But when I try something like all_blank_check(params) I get the following error:
NoMethodError (undefined method `all_blank_check' for #<Class:0x108c08830>):
Do I need to convert the params to an array first? Can't I perform a method on params?
Edit - full source:
def index
#customers = Customer.search_search(params)
end
def self.search_search(params)
search_field = []
search_values = []
array = []
test = ''
if !params[:search].nil? && all_blank_check(params[:search]
if !params[:search].nil? && !params[:search][:company].blank?
search_field << 'customers.company LIKE ?'
search_values << "%#{params[:search][:company]}%"
end
if !params[:search].nil? && !params[:search][:city].blank?
search_field << 'customers.city = ?'
search_values << "#{params[:search][:city]}"
end
if !params[:search].nil? && !params[:search][:phone].blank?
search_field << 'customers.phone_1 = ?'
search_values << "%#{params[:search][:phone]}%"
end
conditions = [search_field.join(' AND ')] + search_values
Customer.where(conditions).includes(:customer_contacts).limit(10)
end
end
def all_blank_check(params)
params[:search].each do |key, value|
array << value unless value.blank?
end
if array.count < 1
return false
end
if array.count > 1
return true
end
end
You can also use more Ruby-minded code like this:
def self.all_blank?(params)
params[:search].count{|key, value| !value.blank?} == 0
end
This counts the values that are not blank; if the number is 0, it means all are blank.
It avoids creating a new array just for counting.
The problem is not the type of params, the problem is that the method all_blank_check does not exist on the object you call it on.
You defined it as an instance method and you're trying to call it from the class method search_param, which won't work.
If you want to make all_blank_check a class method you need to change the definition to def self.all_blank_check(params) - same as search_param.
I have been trying to get rid of all hash keys in my YAML file that have empty (blank) values or empty hashes as values.
This earlier post helped me to get it almost right, but the recursive one-liner leaves my YAML dump with empty hashes whenever there is sufficiently deep nesting.
I would really appreciate any help on this. Thanks!
proc = Proc.new { |k, v| (v.kind_of?(Hash) && !v.empty? ) ? (v.delete_if(&proc); nil) : v.blank? }
hash = {"x"=>{"m"=>{"n"=>{}}}, 'y' => 'content'}
hash.delete_if(&proc)
Actual output
{"x"=>{"m"=>{}}, "y"=>"content"}
Desired output
{"y"=>"content"}
class Hash
def delete_blank
delete_if{|k, v| v.empty? or v.instance_of?(Hash) && v.delete_blank.empty?}
end
end
p hash.delete_blank
# => {"y"=>"content"}
Here's a more generic method:
class Hash
def deep_reject(&blk)
self.dup.deep_reject!(&blk)
end
def deep_reject!(&blk)
self.each do |k, v|
v.deep_reject!(&blk) if v.is_a?(Hash)
self.delete(k) if blk.call(k, v)
end
end
end
{ a: 1, b: nil, c: { d: nil, e: '' } }.deep_reject! { |k, v| v.blank? }
==> { a: 1 }
I think this the most correct version:
h = {a: {b: {c: "",}, d:1}, e:2, f: {g: {h:''}}}
p = proc do |_, v|
v.delete_if(&p) if v.respond_to? :delete_if
v.nil? || v.respond_to?(:"empty?") && v.empty?
end
h.delete_if(&p)
#=> {:a=>{:d=>1}, :e=>2}
I know this thread is a bit old but I came up with a better solution which supports Multidimensional hashes. It uses delete_if? except its multidimensional and cleans out anything with a an empty value by default and if a block is passed it is passed down through it's children.
# Hash cleaner
class Hash
def clean!
self.delete_if do |key, val|
if block_given?
yield(key,val)
else
# Prepeare the tests
test1 = val.nil?
test2 = val === 0
test3 = val === false
test4 = val.empty? if val.respond_to?('empty?')
test5 = val.strip.empty? if val.is_a?(String) && val.respond_to?('empty?')
# Were any of the tests true
test1 || test2 || test3 || test4 || test5
end
end
self.each do |key, val|
if self[key].is_a?(Hash) && self[key].respond_to?('clean!')
if block_given?
self[key] = self[key].clean!(&Proc.new)
else
self[key] = self[key].clean!
end
end
end
return self
end
end
Just a bit related thing. If you want to delete specified keys from nested hash:
def find_and_destroy(*keys)
delete_if{ |k, v| (keys.include?(k.to_s) ? true : ( (v.each { |vv| vv = vv.find_and_destroy(*keys) }) if v.instance_of?(Array) ; (v.each { |vv| vv = vv.find_and_destroy(*keys) }) if v.instance_of?(Hash); false) )}
end
.You can also customize it further
hash = {"x"=>{"m"=>{"n"=>{}}}, 'y' => 'content'}
clean = proc{ |k,v| !v.empty? ? Hash === v ? v.delete_if(&clean) : false : true }
hash.delete_if(&clean)
#=> {"y"=>"content"}
or like #sawa suggested, you can use this proc
clean = proc{ |k,v| v.empty? or Hash === v && v.delete_if(&clean) }