# Casting [![Build Status](https://travis-ci.org/saturnflyer/casting.png?branch=master)](https://travis-ci.org/saturnflyer/casting) [![Code Climate](https://codeclimate.com/github/saturnflyer/casting.png)](https://codeclimate.com/github/saturnflyer/casting) [![Test Coverage](https://codeclimate.com/github/saturnflyer/casting/badges/coverage.svg)](https://codeclimate.com/github/saturnflyer/casting/coverage) [![Gem Version](https://badge.fury.io/rb/casting.png)](http://badge.fury.io/rb/casting) ## Add behavior to your objects without using extend Do it for the life of the object or only for the life of a block of code. Casting gives you real delegation that flattens your object structure compared to libraries like Delegate or Forwardable. With casting, you can implement your own decorators that will be so much simpler than using wrappers. Here's a quick example that you might try in a Rails project: ```ruby # implement a module that contains information for the request response # and apply it to an object in your system. def show @user = user.cast_as(UserRepresenter) end ``` To use proper delegation, your approach should preserve `self` as a reference to the original object receiving a method. When the object receiving the forwarded message has its own and separate notion of `self`, you're working with a wrapper (also called consultation) and not using delegation. The Ruby standard library includes a library called "delegate", but it is a consultation approach. With that "delegate", all messages are forwarded to another object, but the attendant object maintains its own identity. With Casting, your defined methods may reference `self` and during execution it will refer to the original client object. Casting was created while exploring ideas for [cleaning up ruby programs](http://clean-ruby.com). ## Usage To use Casting, you must first extend an object as the delegation client: ```ruby actor = Object.new actor.extend(Casting::Client) ``` Or you may include the module in a particular class: ```ruby class Actor include Casting::Client end actor = Actor.new ``` Your objects will have a few additional methods: `delegation`, `cast`, and if you do not *already* have it defined (from another library, for example): `delegate`. The `delegate` method is aliased to `cast`. Then you may delegate a method to an attendant object: ```ruby actor.delegate(:hello_world, other_actor) ``` Or you may create an object to manage the delegation of methods to an attendant object: ```ruby actor.delegation(:hello_world).to(other_actor).call ``` You may also delegate methods without an explicit attendant instance, but provide a module containing the behavior you need to use: ```ruby actor.delegate(:hello_world, GreetingModule) # or actor.delegation(:hello_world).to(GreetingModule).call ``` Pass arguments to your delegated method: ```ruby actor.delegate(:verbose_method, another_actor, arg1, arg2) actor.delegation(:verbose_method).to(another_actor).with(arg1, arg2).call actor.delegation(:verbose_method).to(another_actor).call(arg1, arg2) ``` _That's great, but why do I need to do these extra steps? I just want to run the method._ Casting gives you the option to do what you want. You can run just a single method once, or alter your object to always delegate. Even better, you can alter your object to delegate temporarily... ### Temporary Behavior Casting also provides an option to temporarily apply behaviors to an object. Once your class or object is a `Casting::Client` you may send the `delegate_missing_methods` message to it and your object will use `method_missing` to delegate methods to a stored attendant. ```ruby actor.hello_world #=> NoMethodError Casting.delegating(actor => GreetingModule) do actor.hello_world #=> output the value / perform the method end actor.hello_world #=> NoMethodError ``` The use of `method_missing` is opt-in. If you don't want that mucking up your method calls, just don't tell it to `delegate_missing_methods`. Before the block is run in `Casting.delegating`, a collection of delegate objects is set on the object to the provided attendant. Then the block yields, and an `ensure` block cleans up the stored attendant. This allows you to nest your `delegating` blocks as well: ```ruby actor.hello_world #=> NoMethodError Casting.delegating(actor => GreetingModule) do actor.hello_world #=> output the value / perform the method Casting.delegating(actor => OtherModule) do actor.hello_world #=> still works! actor.other_method # values/operations from the OtherModule end actor.other_method #=> NoMethodError actor.hello_world #=> still works! end actor.hello_world #=> NoMethodError ``` Currently, by using `delegate_missing_methods` you forever mark that object or class to use `method_missing`. This may change in the future. ### Manual Delegate Management If you'd rather not wrap things in the `delegating` block, you can control the delegation yourself. For example, you can `cast_as` and `uncast` an object with a given module: ```ruby actor.cast_as(GreetingModule) actor.hello_world # all subsequent calls to this method run from the module actor.uncast # manually cleanup the delegate actor.hello_world # => NoMethodError ``` These methods are only defined on your `Casting::Client` object when you tell it to `delegate_missing_methods`. Because these require `method_missing`, they do not exist until you opt-in. ### Duck-typing with NullObject-like behavior Casting has a few modules built in to help with treating your objects like null objects. Take a look at the following example: ```ruby module SpecialStuff def special_link # some link code end end special_user.cast_as(SpecialStuff) special_user.special_link # outputs your link ``` If your app, for example, generates a list of info for a collection of users, how do you manage the objects which don't have the expected behavior? ```ruby [normal_user, other_user, special_user].each do |user| user.special_link #=> blows up for normal_user or other_user end ``` You can cast the other objects with `Casting::Null` or `Casting::Blank`: ```ruby normal_user.cast_as(Casting::Null) other_user.cast_as(Casting::Blank) special_user.cast_as(SpecialStuff) [normal_user, other_user, special_user].each do |user| user.special_link #=> normal_user yields nil, other_user yields "", and special_user yields the special_link end ``` ## I have a Rails app, how does this help me? Well, a common use for this behavior would be in using decorators. When using a wrapper, your forms can behave unexpectedly ```ruby class UsersController def edit @user = UserDecorator.new(User.find(params[:id])) end end <%= form_for(@user) do |f| %> #=>