building a ruby conditional to check for nested params - ruby-on-rails

I'm curious in an controller action, how I can do some simple validation of nested params?
def create
# validate incoming post request
errors = Array.new
person = params[:person]
event = params[:event]
errors << "person email should not be empty" if person[:email].blank?
errors << "person name should not be empty" if person[:name].blank?
errors << "event name should not be empty" if event[:name].blank?
This type of check is barfing. I'm trying to scan for some nested json params, so for example making a post request on
"person":
{
"email":"foo#gmail.com",
"name":"foo"
},
This will validate fine because the nested name is there. Although if I do a request without the nested value, it will barf. How could I write a conditional to check for the nested value, and only stuff in the error value if it's empty. Otherwise, if there is no nested value just continue as normal.

You could use the has_key? method available on Hash class.
errors << "person email should not be empty" if person.has_key?(:email) && person[:email].blank?

Related

Ruby on Rails: How can i iterate over params and convert any empty strings into nil values in a controller?

Rails newbie here.
I have an integration with stripe where users can update the billing address on their card, however, stripe doesn't accept empty strings, only nil values, and it's possible that users won't need to fill in the second address line for example.
How would I go about iterating through params received from a form and convert empty strings into nil?
I have a Stripe Tool module that handles stripe related tasks.
In my controller i have:
def add_billing_address
account_id = current_user.account_id
account = Account.find_by(id: account_id)
stripe_id = account.stripe_customer_id
# convert params empty strings to nil here
StripeTool.add_billing_address(stripe_id: stripe_id,
stripe_token: params[:stripeToken],
address_line1: params[:address_line1],
address_line2: params[:address_line2],
address_city: params[:address_city],
address_state: params[:address_state],
address_zip: params[:address_zip]
)
# redirects and error handling happens after this
You can call .map .each on the params hash in the controller like this:
params.each do |key, value|
params[key] = nil if value === ''
end
But it's probably better to let your form return a nil value when a field contains no data.
I would recommend to avoid modifying the values in the params object, cause it is not good practice to change them in place. It is better to create a new object the has the values you want to use.
stripe_params = params.select { |_,v| v.present? }
This will create a new object without any of the blank attributes. I'm guessing that if an attribute is nil, you might as well not pass it at all.

Accessing Hash Keys with user inputted variables,Nil value No method error NoMethodError

Hi i had created a small ruby project which consists of JSON file. I stored the JSON data into hash keys. AND worte a method to access the data which is present in hash key using user input. But when i try to send use the user input i am getting this error
how_many_ingredients': undefined methodkeys' for nil:NilClass (NoMethodError)
I found this link with same question and tried that solution but still i'm getting the same error
Accessing Hash Keys with user inputted variables, NoMethodError
File one where all the methods are written
require 'json'
class Methods
attr_accessor :name, :text
def initilize(name)
#name = name
#text = text
end
def how_many_ingredients(text)
puts 'text'
file = File.read('a.json')
hash = JSON.parse(file)
#puts hash['recipes']['pizza'].keys
puts hash['recipes'][text].keys
end
end
File 2 where how_Many_ingredients method is accessed, I can see that the variable is passed to that method
require './1'
class Hello < Methods
person = Methods.new
person.test
puts "enter recipie"
person.name
str = gets
person.how_many_ingredients str
end
Note that when you use gets, the input can contain newline and carriage return characters. You'll need to use gets.chomp to filter these. This is likely the cause of the issue in your program.
Compare the following two:
> puts gets.size
"Hello!"
# 7
> puts gets.chomp.size
"Hello!"
# 6
Note that you'll still need to extend your program to account for user inputted keys that are not in your hash.
Your code assumes that there will always be a hash stored at hash['recipes'][text] - you need to cater to the cases where it isn't.
A simple way to do this is to work your way down through the hash with && symbols - if any step is nil (or false), the line will return nil (or false) rather than exploding. eg
puts hash['recipes'] && hash['recipes'][text].is_a?(Hash) && hash['recipes'][text].keys
Note i'm testing that hash['recipes'][text] is a hash (rather than just a string for example) before calling .keys on it.

Validation to contain a certain string Rails

