How do I elegantly handle nil cases in my array assignment - Ruby? - ruby

So I am pushing some elements on my array like this:
upd_city_list << [ j.children[0].text.strip!.gsub(/\s+\W/, ''), j.children[1].text, j.children[1][:href] ]
The above is in an iterator (hence the use of j).
The issue is that from time to time, the j.children[0].text turns up as nil, and Ruby doesn't like that.
I could add a bunch of if statements before this assignment, but that seems a bit inelegant to me.
How do I handle nil cases in this situation in an elegant way?
One possible solution is, when there is a nil value, just push the string none onto the array....but what would that look like?
Thanks.
Edit1:
This is the error I am getting:
NoMethodError: private method ‘gsub’ called for nil:NilClass

The real problem is that strip! returns nil when there are no changes to the string. Your text method is returning a string, it is your strip! method is returning nil. I don't know why it does this. I dislike it, too.
This case of the problem will go away if you just change strip! to strip
In a more general sense, you might create an object to return the array for you. You don't want to go changing (what I assume is) Nokogiri, but you can wrap it in something to hide the train wrecks that result.

You should replace j.children[0].text.strip! with one of two things:
(j.children[0].text || 'none').strip
or
j.children[0].text.to_s.strip
These will, of course, have different effects when the text is nil. I think your ACTUAL problem is that strip! was returning nil, and that should have been obvious to you from the error message.

This might be the case for one to use null object programming pattern. Nil is not a good null object. Try reading here and here. Null object is the elegant way.

nil or a_string will be a_string
so what about (j.children[0].text or 'none')

If you're in rails, this is a great use for the try method.
Also seems that your strip and gsub are redundent. Please consider this implementation:
descriptive_name_1 = j.children[0].text.try(:strip)
descriptive_name_2 = j.children[1].text
descriptive_name_3 = j.children[1][:href]
updated_city_list << [ descriptive_name_1 , descriptive_name_2, descriptive_name_3 ]
w/o try
descriptive_name_1 = j.children[0].text.to_s.strip
descriptive_name_2 = j.children[1].text
descriptive_name_3 = j.children[1][:href]
updated_city_list << [ descriptive_name_1 , descriptive_name_2, descriptive_name_3 ]

If you're in the rails environment you could try try method: https://github.com/rails/rails/blob/82d41c969897cca28bb318f7caf301d520a2fbf3/activesupport/lib/active_support/core_ext/object/try.rb#L50

Related

How to make Ruby Mocha mock only check about one parameter

