Validate Ruby Syntax using Ruby - ruby

I need to validate that a given string is valid Ruby syntax, programmatically, using Ruby. I imagine one way I can do this is by running the code in an EVAL statement, and detecting syntax errors that way.
What's a more proper, safer way I can accomplish this?

Let the code string be code. The standard way is to do something like this:
begin
RubyVM::InstructionSequence.compile(code)
nil
rescue Exception => e
... # Put code here to return `e` itself, print its message, or whatever you like
end
If an error is raised and is rescued, that error will display the syntax error. If not (and nil is returned), then code is syntactically valid Ruby code (which does not guarantee that it is free of other types of errors).
The comments saying it is dangerous to do, etc, does not seem to make sense.

I'd consider checking this in the browser with Opal - https://github.com/opal/opal

Related

Which is correct REPL or command-line?

when i write method missing in Object class i'm getting the output different in each interface.
the code is
class Object
def method_missing(hgh)
puts self
end
end
when i use REPL like irb, i get
when i use the command line, i get no error, any reasons would be helpful, thanks in advance
The tl;dr answer is that both are correct. Just more stuff happen in the REPL.
When you run the code from the command line like:
ruby script.rb
All that happens is that it's evaluated.
Whereas REPLs like IRB read your input, evaluate it and print it in a loop.
In this case evaluating your code literally broke the REPL and resulted in the subsequent print failing.
Now you may be a bit confused by this. "There is a print in both cases, I use puts!". The print I'm referring to here is the result that gets visualised after each evaluation. In this case the method definition result (=> :method_missing).
It might not only be the printing itself. It can be the ton of other under the hood code that the REPL has to execute to keep state like "what code was defined on which line" and so on.
Think of what you just did - you made it so that every object has every method possible to return nil. That is not just for the code you write in the REPL. It's for the code of the REPL itself as well.

Provide alias for Ruby's built-in keyword

For example, I want to make Object#rescue another name so I can use in my code like:
def dangerous
something_dangerous!
dont_worry # instead of rescue here
false
end
I tried
class ::Object
alias :dont_worry :rescue
end
But cannot find the rescue method on Object:
`<class:Object>': undefined method `rescue' for class `Object' (NameError)
Another example is I would like to have when in the language to replace:
if cond
# eval when cond is truthy
end
to
when cond
# eval when cond is truthy
end
Is it possible to give a Ruby keyword alias done in Ruby?
Or I need to hack on Ruby C source code?
Thanks!
This is not possible without some deep changes to the Ruby language itself. The things you describe are not methods but keywords of the language, i.e. the actual core of what is Ruby. As such, these things are not user-changeable at all.
If you still want to change the names of the keywords, you would at least have to adapt the language parser. If you don't change semantics at all, this might do it as is. But if you want to change what these keywords represent, things get messy really quick.
Also note that Ruby in itself is sometimes quite ambiguous (e.g. with regards to parenthesis, dots, spacing) and goes to great length to resolve this in a mostly consistent way. If you change keywords, you would have to ensure that things won't get any more ambiguous. This could e.g. happen with your change of if to when. when is used as a keywords is case statements already and would thus could be a source of ambiguity when used as an if.

Can I put an if/unless clause on the next line in Ruby?

