flows 0.1.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +38 -0
- data/.gitignore +9 -1
- data/.mdlrc +1 -0
- data/.reek.yml +54 -0
- data/.rubocop.yml +44 -2
- data/.ruby-version +1 -1
- data/.yardopts +1 -0
- data/CHANGELOG.md +65 -0
- data/README.md +186 -256
- data/Rakefile +35 -1
- data/bin/.rubocop.yml +5 -0
- data/bin/all_the_errors +55 -0
- data/bin/benchmark +69 -78
- data/bin/benchmark_cli/compare.rb +118 -0
- data/bin/benchmark_cli/compare/a_plus_b.rb +22 -0
- data/bin/benchmark_cli/compare/base.rb +45 -0
- data/bin/benchmark_cli/compare/command.rb +47 -0
- data/bin/benchmark_cli/compare/ten_steps.rb +22 -0
- data/bin/benchmark_cli/examples.rb +23 -0
- data/bin/benchmark_cli/examples/.rubocop.yml +19 -0
- data/bin/benchmark_cli/examples/a_plus_b/dry_do.rb +23 -0
- data/bin/benchmark_cli/examples/a_plus_b/dry_transaction.rb +17 -0
- data/bin/benchmark_cli/examples/a_plus_b/flows_do.rb +22 -0
- data/bin/benchmark_cli/examples/a_plus_b/flows_railway.rb +13 -0
- data/bin/benchmark_cli/examples/a_plus_b/flows_scp.rb +13 -0
- data/bin/benchmark_cli/examples/a_plus_b/flows_scp_mut.rb +13 -0
- data/bin/benchmark_cli/examples/a_plus_b/flows_scp_oc.rb +21 -0
- data/bin/benchmark_cli/examples/a_plus_b/trailblazer.rb +15 -0
- data/bin/benchmark_cli/examples/ten_steps/dry_do.rb +70 -0
- data/bin/benchmark_cli/examples/ten_steps/dry_transaction.rb +64 -0
- data/bin/benchmark_cli/examples/ten_steps/flows_do.rb +69 -0
- data/bin/benchmark_cli/examples/ten_steps/flows_railway.rb +58 -0
- data/bin/benchmark_cli/examples/ten_steps/flows_scp.rb +58 -0
- data/bin/benchmark_cli/examples/ten_steps/flows_scp_mut.rb +58 -0
- data/bin/benchmark_cli/examples/ten_steps/flows_scp_oc.rb +66 -0
- data/bin/benchmark_cli/examples/ten_steps/trailblazer.rb +60 -0
- data/bin/benchmark_cli/helpers.rb +12 -0
- data/bin/benchmark_cli/ruby.rb +15 -0
- data/bin/benchmark_cli/ruby/command.rb +38 -0
- data/bin/benchmark_cli/ruby/method_exec.rb +71 -0
- data/bin/benchmark_cli/ruby/self_class.rb +69 -0
- data/bin/benchmark_cli/ruby/structs.rb +90 -0
- data/bin/console +1 -0
- data/bin/docserver +7 -0
- data/bin/errors +130 -0
- data/bin/errors_cli/contract_error_demo.rb +49 -0
- data/bin/errors_cli/di_error_demo.rb +38 -0
- data/bin/errors_cli/flow_error_demo.rb +22 -0
- data/bin/errors_cli/flows_router_error_demo.rb +15 -0
- data/bin/errors_cli/oc_error_demo.rb +40 -0
- data/bin/errors_cli/railway_error_demo.rb +10 -0
- data/bin/errors_cli/result_error_demo.rb +13 -0
- data/bin/errors_cli/scp_error_demo.rb +17 -0
- data/docs/.nojekyll +0 -0
- data/docs/README.md +13 -0
- data/docs/_sidebar.md +2 -0
- data/docs/index.html +30 -0
- data/flows.gemspec +27 -2
- data/forspell.dict +17 -0
- data/lefthook.yml +21 -0
- data/lib/flows.rb +13 -5
- data/lib/flows/contract.rb +402 -0
- data/lib/flows/contract/array.rb +55 -0
- data/lib/flows/contract/case_eq.rb +43 -0
- data/lib/flows/contract/compose.rb +77 -0
- data/lib/flows/contract/either.rb +53 -0
- data/lib/flows/contract/error.rb +25 -0
- data/lib/flows/contract/hash.rb +75 -0
- data/lib/flows/contract/hash_of.rb +70 -0
- data/lib/flows/contract/helpers.rb +22 -0
- data/lib/flows/contract/predicate.rb +34 -0
- data/lib/flows/contract/transformer.rb +50 -0
- data/lib/flows/contract/tuple.rb +70 -0
- data/lib/flows/flow.rb +96 -7
- data/lib/flows/flow/errors.rb +29 -0
- data/lib/flows/flow/node.rb +132 -0
- data/lib/flows/flow/router.rb +29 -0
- data/lib/flows/flow/router/custom.rb +59 -0
- data/lib/flows/flow/router/errors.rb +11 -0
- data/lib/flows/flow/router/simple.rb +25 -0
- data/lib/flows/plugin.rb +14 -0
- data/lib/flows/plugin/dependency_injector.rb +159 -0
- data/lib/flows/plugin/dependency_injector/dependency.rb +24 -0
- data/lib/flows/plugin/dependency_injector/dependency_definition.rb +16 -0
- data/lib/flows/plugin/dependency_injector/dependency_list.rb +57 -0
- data/lib/flows/plugin/dependency_injector/errors.rb +58 -0
- data/lib/flows/plugin/implicit_init.rb +45 -0
- data/lib/flows/plugin/output_contract.rb +85 -0
- data/lib/flows/plugin/output_contract/dsl.rb +48 -0
- data/lib/flows/plugin/output_contract/errors.rb +74 -0
- data/lib/flows/plugin/output_contract/wrapper.rb +55 -0
- data/lib/flows/plugin/profiler.rb +114 -0
- data/lib/flows/plugin/profiler/injector.rb +35 -0
- data/lib/flows/plugin/profiler/report.rb +48 -0
- data/lib/flows/plugin/profiler/report/events.rb +43 -0
- data/lib/flows/plugin/profiler/report/flat.rb +41 -0
- data/lib/flows/plugin/profiler/report/flat/method_report.rb +81 -0
- data/lib/flows/plugin/profiler/report/raw.rb +15 -0
- data/lib/flows/plugin/profiler/report/tree.rb +98 -0
- data/lib/flows/plugin/profiler/report/tree/calculated_node.rb +116 -0
- data/lib/flows/plugin/profiler/report/tree/node.rb +35 -0
- data/lib/flows/plugin/profiler/wrapper.rb +53 -0
- data/lib/flows/railway.rb +154 -0
- data/lib/flows/railway/dsl.rb +18 -0
- data/lib/flows/railway/errors.rb +17 -0
- data/lib/flows/railway/step.rb +24 -0
- data/lib/flows/railway/step_list.rb +38 -0
- data/lib/flows/result.rb +189 -2
- data/lib/flows/result/do.rb +172 -0
- data/lib/flows/result/err.rb +12 -6
- data/lib/flows/result/errors.rb +29 -17
- data/lib/flows/result/helpers.rb +25 -3
- data/lib/flows/result/ok.rb +12 -6
- data/lib/flows/shared_context_pipeline.rb +299 -0
- data/lib/flows/shared_context_pipeline/dsl.rb +12 -0
- data/lib/flows/shared_context_pipeline/dsl/callbacks.rb +38 -0
- data/lib/flows/shared_context_pipeline/dsl/tracks.rb +52 -0
- data/lib/flows/shared_context_pipeline/errors.rb +17 -0
- data/lib/flows/shared_context_pipeline/mutation_step.rb +29 -0
- data/lib/flows/shared_context_pipeline/router_definition.rb +21 -0
- data/lib/flows/shared_context_pipeline/step.rb +44 -0
- data/lib/flows/shared_context_pipeline/track.rb +54 -0
- data/lib/flows/shared_context_pipeline/track_list.rb +51 -0
- data/lib/flows/shared_context_pipeline/wrap.rb +74 -0
- data/lib/flows/util.rb +17 -0
- data/lib/flows/util/inheritable_singleton_vars.rb +86 -0
- data/lib/flows/util/inheritable_singleton_vars/dup_strategy.rb +98 -0
- data/lib/flows/util/inheritable_singleton_vars/isolation_strategy.rb +91 -0
- data/lib/flows/util/prepend_to_class.rb +179 -0
- data/lib/flows/version.rb +1 -1
- metadata +288 -20
- data/.travis.yml +0 -8
- data/Gemfile.lock +0 -119
- data/bin/demo +0 -66
- data/bin/examples.rb +0 -159
- data/bin/profile_10steps +0 -64
- data/bin/ruby_benchmarks +0 -26
- data/lib/flows/node.rb +0 -27
- data/lib/flows/operation.rb +0 -54
- data/lib/flows/operation/builder.rb +0 -130
- data/lib/flows/operation/builder/build_router.rb +0 -37
- data/lib/flows/operation/dsl.rb +0 -72
- data/lib/flows/operation/errors.rb +0 -75
- data/lib/flows/operation/executor.rb +0 -78
- data/lib/flows/result_router.rb +0 -14
- data/lib/flows/router.rb +0 -22
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
module Flows
|
|
2
|
+
module Util
|
|
3
|
+
module InheritableSingletonVars
|
|
4
|
+
# Strategy which uses `#dup` to copy variables to a child class.
|
|
5
|
+
#
|
|
6
|
+
# Can be applied several times to the same class.
|
|
7
|
+
#
|
|
8
|
+
# Can be applied in the middle of inheritance chain.
|
|
9
|
+
#
|
|
10
|
+
# When your value is a custom class you may need to adjust `#dup` behaviour.
|
|
11
|
+
# It can be done using `initialize_dup` method.
|
|
12
|
+
# Unfortunately it's not documented well in the standard library.
|
|
13
|
+
# So, [this will help you](https://blog.appsignal.com/2019/02/26/diving-into-dup-and-clone.html).
|
|
14
|
+
#
|
|
15
|
+
# @note If you change variables in a parent class after a child being defined
|
|
16
|
+
# it will have no effect on a child. Remember this when working in environments
|
|
17
|
+
# with tricky or experimental autoload mechanism.
|
|
18
|
+
#
|
|
19
|
+
# @see InheritableSingletonVars the parent module's documentation describes the problem this module solves.
|
|
20
|
+
#
|
|
21
|
+
# @since 0.4.0
|
|
22
|
+
module DupStrategy
|
|
23
|
+
VAR_LIST_VAR_NAME = :@inheritable_vars_with_dup
|
|
24
|
+
|
|
25
|
+
# @api private
|
|
26
|
+
module Migrator
|
|
27
|
+
def self.call(from, to)
|
|
28
|
+
parent_var_list = from.instance_variable_get(VAR_LIST_VAR_NAME)
|
|
29
|
+
child_var_list = to.instance_variable_get(VAR_LIST_VAR_NAME) || []
|
|
30
|
+
|
|
31
|
+
to.instance_variable_set(VAR_LIST_VAR_NAME, child_var_list + parent_var_list)
|
|
32
|
+
|
|
33
|
+
parent_var_list.each do |name|
|
|
34
|
+
to.instance_variable_set(name, from.instance_variable_get(name).dup)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# @api private
|
|
40
|
+
module Injector
|
|
41
|
+
def included(mod)
|
|
42
|
+
Migrator.call(self, mod)
|
|
43
|
+
mod.singleton_class.prepend Injector
|
|
44
|
+
|
|
45
|
+
super
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def extended(mod)
|
|
49
|
+
Migrator.call(self, mod)
|
|
50
|
+
mod.singleton_class.prepend Injector
|
|
51
|
+
|
|
52
|
+
super
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def inherited(mod)
|
|
56
|
+
Migrator.call(self, mod)
|
|
57
|
+
mod.singleton_class.prepend Injector
|
|
58
|
+
|
|
59
|
+
super
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
class << self
|
|
64
|
+
# Generates a module which applies behaviour and defaults for singleton variables.
|
|
65
|
+
#
|
|
66
|
+
# @example
|
|
67
|
+
# class MyClass
|
|
68
|
+
# SingletonVarsSetup = Flows::Util::InheritableSingletonVars::DupStrategy.make_module(
|
|
69
|
+
# :@my_list => []
|
|
70
|
+
# )
|
|
71
|
+
#
|
|
72
|
+
# include SingletonVarsSetup
|
|
73
|
+
# end
|
|
74
|
+
#
|
|
75
|
+
# @note Variable names should look like `:@var` or `'@var'`.
|
|
76
|
+
#
|
|
77
|
+
# @param vars_with_default [Hash<Symbol, String => Object>] keys are variable names,
|
|
78
|
+
# values are default values.
|
|
79
|
+
def make_module(vars_with_default = {})
|
|
80
|
+
Module.new.tap do |mod|
|
|
81
|
+
mod.instance_variable_set(VAR_LIST_VAR_NAME, vars_with_default.keys.map(&:to_sym))
|
|
82
|
+
init_vars(mod, vars_with_default)
|
|
83
|
+
mod.extend Injector
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
private
|
|
88
|
+
|
|
89
|
+
def init_vars(mod, vars_with_default)
|
|
90
|
+
vars_with_default.each do |name, value|
|
|
91
|
+
mod.instance_variable_set(name, value)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
module Flows
|
|
2
|
+
module Util
|
|
3
|
+
module InheritableSingletonVars
|
|
4
|
+
# Strategy which uses procs to generate initial values in target class and children.
|
|
5
|
+
#
|
|
6
|
+
# This strategy designed to make fully isolated singleton variables between classes.
|
|
7
|
+
#
|
|
8
|
+
# Can be applied several times to the same class.
|
|
9
|
+
#
|
|
10
|
+
# Can be applied in the middle of inheritance chain.
|
|
11
|
+
#
|
|
12
|
+
# @see InheritableSingletonVars the parent module's documentation describes the problem this module solves.
|
|
13
|
+
#
|
|
14
|
+
# @since 0.4.0
|
|
15
|
+
module IsolationStrategy
|
|
16
|
+
VAR_MAP_VAR_NAME = :@inheritable_vars_with_isolation
|
|
17
|
+
|
|
18
|
+
# @api private
|
|
19
|
+
module Migrator
|
|
20
|
+
def self.call(from, to)
|
|
21
|
+
parent_var_map = from.instance_variable_get(VAR_MAP_VAR_NAME)
|
|
22
|
+
child_var_map = to.instance_variable_get(VAR_MAP_VAR_NAME) || {}
|
|
23
|
+
|
|
24
|
+
to.instance_variable_set(VAR_MAP_VAR_NAME, child_var_map.merge(parent_var_map))
|
|
25
|
+
|
|
26
|
+
parent_var_map.each do |name, value_proc|
|
|
27
|
+
to.instance_variable_set(name, value_proc.call)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# @api private
|
|
33
|
+
module Injector
|
|
34
|
+
def included(mod)
|
|
35
|
+
Migrator.call(self, mod)
|
|
36
|
+
mod.singleton_class.prepend Injector
|
|
37
|
+
|
|
38
|
+
super
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def extended(mod)
|
|
42
|
+
Migrator.call(self, mod)
|
|
43
|
+
mod.singleton_class.prepend Injector
|
|
44
|
+
|
|
45
|
+
super
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def inherited(mod)
|
|
49
|
+
Migrator.call(self, mod)
|
|
50
|
+
mod.singleton_class.prepend Injector
|
|
51
|
+
|
|
52
|
+
super
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
class << self
|
|
57
|
+
# Applies behaviour and defaults for singleton variables.
|
|
58
|
+
#
|
|
59
|
+
# @example
|
|
60
|
+
# class MyClass
|
|
61
|
+
# SingletonVarsSetup = Flows::Util::InheritableSingletonVars::IsolationStrategy.make_module(
|
|
62
|
+
# :@my_list => -> { [] }
|
|
63
|
+
# )
|
|
64
|
+
#
|
|
65
|
+
# include SingletonVarsSetup
|
|
66
|
+
# end
|
|
67
|
+
#
|
|
68
|
+
# @note Variable names should look like `:@var` or `'@var'`.
|
|
69
|
+
#
|
|
70
|
+
# @param vars_with_default [Hash<Symbol, String => Proc>] keys are variable names,
|
|
71
|
+
# values are procs or lambdas which return default values.
|
|
72
|
+
def make_module(vars_with_default = {})
|
|
73
|
+
Module.new.tap do |mod|
|
|
74
|
+
mod.instance_variable_set(VAR_MAP_VAR_NAME, vars_with_default.dup)
|
|
75
|
+
init_vars(mod, vars_with_default)
|
|
76
|
+
mod.extend Injector
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
private
|
|
81
|
+
|
|
82
|
+
def init_vars(mod, vars_with_default)
|
|
83
|
+
vars_with_default.each do |name, value_proc|
|
|
84
|
+
mod.instance_variable_set(name, value_proc.call)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
module Flows
|
|
2
|
+
module Util
|
|
3
|
+
# In the situation when a module is included into another module and only afterwards included into class,
|
|
4
|
+
# allows to force particular module to be prepended to a class only.
|
|
5
|
+
#
|
|
6
|
+
# When you write some module to abstract out some behaviour you may
|
|
7
|
+
# need a way to expand initializer behaviour of a target class.
|
|
8
|
+
# You can prepend a module with an initializer wrapper inside `.included(mod)`
|
|
9
|
+
# or `.extended(mod)` callbacks. But it will not work if you include your module into module
|
|
10
|
+
# and only after to a class. It's one of the cases when `PrependToClass` can help you.
|
|
11
|
+
#
|
|
12
|
+
# Let's show it on example: we need a module which expands initializer to accept `:data`
|
|
13
|
+
# keyword argument and sets its value:
|
|
14
|
+
#
|
|
15
|
+
# class MyClass
|
|
16
|
+
# prepend HasData
|
|
17
|
+
#
|
|
18
|
+
# attr_reader :greeting
|
|
19
|
+
#
|
|
20
|
+
# def initialize
|
|
21
|
+
# @greeting = 'Hello'
|
|
22
|
+
# end
|
|
23
|
+
# end
|
|
24
|
+
#
|
|
25
|
+
# module HasData
|
|
26
|
+
# attr_reader :data
|
|
27
|
+
#
|
|
28
|
+
# def initialize(*args, **kwargs, &block)
|
|
29
|
+
# @data = kwargs[:data]
|
|
30
|
+
#
|
|
31
|
+
# filtered_kwargs = kwargs.reject { |k, _| k == :data }
|
|
32
|
+
#
|
|
33
|
+
# if filtered_kwargs.empty? # https://bugs.ruby-lang.org/issues/14415
|
|
34
|
+
# super(*args, &block)
|
|
35
|
+
# else
|
|
36
|
+
# super(*args, **filtered_kwargs, &block)
|
|
37
|
+
# end
|
|
38
|
+
# end
|
|
39
|
+
#
|
|
40
|
+
# def big_data
|
|
41
|
+
# data.upcase
|
|
42
|
+
# end
|
|
43
|
+
# end
|
|
44
|
+
#
|
|
45
|
+
# x = MyClass.new(data: 'aaa')
|
|
46
|
+
#
|
|
47
|
+
# x.greeting
|
|
48
|
+
# # => 'Hello'
|
|
49
|
+
#
|
|
50
|
+
# x.data
|
|
51
|
+
# # => 'aaa'
|
|
52
|
+
#
|
|
53
|
+
# x.big_data
|
|
54
|
+
# # => 'aaa'
|
|
55
|
+
#
|
|
56
|
+
# This implementation works, but has a problem:
|
|
57
|
+
#
|
|
58
|
+
# class AnotherClass
|
|
59
|
+
# include Stuff
|
|
60
|
+
#
|
|
61
|
+
# attr_reader :greeting
|
|
62
|
+
#
|
|
63
|
+
# def initialize
|
|
64
|
+
# @greeting = 'Hello'
|
|
65
|
+
# end
|
|
66
|
+
# end
|
|
67
|
+
#
|
|
68
|
+
# module Stuff
|
|
69
|
+
# prepend HasData
|
|
70
|
+
# end
|
|
71
|
+
#
|
|
72
|
+
# x = AnotherClass.new(data: 'aaa')
|
|
73
|
+
# # ArgumentError: wrong number of arguments (given 1, expected 0)
|
|
74
|
+
#
|
|
75
|
+
# This happens because `prepend` prepends our patch to `Stuff` module, not class.
|
|
76
|
+
# {PrependToClass} solves this problem:
|
|
77
|
+
#
|
|
78
|
+
# module HasData
|
|
79
|
+
# attr_reader :data
|
|
80
|
+
#
|
|
81
|
+
# InitializePatch = Flows::Util::PrependToClass.make_module do
|
|
82
|
+
# def initialize(*args, **kwargs, &block)
|
|
83
|
+
# @data = kwargs[:data]
|
|
84
|
+
#
|
|
85
|
+
# filtered_kwargs = kwargs.reject { |k, _| k == :data }
|
|
86
|
+
#
|
|
87
|
+
# if filtered_kwargs.empty? # https://bugs.ruby-lang.org/issues/14415
|
|
88
|
+
# super(*args, &block)
|
|
89
|
+
# else
|
|
90
|
+
# super(*args, **filtered_kwargs, &block)
|
|
91
|
+
# end
|
|
92
|
+
# end
|
|
93
|
+
# end
|
|
94
|
+
#
|
|
95
|
+
# include InitializePatch
|
|
96
|
+
# end
|
|
97
|
+
#
|
|
98
|
+
# module Stuff
|
|
99
|
+
# include HasData
|
|
100
|
+
# end
|
|
101
|
+
#
|
|
102
|
+
# class MyClass
|
|
103
|
+
# include Stuff
|
|
104
|
+
#
|
|
105
|
+
# attr_reader :greeting
|
|
106
|
+
#
|
|
107
|
+
# def initialize
|
|
108
|
+
# @greeting = 'Hello'
|
|
109
|
+
# end
|
|
110
|
+
# end
|
|
111
|
+
#
|
|
112
|
+
# x = MyClass.new(data: 'data')
|
|
113
|
+
#
|
|
114
|
+
# x.data
|
|
115
|
+
# # => 'data'
|
|
116
|
+
#
|
|
117
|
+
# x.greeting
|
|
118
|
+
# # => 'hello'
|
|
119
|
+
#
|
|
120
|
+
# @note this solution is designed to patch `include` behaviour and
|
|
121
|
+
# has no effect on `extend`.
|
|
122
|
+
module PrependToClass
|
|
123
|
+
class << self
|
|
124
|
+
# Allows to prepend some module to class when
|
|
125
|
+
# host module included into class.
|
|
126
|
+
#
|
|
127
|
+
# Under the hood two modules are created:
|
|
128
|
+
#
|
|
129
|
+
# * "to prepend" module made from provided block
|
|
130
|
+
# * "container" module which will be returned by this method
|
|
131
|
+
#
|
|
132
|
+
# When you include "container" module into your module `Mod`
|
|
133
|
+
# you're enabling the following behaviour:
|
|
134
|
+
#
|
|
135
|
+
# * when `Mod` included into class - "to prepend" module will be prepended to class
|
|
136
|
+
# * when `Mod` is included into some module `Mod2` - `Mod2` also will
|
|
137
|
+
# prepend "to prepend" module when included into class.
|
|
138
|
+
# * you can include `Mod` into `Mod2`, then include `Mod2` into `Mod3` -
|
|
139
|
+
# desribed behavior works for include chain of any length.
|
|
140
|
+
#
|
|
141
|
+
# Moreover, this behaviour also works with `extend`, not only `include`.
|
|
142
|
+
#
|
|
143
|
+
# @yield body for module which will be prepended
|
|
144
|
+
# @return [Module] module to be included or extended into your module
|
|
145
|
+
def make_module(&module_body)
|
|
146
|
+
Module.new.tap do |mod|
|
|
147
|
+
to_prepend_mod = Module.new(&module_body)
|
|
148
|
+
mod.const_set(:ToPrepend, to_prepend_mod)
|
|
149
|
+
|
|
150
|
+
set_injector_mod(mod, to_prepend_mod)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
private
|
|
155
|
+
|
|
156
|
+
def set_injector_mod(mod, module_to_prepend)
|
|
157
|
+
injector = make_injector_mod(module_to_prepend)
|
|
158
|
+
|
|
159
|
+
mod.const_set(:Injector, injector)
|
|
160
|
+
mod.singleton_class.prepend(injector)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def make_injector_mod(module_to_prepend)
|
|
164
|
+
Module.new.tap do |injector|
|
|
165
|
+
injector.define_method(:included) do |target_mod|
|
|
166
|
+
if target_mod.class == Class
|
|
167
|
+
target_mod.prepend(module_to_prepend)
|
|
168
|
+
else # Module
|
|
169
|
+
target_mod.singleton_class.prepend injector
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
super(target_mod)
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
data/lib/flows/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: flows
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1
|
|
4
|
+
version: 0.5.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Roman Kolesnev
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2020-06-29 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -30,14 +30,14 @@ dependencies:
|
|
|
30
30
|
requirements:
|
|
31
31
|
- - "~>"
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '
|
|
33
|
+
version: '13.0'
|
|
34
34
|
type: :development
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
38
|
- - "~>"
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: '
|
|
40
|
+
version: '13.0'
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
42
|
name: rspec
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -52,6 +52,76 @@ dependencies:
|
|
|
52
52
|
- - "~>"
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
54
|
version: '3.0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: yard
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '0'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: forspell
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - "~>"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: 0.0.8
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: 0.0.8
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: inch
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '0'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - ">="
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '0'
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: mdl
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - ">="
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '0'
|
|
104
|
+
type: :development
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - ">="
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '0'
|
|
111
|
+
- !ruby/object:Gem::Dependency
|
|
112
|
+
name: reek
|
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - ">="
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: '0'
|
|
118
|
+
type: :development
|
|
119
|
+
prerelease: false
|
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
+
requirements:
|
|
122
|
+
- - ">="
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: '0'
|
|
55
125
|
- !ruby/object:Gem::Dependency
|
|
56
126
|
name: rubocop
|
|
57
127
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -66,6 +136,20 @@ dependencies:
|
|
|
66
136
|
- - ">="
|
|
67
137
|
- !ruby/object:Gem::Version
|
|
68
138
|
version: '0'
|
|
139
|
+
- !ruby/object:Gem::Dependency
|
|
140
|
+
name: rubocop-md
|
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
|
142
|
+
requirements:
|
|
143
|
+
- - ">="
|
|
144
|
+
- !ruby/object:Gem::Version
|
|
145
|
+
version: '0'
|
|
146
|
+
type: :development
|
|
147
|
+
prerelease: false
|
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
149
|
+
requirements:
|
|
150
|
+
- - ">="
|
|
151
|
+
- !ruby/object:Gem::Version
|
|
152
|
+
version: '0'
|
|
69
153
|
- !ruby/object:Gem::Dependency
|
|
70
154
|
name: rubocop-performance
|
|
71
155
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -94,6 +178,20 @@ dependencies:
|
|
|
94
178
|
- - ">="
|
|
95
179
|
- !ruby/object:Gem::Version
|
|
96
180
|
version: '0'
|
|
181
|
+
- !ruby/object:Gem::Dependency
|
|
182
|
+
name: awesome_print
|
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
|
184
|
+
requirements:
|
|
185
|
+
- - ">="
|
|
186
|
+
- !ruby/object:Gem::Version
|
|
187
|
+
version: '0'
|
|
188
|
+
type: :development
|
|
189
|
+
prerelease: false
|
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
191
|
+
requirements:
|
|
192
|
+
- - ">="
|
|
193
|
+
- !ruby/object:Gem::Version
|
|
194
|
+
version: '0'
|
|
97
195
|
- !ruby/object:Gem::Dependency
|
|
98
196
|
name: pry
|
|
99
197
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -150,6 +248,20 @@ dependencies:
|
|
|
150
248
|
- - ">="
|
|
151
249
|
- !ruby/object:Gem::Version
|
|
152
250
|
version: '0'
|
|
251
|
+
- !ruby/object:Gem::Dependency
|
|
252
|
+
name: kalibera
|
|
253
|
+
requirement: !ruby/object:Gem::Requirement
|
|
254
|
+
requirements:
|
|
255
|
+
- - ">="
|
|
256
|
+
- !ruby/object:Gem::Version
|
|
257
|
+
version: '0'
|
|
258
|
+
type: :development
|
|
259
|
+
prerelease: false
|
|
260
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
261
|
+
requirements:
|
|
262
|
+
- - ">="
|
|
263
|
+
- !ruby/object:Gem::Version
|
|
264
|
+
version: '0'
|
|
153
265
|
- !ruby/object:Gem::Dependency
|
|
154
266
|
name: ruby-prof
|
|
155
267
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -178,6 +290,62 @@ dependencies:
|
|
|
178
290
|
- - ">="
|
|
179
291
|
- !ruby/object:Gem::Version
|
|
180
292
|
version: '0'
|
|
293
|
+
- !ruby/object:Gem::Dependency
|
|
294
|
+
name: gli
|
|
295
|
+
requirement: !ruby/object:Gem::Requirement
|
|
296
|
+
requirements:
|
|
297
|
+
- - ">="
|
|
298
|
+
- !ruby/object:Gem::Version
|
|
299
|
+
version: '0'
|
|
300
|
+
type: :development
|
|
301
|
+
prerelease: false
|
|
302
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
303
|
+
requirements:
|
|
304
|
+
- - ">="
|
|
305
|
+
- !ruby/object:Gem::Version
|
|
306
|
+
version: '0'
|
|
307
|
+
- !ruby/object:Gem::Dependency
|
|
308
|
+
name: rainbow
|
|
309
|
+
requirement: !ruby/object:Gem::Requirement
|
|
310
|
+
requirements:
|
|
311
|
+
- - ">="
|
|
312
|
+
- !ruby/object:Gem::Version
|
|
313
|
+
version: '0'
|
|
314
|
+
type: :development
|
|
315
|
+
prerelease: false
|
|
316
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
317
|
+
requirements:
|
|
318
|
+
- - ">="
|
|
319
|
+
- !ruby/object:Gem::Version
|
|
320
|
+
version: '0'
|
|
321
|
+
- !ruby/object:Gem::Dependency
|
|
322
|
+
name: warning
|
|
323
|
+
requirement: !ruby/object:Gem::Requirement
|
|
324
|
+
requirements:
|
|
325
|
+
- - ">="
|
|
326
|
+
- !ruby/object:Gem::Version
|
|
327
|
+
version: '0'
|
|
328
|
+
type: :development
|
|
329
|
+
prerelease: false
|
|
330
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
331
|
+
requirements:
|
|
332
|
+
- - ">="
|
|
333
|
+
- !ruby/object:Gem::Version
|
|
334
|
+
version: '0'
|
|
335
|
+
- !ruby/object:Gem::Dependency
|
|
336
|
+
name: dry-monads
|
|
337
|
+
requirement: !ruby/object:Gem::Requirement
|
|
338
|
+
requirements:
|
|
339
|
+
- - "~>"
|
|
340
|
+
- !ruby/object:Gem::Version
|
|
341
|
+
version: '1.3'
|
|
342
|
+
type: :development
|
|
343
|
+
prerelease: false
|
|
344
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
345
|
+
requirements:
|
|
346
|
+
- - "~>"
|
|
347
|
+
- !ruby/object:Gem::Version
|
|
348
|
+
version: '1.3'
|
|
181
349
|
- !ruby/object:Gem::Dependency
|
|
182
350
|
name: dry-transaction
|
|
183
351
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -213,41 +381,140 @@ executables: []
|
|
|
213
381
|
extensions: []
|
|
214
382
|
extra_rdoc_files: []
|
|
215
383
|
files:
|
|
384
|
+
- ".github/workflows/test.yml"
|
|
216
385
|
- ".gitignore"
|
|
386
|
+
- ".mdlrc"
|
|
387
|
+
- ".reek.yml"
|
|
217
388
|
- ".rspec"
|
|
218
389
|
- ".rubocop.yml"
|
|
219
390
|
- ".ruby-version"
|
|
220
|
-
- ".
|
|
391
|
+
- ".yardopts"
|
|
392
|
+
- CHANGELOG.md
|
|
221
393
|
- CODE_OF_CONDUCT.md
|
|
222
394
|
- Gemfile
|
|
223
|
-
- Gemfile.lock
|
|
224
395
|
- LICENSE.txt
|
|
225
396
|
- README.md
|
|
226
397
|
- Rakefile
|
|
398
|
+
- bin/.rubocop.yml
|
|
399
|
+
- bin/all_the_errors
|
|
227
400
|
- bin/benchmark
|
|
401
|
+
- bin/benchmark_cli/compare.rb
|
|
402
|
+
- bin/benchmark_cli/compare/a_plus_b.rb
|
|
403
|
+
- bin/benchmark_cli/compare/base.rb
|
|
404
|
+
- bin/benchmark_cli/compare/command.rb
|
|
405
|
+
- bin/benchmark_cli/compare/ten_steps.rb
|
|
406
|
+
- bin/benchmark_cli/examples.rb
|
|
407
|
+
- bin/benchmark_cli/examples/.rubocop.yml
|
|
408
|
+
- bin/benchmark_cli/examples/a_plus_b/dry_do.rb
|
|
409
|
+
- bin/benchmark_cli/examples/a_plus_b/dry_transaction.rb
|
|
410
|
+
- bin/benchmark_cli/examples/a_plus_b/flows_do.rb
|
|
411
|
+
- bin/benchmark_cli/examples/a_plus_b/flows_railway.rb
|
|
412
|
+
- bin/benchmark_cli/examples/a_plus_b/flows_scp.rb
|
|
413
|
+
- bin/benchmark_cli/examples/a_plus_b/flows_scp_mut.rb
|
|
414
|
+
- bin/benchmark_cli/examples/a_plus_b/flows_scp_oc.rb
|
|
415
|
+
- bin/benchmark_cli/examples/a_plus_b/trailblazer.rb
|
|
416
|
+
- bin/benchmark_cli/examples/ten_steps/dry_do.rb
|
|
417
|
+
- bin/benchmark_cli/examples/ten_steps/dry_transaction.rb
|
|
418
|
+
- bin/benchmark_cli/examples/ten_steps/flows_do.rb
|
|
419
|
+
- bin/benchmark_cli/examples/ten_steps/flows_railway.rb
|
|
420
|
+
- bin/benchmark_cli/examples/ten_steps/flows_scp.rb
|
|
421
|
+
- bin/benchmark_cli/examples/ten_steps/flows_scp_mut.rb
|
|
422
|
+
- bin/benchmark_cli/examples/ten_steps/flows_scp_oc.rb
|
|
423
|
+
- bin/benchmark_cli/examples/ten_steps/trailblazer.rb
|
|
424
|
+
- bin/benchmark_cli/helpers.rb
|
|
425
|
+
- bin/benchmark_cli/ruby.rb
|
|
426
|
+
- bin/benchmark_cli/ruby/command.rb
|
|
427
|
+
- bin/benchmark_cli/ruby/method_exec.rb
|
|
428
|
+
- bin/benchmark_cli/ruby/self_class.rb
|
|
429
|
+
- bin/benchmark_cli/ruby/structs.rb
|
|
228
430
|
- bin/console
|
|
229
|
-
- bin/
|
|
230
|
-
- bin/
|
|
231
|
-
- bin/
|
|
232
|
-
- bin/
|
|
431
|
+
- bin/docserver
|
|
432
|
+
- bin/errors
|
|
433
|
+
- bin/errors_cli/contract_error_demo.rb
|
|
434
|
+
- bin/errors_cli/di_error_demo.rb
|
|
435
|
+
- bin/errors_cli/flow_error_demo.rb
|
|
436
|
+
- bin/errors_cli/flows_router_error_demo.rb
|
|
437
|
+
- bin/errors_cli/oc_error_demo.rb
|
|
438
|
+
- bin/errors_cli/railway_error_demo.rb
|
|
439
|
+
- bin/errors_cli/result_error_demo.rb
|
|
440
|
+
- bin/errors_cli/scp_error_demo.rb
|
|
233
441
|
- bin/setup
|
|
442
|
+
- docs/.nojekyll
|
|
443
|
+
- docs/README.md
|
|
444
|
+
- docs/_sidebar.md
|
|
445
|
+
- docs/index.html
|
|
234
446
|
- flows.gemspec
|
|
447
|
+
- forspell.dict
|
|
448
|
+
- lefthook.yml
|
|
235
449
|
- lib/flows.rb
|
|
450
|
+
- lib/flows/contract.rb
|
|
451
|
+
- lib/flows/contract/array.rb
|
|
452
|
+
- lib/flows/contract/case_eq.rb
|
|
453
|
+
- lib/flows/contract/compose.rb
|
|
454
|
+
- lib/flows/contract/either.rb
|
|
455
|
+
- lib/flows/contract/error.rb
|
|
456
|
+
- lib/flows/contract/hash.rb
|
|
457
|
+
- lib/flows/contract/hash_of.rb
|
|
458
|
+
- lib/flows/contract/helpers.rb
|
|
459
|
+
- lib/flows/contract/predicate.rb
|
|
460
|
+
- lib/flows/contract/transformer.rb
|
|
461
|
+
- lib/flows/contract/tuple.rb
|
|
236
462
|
- lib/flows/flow.rb
|
|
237
|
-
- lib/flows/
|
|
238
|
-
- lib/flows/
|
|
239
|
-
- lib/flows/
|
|
240
|
-
- lib/flows/
|
|
241
|
-
- lib/flows/
|
|
242
|
-
- lib/flows/
|
|
243
|
-
- lib/flows/
|
|
463
|
+
- lib/flows/flow/errors.rb
|
|
464
|
+
- lib/flows/flow/node.rb
|
|
465
|
+
- lib/flows/flow/router.rb
|
|
466
|
+
- lib/flows/flow/router/custom.rb
|
|
467
|
+
- lib/flows/flow/router/errors.rb
|
|
468
|
+
- lib/flows/flow/router/simple.rb
|
|
469
|
+
- lib/flows/plugin.rb
|
|
470
|
+
- lib/flows/plugin/dependency_injector.rb
|
|
471
|
+
- lib/flows/plugin/dependency_injector/dependency.rb
|
|
472
|
+
- lib/flows/plugin/dependency_injector/dependency_definition.rb
|
|
473
|
+
- lib/flows/plugin/dependency_injector/dependency_list.rb
|
|
474
|
+
- lib/flows/plugin/dependency_injector/errors.rb
|
|
475
|
+
- lib/flows/plugin/implicit_init.rb
|
|
476
|
+
- lib/flows/plugin/output_contract.rb
|
|
477
|
+
- lib/flows/plugin/output_contract/dsl.rb
|
|
478
|
+
- lib/flows/plugin/output_contract/errors.rb
|
|
479
|
+
- lib/flows/plugin/output_contract/wrapper.rb
|
|
480
|
+
- lib/flows/plugin/profiler.rb
|
|
481
|
+
- lib/flows/plugin/profiler/injector.rb
|
|
482
|
+
- lib/flows/plugin/profiler/report.rb
|
|
483
|
+
- lib/flows/plugin/profiler/report/events.rb
|
|
484
|
+
- lib/flows/plugin/profiler/report/flat.rb
|
|
485
|
+
- lib/flows/plugin/profiler/report/flat/method_report.rb
|
|
486
|
+
- lib/flows/plugin/profiler/report/raw.rb
|
|
487
|
+
- lib/flows/plugin/profiler/report/tree.rb
|
|
488
|
+
- lib/flows/plugin/profiler/report/tree/calculated_node.rb
|
|
489
|
+
- lib/flows/plugin/profiler/report/tree/node.rb
|
|
490
|
+
- lib/flows/plugin/profiler/wrapper.rb
|
|
491
|
+
- lib/flows/railway.rb
|
|
492
|
+
- lib/flows/railway/dsl.rb
|
|
493
|
+
- lib/flows/railway/errors.rb
|
|
494
|
+
- lib/flows/railway/step.rb
|
|
495
|
+
- lib/flows/railway/step_list.rb
|
|
244
496
|
- lib/flows/result.rb
|
|
497
|
+
- lib/flows/result/do.rb
|
|
245
498
|
- lib/flows/result/err.rb
|
|
246
499
|
- lib/flows/result/errors.rb
|
|
247
500
|
- lib/flows/result/helpers.rb
|
|
248
501
|
- lib/flows/result/ok.rb
|
|
249
|
-
- lib/flows/
|
|
250
|
-
- lib/flows/
|
|
502
|
+
- lib/flows/shared_context_pipeline.rb
|
|
503
|
+
- lib/flows/shared_context_pipeline/dsl.rb
|
|
504
|
+
- lib/flows/shared_context_pipeline/dsl/callbacks.rb
|
|
505
|
+
- lib/flows/shared_context_pipeline/dsl/tracks.rb
|
|
506
|
+
- lib/flows/shared_context_pipeline/errors.rb
|
|
507
|
+
- lib/flows/shared_context_pipeline/mutation_step.rb
|
|
508
|
+
- lib/flows/shared_context_pipeline/router_definition.rb
|
|
509
|
+
- lib/flows/shared_context_pipeline/step.rb
|
|
510
|
+
- lib/flows/shared_context_pipeline/track.rb
|
|
511
|
+
- lib/flows/shared_context_pipeline/track_list.rb
|
|
512
|
+
- lib/flows/shared_context_pipeline/wrap.rb
|
|
513
|
+
- lib/flows/util.rb
|
|
514
|
+
- lib/flows/util/inheritable_singleton_vars.rb
|
|
515
|
+
- lib/flows/util/inheritable_singleton_vars/dup_strategy.rb
|
|
516
|
+
- lib/flows/util/inheritable_singleton_vars/isolation_strategy.rb
|
|
517
|
+
- lib/flows/util/prepend_to_class.rb
|
|
251
518
|
- lib/flows/version.rb
|
|
252
519
|
- profile/.keep
|
|
253
520
|
homepage: https://github.com/ffloyd/flows
|
|
@@ -272,5 +539,6 @@ requirements: []
|
|
|
272
539
|
rubygems_version: 3.0.3
|
|
273
540
|
signing_key:
|
|
274
541
|
specification_version: 4
|
|
275
|
-
summary: Ruby framework for building
|
|
542
|
+
summary: Ruby framework for building your Business Logic Layer inside Rails and other
|
|
543
|
+
frameworks.
|
|
276
544
|
test_files: []
|