light_operations 0.0.3 → 0.0.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c894c1e7f9c3a1a15692c6875f4ded5b88d458d9
4
- data.tar.gz: 9d841c8af28c066013159703d302ef42cb354991
3
+ metadata.gz: f01d43765f156039ef8227aa36ef7fc853bbc722
4
+ data.tar.gz: 6a28e10184931953b04722b98ad505e100636307
5
5
  SHA512:
6
- metadata.gz: 2c3dcbef05689a60f891fc292b4fb96079406e6703d5ce7df0ca56ca2fee36ccc6808bb4987bbbfe4f095ec03f2dee656ff1aaa90dc5155a6982ddd4a522f3fb
7
- data.tar.gz: a1b15f8668462cf6409450825a6a9f23d2e20040b44e53a8b39a42f17a73499a23bf28ab70ead3a583911e3453836bbc13e15bcbfe69163c2aae3df8f8a68952
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!({ vote: 'could not be updated!' })
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 = article_vote_bumper_op.run.success? ? { success: true } : article_vote_bumper_op.errors
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 article_vote_bumper_op
60
- @article_vote_bumper_op ||= ArticleVoteBumperOperation.new(article_model: article)
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 recursion execution for collect newsfeeds from 2 sources
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
- on(success: :display_news, fail: :second_attempt)
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]/swift_operations/fork )
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`)
@@ -13,6 +13,7 @@ module LightOperations
13
13
 
14
14
  # do no.t override this method
15
15
  def run(params = {})
16
+ clear_subject_with_errors!
16
17
  @subject = execute(params)
17
18
  execute_actions
18
19
  self
@@ -1,3 +1,3 @@
1
1
  module LightOperations
2
- VERSION = '0.0.3'
2
+ VERSION = '0.0.4'
3
3
  end
@@ -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.3
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-02-28 00:00:00.000000000 Z
11
+ date: 2015-03-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport