Usable

Rack style mixins for Ruby objects. Mount your modules like you mean it!

module VersionKit
  def save_version
    "Saving up to #{self.class.usable_config.max_versions} versions to #{self.class.usable_config.table_name}"
  end

  def destroy_version
    "Deleting versions from #{self.class.usable_config.table_name}"
  end
end

class Model
  extend Usable

  usable VersionKit, only: :save_version do |config|
    config.max_versions = 10
    config.table_name = 'custom_versions'
  end
end

>> Model.usable_config.table_name
=> "custom_versions"
>> Model.new.save_version
=> "Saving up to 10 versions to custom_versions"
>> Model.usable_config.available_methods[:save_version].bind(self).call
=> "Saving up to 10 versions to custom_versions"
>> Model.new.respond_to? :destroy_version     
=> false
>> Model.usable_config.available_methods[:destroy_version].bind(self).call
=> nil

What's going on here? Well #save_versions is now extended onto the Model class, but #destroy_version is not!

But wait, you undefined my methods?

Yes. Well ... yes, at least on the copy of the module included in the target class. But, checking if an object responds to a method all time doesn't produce very confident code. That's why it is encouraged to reference methods through the Model.usable_config.available_methods hash. This way you can confidently call methods, just don't rely on the return value! Methods that are removed via :only will return nil.

Seperate included module from configurable methods

Sometimes you want to define methods on the module but not have them be configurable. Define a module within the usable module namespace and name it UsableSpec, and Usable will use that module to configure the available methods. Any naming conflicts will be resolved by giving precedence to the parent module.

For example:

module VersionKit
  module UsableSpec
    def version
      "yo"
    end

    def name
      "nope"
    end
  end

  def name
    "yup"
  end

  def self.included(base)
    puts base.usable_config.available_methods[:version].bind(self).call
  end
end

>> Example = Class.new.extend Usable
=> Example
>> Example.usable VersionKit
yo
=> Example
>> Example.new.version
=> "yo"
>> Example.new.name
=> "yup"

Installation

Add this line to your application's Gemfile:

gem 'usable'

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/ridiculous/usable.