YARD #return not null - ruby

How do I document a function that returns a value, while a null return value should raise a warning?
So currently the method below would be valid because yard allows for the specified type or void.
# #return [String]
def say_hello
nil
end

Multiple Return Types in YARD
In YARD, the documentation provides a clear example of how to define multiple return types in the Declaring Types section. The same section says you're allowed to use the void meta-type to mean "no meaningful value," but since Ruby doesn't really have void methods I think that using nil as an expected return type (rather than void) is generally more semantically useful.
So, to document a return type of either String or void, you could express it thus:
# #return [String, void]
def say_hello
nil
end
Documentation Can't Enforce Contracts
YARD is for documentation, and doesn't actually enforce the types found in method signatures or contracts of any kind. So, your question seems like an X/Y problem because it's still up to the method or the caller to raise warnings or trigger an exception.
A better way to express what you seem to want is to use Kernel#warn or Kernel#raise to address your expections. For example, consider:
# #param string [String]
# #raise [ArgumentError] argument missing or not String
# #return [String]
def say_hello string
unless string.is_a? String
raise ArgumentError, "string: #{string.class}, not String"
end
string
end
Ruby is a duck-typed language. Even though my example above seems like an improvement, it may still ultimately be a work-around for doing something non-Rubyish within your code. Without seeing your real code, this seems like a more reasonable way to enforce a contract between this method and an unknown caller. Of course, your mileage may certainly vary.

Related

Can YARD be used to declare #return type as type of one of the arguments?

What I want to do is basically:
# #param [class] cls
# #return [instanceof(cls)]
def get(cls)
# Imagine this is method and object has property #instances = {}
if !#instances.keys.include?(cls)
#instances[cls] = cls.new()
end
#instances[cls]
end
It can be very handy for the service container pattern, when using it like
service = container.get(MyClass) # language server (e.g. solargraph) will see service variable as of type MyClass
Is it possible with YARD?
UPDATE:
fixed example of function, it did return simply cls.new() in the past
Just Declare a Type or Duck-Type in Your Return Tag
YARD already does this. What you're likely misunderstanding is how YARD uses brackets to document the type of the return value. You're also welcome to add free-form descriptions if you really feel it's necessary. However, pragmatically speaking, you're really just expecting your cls argument to respond to #new, so I'd rewrite it (and make it slightly more idiomatic) like so:
# #param klass [#new] a Class or other object that
# can +#respond_to? :new+
# #return [Class] an instantiated instance of +klass+
def get klass
klass.new
end
If you want to be more explicit, you could change the type in the #param tag to:
# #param klass [Class, #new]
so that it's clear that you're expecting a Class, but will also accept a duck-typed object that responds to #new. Since the method is exactly one line long, I think it's pretty unambiguous either way, especially with the description. Your taste and code style may vary, though.
In either case, the square brackets should declare one or more types, even if they're duck-types. So, yes, you can do what you want.
Ruby 3.2 Note
Unless something changes in the next month, in Ruby 3.2.0 you can make this even shorter with an "endless" method definition. For example, using ruby-3.2.0-preview2:
# #param klass [Class, #new]
# #return [Class] instance of +klass+
def get(klass) = klass.new
Whether or not you like the new syntax, it certainly works. For example:
obj = get Object
obj.instance_of? Object
#=> true
This isn't better or worse than the old syntax, but I do think it lets you tighten up the code and the documentation while increasing clarity. Again, your mileage may vary.

Ensuring interface compliance in Ruby?

