pathway 0.0.17 → 0.0.18
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +106 -4
- data/lib/pathway/plugins/sequel_models.rb +1 -1
- data/lib/pathway/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 51bd7ec68ed98d860f4f2703fd052a0649060c3b
|
4
|
+
data.tar.gz: 7431743f7a87a6a477520f65a9df64b4702959a8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bca57cec6205c20f2868cda4a35fa764bb1debe0496e76e72cfd00669b2ae6dd19a8d7ee279d37786f3aa61e9f623a6518504c2e7da357d9c380167ea34933d4
|
7
|
+
data.tar.gz: 4c62b4c47cf3337f87366032219f9937ef16b7683664fe191067787ebac964b8947c4d278fe86b1b26e32fd4e0427b215cbac81748e267b5e455ea91477baf30
|
data/README.md
CHANGED
@@ -4,9 +4,7 @@
|
|
4
4
|
[![CircleCI](https://circleci.com/gh/pabloh/pathway/tree/master.svg?style=shield)](https://circleci.com/gh/pabloh/pathway/tree/master)
|
5
5
|
[![Coverage Status](https://coveralls.io/repos/github/pabloh/pathway/badge.svg?branch=master)](https://coveralls.io/github/pabloh/pathway?branch=master)
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
TODO: Delete this and the text above, and describe your gem
|
7
|
+
Pathway allows you to encapsulate your app's business logic into operation objects (also known as application services on the DDD lingo).
|
10
8
|
|
11
9
|
## Installation
|
12
10
|
|
@@ -24,9 +22,113 @@ Or install it yourself as:
|
|
24
22
|
|
25
23
|
$ gem install pathway
|
26
24
|
|
25
|
+
## Introduction
|
26
|
+
|
27
|
+
Pathway helps you separate your business logic from the rest of your application; regardless if is an HTTP backend, a background processing daemon, etc.
|
28
|
+
The main concept Pathway relies upon to build domain logic modules is the operation, this important concept will be explained in detail in the following sections.
|
29
|
+
|
30
|
+
|
31
|
+
Pathway also aims to be easy to use, stay lightweight and modular, avoid unnecessary heavy dependencies, keep the core classes clean from monkey patching and help yielding an organized and uniform codebase.
|
32
|
+
|
27
33
|
## Usage
|
28
34
|
|
29
|
-
|
35
|
+
### Core API and concepts
|
36
|
+
|
37
|
+
As mentioned earlier the operation is a crucial concept Pathway leverages upon. Operations not only structure your code (using steps as will be explained latter) but also express meaningful business actions. Operations can be thought as use cases too: they represent an activity -to be perform by an actor interacting with the system- which should be understandable by anyone familiar with the business regardless of their technical expertise.
|
38
|
+
|
39
|
+
|
40
|
+
Operations should ideally don't contain any business rules but instead orchestrate and delegate to other more specific subsystems and services. The only logic present then should be glue code or any adaptations required to make interactions with the inner system layers possible.
|
41
|
+
|
42
|
+
#### Function object protocol (the `call` method)
|
43
|
+
|
44
|
+
Operations works as function objects, they are callable and hold no state, as such, any object that responds to `call` and returns a result object can be a valid operation and that's the minimal protocol they needs to follow.
|
45
|
+
The result object must follow its own protocol as well (and a helper class is provided for that end) but we'll talk about that in a minute.
|
46
|
+
|
47
|
+
Let's see an example:
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
class MyFirstOperation
|
51
|
+
def call(params)
|
52
|
+
result = Repository.create(params)
|
53
|
+
|
54
|
+
if result.valid?
|
55
|
+
Pathway::Result.success(result)
|
56
|
+
else
|
57
|
+
Pathway::Result.failure(:create_error)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
result = MyFirstOperation.new.call(foo: 'foobar')
|
63
|
+
if result.success?
|
64
|
+
puts result.value.inspect
|
65
|
+
else
|
66
|
+
puts "Error: #{result.error}"
|
67
|
+
end
|
68
|
+
|
69
|
+
```
|
70
|
+
|
71
|
+
Note first we are not inheriting from any class nor including any module. This won't be the case in general as `pathway` provides classes to help build your operations, but it serves to illustrate how little is needed to implement one.
|
72
|
+
|
73
|
+
Also, let's ignore the specifics about `Repository.create(...)`, we just need to know that is some backend service which can return a value.
|
74
|
+
|
75
|
+
|
76
|
+
We now provide a `call` method for our class. It will just check if the result is available and then wrap it into a successful `Result` object when is ok, or a failing one when is not.
|
77
|
+
And that's it, you can then call the operation object, check whether it was completed correctly with `success?` and get the resulting value.
|
78
|
+
|
79
|
+
By following this protocol, you will be able to uniformly apply the same pattern on every HTTP endpoint (or whatever means your app has to communicates with the outside world). The upper layer of the application is now offloading all the domain logic to the operation and only needs to focus on the data transmission details.
|
80
|
+
|
81
|
+
Maintaining always the same operation protocol will also be very useful when composing them.
|
82
|
+
|
83
|
+
|
84
|
+
#### Operation result
|
85
|
+
|
86
|
+
As should be evident by now an operation should always return either a successful or failed result. This concepts are represented by following a simple protocol, which `Pathway::Result` subclasses comply.
|
87
|
+
|
88
|
+
As we seen before, by querying `success?` on the result we can see if the operation we just ran went well, you can also call to `failure?` for a negated version.
|
89
|
+
|
90
|
+
The actual result value produced by the operation is be accessible at the `value` method and the error description (if there's any) at `error` when the operation fails.
|
91
|
+
|
92
|
+
To return wrapped values or errors from your operation you can must call to `Pathway::Result.success(value)` or `Pathway::Result.failure(error)`.
|
93
|
+
|
94
|
+
|
95
|
+
It is worth mentioning that when you inherit from `Pathway::Operation` you'll have helper methods at your disposal to create result objects easier, for instance the previous section's example could be written as follows:
|
96
|
+
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
class MyFirstOperation < Pathway::Operation
|
100
|
+
def call(params)
|
101
|
+
result = Repository.create(params)
|
102
|
+
|
103
|
+
result.valid? ? success(result) : failure(:create_error)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
```
|
107
|
+
|
108
|
+
#### Error objects
|
109
|
+
#### Initialization and context
|
110
|
+
#### Steps
|
111
|
+
|
112
|
+
Finally the steps, these are the heart of the `Operation` class and the main reason you will want to inherit your own classes from `Pathway::Operation`.
|
113
|
+
|
114
|
+
#### Execution process state
|
115
|
+
#### Alternative invocation syntaxes and pattern matching DSL
|
116
|
+
|
117
|
+
### Plugins
|
118
|
+
#### Plugin architecture
|
119
|
+
|
120
|
+
#### `SimpleAuth` plugin
|
121
|
+
#### `DryValidation` plugin
|
122
|
+
#### `SequelModels` plugin
|
123
|
+
#### `Responder` plugin
|
124
|
+
|
125
|
+
### Testing tools
|
126
|
+
#### Rspec config
|
127
|
+
#### Rspec matchers
|
128
|
+
|
129
|
+
## Best practices
|
130
|
+
### Operation object design and organization
|
131
|
+
### Testing recomendations
|
30
132
|
|
31
133
|
## Development
|
32
134
|
|
@@ -24,7 +24,7 @@ module Pathway
|
|
24
24
|
module ClassMethods
|
25
25
|
attr_accessor :model_class, :search_field
|
26
26
|
|
27
|
-
def model(model_class, search_by:
|
27
|
+
def model(model_class, search_by: model_class.primary_key, set_result_key: true)
|
28
28
|
self.model_class = model_class
|
29
29
|
self.search_field = search_by
|
30
30
|
self.result_key = Inflecto.underscore(model_class.name.split('::').last).to_sym if set_result_key
|
data/lib/pathway/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pathway
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.18
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pablo Herrero
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-10-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: inflecto
|