light_operations 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +178 -8
- data/lib/light_operations/core.rb +1 -0
- data/lib/light_operations/version.rb +1 -1
- data/spec/lib/core_spec.rb +33 -9
- 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: f01d43765f156039ef8227aa36ef7fc853bbc722
|
4
|
+
data.tar.gz: 6a28e10184931953b04722b98ad505e100636307
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0709083f10d462b9421924e4313323b38dd077a3908827fef106eb8cfee646a6128cde566399fff48cc431c72cc18d5e1c86a8bfa3044d9f61a59a4ba9219527
|
7
|
+
data.tar.gz: 80297934730c17e80022a7ecb2c843a3882ae14cd6041fab3a8fa08941e446c47aa6dca8c507005dfc2014dd0777fbe884d8793d6354065193282d61087ce6d5
|
data/README.md
CHANGED
@@ -19,6 +19,109 @@ Or install it yourself as:
|
|
19
19
|
|
20
20
|
$ gem install light_operations
|
21
21
|
|
22
|
+
## How it works
|
23
|
+
|
24
|
+
Basicly this is a Container for buissnes logic.
|
25
|
+
|
26
|
+
You can define dependencies during initialization and run with custom parameters.
|
27
|
+
When you define deferred actions on `success` and `fail` before operation execution is finished,
|
28
|
+
after execution one of those action depend for execution result will be executed.
|
29
|
+
Actions could be a block (Proc) or you could delgate execution to method other object,
|
30
|
+
by binding operation with specific object with those methods.
|
31
|
+
You also could use operation as simple execution and check status by `success?` or `fail?` method
|
32
|
+
and then by using `subject` and `errors` method build your own logic to finish your result.
|
33
|
+
There is many possible usecases where and how you could use operations.
|
34
|
+
You can build csacade of opreations, use them one after the other,
|
35
|
+
use them recursively and a lot more.
|
36
|
+
|
37
|
+
Class
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
class MyOperation < LightOperations::Core
|
41
|
+
def execute(_params = nil)
|
42
|
+
dependency(:my_service) # when missing MissingDependency error will be raised
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
```
|
47
|
+
|
48
|
+
Initialization
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
MyOperation.new(my_service: MyService.new)
|
52
|
+
```
|
53
|
+
|
54
|
+
You can add deferred actions for success and fail
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
# 1
|
58
|
+
MyOperation.new.on_success { |model| render :done, locals: { model: model } }
|
59
|
+
# 2
|
60
|
+
MyOperation.new.on(success: -> () { |model| render :done, locals: { model: model } )
|
61
|
+
```
|
62
|
+
|
63
|
+
When you bind operation with other object you could delegate actions to binded object methods
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
# 1
|
67
|
+
MyOperation.new.bind_with(self).on_success(:done)
|
68
|
+
# 2
|
69
|
+
MyOperation.new.bind_with(self).on(success: :done)
|
70
|
+
```
|
71
|
+
|
72
|
+
Execution method `#run` finalize actions execution
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
MyOperation.new.bind_with(self).on(success: :done).run(params)
|
76
|
+
```
|
77
|
+
|
78
|
+
After execution operation hold execution state you could get back all info you need
|
79
|
+
|
80
|
+
- `#success?` => `true/false`
|
81
|
+
- `#fail?` => `true/false`
|
82
|
+
- `#subject?` => `success or fail object`
|
83
|
+
- `#errors` => `errors by default array but you can return any objec tou want`
|
84
|
+
|
85
|
+
Default usage
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
operation.new(dependencies)
|
89
|
+
.on(success: :done, fail: :show_error)
|
90
|
+
.bind_with(self)
|
91
|
+
.run(params)
|
92
|
+
```
|
93
|
+
|
94
|
+
or
|
95
|
+
|
96
|
+
```ruby
|
97
|
+
operation.new(dependencies).tap do |op|
|
98
|
+
return op.run(params).success? ? op.subject : op.errors
|
99
|
+
end
|
100
|
+
```
|
101
|
+
|
102
|
+
#### success block or method receive subject as argument
|
103
|
+
`(subject) -> { }`
|
104
|
+
|
105
|
+
or
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
def success_method(subject)
|
109
|
+
...
|
110
|
+
end
|
111
|
+
|
112
|
+
```
|
113
|
+
#### fail block or method receive subject and errors as argument
|
114
|
+
`(subject, errors) -> { }`
|
115
|
+
|
116
|
+
or
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
def fail_method(subject, errors)
|
120
|
+
...
|
121
|
+
end
|
122
|
+
|
123
|
+
```
|
124
|
+
|
22
125
|
## Usage
|
23
126
|
|
24
127
|
|
@@ -37,10 +140,11 @@ class ArticleVoteBumperOperation < LightOperations::Core
|
|
37
140
|
article.vote = article.vote.next
|
38
141
|
article.save
|
39
142
|
end
|
143
|
+
{ success: true }
|
40
144
|
end
|
41
145
|
|
42
146
|
def on_ar_error(_exception)
|
43
|
-
fail!(
|
147
|
+
fail!(vote: 'could not be updated!')
|
44
148
|
end
|
45
149
|
end
|
46
150
|
```
|
@@ -50,14 +154,14 @@ Controller
|
|
50
154
|
```ruby
|
51
155
|
class ArticleVotesController < ApplicationController
|
52
156
|
def up
|
53
|
-
response =
|
157
|
+
response = operation.run.success? ? response.subject : response.errors
|
54
158
|
render :up, json: response
|
55
159
|
end
|
56
160
|
|
57
161
|
private
|
58
162
|
|
59
|
-
def
|
60
|
-
@
|
163
|
+
def operation
|
164
|
+
@operation ||= ArticleVoteBumperOperation.new(article_model: article)
|
61
165
|
end
|
62
166
|
|
63
167
|
def article
|
@@ -66,7 +170,7 @@ class ArticleVotesController < ApplicationController
|
|
66
170
|
end
|
67
171
|
```
|
68
172
|
|
69
|
-
#### Basic
|
173
|
+
#### Basic recursive execution to collect newsfeeds from 2 sources
|
70
174
|
|
71
175
|
Operation
|
72
176
|
|
@@ -92,7 +196,8 @@ class NewsFeedsController < ApplicationController
|
|
92
196
|
BACKUP_NEWS_URL = 'http://rss.not_so_bad_news.pl'
|
93
197
|
def news
|
94
198
|
collect_feeds_op
|
95
|
-
|
199
|
+
.bind_with(self)
|
200
|
+
.on(success: :display_news, fail: :second_attempt)
|
96
201
|
.run(url: DEFAULT_NEWS_URL)
|
97
202
|
end
|
98
203
|
|
@@ -105,7 +210,7 @@ class NewsFeedsController < ApplicationController
|
|
105
210
|
end
|
106
211
|
|
107
212
|
def display_news(news)
|
108
|
-
render :display_news, locals { news: news }
|
213
|
+
render :display_news, locals: { news: news }
|
109
214
|
end
|
110
215
|
|
111
216
|
def display_old_news
|
@@ -121,7 +226,58 @@ class NewsFeedsController < ApplicationController
|
|
121
226
|
end
|
122
227
|
```
|
123
228
|
|
229
|
+
#### Basic with active_model/active_record object
|
230
|
+
|
231
|
+
Operation
|
232
|
+
|
233
|
+
```ruby
|
234
|
+
class AddBookOperation < LightOperations::Core
|
235
|
+
def execute(params = {})
|
236
|
+
dependency(:book_model).new(params).tap do |model|
|
237
|
+
model.valid? # this method automatically provide errors from model.errors
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
```
|
242
|
+
|
243
|
+
Controller
|
244
|
+
|
245
|
+
```ruby
|
246
|
+
class BooksController < ApplicationController
|
247
|
+
def index
|
248
|
+
render :index, locals: { collection: Book.all }
|
249
|
+
end
|
250
|
+
|
251
|
+
def new
|
252
|
+
render_book_form
|
253
|
+
end
|
254
|
+
|
255
|
+
def create
|
256
|
+
add_book_op
|
257
|
+
.bind_with(self)
|
258
|
+
.on(success: :book_created, fail: :render_book_form)
|
259
|
+
.run(permit_book_params)
|
260
|
+
end
|
261
|
+
|
262
|
+
private
|
263
|
+
|
264
|
+
def book_created(book)
|
265
|
+
redirect_to :index, notice: "book #{book.name} created"
|
266
|
+
end
|
267
|
+
|
268
|
+
def render_book_form(book = Book.new, _errors = nil)
|
269
|
+
render :new, locals: { book: book }
|
270
|
+
end
|
124
271
|
|
272
|
+
def add_book_op
|
273
|
+
@add_book_op ||= AddBookOperation.new(book_model: Book)
|
274
|
+
end
|
275
|
+
|
276
|
+
def permit_book_params
|
277
|
+
params.requre(:book)
|
278
|
+
end
|
279
|
+
end
|
280
|
+
```
|
125
281
|
|
126
282
|
#### Simple case when you want have user authorization
|
127
283
|
|
@@ -253,13 +409,27 @@ class AuthController < ApplicationController
|
|
253
409
|
end
|
254
410
|
```
|
255
411
|
|
412
|
+
Register success and fails action is avialable by `#on` like :
|
413
|
+
|
414
|
+
```ruby
|
415
|
+
def create
|
416
|
+
auth_op.bind_with(self).on(success: :dashboard, fail: :show_error).run(params)
|
417
|
+
end
|
418
|
+
```
|
419
|
+
|
420
|
+
Operation have some helper methods (to improve recursive execution)
|
421
|
+
|
422
|
+
- `#clear!` => return operation to init state
|
423
|
+
- `#unbind!` => unbind binded object
|
424
|
+
- `#clear_subject_with_errors!` => clear subject and errors
|
425
|
+
|
256
426
|
When operation status is most importent we can simply use `#success?` or `#fail?` on the executed operation
|
257
427
|
|
258
428
|
Errors are available by `#errors` after operation is executed
|
259
429
|
|
260
430
|
## Contributing
|
261
431
|
|
262
|
-
1. Fork it ( https://github.com/[my-github-username]/
|
432
|
+
1. Fork it ( https://github.com/[my-github-username]/light_operations/fork )
|
263
433
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
264
434
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
265
435
|
4. Push to the branch (`git push origin my-new-feature`)
|
data/spec/lib/core_spec.rb
CHANGED
@@ -5,6 +5,16 @@ describe LightOperations::Core do
|
|
5
5
|
let(:params) { { login: 'pawel', password: 'abc' } }
|
6
6
|
let(:dependencies) { { login_service: login_service } }
|
7
7
|
|
8
|
+
let(:binding_object) do
|
9
|
+
Class.new(Object).tap do |klass|
|
10
|
+
klass.class_eval do
|
11
|
+
def success_action(_subject); end
|
12
|
+
|
13
|
+
def error_action(_subject, _errors); end
|
14
|
+
end
|
15
|
+
end.new
|
16
|
+
end
|
17
|
+
|
8
18
|
def subject_factory(&block)
|
9
19
|
Class.new(described_class).tap do |klass|
|
10
20
|
klass.class_eval(&block)
|
@@ -18,15 +28,6 @@ describe LightOperations::Core do
|
|
18
28
|
end
|
19
29
|
|
20
30
|
context 'use cases' do
|
21
|
-
let(:binding_object) do
|
22
|
-
Class.new(Object).tap do |klass|
|
23
|
-
klass.class_eval do
|
24
|
-
def success_action(_subject); end
|
25
|
-
|
26
|
-
def error_action(_subject, _errors); end
|
27
|
-
end
|
28
|
-
end.new
|
29
|
-
end
|
30
31
|
|
31
32
|
# dependency using
|
32
33
|
|
@@ -234,4 +235,27 @@ describe LightOperations::Core do
|
|
234
235
|
subject.clear!
|
235
236
|
end
|
236
237
|
end
|
238
|
+
|
239
|
+
context 'Operation executed several times' do
|
240
|
+
subject do
|
241
|
+
subject_factory do
|
242
|
+
def execute(params = {})
|
243
|
+
fail!(:missing_result) unless params.key?(:result)
|
244
|
+
params[:result]
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
it 'always start with clean state of subject and errors' do
|
250
|
+
|
251
|
+
subject
|
252
|
+
.bind_with(binding_object)
|
253
|
+
.on(success: :success_action, fail: :error_action)
|
254
|
+
|
255
|
+
expect(binding_object).to receive(:error_action).with(nil, :missing_result)
|
256
|
+
subject.run
|
257
|
+
expect(binding_object).to receive(:success_action).with(:success)
|
258
|
+
subject.run(result: :success)
|
259
|
+
end
|
260
|
+
end
|
237
261
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: light_operations
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pawel Niemczyk
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-03-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|