Skip to content

Instantly share code, notes, and snippets.

@iamvery
Created May 8, 2014 02:04
Show Gist options
  • Save iamvery/758641e6c4d493384f90 to your computer and use it in GitHub Desktop.
Save iamvery/758641e6c4d493384f90 to your computer and use it in GitHub Desktop.
Thoughts on contracts.ruby

I watching the demo/use cases video for contracts.ruby and recorded some thoughts.

Use case 1

The use case states that you can avoid bugs like needing string keys instead of symbol keys be enforcing a contract that requires the method input to be a string.

IMO you should instead create a flexible interface that will allow any "stringable" input to be given to the method. That is, use the implicit string conversion methods to_s to make a string of it for indexing into the hash.

Use case 2

This example is to verify that a file is writable before performing some long running operation that will inevitably fail when the file cannot be written to.

I really like the example, but would like to point out that this is a way to describe the characteristics of the input rather than anything to do with types (though he does enforce a string in his example). This looks like a nice way to encapsulate some characterist about some input and fail early when not met.

Use case 3

This example says that by using a contract that verifies the organization of an email address you can prevent accidental granting of access to unapproved email addresses.

Again, this is about a characteristic of the input rather than the type. However in this example I believe it is enforcing an uneccessary restriction about the input, much as a type check would be. It makes the interface more rigid than necessery. Instead this should fall to an authorization system that validates the input independently of the method invocation.


What do you think?

@stevenharman
Copy link

Agreed. In most of the examples, the type information is not actually useful, but rather a characteristic of the object being passed is - e.g., "can this thing act like a string, or give me something that can?" In those cases, I would instead rely on Ruby's conversion protocols to allow for more flexibility in what you take in, while still helping to enforce (via useful runtime errors) that you're getting what you'd expect.

Overall, I find use of Ruby's conversion methods, and building objects and interfaces which leverage them, to be woefully underutilized.

@wallace
Copy link

wallace commented May 8, 2014

Great summary, @iamvery. 👍

I'm (still) on the lookout for when bringing typing to ruby makes sense.

@letsbreelhere
Copy link

Agreed @stevenharman. Something like this seems more useful to me than plain class expectations:

def responds_to(*methods)
  Class.new("SomeCleverNameUsing#{methods.camelize}") do
    def self.valid?(inst)
      methods.all? { |m| inst.respond_to?(m) }
    end
  end
end

To be used e.g. as Contract responds_to(:concat) => responds_to(:to_i, :foo, :baz).

I think this would provide something closer to just enough flexibility to still allow the right kind of duck typing, and make intent clear along the way.

@letsbreelhere
Copy link

h/t to Steven for pointing out that there's already a RespondTo contract :p

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment