contracts.ruby
contracts.ruby copied to clipboard
Docs out of date for `Contract.valid?`?
The docs for Contract.valid
read:
# Used to verify if an argument satisfies a contract.
#
# Takes: an argument and a contract.
#
# Returns: a tuple: [Boolean, metadata]. The boolean indicates
# whether the contract was valid or not. If it wasn't, metadata
# contains some useful information about the failure.
but when I call it, I’m just getting back a boolean — not a tuple.
So:
- Perhaps the docstring should be updated?
- I’d be happy to submit a PR for this, if desired.
- Is there some other way to “manually” validate a value against a contract and, when a value is invalid, obtain the specific reason?
- I saw in the code that there’s a callback mechanism for handling failures, but I’m not smart enough to understand how to use that mechanism to actually retrieve the failure data/reason… 😬
Thanks!
Hi, I think the failure_callback could be perfect for your use case. Accessing the data object is easy, it is passed in as an argument to the callback. Check out this example:
require "contracts"
include Contracts
Contract.override_failure_callback do |data|
p data
end
Contract Num, Num => Num
def add a, b
a + b
end
add([], [])
This will just print out the data
object and you can see all the info that is passed in.
@egonSchiele thank you for the rapid response! I totally see how the callback is great for debugging, logging, and other side effects. But in my case I need the metadata back as a value, within the same context from which I called valid?
I apologize if it’s obvious how to do this; I’m still fairly new to Ruby.
@aviflax
Could you elaborate on what context you are calling valid?
from? And what kind of value you are looking to get back?
Yeah, I could, but… I just realized, that in my specific case, I actually can use the callback mechanism, because in fact all I want to do with the failure metadata is log it… so I just need a side effect. So I think I can get that going.
I guess I was just intuitively uncomfortable, initially, with the callback mechanism, because in general I don’t like callbacks; I like functions with inputs and outputs. If, for example, I needed to actually do something with the failure metadata, like collect multiple errors together and generate a report of some kind… I guess I could rig up a way to do so with callbacks, but it would just seem so much simpler to me to just get back a value from a simple function call.
Would there be any interest in adding another method to Contract
, maybe something like valid_or_reason
that would return a tuple as described in the docs? Do you think that would be difficult to implement?
Thanks!
Sure, that sounds useful @aviflax . I'd say it would be medium difficulty, and would give you a nice tour of this codebase :)
@aviflax This may be helpful:
some_contract = Or[ ArrayOf[String], ArrayOf[Integer] ]
unless Contract.valid?(some_value, some_contract)
# logs "((a collection Array of String) or (a collection Array of Integer))"
logger.warn Contracts::Formatters::Expected.new( some_contract ).contract.to_s
end
Generating the message is a bit verbose, but you can write your own helper method to make that easier.
EDIT: Sorry, the above was more complex than it needed to be. You can simply do this
logger.warn some_contract.to_s
This only difference is that you won't have the outermost parentheses.
Very helpful — thank you!