#inspect on BasicObject oddity - ruby

The following happens when inheriting from BasicObject:
class Test < BasicObject
def inspect
"foobar"
end
end
test = Test.new
test.inspect
# => "foobar"
test
(Object doesn't support #inspect)
=>
Is it possible to implement inspect in a way for it to behave normally in IRB?

It's a bug in IRb, or more precisely, in IRB::ColorPrinter#pp:
def pp(obj)
if obj.is_a?(String)
# Avoid calling Ruby 2.4+ String#pretty_print that splits a string by "\n"
text(obj.inspect)
else
super
end
end
BasicObject does not have is_a?, so this will raise a NoMethodError exception. Any exception raised by an IRb Inspector, in turn, is treated the same, regardless of cause and origin:
# Proc to call when the input is evaluated and output in irb.
def inspect_value(v)
#inspect.call(v)
rescue
puts "(Object doesn't support #inspect)"
''
end
This is a combination of two anti-patterns: explicit inheritance checking and Pokemon exception handling. Hey, nobody ever claimed the Ruby standard library is an example of good code. (And nobody ever claimed IRb is a good REPL either.)
The fact that this error-swallowing behavior is misleading was already filed as a bug a month ago:
It seems like IRB::Inspector#inspect_value can swallow errors and then only provides a misleading and unhelpful message (Object doesn't support #inspect).
There is actually a Pull Request which addresses this bug report and improves this very error message and would have immediately told you what is going wrong. In fact, the example the Bug Report and the Pull Request uses to motivate the change is literally your problem: an object that doesn't respond to is_a?:
Before
irb(main):001:0> c = Cat.new "foo"
(Object doesn't support #inspect)
After
irb(main):001:0> c = Cat.new "foo"
An error occurred when inspecting the object: #<NoMethodError: > undefined method `is_a?' for foo:Cat
if obj.is_a?(String)
^^^^^^>
Result of Kernel#inspect: #<Cat:0x0000000109090d80 #name="foo">
However, the Bug Report and the Pull Request are only about the error message, they do not address the error itself, which could be addressed by using Module#=== instead of Object#is_a?.

Related

How is constant accessibility determined?

Ruby seems to have undergone changes several times with respect to constant accessibility. For Ruby 1.9.2, there is description regarding constant accessibility in the question and answer here, but what is written there is not true anymore.
In Ruby 2.3, if I have a constant defined in a class:
class A
Foo = :a
end
I cannot access it via instance_eval or class_eval:
A.new.instance_eval{Foo} # => error
A.class_eval{Foo} # => error
A.instance_eval{Foo} # => error
although I can access it from the class body:
class A; Foo end # => :a
How does constant accessibility work as of Ruby 2.3? If possible, please explain the historical changes of constant accessibility in Ruby and the arguments that led to them.
Note: Below answer is based on Ruby 2.2, have not verified on 2.3
As per documentation of Module#class_eval
Evaluates the string or block in the context of mod, except that when
a block is given, constant/class variable lookup is not affected.
Though below code errored out,
A.class_eval{Foo} # uninitialized constant Foo (NameError)
following works where a string is being evaled.
A.class_eval("Foo") #=> :a
It also seems that Constant requires Module name or Class name for it to be evaluated properly. Below works:
A.class_eval{self::Foo} #=> :a
So, does these:
A.new.instance_eval {self.class::Foo} #=> :a
A.instance_eval {self::Foo} #=> :a
Constant lookup in Ruby uses lexical scope which traverses the current scope and containing modules. The lookup path can be viewed via Module.nesting
> module Out
> module In
> puts Module.nesting
> end
> end
Out::In # gets searched for a given constant first
Out
The lookup remains the same inside a block to enable closure behavior, even for class_eval.
However, in Ruby 1.9, the receiver for instance_eval, instance_exec,
class_eval, and class_exec was prepended to the lookup path, which means all constant references would be looked up in the receiver's scope first.
Yehuda Katz raised an issue citing significant breakage:
Consider the case of RSpec:
describe "Date" do
it "equals itself" do
Date.today.should == Date.today
end
end
If we use dynamic scope first, then if RSpec adds Spec::Date, it will
suddenly break this spec. Lexical scope is more intuitive, and is
expected by many normal uses today.
The behaviour was subsequently reverted back to 1.8.

In Ruby, $? is a 'read-only' variable that can't be directly assigned to. Any idea how I can do this for my own custom variable/class?

The issue I am faced with is that I need to prevent a Ruby class from being manipulated after it is defined. I can freeze it, but that doesn't stop people from just overwriting it all together.
I realize that some will want to respond with some sort of "Ruby isn't meant to be used like this" mantra. I get it, but my case is very special. This is for codewars.com where user submitted solutions are combined with a custom test framework, so I need to stop the user-submitted code from tinkering with the Test class.
I had thought that it wasn't possible at all to make constants true constants, but I noticed that the $? global variable is like this. Its likely that its because its built-in to the language to be like this and not something that can be done with custom variables.
That's because it is built into the language.
In Ruby there is no way to truly define a constant. The closest you can come is writing custom getters/setters and throwing an error if a variable has already been set.
Throw exception when re-assigning a constant in Ruby?
This is defined as "The status of the last executed child process." so, if you assign something to that variable, it will be immediately overwritten by the language with the result of the last (your) assignation.
I would think about a custom implementation - perhaps extracted into some helper gem?
def foo
#foo
end
def foo=(foo)
if defined?(#foo)
warn "warning: already initialized foo"
else
#foo = foo
end
end
self.foo = :bar
puts foo # => bar
self.foo = :baz # => warning: already initialized foo
puts foo # => bar

Why does Ruby call the `call` method when I don't supply the method name?

Given the following module:
module Foo
def self.call
'foo'
end
end
I would of course expect the following to work:
puts Foo.call # outputs "foo"
However, I did not expect this to work:
puts Foo.() # outputs "foo"
Apparently when the method name is left off, Ruby assumes that I want to call the call method. Where is this documented, and why does it behave that way?
Proc#call:
Invokes the block, setting the block’s parameters to the values in params using something close to method calling semantics. Generates a warning if multiple values are passed to a proc that expects just one (previously this silently converted the parameters to an array). Note that prc.() invokes prc.call() with the parameters given. It’s a syntax sugar to hide “call”.
I did some research and found method #() is a syntactic sugar of the method #call..Look at the error as below :
module Foo
def self.bar
12
end
end
Foo.()
#undefined method `call' for Foo:Module (NoMethodError)
As OP defined the #call method in module Foo class,Foo#call is called in an attempt of Foo.().
Here is some more examples :
"ab".method(:size).() # => 2
"ab".method(:size).call # => 2
"ab".() # undefined method `call' for "ab":String (NoMethodError)
See here what Matz said So compromise with object.() syntax introduced in 1.9...
Apparently, as Arup is saying, this is syntactic sugar introduced a while ago, possibly for the single cause of making Proc objects easier to work with. (You don't have to explicitly call them, but can just do prc.() instead).
I also concluded that this is definitely a Ruby 1.9+ feature. If I switch my JRuby to 1.8 mode, I get this instead:
SyntaxError: spam.rb:12: syntax error, unexpected tLPAREN2
So, somewhere in the changelogs for Ruby 1.9 it can probably be found, if someone really wants to dig it out from the caves... :)

Throw exception when re-assigning a constant in Ruby?

I've long been aware that "constants" in Ruby (i.e., variable names that are capitalized) aren't really constant. Like other programming languages, a reference to an object is the only thing stored in the variable/constant. (Sidebar: Ruby does have the facility to "freeze" referenced objects from being modified, which as far as I know, isn't an ability offered in many other languages.)
So here's my question: when you re-assign a value into a constant, you get a warning like so:
>> FOO = 'bar'
=> "bar"
>> FOO = 'baz'
(irb):2: warning: already initialized constant FOO
=> "baz"
Is there a way to force Ruby to throw an exception instead of printing a warning? It's tough to figure out why reassignments happen sometimes.
Look at Can you ask ruby to treat warnings as errors? to see how it is possible in some cases to treat warnings as errors.
Otherwise I guess you'd have to write a custom method to assign constants and raise the exception if already assigned.
If you know that a reassignment happens to a specific constant, you can also add a sanity check just before the assignment.
You can't intercept it directly, no.
If you really need to do this, I can think of a very dirty hack, though. You could redirect the standard error IO to a custom IO object. The write method could then check for what is being written; if it contains "warning: already initialized constant", then you raise, otherwise you forward the call to the standard error's write.
If the constant is within a class or a module, then you could freeze the class or module:
# Normal scenario
$VERBOSE = true
class Foo
BAR = 1
end
Foo::BAR = 2 # warning: already initialized constant BAR
# Using freeze
Foo.freeze
Foo::BAR = 3
RuntimeError: can't modify frozen Class
from (irb):8
from /Users/agrimm/.rbenv/versions/1.9.3-p194/bin/irb:12:in `<main>'
For your scenario, you could freeze Object, but that sounds kind of scary.

Optional parens in Ruby for method with uppercase start letter?

I just started out using IronRuby (but the behaviour seems consistent when I tested it in plain Ruby) for a DSL in my .NET application - and as part of this I'm defining methods to be called from the DSL via define_method.
However, I've run into an issue regarding optional parens when calling methods starting with an uppercase letter.
Given the following program:
class DemoClass
define_method :test do puts "output from test" end
define_method :Test do puts "output from Test" end
def run
puts "Calling 'test'"
test()
puts "Calling 'test'"
test
puts "Calling 'Test()'"
Test()
puts "Calling 'Test'"
Test
end
end
demo = DemoClass.new
demo.run
Running this code in a console (using plain ruby) yields the following output:
ruby .\test.rb
Calling 'test'
output from test
Calling 'test'
output from test
Calling 'Test()'
output from Test
Calling 'Test'
./test.rb:13:in `run': uninitialized constant DemoClass::Test (NameError)
from ./test.rb:19:in `<main>'
I realize that the Ruby convention is that constants start with an uppercase letter and that the general naming convention for methods in Ruby is lowercase. But the parens are really killing my DSL syntax at the moment.
Is there any way around this issue?
This is just part of Ruby's ambiguity resolution.
In Ruby, methods and variables live in different namespaces, therefore there can be methods and variables (or constants) with the same name. This means that, when using them, there needs to be some way to distinguish them. In general, that's not a problem: messages have receivers, variables don't. Messages have arguments, variables don't. Variables are assigned to, messages aren't.
The only problem is when you have no receiver, no argument and no assignment. Then, Ruby cannot tell the difference between a receiverless message send without arguments and a variable. So, it has to make up some arbitrary rules, and those rules are basically:
for an ambiguous token starting with a lowercase letter, prefer to interpret it as a message send, unless you positively know it is a variable (i.e. the parser (not(!) the interpreter) has seen an assignment before)
for an ambiguous token starting with an uppercase letter, prefer to interpret it as a constant
Note that for a message send with arguments (even if the argument list is empty), there is no ambiguity, which is why your third example works.
test(): obviously a message send, no ambiguity here
test: might be a message send or a variable; resolution rules say it is a message send
Test(): obviously a message send, no ambiguity here
self.Test: also obviously a message send, no ambiguity here
Test: might be a message send or a constant; resolution rules say it is a constant
Note that those rules are a little bit subtle, for example here:
if false
foo = 'This will never get executed'
end
foo # still this will get interpreted as a variable
The rules say that whether an ambiguous token gets interpreted as a variable or a message send is determined by the parser and not the interpreter. So, because the parser has seen foo = whatever, it tags foo as a variable, even though the code will never get executed and foo will evaluate to nil as all uninitialized variables in Ruby do.
TL;DR summary: you're SOL.
What you could do is override const_missing to translate into a message send. Something like this:
class DemoClass
def test; puts "output from test" end
def Test; puts "output from Test" end
def run
puts "Calling 'test'"
test()
puts "Calling 'test'"
test
puts "Calling 'Test()'"
Test()
puts "Calling 'Test'"
Test
end
def self.const_missing(const)
send const.downcase
end
end
demo = DemoClass.new
demo.run
Except this obviously won't work, since const_missing is defined on DemoClass and thus, when const_missing is run, self is DemoClass which means that it tries to call DemoClass.test when it should be calling DemoClass#test via demo.test.
I don't know how to easily solve this.

Resources