actionizer 0.8.0 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -2
- data/README.md +65 -11
- data/lib/actionizer/failure.rb +1 -1
- data/lib/actionizer/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: fbc6064d47f58c9d86ac8c9affc380faf93177e8
|
4
|
+
data.tar.gz: c5ebe99c56d627b9fbde668dfebb9429323d0fcc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 933e1fa6228e751600e00ac4553038fb8067e29a77f20e12f820668a5ca34e27e374a6896ff1eebe5596629a4b997d347a0cd8da095bf7507d8ceee108e28c42
|
7
|
+
data.tar.gz: 63b679a33a98811c45984b4e5ef74e0a35f798e63571047108825364a82fe587526a5d6c9ec18e9278d903c9b81b26b35e60b6e9bd55c8eb4f759cf06a1194d0
|
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
@@ -1,12 +1,19 @@
|
|
1
1
|
# Actionizer
|
2
2
|
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/actionizer.svg)](https://badge.fury.io/rb/actionizer)
|
3
4
|
[![Build Status](https://travis-ci.org/mikenichols/actionizer.svg?branch=master)](https://travis-ci.org/mikenichols/actionizer)
|
4
5
|
[![Test Coverage](https://codeclimate.com/github/mikenichols/actionizer/badges/coverage.svg)](https://codeclimate.com/github/mikenichols/actionizer/coverage)
|
5
6
|
[![Code Climate](https://codeclimate.com/github/mikenichols/actionizer/badges/gpa.svg)](https://codeclimate.com/github/mikenichols/actionizer)
|
6
7
|
|
7
8
|
## Turn your classes into small, modular, resuable Actions!
|
8
9
|
|
9
|
-
|
10
|
+
This gem is an implementation of the Interactor pattern. This pattern has also been called [Ports and Adapters](http://www.dossier-andreas.net/software_architecture/ports_and_adapters.html) or [Hexagonal Architecture](http://victorsavkin.com/post/42542190528/hexagonal-architecture-for-rails-developers) or [DCI (Data-Context-Interaction)](https://en.wikipedia.org/wiki/Data,_context_and_interaction) but the ideas are pretty much the same. Its goal is to provide a simple pattern for writing truly single-purpose classes. These classes are then easy to reason about, easy to test, and easy to change. They aren't coupled to all sorts of other parts of your system; they are self-contained and perform one and only one action.
|
11
|
+
|
12
|
+
This is not remotely a new or unique idea as there have been [many](http://blog.8thlight.com/uncle-bob/2011/11/22/Clean-Architecture.html) [awesome](http://jamesgolick.com/2010/3/14/crazy-heretical-and-awesome-the-way-i-write-rails-apps.html) [articles](http://jeffreypalermo.com/blog/the-onion-architecture-part-1/) and previous [projects](https://github.com/collectiveidea/interactor/) written to implement clean, modular classes. Uncle Bob even wrote a [great post](https://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html) highlighting similarities between the big architectural ideas of the last decade or so. While you should totally go read that article, I'll summarize it here for you. Your app is not your framework. It's super effective to decouple your business logic from your framework, database, UI, and third-party APIs.
|
13
|
+
|
14
|
+
I like to think of your business logic as being the "gooey center" of your app. That's where the most critical code lives. You want to keep it isolated from the details of databases, file systems, and networks. This inner layer should be so simple as to be boring because it's crucial to get it correct, so you want to make the code clean and simple, even stupid simple. `Actionizer` will help you write boring, stupid simple code.
|
15
|
+
|
16
|
+
### Installation
|
10
17
|
|
11
18
|
Add this line to your application's Gemfile:
|
12
19
|
|
@@ -14,15 +21,11 @@ Add this line to your application's Gemfile:
|
|
14
21
|
gem 'actionizer'
|
15
22
|
```
|
16
23
|
|
17
|
-
And then
|
18
|
-
|
19
|
-
$ bundle
|
24
|
+
And then run:
|
20
25
|
|
21
|
-
|
26
|
+
$ bundle install
|
22
27
|
|
23
|
-
|
24
|
-
|
25
|
-
## Usage
|
28
|
+
### Basic usage
|
26
29
|
|
27
30
|
Include `Actionizer` in your class and define an instance method. That instance method will be automatically invoked when you call the class method of the same name. Any Action defined with `Actionizer` will automatically return a hash-like result you can check for `success?` or `failure?`.
|
28
31
|
|
@@ -39,6 +42,8 @@ class CreateUser
|
|
39
42
|
end
|
40
43
|
```
|
41
44
|
|
45
|
+
### Check result status with `success?` and `failure?`
|
46
|
+
|
42
47
|
Actions are successful by default:
|
43
48
|
```ruby
|
44
49
|
result = SuccessfulAction.call(id: 1234)
|
@@ -49,6 +54,25 @@ result.failure?
|
|
49
54
|
#=> false
|
50
55
|
```
|
51
56
|
|
57
|
+
You can simplify your controllers drastically having them simply check for `success?`
|
58
|
+
```ruby
|
59
|
+
class UserController < ApplicationController
|
60
|
+
|
61
|
+
def create
|
62
|
+
result = CreateUser.call(name: params.fetch(:name), email: params.fetch(:email))
|
63
|
+
|
64
|
+
if result.success?
|
65
|
+
render :dashboard, user: result.user
|
66
|
+
else
|
67
|
+
redirect_to :new, error: "Couldn't create user because #{result.error_reason}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
```
|
73
|
+
|
74
|
+
### Signal failure with `fail!`
|
75
|
+
|
52
76
|
You can immediately stop execution with the `fail!` method.
|
53
77
|
```ruby
|
54
78
|
class DeleteAccount
|
@@ -64,7 +88,7 @@ class DeleteAccount
|
|
64
88
|
end
|
65
89
|
```
|
66
90
|
|
67
|
-
When an action
|
91
|
+
When you fail an action with `fail!`, the result it returns will return true for `failure?` and false for `success?`.
|
68
92
|
```ruby
|
69
93
|
result = FailingAction.call(id: 1234)
|
70
94
|
|
@@ -74,7 +98,9 @@ result.failure?
|
|
74
98
|
#=> true
|
75
99
|
```
|
76
100
|
|
77
|
-
|
101
|
+
### Composing Actions
|
102
|
+
|
103
|
+
The most common way to use `Actionizer` is to compose small pieces of functionality (which can themselves be Actions) into larger pieces of functionality to give that sequence of Actions a name and simple interface. Say you want to create a user and send them a welcome email as part of the onboarding process. Then you might do something like this:
|
78
104
|
```ruby
|
79
105
|
class OnboardUser
|
80
106
|
include Actionizer
|
@@ -89,8 +115,11 @@ class OnboardUser
|
|
89
115
|
end
|
90
116
|
```
|
91
117
|
|
118
|
+
This code is self-documenting because there's no wiki or comments to read about what's going on here. The code is telling you exactly what's going on.
|
92
119
|
|
93
|
-
|
120
|
+
### Error-checking shorthand: `*_or_fail`
|
121
|
+
|
122
|
+
To automatically check for `failure?` and bubble up errors on failure, there's a shorthand: `<METHOD>_or_fail`. It works for any instance method defined on the class you specify.
|
94
123
|
```ruby
|
95
124
|
class OnboardUser
|
96
125
|
include Actionizer
|
@@ -103,6 +132,31 @@ class OnboardUser
|
|
103
132
|
end
|
104
133
|
```
|
105
134
|
|
135
|
+
### Explicitly declare your inputs with `inputs_for`
|
136
|
+
|
137
|
+
To more explicitly document the inputs to your Actions, you can use `inputs_for`.
|
138
|
+
```ruby
|
139
|
+
class CreateUser
|
140
|
+
include Actionizer
|
141
|
+
|
142
|
+
inputs_for :call do
|
143
|
+
required :name
|
144
|
+
required :email
|
145
|
+
optional :phone_number
|
146
|
+
end
|
147
|
+
def call
|
148
|
+
result = CreateUser.call(user_params)
|
149
|
+
end
|
150
|
+
|
151
|
+
private
|
152
|
+
|
153
|
+
def user_params
|
154
|
+
{ name: input.name, email: input.email, phone_number: input.phone_number }.compact
|
155
|
+
end
|
156
|
+
end
|
157
|
+
```
|
158
|
+
|
159
|
+
You'll get an `ArgumentError` if you pass any params not defined in the `inputs_for` block, or if you don't supply all required params. This is completely opt-in so if you don't provide an `inputs_for` block, no checking is performed.
|
106
160
|
|
107
161
|
## Development
|
108
162
|
|
data/lib/actionizer/failure.rb
CHANGED
data/lib/actionizer/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: actionizer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Nichols
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-11-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|