README.md in usable-1.1.1 vs README.md in usable-1.2.0
- old
+ new
@@ -1,88 +1,95 @@
# Usable [](http://badge.fury.io/rb/usable)
-A simple way to mount and configure your modules. Usable gives you control over which methods are included, and the class
-level config provides a safe interface for calling them.
+A simple way to mount and configure your modules. Usable gives you control over which methods are included, and a simple
+interface to help you call dynamic methods with confidence.
```ruby
-module VersionKit
+module VersionMixin
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|
+ usable VersionMixin, only: :save_version do |config|
config.max_versions = 10
config.table_name = 'custom_versions'
end
+
+ def save
+ self.class.usable_method(self, :save_version).call
+ 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(Model.new).call
-=> "Saving up to 10 versions to custom_versions"
->> Model.new.respond_to? :destroy_version
-=> false
->> Model.usable_config.available_methods[:destroy_version].bind(Model.new).call
-=> nil
+model = Model.new
+model.save_version # => "Saving up to 10 versions to custom_versions"
+model.destroy_version # => NoMethodError: undefined method `destroy_version' for #<Model:...
```
-What's going on here? Well `#save_versions` is now extended onto the `Model` class, but `#destroy_version` is not!
+You'll notice that `#save_versions` is now included on `Model`, but `#destroy_version` isn't defined.
-## But wait, you undefined my methods?
+## Confidently calling 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](http://www.confidentruby.com/). 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, because methods that are removed via `:only` will return `nil`.
+We should all be writing [confident code](http://www.confidentruby.com/). That's why it is encouraged
+to reference methods through the `usable_method` class level function. Methods passed in with the `:only` option
+will always return `nil` when called.
+Here's the same example as above, rewritten to call methods through the Usable interface:
+
+```ruby
+Model.usable_method(model, :save_version).call # => "Saving up to 10 versions to custom_versions"
+Model.usable_method(model, :destroy_version).call # => nil
+```
+
## Separate 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:
```ruby
-module VersionKit
+module Mixin
+ def name
+ "defined by Mixin"
+ end
+
+ def from_mixin
+ "always here"
+ end
+
+ # @description Usable will apply the :only to just the methods defined by this module
module UsableSpec
- def version
- "yo"
+ def from_spec
+ "can be excluded"
end
-
+
def name
- "nope"
+ "defined by UsableSpec"
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"
+class Example
+ extend Usable
+ usable Mixin, only: [:name, :from_spec]
+end
+
+Example.new.from_spec # => "can be excluded"
+Example.new.from_mixin # => "always here"
+Example.new.name # => "defined by Mixin"
+Example.ancestors # => [Example, Mixin, Example::MixinUsableSpecUsed, Object, Kernel, BasicObject] (ruby -v 2.3.0)
```
+
+Noticed that Usable assigns the modified module to a constant with the same name as the given module, but with "Used" appended.
+The main module and the spec were both included, but `Mixin` was not modified, so it didn't need a new name.
## Installation
Add this line to your application's Gemfile: