Coactive

Make classes coactive.

Dependencies

  • ruby 2.3+
  • activesupport 5.0+

Installation

Add this line to your application's Gemfile:

gem 'coactive'

Then execute:

$ bundle

Usage

Include Coactive::Base to your base class:

class Base
  include Coactive::Base
end

Define coactive classes that inherit your base class:

class A < Base
end

class B < Base
end

class C < Base
  coact A
  coact B
end

You can lookup coactors as follows:

C.new.coactors
# => [A, B]

Named coactors

You can also define coactive classes by using specific name:

class A < Base
  coaction :coactive_name
end

class B < Base
  coaction :coactive_name
end

class C < Base
  coact :coactive_name
end

C.new.coactors
# => [A, B]

Coactors are looked up from descendants of your base class. Note that the coactors are unordered.

In development mode of rails, it is necessary to load source files for looking up classes having specific coaction. You can configure source file locations by load_paths as the following example:

class Base
  include Coactive::Base

  configure_coactive do |config|
    config.load_paths = ['app/coactors']
  end
end

Object-based coactors

You can also define coactive classes by using object:

class ModelA
end

class ModelB
end

class Base::ModelA < Base
end

class Base::ModelB < Base
end

class Base::C < Base
  coact ModelA
  coact ModelB
end

Base::C.new.coactors
#=> [Base::ModelA, Base::ModelB]

Coactors are looked up from the namespace corresponding with caller classes.

You can also looked up coactors corresponding with superclass of object. You can configure this feature by lookup_superclass_for_object and lookup_superclass_until:

class Base
  include Coactive::Base

  configure_coactive do |config|
    config.lookup_superclass_for_object = true
    config.lookup_superclass_until = ['ActiveRecord::Base', 'ActiveModel::Base']
  end
end

Dynamic coactors

You can also dynamically lookup coactors by using block or instance method:

class A < Base
end

class B < Base
end

class C < Base
  # use block
  coact do
    if @condition == 'A'
      A
    else
      B
    end
  end

  def initialize(condition)
    @condition = condition
  end
end

C.new('A').coactors
#=> [A]
C.new('B').coactors
#=> [B]

class D < Base
  # use method
  coact :coactivate_with_condition

  def initialize(condition)
    @condition = condition
  end

  def coactivate_with_condition
    if @condition == 'A'
      A
    else
      B
    end
  end
end

D.new('A').coactors
#=> [A]
D.new('B').coactors
#=> [B]

Nested coactors

You can define nested coactors. For example:

class NestedA < Base
end

class NestedB < Base
end

class A < Base
  coact NestedA
end

class B < Base
  coact NestedB
end

class C < Base
  coact A
  coact B
end

C.new.coactors.map { |klass| [klass] + klass.new.coactors }.flatten
#=> [A, NestedA, B, NestedB]

Configuration

You can set configurations in your base class as follows:

class Base
  include Coactive::Base

  configure_coactive do |config|
    # path to source files for coactors
    config.load_paths = ['app/coactors']
    # suffix of class that inherits base class
    config.class_suffix = 'Coactor'
    # cache coactors in memory
    config.use_cache = true
    # lookup coactors corresponding with superclass of object
    config.lookup_superclass_for_object = true
    # lookup coactors until superclass is not in the list
    config.lookup_superclass_until = ['ActiveRecord::Base', 'ActiveModel::Base']
  end
end

Contributing

Bug reports and pull requests are welcome at https://github.com/kanety/coactive.

License

The gem is available as open source under the terms of the MIT License.