I have a parameter in one of my models called "facebook_url". How can I be sure the user input contains "facebook.com" and isn't some random website?
Right now I have:
unless self.facebook_url[/\Afacebook.com\/\//]
self.facebook_url = "" unless self.facebook_url == ""
end
But this isn't really working how I want it.
The regular expression would be:
\A((http|https):\/\/)?(www.)?facebook.com\/
Will match a string that contains a valid facebook url:
facebook.com/
www.facebook.com/
https://facebook.com/
https://www.facebook.com/
http://facebook.com/
http://www.facebook.com/
Now, to validate your rails model use rails validations on your model file like this:
validates :facebook_url, format: { with: /\A((http|https):\/\/)?(www.)?facebook.com\//,
message: "use a valid facebook url" }
Using your example code, would be:
if self.facebook_url.match(/\A((http|https):\/\/)?(www.)?facebook.com\//)
# valid facebook url
else
# invalid facebook url
end
You can test the regular expression on www.rubular.com

Checkboxs in a from: has_many. Multiple values aren't being read by rails4?

There seems to be an inconsistency between what is being submitted from the form, and what the rails server is identifying as params... unless I'm doing something wrong / not understanding how parameter arrays work... which is possible.
this is how I'm making my checkboxes:
current_event.competitions.map { |competition|
content_tag(:div, class: "checkbox"){
check_box_tag("attendance[competition_ids]", competition.id, #attendance.competitions.include?(competition.id)) +
label("attendance[competition_ids]", competition.id, label_with_price(competition))
}
}.join.html_safe
this is what the chrome web inspector is saying is being sent to the server:
attendance[competition_ids]:1
attendance[competition_ids]:2
attendance[competition_ids]:3
but Rails is throwing this error:
ActiveModel::ForbiddenAttributesError
and this is my params helper method is my controller
params[:attendance].permit(:package, :level, competition_ids: [])
params identified by rails:
Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"qZ1gtwZoXgs9P0HdberzrsMO7L1NftmB8yGso0WquOY=",
"attendance"=>{"competition_ids"=>"3"},
"discounts"=>[""],
"commit"=>"Register"}
shouldn't my params look more like:
"attendance"=>{"competition_ids"=>["1","2","3"]}
?
There's no inconsistency actually. All of the checkboxes have the same name attribute, so you only see the "last" value. It's basically setting the param value to 1, then 2 and then 3.
If you want an array, the name attribute has to end with [], i.e. attendance[competition_ids][]
That'll be interpreted server-side as an array of values.
If you think of it as a Ruby hash, it makes sense
params["attendance"]["competition_ids"] = 1
params["attendance"]["competition_ids"] = 2
params["attendance"]["competition_ids"] = 3
the same key is being overwritten again and again. But if you add the [] to the name, the behavior is closer to
params["attendance"]["competition_ids"] = []
params["attendance"]["competition_ids"] << 1
params["attendance"]["competition_ids"] << 2
params["attendance"]["competition_ids"] << 3

Convert Class Object to string in Ruby

I m in a situation where i need to convert an Object to string so that i can check for Invalid characters/HTML in any filed of that object.
Here is my function for spam check
def seems_spam?(str)
flag = str.match(/<.*>/m) || str.match(/http/) || str.match(/href=/)
Rails.logger.info "** was spam #{flag}"
flag
end
This method use a string and look for wrong data but i don't know how to convert an object to string and pass to this method. I tried this
#request = Request
spam = seems_spam?(#request.to_s)
Please guide
Thanks
You could try #request.inspect
That will show fields that are publicly accessible
Edit: So are you trying to validate each field on the object?
If so, you could get a hash of field and value pairs and pass each one to your method.
#request.instance_values.each do |field, val|
if seems_spam? val
# handle spam
end
If you're asking about implementing a to_s method, Eugene has answered it.
You need to create "to_s" method inside your Object class, where you will cycle through all fields of the object and collecting them into one string.
It will look something like this:
def to_s
attributes.each_with_object("") do |attribute, result|
result << "#{attribute[1].to_s} "
end
end
attribute variable is an array with name of the field and value of the field - [id, 1]
Calling #object.to_s will result with a string like "100 555-2342 machete " which you can check for spam.

Resources