{"action":"created","issue":{"url":"https://api.github.com/repos/egonSchiele/contracts.ruby/issues/227","repository_url":"https://api.github.com/repos/egonSchiele/contracts.ruby","labels_url":"https://api.github.com/repos/egonSchiele/contracts.ruby/issues/227/labels{/name}","comments_url":"https://api.github.com/repos/egonSchiele/contracts.ruby/issues/227/comments","events_url":"https://api.github.com/repos/egonSchiele/contracts.ruby/issues/227/events","html_url":"https://github.com/egonSchiele/contracts.ruby/pull/227","id":135829214,"number":227,"title":"Adds DescendantOf","user":{"login":"naps62","id":283819,"avatar_url":"https://avatars.githubusercontent.com/u/283819?v=3","gravatar_id":"","url":"https://api.github.com/users/naps62","html_url":"https://github.com/naps62","followers_url":"https://api.github.com/users/naps62/followers","following_url":"https://api.github.com/users/naps62/following{/other_user}","gists_url":"https://api.github.com/users/naps62/gists{/gist_id}","starred_url":"https://api.github.com/users/naps62/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/naps62/subscriptions","organizations_url":"https://api.github.com/users/naps62/orgs","repos_url":"https://api.github.com/users/naps62/repos","events_url":"https://api.github.com/users/naps62/events{/privacy}","received_events_url":"https://api.github.com/users/naps62/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":1,"created_at":"2016-02-23T18:57:11Z","updated_at":"2016-02-23T22:17:47Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/egonSchiele/contracts.ruby/pulls/227","html_url":"https://github.com/egonSchiele/contracts.ruby/pull/227","diff_url":"https://github.com/egonSchiele/contracts.ruby/pull/227.diff","patch_url":"https://github.com/egonSchiele/contracts.ruby/pull/227.patch"},"body":"This adds the ability to make assertions on the class hierarchy of a class being passed as an argument. It should be useful when using things like the Adapter Pattern or Dependency Injection.\r\n\r\nConsider the following example:\r\n\r\n```ruby\r\nclass SlackClientAdapter < BaseClient\r\nend\r\n\r\nclass HipchatClientAdapter < BaseClient\r\nend\r\n\r\nContract C::DescendantOf[BaseClient] => BaseClient\r\ndef start_client(client_class)\r\n  @client = client_class.new\r\nend\r\n\r\nstart_client(SlackClientAdapter)\r\nstart_client(HipchatClientAdapter)\r\n```\r\n\r\nThis is a demonstration of what I'm actually doing on a real app (although the example itself is totally made up).\r\n\r\nI want to be able to assert that the argument is not just a class, but a descendant of `BaseClient` (this base client serves as an abstract class this example. God, I feel like a Java developer all over again...)\r\n\r\nI'm not sure if this is something that's already been proposed and discarded, so I didn't take a lot of care in the organization of the code I added.\r\nShould this be accepted, I'll gladly make any required changes before merging."},"comment":{"url":"https://api.github.com/repos/egonSchiele/contracts.ruby/issues/comments/187940858","html_url":"https://github.com/egonSchiele/contracts.ruby/pull/227#issuecomment-187940858","issue_url":"https://api.github.com/repos/egonSchiele/contracts.ruby/issues/227","id":187940858,"user":{"login":"egonSchiele","id":97219,"avatar_url":"https://avatars.githubusercontent.com/u/97219?v=3","gravatar_id":"","url":"https://api.github.com/users/egonSchiele","html_url":"https://github.com/egonSchiele","followers_url":"https://api.github.com/users/egonSchiele/followers","following_url":"https://api.github.com/users/egonSchiele/following{/other_user}","gists_url":"https://api.github.com/users/egonSchiele/gists{/gist_id}","starred_url":"https://api.github.com/users/egonSchiele/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/egonSchiele/subscriptions","organizations_url":"https://api.github.com/users/egonSchiele/orgs","repos_url":"https://api.github.com/users/egonSchiele/repos","events_url":"https://api.github.com/users/egonSchiele/events{/privacy}","received_events_url":"https://api.github.com/users/egonSchiele/received_events","type":"User","site_admin":false},"created_at":"2016-02-23T22:17:47Z","updated_at":"2016-02-23T22:17:47Z","body":"Hi @naps62 ,\r\nActually, that's how things currently work...you don't need `DescendantOf` for this functionality. For example:\r\n\r\n```ruby\r\nrequire \"contracts\"\r\ninclude Contracts\r\n\r\nclass Animal; end\r\nclass Dog < Animal; end\r\nclass Cat < Animal; end\r\n\r\nContract Animal => Any\r\ndef foo x\r\n  p x\r\nend\r\n\r\nfoo Dog.new # works, Dog is an Animal (checked with is_a?)\r\nfoo \"hello\" # fails, String is not an Animal\r\n```\r\n\r\nThe check happens [here](https://github.com/egonSchiele/contracts.ruby/blob/master/lib/contracts/validators.rb#L57).\r\n\r\nIf you want to do the opposite (i.e. accept `Animal` only, not `Dog` or `Cat`), use `Exactly`:\r\n\r\n```ruby\r\nContract Exactly[Animal] => Any\r\ndef foo x\r\n  p x\r\nend\r\n```"}}