Rspec question
having
puts x.y
x.y = nil
I want to test if this line has been executed
so something like
allow(x).to receive(:y) { 'abc' }
and then
expect(x).to have_received(:y).with nil
but with(nil) doesn't work
any suggestions?
I want to make sure y is set to nil, but I cannot just check the value since I am stubbing x.y beforehand
You are attempting to assert that
x.y(nil)
is called.
To assert that the method y= is called with nil parameter, you'd want to use the :y= symbol instead.
allow(x).to receive(:y=){ "xyz" }
expect(x).to have_received(:y=).with(nil)
Related
The output for this code:
x = puts 'hello'
puts x
was:
hello
hello
But I expected the output to be one hello. puts x didn't return the value I expected. I thought puts x was the same as puts 'hello'. What am I missing?
In your example, you are declaring the value of x to be puts 'hello'. The value of x in this case is nil. Try it out in your irb console.
x
=> nil
If you try to call nil on puts you will get this output
puts nil
=>
nil
As for why the method returns nil
The puts method echoes the previous argument to the console. In this case, nil is actually the return value.
Here is some info from the documentation
puts(obj, ...) → nil
Writes the given objects to ios as with IO#print. Writes a record separator (typically a newline) after any that do not already end with a newline sequence. If called with an array argument, writes each element on a new line. If called without arguments, outputs a single record separator.
http://ruby-doc.org/core-2.2.2/IO.html#method-i-puts
Here is the source code for that method
if (argc == 0) {
rb_io_write(out, rb_default_rs);
return Qnil;
}
Take a look at that last line. return Qnil;
i am working though LearnTocodethehardway.com http://ruby.learncodethehardway.org/book/ex25.html
On ex25. In the example there is a module that has a bunch of methods that return or print values. The Print_last_word method when supplied with an array of strings just puts nil. it does this even in his example output. My question would then be why?
To be precise, it doesn't puts nil - it puts the last word and returns nil. Here's the example output:
>> Ex25.print_last_word(words)
wait. # <- this is the output
=> nil # <- this is the return value
puts always returns nil.
UPDATE
There seems to be a bug in print_first_word:
module Ex25
def Ex25.print_first_word(words)
word = words.pop(0)
puts word
end
end
Ex25.print_first_word(["foo", "bar", "baz"])
#=> nil
This is because ["foo", "bar", "baz"].pop(0) returns an empty array and puts [] just returns nil without printing anything.
A working implementation (in the exercise's style) could look like this:
module Ex25
def Ex25.print_first_word(words)
word = words.shift
puts word
end
end
To make it more easy to understand:
"puts" never is used for its return value. It is used for its side effect if you wanted a method that would return a word, you would then do something with that word.
words = ["apple", "cucumber", "apple"]
def give_me_first_word(array_of_words)
array_of_words.first
end
variable_to_be_used_later = give_me_first_word(words)
This function would return "apple"(and in this case the variable would be assigned "apple"), but it would puts nothing to the screen. This value would then be used in another program or function.
If you puts a value, you would then not need to return it to any other program as it's already served its purpose. It's actually intuitive because if puts also returned the value, it would be doing two things. The counterpart to "puts" would be "return" that simply returns the value and does not display it to the screen.
I could not find anywhere a formal specification of the if (or unless) modifier:
123 if x > 0
what is the value of the above statement if x is not above zero? irb suggests nil, but is it documented anywhere?
(yes, this is perhaps a stupid question, sorry, could not find a spec).
An expression that is not evaluated is the same as not existing. And your condition should be part of some code block such as definition or the main environment, etc. A code block without a content is evaluated to nil.
class A; end
# => nil
def foo; end
foo
# => nil
()
# => nil
begin; end
# => nil
eval("")
# => nil
So the reason your example returns nil has nothing to do with condition itself. It is just due to there being no evaluation.
Yes, it is nil. What else could it return? The statement in the if clause is used for the if itself, and the then statement is not executed. There is nothing to return, so nil.
Relevant spec
it "returns nil if else-body is empty and expression is false" do
if false
123
else
end.should == nil
end
If I execute this ruby code:
def foo
100
end
p defined?(foo), foo
if false
foo = 200
end
p defined?(foo), foo
The output I get is:
"method"
100
"local-variable"
nil
Can someone explain to me why foo is set to nil after not executing the if? Is this expected behavior or a ruby bug?
Names on the left hand side of assignments get set to nil, even if the code can't be reached as in the if false case.
>> foo
NameError: undefined local variable or method `foo' for main:Object
...
>> if false
.. foo = 1
.. end #=> nil
>> foo #=> nil
When Ruby tries to resolve barewords, it first looks for local variables (there's a reference to that in the Pickaxe book, which I can't seem to find at the moment). Since you now have one called foo it displays nil. As Mischa noted, the method still can be called as foo().
This is what my pal and Ruby super-expert Josh Cheek had to say:
When Ruby sees the assignment, it initializes the variable in the current scope and sets it to nil. Since the assignment didn't get run, it didn't update the value of foo.
if statements don't change scope like blocks do. This is also the most important difference between
for x in xs
and
xs.each { |x| }
Here's another example:
a = 123 if a # => nil
a # => nil
We shouldn't be able to say if a because we never set a, but Ruby sees the a = 123 and initializes a, then gets to if a at which point a is nil
I'd consider it a quirk of the interpreter, really. Gary Bernhardt makes fun of it in wat (https://www.destroyallsoftware.com/talks/wat) with a = a
-Josh
At a point in my code, I expect current_part to sometimes be nil, and I want to run some code (inside an if block) when that's not the case.
Using script/server --debugger, I've established that current_part is in fact nil at that point when the following errors occur.
All the following versions generate the can't convert nil into String error on the second line:
#
def map_concepts_to_part(part, current_part)
if current_part
part.concepts.map { |concept| content_tag(:li, "stuff...")}.join
end
end
#
def map_concepts_to_part(part, current_part)
if test_if_exists(current_part)
part.concepts.map { |concept| content_tag(:li, "stuff...")}.join
end
end
def test_if_exists(test_subject)
test_subject rescue nil
end
#
def map_concepts_to_part(part, current_part)
if test_if_complete(current_part)
part.concepts.map { |concept| content_tag(:li, "stuff...")}.join
end
end
def test_if_complete(test_subject)
test_subject.id rescue nil
end
#
def test_if_complete(part, current_part)
unless current_part.to_s == ""
part.concepts.map { |concept| content_tag(:li, "stuff...")}.join
end
end
#
def test_if_complete(part, current_part)
unless current_part.nil?
part.concepts.map { |concept| content_tag(:li, "stuff...")}.join
end
end
#
PS, the truncated line in each of the above is:
part.concepts.map { |concept| content_tag(:li, "Concept: “" + concept.title + "”", :class => "one_concept") + content_tag(:li, "Attached images (" + concept.images.size.to_s + ")", :class => "all_images") + content_tag(:li, "Attached docs (XX)", :class => "all_docs")}.join
The test current_part.to_s == "" returns true on my ruby system when current_part is nil. Unlike some other languages, you can say nil.to_s and nil.nil? and have them work. I think there is something else that is causing the problem. Can you show more of the code?
(My tests were in ruby 1.8.6)
Edit: Looking around, what usually causes the above error is an expression such as "text" + nil, not nil.to_s. Do you have anything like that around?
The problem is in your truncated line where concept.title meets the plus.
When you do
"Foo" + some_obj.some_attr
and some_attr in the object is nil, Ruby won't autocast it to string. Might happen often (!) since Rails casts NULL value in the DB to nils. Workarounds are in-string evaluation:
"Foo #{some_obj.attr_that_can_be_nil}"
pattern substitution (automatically truncates nil)
"Foo %s" % some_obj.attr_that_can_be_nil
or array joining (idem ditto)
["Foo ", some_obj.attr_that_can_be_nil].join
The reason you could not find it is that your "truncated line" deserves it's own 5-6 lines unwrapped properly, that way it would be much easier for you to spot the problem.
On a sidenote, you don't need “ and friends - just type it literally since Rails is UTF-8 nowadays anyway. Moreover, when passing stuff to tag helpers you might get this thing converted to “ which is totally not what you want (if helpers escape entities - I don't remember if they do, but Builder certainly does).
Tobias is right. You cannot access any member of the object if it is nil (as it does not exist).
You must check for nil value before performing any operation or before accessing any member varible or function.
In cpp, is it like:
if(!current_part) {
perform operation
}
`
This is a very common NPE(Null Pointer Exception) in almost every programming language.
If the object is nil, then you can't use any of its member because the object itself does not exist. So the comparison should be of the object and nil, not a member of the object and nil.
It's like a null pointer exception.
You should use something like
x = get_some_object if x.nil?
to initialize the variable x if uninitialized.
Is that a local variable in some partial? if so, then even doing current_part.nil? will raise an error in case that variable is not passed to the partial.
to overcome this do:
counter_part = defined?(counter_part) ? : counter_part : nil
BTW: Normally, Ruby looks for an assignment statement to determine whether something is a
variable—if a name hasn’t been assigned to, then Ruby assumes that name is a method call, so executing the following statement will raise an error if x wasn't initialized:
irb(main):001:0> puts "yes nil" if x.nil?
#=>NameError: undefined local variable or method `x' for main:Object