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
- Fork it
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Added some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create new Pull Request