I want to mock this function:
def self.set_segment_info(segment_info, history_record)
history_record.segment_info = segment_info
end
In my test, I want a mock that only confirms that I called set_segment_info with an expected value. I don't care about what I pass in for history_record.
How would I do this? I tried
SegmentHistoryRecord.expects(:set_segment_info).with(:segment_info => expected_segment_info, :history_record => anything)
But that doesn't work.
I ran into this today and ended up doing something like:
SegmentHistoryRecord.expects(:set_segment_info).with(
expected_segment_info,
anything
)
I find it more readable that the do version and it helped me avoid a rubocop issue with too many parameters.
Here's an implementation where, if your function takes a lot of parameters, it's more convenient to specify a value for just the one you care about, instead of for all of them:
expected_segment_info = # ...
SegmentHistoryRecord.expects(:set_segment_info).with() { |actual_parameters| actual_parameters[:segment_info] == expected_segment_info }
(Where, as in the original question, set_segment_info is the function being mocked, and segment_info is the parameter whose value you want to match. Note that the history_record parameter -- and any others that might be present -- don't need to be included.)
SegmentHistoryRecord.expects(:set_segment_info).with() do |param1, param2|
# change below to your verification for :segment_info
# and leave param2 doing nothing, the expectation will ignore param2
param1 == expected_segment_info
end

Better ternary condition

Method find_something may return nil. In the following code,
something = find_something(id) ? find_something(id) : create_something(foo)
find_something(id) is called twice. This is a smell that I want to avoid. Is there a way to avoid redundancy in this expression?
Anything like this?
something = find_something(id) || create_something(foo)
There's not quite enough detail given to say this with confidence, though it might be this is a case for find_or_create_by.
If this does suit, you would just do:
something = YourModel.find_or_create_by(id: id)
You can also provide a block to this, which is passed to the create method if no record is found. For example:
something = YourModel.find_or_create_by(id: id) do |instance|
# this block only gets executed on create
instance.some_new_attribute = 'goes here'
end
Hope that's useful - let me know if it suits your use case.

How to use polymorphism to remove a switch statement which compares strings?

I am new to Ruby, so let me describe the context of my problem first:
I have a json as input which has the following key / value pair:
{
"service": "update"
}
The value has many different values for example: insert,delete etc.
Next there is a method x which handles the different requests:
def x(input)
case input[:service]
services = GenericService.new
when "update"
result = services.service(UpdateService.new,input)
when "insert"
result = services.service(InsertService.new,input)
when "delete"
result = services.service(DeleteService.new,input)
....
....
else
raise "Unknown service"
end
puts JSON.pretty_generate(result)
end
What is bothering me is that I still need to use a switch statement to check the String values (reminds me of 'instance of' ugh..). Is there a cleaner way (not need to use a switch)?
Finally I tried to search for an answer to my question and did not succeed, if however I missed it feel free to comment the related question.
Update: I was thinking to maybe cast the string to the related class name as follows: How do I create a class instance from a string name in ruby? and then call result = services.services(x.constantize.new,input) , then the class names ofcourse needs to match the input of the json.
You can try something like:
def x(input)
service_class_name = "#{input[:service].capitalize}Service"
service_class = Kernel.const_get(service_class_name)
service_class.new(input).process
end
In addition you might want to check if this is a valid Service class name at all.
I don't understand why you want to pass the service to GenericService this seems strange. let the service do it's job.
If you're trying to instatiate a class by it's name you're actually speaking about Reflection rather than Polymorphism.
In Ruby you can achieve this in this way:
byName = Object.const_get('YourClassName')
or if you are in a Rails app
byName= 'YourClassName'.constantize
Hope this helps
Just first thoughts, but you can do:
eval(services.service("#{input[:service].capitalize}Service.new, #{input})") if valid_service? input[:service]
def valid_service?
w%(delete update insert).include? input[:service]
end
As folks will no doubt shout, eval needs to be used with alot of care

What is my method returning here?

I have a question that I've already found the solution to (or perhaps it is just chance), but I'm hoping someone can explain why it works, and what Ruby is doing being the scenes here.
I'm doing something with fixed width output text and ANSI color codes. I don't want the escaped characters to count towards my length, so I wrote a little method for the String class to calculate the length excluding the color codes:
def length_minus_codes
color_codes = [ "\033[30m",
"\033[0m" ,
"\033[31m",
"\033[32m",
"\033[33m",
"\033[34m",
"\033[35m",
"\033[36m",
"\033[37m",
"\033[40m",
"\033[41m",
"\033[42m",
"\033[43m",
"\033[44m",
"\033[45m",
"\033[46m",
"\033[47m",
"\033[1m",
"\033[22m",
"\033[7m",
"\033[27m"]
#Create new variable to strip
stripped_self = self
#loop through color code array
for index in 0 ... color_codes.size
#strip color codes from string
stripped_self.gsub!(color_codes[index],"")
end
#return variance of self to stripped self to
#get length of string not including color codes
return self.length - (self.length - stripped_self.length)
end
end
I thought it was working fine, until I realized that after it was called, the string it was called on had the character codes stripped from it.
I tried a few things, before decided to change this:
stripped_self.gsub!(color_codes[index],"")
To this:
stripped_self = stripped_self.gsub(color_codes[index],"")
Now it is working fine.
What I don't understand is why? I understand the basic concept of in place methods (!) which I was using on the gsub, but it wasn't modifying self, but rather a variable that I set in the method, and second I only want to return the length of the string, not an actual string.
Can anyone explain what is happening here?
When you do
stripped_self = self
you are simply creating a new reference to the self string object, you are not creating a new string. So any in-place modifications (by gsub! in this case) will be reflected on the self object.
If you want to create a new object that is not a reference, you need to duplicate the object:
stripped_self = self.dup
Possibly a simpler solution here is just to use the non-bang version of gsub and save that to a variable. gsub! changes the receiver as bang methods often do, gsub will simply return a modified object safely without effecting the receiver.

How to check if a key exists in a Ruby hash?

I am using search of Net::LDAP, the returned entry is something like this.
#<Net::LDAP::Entry:0x7f47a6491c00
#myhash=
{:loginshell=>["/bin/bash"],
:cn=>["M... R..."],
:homedirectory=>["/mnt/home/m..."],
:uid=>["m..."],
:userpassword=>["{CRYPT}$1$3zR/C...$R1"],
...
}>
I tried to do the following, but failed.
(1)
e = entry.to_hash
e.has_key? "uid"
(2)
entry.has_key? "uid"
The first error says "to_hash" undefined, the second "has_key" undefined. Then I really don't know how to do it, basically I want to find if "uid" is present and if so get its correspondent value.
Thank you very much for the tip.
BTW, it only responds to "entry.uid", but if the search key is provided as a string, how to do that? for example,
def get_value(key)
if entry has key
return key's value
end
end
:uid is a Symbol. That's not a String.
try this:
e.has_key? :uid
The key "uid" doesn't exist. Try
e = Entry.new.myhash
e.has_key?(:uid)
That should return true. If that gives you an error, the problem might lie in your class. Make sure that myhash is defined in the initialize method, and that you use a getter method (or attr_reader) to be able to access the variable. You could use
attr_reader :myhash
right before the initialize method.

Resources