Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/kanety/coactive


https://github.com/kanety/coactive

Last synced: 2 months ago
JSON representation

Awesome Lists containing this project

README

        

# Coactive

Make classes coactive.

## Dependencies

* ruby 2.3+
* activesupport 5.0+

## Installation

Add this line to your application's Gemfile:

```ruby
gem 'coactive'
```

Then execute:

$ bundle

## Usage

### Coactors

Include `Coactive::Base` to your base class:

```ruby
class Base
include Coactive::Base
end
```

Define coactive classes that inherit your base class:

```ruby
class A < Base
end

class B < Base
end

class C < Base
coact A
coact B
end
```

You can lookup coactors as follows:

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

#### Named coactors

You can also define coactive classes by using specific name:

```ruby
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:

```ruby
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:

```ruby
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`:

```ruby
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:

```ruby
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:

```ruby
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:

```ruby
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:

```ruby
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:

```ruby
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`:

```ruby
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:

```ruby
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:

```ruby
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:

```ruby
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](http://opensource.org/licenses/MIT).