I really like Ruby but I'm confused over how exactly I should implement an interface. In the example shown below, a class A calls a method get_data on an object that is passed in as a constructor argument. What is the best practice for ensuring that obj is of the correct type and implements the method get_data? I have seen code that simply "specifies the required interface" in the RDoc for the class's initialize method: "Argument must include module Bar", "Argument must be of type Blah" or "Argument must have the method 'get_data'". Is there a better programmatic way of doing this?
class A
def initialize(obj)
#obj = obj
end
def foo
# Do something with #obj
var = #obj.get_data
...
end
end
I think what you are looking at is a Dependency injection, instead of hard coding SomeClass.new.get_data in your method foo
It's a level of trust that class A has, that an object initializing A should pass an argument, which is an object of SomeClass like this.
object_a = A.new(SomeClass.new).
So if you want to check explicitly if #obj can can respond to the message get_data you can use ruby's respond_to?(:method_name)
So your code becomes this
def foo
# Do something with #obj
if #obj.respond_to?(:get_data)
var = #obj.get_data
end
...
end
Ruby is a dynamically typed language and it's also duck-typed, when you decide to use duck-typing features you should probably just mention it in documentation and call it, if it's not there it will crash, think about it carefully, pretend you could check that get_data exists(and you could as we shall see later), what do you want to do ?
Do you want to return nil ? if foo should return a value you could return nil but you'll risk your users a NoSuchMethodError for nil:NilClass, if foo is meant to return nothing(semantically void) then nil is no option to you, do you want to raise an Error ? well probably the very reason you want to check that get_data exists is to protect against type errors but now you raise another error, maybe you want to raise a specific error but honestly NoSuchMethodError : method get_data missing in someObject:SomeClass is specific enough in my opinion.
With that in my mind I'm going to show you how to check for it anyway, there are many ways, a very simple way :
def foo
begin
var = #obj.get_data
rescue NoMethodError
// handle the case where get_data does not exist
end
end
A probably better way is to check that obj has get_data in initialize using the above way or this :
def initialize(obj)
if(!obj.respond_to? :get_data)
//handle the case where get_data is not defined
end
end
The problem with the last code is that all you check is obj having get_data, you don't check whether it has proper signature, if you want to check that you can :
def initialize(obj)
unless(!obj.respond_to? :get_data)
params = obj.method(:get_data).parameters
//if get_data for example takes x,y then params will be `[[:req,:x],[:req,:y]]`
end
end
Of course you can't check that get_data accepts specific types because ruby is dynamically typed.
If you also want to check the method's return type after you check for its existence and parameters you can call it because it's safe and check the return type like this :
a=obj.get_data
if(a.is_a? SomeClass)
//good
end
If you want to check that get_data returns nothing(semantically void method), well in ruby returning nil is analogous to void but so is the failure to return a value so you can't check for that.

Define variable type Ruby in function declaration

