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
Coactors
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 ItemA
end
class ItemB
end
class Base::ItemA < Base
end
class Base::ItemB < Base
end
class Base::C < Base
coact ItemA
coact ItemB
end
Base::C.new.coactors
#=> [Base::ItemA, Base::ItemB]
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]
Context
You can define variables used in a coactor as a context by including Coactive::Initializer
.
The variables are stored in context
as follows:
class Base
include Coactive::Base
include Coactive::Initializer
end
class A < Base
context :input
end
coactor = A.new(input: 'something')
coactor.context.input
#=> something
Required context
You can also define required context as follows:
class A < Base
context :input, required: true
end
A.new
#=> Coactive::MissingContextError (missing required context: input)
Default value
You can also define default value as follows:
class A < Base
context :input, default: 'something'
end
coactor = A.new
coactor.context.input
#=> something
Contextualizer
You can copy context variables to a coactor by calling contextualize
:
class Base
include Coactive::Base
include Coactive::Initializer
include Coactive::Contextualizer
end
class A < Base
context :input
end
coactor = A.new(input: 'something')
coactor.contextualize
coactor.input
#=> something
coactor.instance_variable_get(:@input)
#=> something
Output
You can also set context when finished contextualize
block:
class A < Base
context :result, output: true
def call
@result = 'something'
end
end
coactor = A.new
coactor.contextualize { coactor.call }
coactor.context.result
#=> something
Output return value
You can also set context from return value of contextualize
block:
class A < Base
context :result, output: :return
end
coactor = A.new
coactor.contextualize { 'return value' }
coactor.context.result
#=> return value
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.