In Perl, I often find myself using the following pattern:
croak "incompatible object given: $object"
unless $object->isa('ExampleObject') and $object->can('foo');
I tried to translate this into Ruby like this:
raise ArgumentError, "incompatible object given: #{object.inspect}"
unless object.is_a?(ExampleObject) and object.respond_to?(:foo)
But that does not work because Ruby interprets unless as the start of a new statement. As far as I understand, I can put a backslash at the end of the first line, but that looks ugly and feels wrong. I could also use a regular unless condition raise error end structure, but I like the style of the original form more. Is there a nice (and idiomatic) way to write this as a single statement in Ruby?
Can I put an if/unless clause on the next line in Ruby?
You can't. From page 107 (PDF page 127) of the final draft of ISO Ruby which usually isn't relevant, but basic things like this are and it also spares us from having to read parse.y:
unless-modifier-statement ::
statement [no line-terminator here] unless expression
This is pretty clear. It just doesn't get more similar to your Perl example than:
raise ArgumentError, "incompatible object given: #{object.inspect}" unless
object.is_a?(ExampleObject) and object.respond_to?(:foo)`
or:
raise ArgumentError, "incompatible object given: #{object.inspect}" \
unless object.is_a?(ExampleObject) and object.respond_to?(:foo)
Just as you feel wrong to put a backslash at the end to force a single line statement, it is wrong to use a single line statement when it extends beyond a single line.
This is not really a solution, I was sloppy when reading the question. The OP wants a solution without backslash.
You should be able to do this:
raise ArgumentError, "incompatible object given: #{object.inspect}" \
unless object.is_a?(ExampleObject) and object.respond_to?(:foo)
The \ characters tells ruby to keep reading as if there was no line break.
As far as I know there is no other way than a \, since otherwise, as you already said, Ruby thinks it's a new statement.
Keep in mind that style guides and conventions differ from language to language. In Ruby I'd not expect an if/unless statement in a line coming after it's code. In fact I even dislike putting if/unless at the end of a line, since it reverses the reading direction from If this, then that to that, if this (then what? Ah, I need to read back again), especially when the condition is more complex than raise 'foo' if bar.empty?.
In Perl and other languages though this might be different, since you have other conventions, style guides and this ;-thingy ;)

When to use curly braces vs parenthesis in expect Rspec method?

I had a test that did this:
expect(#parser.parse('adsadasdas')).to raise_error(Errno::ENOENT)
and it didn't work. I changed to:
expect { #parser.parse('adsadasdas') }.to raise_error(Errno::ENOENT)
And it worked.
When do we use curly braces and when do we use parentheses with expect?
In response to OP's comment, I've edited and completely rewritten my answer. I realize that my original answer was oversimplified, so much so that it could be considered incorrect.
Your question was actually addressed somewhat by this other StackOverflow question.
One poster, Peter Alfvin, makes a good point when he says:
As for rules, you pass a block or a Proc if you're trying to test
behavior (e.g. raising errors, changing some value). Otherwise, you
pass a "conventional" argument, in which case the value of that
argument is what is tested.
The reason you're encountering the phenomenon you're seeing has to do with the raising of errors. When you pass #parser.parse('adsadasdas') as an argument (use parentheses) to expect, you are essentially telling ruby:
Evaluate #parser.parse('adsadasdas') first.
Take the result and pass this to expect.
expect should see if this result matches my expectation (that is, that Errno:ENOENT will be raised).
But, what happens is: when ruby evaluates #parser.parse('adsadasdas'), an error is raised right then and there. Ruby doesn't even get a chance to pass the result on to expect. (For all we care, you could have passed #parser.parse('adsadasdas') as an argument to any function... like multiply() or capitalize()) The error is raised, and expect never even gets a chance to do its work.
But when you pass #parser.parse('adsadasdas') as a proc (a code block) to expect using curly braces, what you are telling ruby is this:
expect, get ready to do some work.
expect, I would like you to keep track of what happens as we evaluate #parser.parse('adsadasdas').
Ok, expect, did the code block that was just evaluated raise a Errno:ENOENT error? I was expecting that it would.
When you pass a code block to expect, you are telling expect that you want it to examine the resulting behavior, the changes, made by your code block's execution, and then to let you know if it meets up to the expectations that you provide it.
When you pass an argument to expect, you are telling ruby to evaluate that argument to come to some value before expect even gets involved, and then you are passing that value to expect to see if it meets up to some expectation.
TL;DR: use expect(exp) to specify something about the value of exp and use expect { exp } to specify a side effect that occurs when exp is executed.
Let's unpack this a bit. Most of RSpec's matchers are value matchers. They match (or not) against any ruby object. In contrast, a handful of RSpec's matchers can only be matched against a block, because they have to observe the block while it's running in order to operate properly. These matchers concern side effects that take place (or not) while the block executes. The matcher would have no way to tell if the named side effect had occurred unless it is passed a block to execute. Let's consider the built-in block matchers (as of RSpec 3.1) one-by-one:
raise_error
Consider that one can return an exception from a method, and that is different than raising the exception. Raising an exception is a side effect, and can only be observed by the matcher by it executing the block with an appropriate rescue clause. Thus, this matcher must receive a block to work properly.
throw_symbol
Throwing symbols is similar to raising errors -- it causes a stack jump and is a side effect that can only be observed by running a block inside an appropriate catch block.
change
Mutation to state is a side effect. The matcher can only tell if there was a change to some state by checking the state before hand, running the block, then checking the state after.
output
I/O is a side effect. For the output matcher to work, it has to replace the appropriate stream ($stdout or $stderr) with a new StringIO, execute the block, restore the stream to its original value, and then check the contents of theStringIO`.
yield_control/yield_with_args/yield_with_no_args/yield_with_successive_args
These matchers are a bit different. Yielding isn't really a side effect (it's really just syntactic sugar for calling another function provided by the caller), but yielding can't be observed by looking at the return value of the expression. For the yield matchers to work, they provide a probe object that you pass on to the method-under-test as a block using the &probe syntax:
expect { |probe| [1, 2, 3].each(&probe) }.to yield_with_successive_args(1, 2, 3)
What do all these matchers have in common? None of them can work on simple ruby values. Instead, they all have to wrap a block in an appropriate context (i.e. rescuing, catching or checking before/after values).
Note that in RSpec 3, we added some logic to provide users clear errors when they use the wrong expect form with a given matcher. However, in the specific case of expect(do_something).to raise_error, there's nothing we can do to provide you a clear explanation there -- if do_something raises an error (as you expect it to...), then the error is raised before ruby evaluates the to argument (the raise_error matcher) so RSpec has no way to check with the matcher to see if supports value or block expectations.
in short:
use curly-brace (a block): when you want to test the behavior
use parenthesis when you want to test the returned value
worth reading: As for rules, you pass a block or a Proc if you're trying to test behavior (e.g. raising errors, changing some value). Otherwise, you pass a "conventional" argument, in which case the value of that argument is what is tested. - from this answer
In the test written with parentheses, the code is executed normally, including all normal error handling. The curly-brace syntax defines a block object upon which you can place the expectation. It encapsulates the code you expect to be broken and allows rspec to catch the error and provide its own handling (in this case, a successful test).
You can think of it this way as well: with the parentheses, the code is executed before being passed to the expect method, but with the block, expect will run the code itself.

