README.md in moosex-0.0.14 vs README.md in moosex-0.0.15

- old
+ new

@@ -585,11 +585,11 @@ ```ruby require 'moosex' module Eq - include MooseX.disable_warnings() # or enable_warningss (default) + include MooseX requires :equal def no_equal(other) ! self.equal(other) @@ -601,15 +601,14 @@ has value: { is: :ro, required: true } end class Currency - include Valuable - include Eq # will warn unless disable_warnings was called. - # to avoid warnings, you should include after - # define all required modules, - + include Valuable.init(warnings: false) # default is true! + include Eq # default will warn about equal missing. + # alternative: include after equal definition! + def equal(other) self.value == other.value end # include Eq # warning 'safe' include, after equal declaration @@ -620,18 +619,69 @@ c1.equal(c2) # true, they have the same value c1.no_equal(c2) # will return false ``` -Roles can support has to describe attributes, and you can reuse code easily. You can also use after/before/around only in methods defined/imported in that Module. +Roles can support has to describe attributes, and you can reuse code easily. -For example, we can add a 'after' block for no_equal inside Eq, but not for equal - this limitation will be fixed soon (see issue #41 @ github). - ### requires You can also mark one or more methods as 'required'. When you do this, we will raise one exception if you try to create a new instance and the class does not implement it. It is a safe way to create interfaces or abstract classes. It uses respond_to? to verify. + +## Parametric Roles + +Parametric roles is a good way of reuse code based on roles. For example, to create one or more attributes in the class who includes our role, we just add the code to be executed in the on_init hook. + +```ruby +module EasyCrud + include MooseX + + on_init do |*attributes| + attributes.each do | attr | + has attr, { is: :rw, predicate: "has_attr_#{attr}_or_not?" } + end + end +end + +class LogTest + include EasyCrud.init(:a, :b) + ... +``` + +when we call `init` with arguments, we will call all on_init blocks defined in the role. In this example we inject attributes 'a' and 'b' with reader/writter and a predicate based on the name ex: `has_attr_a_or_not?` + +### composable parametric roles + +To combine one or more parametric roles to another parametric role you should do something like this: + +```ruby +module Logabble2 + include MooseX + + on_init do |args| + args[:klass] = self + include Logabble.init(args) + end +end + +module Logabble + include MooseX + + on_init do | args | + + klass = args[:klass] || self # <= THIS will guarantee you will + methods = args[:methods] || [] # modify the right class + + methods.each do |method| + klass.around(method) do |original, object, *args| + # add log around method + # call original method + # return + ... +``` + ## BUILD If you need run some code after the creation of the object (like some extra validation), you should implement the BUILD method. ```ruby @@ -677,9 +727,58 @@ ex1 = BuildArgsExample2.new(1,2) # x == 1, y == 2 ex2 = BuildArgsExample2.new(1) # x == 1, y == 8 ex3 = BuildArgsExample2.new() # x == 4, y == 8 ``` + +## EVENTS + +MooseX has a built-in event system, and it should be useful if you want to avoid after/before hooks ( depends of what is yout problem ). + +```ruby +require 'moosex' +require 'moosex/event' + +class Example + include MooseX + include MooseX::Event + + def ping + self.emit(:pinged) + end +end + +e = Example.new + +e.on(:pinged) do |object| + puts "Ping!" +end + +e.once(:pinged) do |object| + puts "Ping Once!" +end + +e.ping # will print two messages, "Ping!" and "Ping Once!" +e.ping # will print just "Ping!" + +e.remove_all_listeners(:pinged) + +e.ping # will no longer print nothing + +# you can use arguments +# consider you have one logger attribute in this example +listener = e.on(:error) do |obj, message| + obj.logger.fatal("Error: #{message}") +end + +e.emit(:error, "ops") # will log, as fatal, "Error: ops" + +e.remove_listener( error: listener ) + +e.emit(:error, "...") # will no longer log nothing +``` + +If you want to restrict how many different events you can handle, you should overload the `has_events` method and return one array of valid events. If you want accept all, you should return nil (default). ## IMPORTANT This module is experimental. I should test more and more to be possible consider this "production ready". If you find some issue/bug please add here: https://github.com/peczenyj/MooseX/issues