Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Ruby Module helper gem to implement traits behavior with easy
Ruby
Branch: master

README.md

Penetrator

This gem aimed to help improving code reuse in ruby projects. Highly inspired by http://github.com/makandra/modularity gem but slightly modified for supporting conventional super inheritance methods chaining. Also much of code was shamelessly borrowed from ActiveSupport::Concern so I should say thanks that Ruby Hackers, who wrote it. All that what left to do for me - just to take the best from both worlds.

Installation

Add this line to your application's Gemfile:

gem 'penetrator'

And then execute:

$ bundle

Or install it yourself:

$ gem install penetrator

Usage

(Rails specific example)

config/application.rb

    config.autoload_paths += Rails.root.join( 'app', 'traits' )

File: app/controllers/traits/crudable_trait.rb

      module CrudableTrait
        extend Penetrator::Concern

        included do
         helper_method :resource, :resources # they are will be used in views
        end
        #
        # Implementation
        public

        def index
          respond_to do |format|
            format.html { render layout: take_layout }
            format.json { render json:   resources   }
            format.js
          end
        end

        def show
          respond_to do |format|
            format.html { render layout: take_layout }
            format.json { render json:   resource    }
            format.js
          end
        end

        # ... and so on ...

        private
          def take_layout
             # ...
          end

          def resource
            @_resource ||= resource_class.find(params[:id])
          end

          def resources
            @_resources ||= resource_class.order(default_order).all
          end
       end

File: app/controllers/accomodations_controller.rb

      class AccomodationsController < ApplicationController
        #
        # CrudableTrait assumes that this mehod exists
        private
        def resource_class
          Accomodation
        end

        behave_like "crudable"

        # Override public traits method
        def index
          if current_user.is_admin?
            # ...
          else
            super
          end
        end

        private

        # Override traits methods
        #
        def default_order
          "accomodations.name desc"
        end

        public

        # Override traits methods
        # with respecting call chaining
        #
        def kill_all_humans
          "Yes" or super
        end

      end

What makes this gem different from ActiveSupport::Concern ? Well, here you can parameterize your included modules-traits! (Extracted from spec/coerce_spec.rb )

File: app/traits/can_have_args_trait.rb

    module CanHaveArgsTrait
      extend Penetrator::Concern
      included do |*args|
        args.each do |method_name|
          define_method(method_name) do
            method_name.to_s + "-chunked!"
          end
        end
      end # included
    end # CanHaveArgs

File: app/models/my_model.rb

    class Victim
      behaves_like :CanHaveArgs, 'first', 'second'
    end

    obj = Victim.new

    obj.first   # => first-chunked!
    obj.second  # => second-chunked!

Also you can freely utilize ClassMethods internal module as you usually do with ActiveSupport::Concern

    module RichTrait
      extend Penetrator::Concern
      module ClassMethods
        def class_method
          # ... add what you want ...
        end
      end

      def instance_method
      end
    end

You can even extend arbitrary instance of any class with your trait:

    module HtmlSanitizerTrait
      extend Penetrator::Concern

      def cleanup
        # ...
      end
    end

    string_of_dirty_html = "Something <span>dirty</span> and even <marquee>fearing ugly</marquee>"
    string_of_dirty_html.behave_like 'html_sanitizer'

behave_like also accepts block which can be used in included section. I have no idea whom need that, but decided to make it possible.

    module HtmlSanitizerTrait
      included do |*args, block|
        args.each do |method_name|
          define_method(method_name) do
            method_name.to_s
          end
       end
       block.call if block
     end # included

    class VictimWithBlock
      behaves_like :HtmlSanitizerTrait, 'cleanup_processor' do
        class_variable_set(:@@foo, "I'm set")
        # ... something useful
      end
    end

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Added some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request
Something went wrong with that request. Please try again.