How do Ruby programmers do type checking?

Since there is no type in ruby, how do Ruby programmers make sure a function receives correct arguments? Right now, I am repeating if object.kind_of/instance_of statements to check and raise runtime errors everywhere, which is ugly. There must be a better way of doing this.
My personal way, which I am not sure if it a recommended way in general, is to type-check and do other validations once an error occurs. I put the type check routine in a rescue block. This way, I can avoid performance loss when correct arguments are given, but still give back the correct error message when an error occurs.
def foo arg1, arg2, arg3
...
main_routine
...
rescue
## check for type and other validations
raise "Expecting an array: #{arg1.inspect}" unless arg1.kind_of?(Array)
raise "The first argument must be of length 2: #{arg1.inspect}" unless arg1.length == 2
raise "Expecting a string: #{arg2.inspect}" unless arg2.kind_of?(String)
raise "The second argument must not be empty" if arg2.empty?
...
raise "This is `foo''s bug. Something unexpected happened: #{$!.message}"
end
Suppose in the main_routine, you use the method each on arg1 assuming that arg1 is an array. If it turns out that it is something else, to which each is not defined, then the bare error message will be something like method each not defined on ..., which, from the perspective of the user of the method foo, might be not helpful. In that case, the original error message will be replaced by the message Expecting an array: ..., which is much more helpful.
Ruby is, of course, dynamically typed.
Thus the method documentation determines the type contract; the type-information is moved from the formal type-system to the [informal type specification in the] method documentation. I mix generalities like "acts like an array" and specifics such as "is a string". The caller should only expect to work with the stated types.
If the caller violates this contract then anything can happen. The method need not worry: it was used incorrectly.
In light of the above, I avoid checking for a specific type and avoid trying to create overloads with such behavior.
Unit-tests can help ensure that the contract works for expected data.
If a method has a reason to exist, it will be called.
If reasonable tests are written, everything will be called.
And if every method is called, then every method will be type-checked.
Don't waste time putting in type checks that may unnecessarily constrain callers and will just duplicate the run-time check anyway. Spend that time writing tests instead.
I recommend to use raise at the beginning of the method to add manual type checking, simple and effective:
def foo(bar)
raise TypeError, "You called foo without the bar:String needed" unless bar.is_a? String
bar.upcase
end
Best way when you don't have much parameters, also a recommendation is to use keyword arguments available on ruby 2+ if you have multiple parameters and watch for its current/future implementation details, they are improving the situation, giving the programmer a way to see if the value is nil.
plus: you can use a custom exception
class NotStringError < TypeError
def message
"be creative, use metaprogramming ;)"
#...
raise NotStringError
You can use a Design by Contract approach, with the contracts ruby gem. I find it quite nice.

Resources