How can i declare variable type in function definition?
In the next code:
def f(a, b)
#...
end
I can forget with what type it works with. How can i declare that it is a fixnum? Thanks
There are two points of confusion in your question:
Variables don't have types in Ruby. Only objects do.
Fixnum is not a type, it is a class. Classes aren't types. Protocols are types. Any object which speaks the correct protocol is of the same type. Some examples of well-known protocols in Ruby that do not have a corresponding class or mixin are what I will call the Appendable protocol (consisting of a method named << that appends its argument to its receiver) or the Iterable protocol (consisting of a method named each which yields each of its receiver's elements in turn). [Note: I made up those names. In reality, those Protocols are so deeply ingrained in Ruby culture that they don't even have names.]
In general, you will describe the protocol(s) your method expects from its arguments in the documentation of the method, but sometimes this description is left out if e.g. it is obvious from the name of the method or the name of the parameter.
See comments and the other answers for a clarification about what is really a "type".
How you can test a "variable's type" (in Ruby every variable references an object and we don't talk about types but classes)
How you can test for a class :
variable = MyClass.new
variable.class # => "MyClass"
variable.is_a?(MyClass) # => true
But typically in Ruby we are more interested in what the object can do, what we can do with them :
if variable.respond_to?(increment)
variable.increment
else
raise "Error : must provide as 'variable' an object that can increment itself"
end
Now to answer your question, I would "declare" the behavior in the function definition / comments using rdoc syntax
# Description of f
# Params:
# +a+:: command line string to be executed by the system
# +b+:: +Proc+ object that ....
def f(a, b)
# code
end

How to make sure method have certain type in ruby

As you probably know, ruby, like most scripting languages is dynamically-typed. How can I make sure parameter passed to my method is instance of specific class?
Check its type.
That said, why? In general you should be caring about its duckitude, not its type. I'm generally suspicious when something is tied to its type rather than its messages.
Use is_a?:
def your_method(param)
if param.is_a? String
# it's a string
else
# not a string
end
end
Or, if you have lots of classes to check, a case statement:
def your_method(param)
case param
when String
# it's a string
when Array
# it's an array
when Rational
# it's a rational
# ...
else
# it's not any of these classes
end
end
It is up to caller to provide the argument of correct type. It is common in Ruby to raise ArgumentError if the method is called with an argument of unsupported type. Example:
def do_something_with_a_number(number)
raise ArgumentError, "you have to provide a number" unless number.is_a?(Fixnum)
...
end

Ruby - extend built-in variables in Ruby

I would like to extend the functionality of variables in Ruby. The reason is I am working on something similar of a type system or value checker (this sounds a bit crazy but the whole idea is to long to explain, just the reason I would like to extend default variables).
I have read that in Ruby, everything is an object; so variables are objects. And Ruby is supposed to be quite liberal concerning what can be changed via meta-programming.
Is there some kind of 'Class' associated with local variables that I could extend?
I would like to associate a string-variable for each variable that holds a string representation of a type. Further I would like to intercept variable assignments and execute a method each time a new value is assigned to a variable. This is so I can check, if the new value is correct according to the type (stored as string in the Variable).
If local variables in Ruby are defined as object of a class, I can extend that class or modify it via a ruby mixin.
The workaround would be to create a new class for my Variables (and not use the build in local variables of Ruby). This class can have a value attribute, a attribute (stored as string) for the type and a get- and set-method. This way I can solve my problem, but I would like to extend the built in variables in ruby, if that is possible.
current work in progress
class Fixnum
attr_accessor :tp
#tp
def mytype ( type )
#tp = type
end
def typecheck
#call typechecker
puts "checked"
end
end
Test Code:
a = 3
a.mytype("nat")
puts a.tp
a.typecheck
There are still two problems.
First, I think it is not possible to add a new constructor to Fixnum. Second, I would like to intercept the variable access, i.e. "b = a" calls the method 'typecheck' of a. But this would require something similar to Aspect Oriented Programming and I am not sure if this can be solved with Ruby's meta-programming facilitates.
I have read that in Ruby, everything is an object
That depends on your definition of "object" and every-"thing". "Object" can mean "entity that can be manipulated by the program" (which I will call object from now on), or "value that is a member of the object system" (which I will call Object from now on).
In Ruby, everything that can be manipulated by the program (i.e. every object) is also an Object, i.e. an instance of a class. This is unlike Java, for example, where primitives can be manipulated by the program (i.e. are objects in that sense of the word), but aren't Objects. In Ruby, this distinction doesn't exist: every object is an Object and every Object is also an object.
However, there are things in the language, which cannot be manipulated by the program and which aren't instances of a class, i.e. they are neither object s nor Objects. These are, for example, methods, variables, syntax, parameter lists, arguments lists, keywords.
Note: you can use Ruby's reflection API to give you an object that represents a method or a parameter list, but that object is only a proxy, it is not the real thing.
So, when we say "everything is an object", what we really mean is that "every object is an Object", i.e. that everything which can be manipulated by the program is also a member of the object system, or in other words, there are no values outside of the object system (unlike primitives in Java). We do not mean that everything that exists in the language can also be manipulated at runtime by the program.
so variables are objects
No, unfortunately, they are neither object s nor Objects.
This is also clearly stated in the Ruby Language Specification (emphasis added by me):
6.2 Variables
6.2.1 General description
A variable is denoted by a name, and refers to an object, which is called the value of the variable.
A variable itself is not an object.
In the book The Ruby Programming Language by Matz and David Flanagan it says on page 2:
every value is an object
Note, it doesn't say every-thing, only every value.
See also the question Is variable is object in ruby?
There are a couple things you can do. For starters, all (or very nearly all) Ruby classes (including "primitives" such as numbers) support a to_s method which returns a string representation of the object. For numbers, to_s will just return the string representation of that number (e.g., "42" for 42). The string value returned for other classes will vary. The nice thing is that you can override a class's methods via "monkey patching". Here's an extremely contrived example:
class Array
def to_s
return "An array of size #{self.size}."
end
end
a = [1, 2, 3, 4]
puts a.to_s
# => "An array of size 4."
For your other question regarding executing a method every time a variable's value is set, the way to handle this is to always interact with the variable through its accessor methods. This way you can implement custom code inside a property's getter and setter methods (or simply call another method from inside the accessor). Like this:
class MyClass
# Use the default getter for var.
attr_reader :var
def initialize
#var = 1
end
# Custom setter for var.
def var= v
#var = v
puts "var changed to #{v}"
end
end
mc = MyClass.new
mc.var = 9
# => "var chaged to 9"
You could do something like this, but it only works for globals:
$type_checked = {:$a => String, :$b => Array}
$type_checked.keys.each do |var|
trace_var(var) do |obj|
puts "hey, don't assign #{var} to a #{obj.class}" unless $type_checked[var] == obj.class
#or raise an Error
end
end
$a = 1
$a = "a"
$b = 1
#output:
#hey, don't assign $a to a Fixnum
#hey, don't assign $b to a Fixnum
But this clearly goes against the grain of the language.

Resources