light_operations 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: db2c6c8440e6a48a6ef168af4a7b545132437356
4
- data.tar.gz: 575327c90f66617d35a9954efe5870403695faf2
3
+ metadata.gz: c894c1e7f9c3a1a15692c6875f4ded5b88d458d9
4
+ data.tar.gz: 9d841c8af28c066013159703d302ef42cb354991
5
5
  SHA512:
6
- metadata.gz: 84d0758c75019a861344f0ad49ffc2283af17c21aa8c385e84b54eaee2d15f6d746897f65e364527dbf7c8a055261f58aa294212124e30917cd795baab2ab895
7
- data.tar.gz: b78d2ae0b6f0776491cc0725c24f543e142e11e2d39814dbcf21fb83beaa5768793ad3408d3b8c4704c6e828aa840b3c3284476bd8e0bd6a360698350355d075
6
+ metadata.gz: 2c3dcbef05689a60f891fc292b4fb96079406e6703d5ce7df0ca56ca2fee36ccc6808bb4987bbbfe4f095ec03f2dee656ff1aaa90dc5155a6982ddd4a522f3fb
7
+ data.tar.gz: a1b15f8668462cf6409450825a6a9f23d2e20040b44e53a8b39a42f17a73499a23bf28ab70ead3a583911e3453836bbc13e15bcbfe69163c2aae3df8f8a68952
data/README.md CHANGED
@@ -24,6 +24,105 @@ Or install it yourself as:
24
24
 
25
25
  ### Uses cases
26
26
 
27
+ #### Basic vote logic
28
+
29
+ Operation
30
+
31
+ ```ruby
32
+ class ArticleVoteBumperOperation < LightOperations::Core
33
+ rescue_from ActiveRecord::ActiveRecordError, with: :on_ar_error
34
+
35
+ def execute(_params = nil)
36
+ dependency(:article_model).tap do |article|
37
+ article.vote = article.vote.next
38
+ article.save
39
+ end
40
+ end
41
+
42
+ def on_ar_error(_exception)
43
+ fail!({ vote: 'could not be updated!' })
44
+ end
45
+ end
46
+ ```
47
+
48
+ Controller
49
+
50
+ ```ruby
51
+ class ArticleVotesController < ApplicationController
52
+ def up
53
+ response = article_vote_bumper_op.run.success? ? { success: true } : article_vote_bumper_op.errors
54
+ render :up, json: response
55
+ end
56
+
57
+ private
58
+
59
+ def article_vote_bumper_op
60
+ @article_vote_bumper_op ||= ArticleVoteBumperOperation.new(article_model: article)
61
+ end
62
+
63
+ def article
64
+ Article.find(params.require(:id))
65
+ end
66
+ end
67
+ ```
68
+
69
+ #### Basic recursion execution for collect newsfeeds from 2 sources
70
+
71
+ Operation
72
+
73
+ ```ruby
74
+ class CollectFeedsOperation < LightOperations::Core
75
+ rescue_from Timeout::Error, with: :on_timeout
76
+
77
+ def execute(params = {})
78
+ dependency(:http_client).get(params.fetch(:url)).body
79
+ end
80
+
81
+ def on_timeout
82
+ fail!
83
+ end
84
+ end
85
+ ```
86
+
87
+ Controller
88
+
89
+ ```ruby
90
+ class NewsFeedsController < ApplicationController
91
+ DEFAULT_NEWS_URL = 'http://rss.best_news.pl'
92
+ BACKUP_NEWS_URL = 'http://rss.not_so_bad_news.pl'
93
+ def news
94
+ collect_feeds_op
95
+ on(success: :display_news, fail: :second_attempt)
96
+ .run(url: DEFAULT_NEWS_URL)
97
+ end
98
+
99
+ private
100
+
101
+ def second_attempt(_news, _errors)
102
+ collect_feeds_op
103
+ .on_fail(:display_old_news)
104
+ .run(url: BACKUP_NEWS_URL)
105
+ end
106
+
107
+ def display_news(news)
108
+ render :display_news, locals { news: news }
109
+ end
110
+
111
+ def display_old_news
112
+ end
113
+
114
+ def collect_feeds_op
115
+ @collect_feeds_op ||= CollectFeedsOperation.new(http_client: http_client)
116
+ end
117
+
118
+ def http_client
119
+ MyAwesomeHttpClient
120
+ end
121
+ end
122
+ ```
123
+
124
+
125
+
27
126
  #### Simple case when you want have user authorization
28
127
 
29
128
  Operation
@@ -32,19 +131,19 @@ Operation
32
131
  class AuthOperation < LightOperations::Core
33
132
  rescue_from AuthFail, with: :on_auth_error
34
133
 
35
- def execute
36
- dependency(:auth_service).login(login: login, password: password)
134
+ def execute(params = {})
135
+ dependency(:auth_service).login(login: login(params), password: password(params))
37
136
  end
38
137
 
39
138
  def on_auth_error(_exception)
40
139
  fail!([login: 'unknown']) # or subject.errors.add(login: 'unknown')
41
140
  end
42
141
 
43
- def login
142
+ def login(params)
44
143
  params.fetch(:login)
45
144
  end
46
145
 
47
- def password
146
+ def password(params)
48
147
  params.fetch(:password)
49
148
  end
50
149
  end
@@ -60,10 +159,10 @@ class AuthController < ApplicationController
60
159
 
61
160
  def create
62
161
  auth_op
63
- .run
64
162
  .bind_with(self)
65
163
  .on_success(:create_session_with_dashbord_redirection)
66
164
  .on_fail(:render_account_with_errors)
165
+ .run(params)
67
166
  end
68
167
 
69
168
  private
@@ -78,7 +177,7 @@ class AuthController < ApplicationController
78
177
  end
79
178
 
80
179
  def auth_op
81
- @auth_op ||= AuthOperation.new(params, auth_service: auth_service)
180
+ @auth_op ||= AuthOperation.new(auth_service: auth_service)
82
181
  end
83
182
 
84
183
  def auth_service
@@ -97,9 +196,9 @@ class AuthController < ApplicationController
97
196
 
98
197
  def create
99
198
  auth_op
100
- .run
101
199
  .on_success{ |account| create_session_with_dashbord_redirection(account) }
102
200
  .on_fail { |account, _errors| render :new, locals: { account: account } }
201
+ .run(params)
103
202
  end
104
203
 
105
204
  private
@@ -110,7 +209,7 @@ class AuthController < ApplicationController
110
209
  end
111
210
 
112
211
  def auth_op
113
- @auth_op ||= AuthOperation.new(params, auth_service: auth_service)
212
+ @auth_op ||= AuthOperation.new(auth_service: auth_service)
114
213
  end
115
214
 
116
215
  def auth_service
@@ -128,7 +227,7 @@ class AuthController < ApplicationController
128
227
  end
129
228
 
130
229
  def create
131
- auth_op.run.on_success(&go_to_dashboard).on_fail(&go_to_login)
230
+ auth_op.on_success(&go_to_dashboard).on_fail(&go_to_login).run(params)
132
231
  end
133
232
 
134
233
  private
@@ -145,7 +244,7 @@ class AuthController < ApplicationController
145
244
  end
146
245
 
147
246
  def auth_op
148
- @auth_op ||= AuthOperation.new(params, auth_service: auth_service)
247
+ @auth_op ||= AuthOperation.new(auth_service: auth_service)
149
248
  end
150
249
 
151
250
  def auth_service
@@ -5,44 +5,68 @@ module LightOperations
5
5
  include ::ActiveSupport::Rescuable
6
6
  MissingDependency = Class.new(StandardError)
7
7
 
8
- attr_reader :dependencies, :params, :subject, :bind_oject
8
+ attr_reader :dependencies, :bind_object, :subject
9
9
 
10
- def initialize(params = {}, dependencies = {})
11
- @params, @dependencies = params, dependencies
10
+ def initialize(dependencies = {})
11
+ @dependencies = dependencies
12
12
  end
13
13
 
14
- # do not override this method
15
- def run
16
- @subject = execute
14
+ # do no.t override this method
15
+ def run(params = {})
16
+ @subject = execute(params)
17
+ execute_actions
17
18
  self
18
19
  rescue => exception
19
20
  rescue_with_handler(exception) || raise
20
21
  self
21
22
  end
22
23
 
23
- def bind_with(binding_obj)
24
- @bind_oject = binding_obj
24
+ def on_success(binded_method = nil, &block)
25
+ actions[:success] = binded_method || block
25
26
  self
26
27
  end
27
28
 
28
- def on_success(binded_method = nil, &block)
29
- if success?
30
- bind_oject.send(binded_method, subject) if can_use_binding_method?(binded_method)
31
- block.call(subject) if block_given?
32
- end
29
+ def on_fail(binded_method = nil, &block)
30
+ actions[:fail] = binded_method || block
33
31
  self
34
32
  end
35
33
 
36
- def on_fail(binded_method = nil, &block)
37
- unless success?
38
- bind_oject.send(binded_method, subject, errors) if can_use_binding_method?(binded_method)
39
- block.call(subject, errors) if block_given?
34
+ def on(actions_with_responses = {})
35
+ actions_with_responses.slice(:success, :fail).each do |action, response|
36
+ actions[action] = response
40
37
  end
41
38
  self
42
39
  end
43
40
 
41
+ def clear!
42
+ clear_actions!
43
+ unbind!
44
+ clear_subject_with_errors!
45
+ self
46
+ end
47
+
48
+ def unbind!
49
+ @bind_object = nil
50
+ self
51
+ end
52
+
53
+ def clear_subject_with_errors!
54
+ @subject, @fail_errors, @errors = nil, nil, nil
55
+ self
56
+ end
57
+
58
+ def clear_actions!
59
+ @actions = {}
60
+ self
61
+ end
62
+
63
+ def bind_with(bind_object)
64
+ @bind_object = bind_object
65
+ self
66
+ end
67
+
44
68
  def errors
45
- @errors ||= (subject.respond_to?(:errors) ? subject.errors : [])
69
+ @errors ||= fail_errors || (subject.respond_to?(:errors) ? subject.errors : [])
46
70
  end
47
71
 
48
72
  def fail?
@@ -55,16 +79,36 @@ module LightOperations
55
79
 
56
80
  protected
57
81
 
58
- def can_use_binding_method?(method_name)
59
- method_name && bind_oject && bind_oject.respond_to?(method_name)
82
+ attr_reader :fail_errors
83
+
84
+ def execute_actions
85
+ success? ? execute_success_action : execute_fail_action
60
86
  end
61
87
 
62
- def execute
63
- fail 'Not implemented yet'
88
+ def execute_success_action
89
+ return unless actions.key?(:success)
90
+ action = actions[:success]
91
+ bind_object.send(action, subject) if action.is_a?(Symbol) && bind_object
92
+ action.call(subject) if action.is_a?(Proc)
93
+ end
94
+
95
+ def execute_fail_action
96
+ return unless actions.key?(:fail)
97
+ action = actions[:fail]
98
+ bind_object.send(action, subject, errors) if action.is_a?(Symbol) && bind_object
99
+ action.call(subject, errors) if action.is_a?(Proc)
64
100
  end
65
101
 
66
102
  def fail!(errors = [])
67
- @errors = errors
103
+ @fail_errors = errors
104
+ end
105
+
106
+ def actions
107
+ @actions ||= {}
108
+ end
109
+
110
+ def execute(_params = {})
111
+ fail 'Not implemented yet'
68
112
  end
69
113
 
70
114
  def dependency(name)
@@ -1,3 +1,3 @@
1
1
  module LightOperations
2
- VERSION = '0.0.2'
2
+ VERSION = '0.0.3'
3
3
  end
@@ -8,13 +8,13 @@ describe LightOperations::Core do
8
8
  def subject_factory(&block)
9
9
  Class.new(described_class).tap do |klass|
10
10
  klass.class_eval(&block)
11
- end.new(params, dependencies)
11
+ end.new(dependencies)
12
12
  end
13
13
 
14
- subject { described_class.new(params, dependencies) }
14
+ subject { described_class.new(dependencies) }
15
15
 
16
16
  it 'raise error when #execute is not implemented' do
17
- expect { subject.run }.to raise_error('Not implemented yet')
17
+ expect { subject.on(success: :do_nothing).run }.to raise_error('Not implemented yet')
18
18
  end
19
19
 
20
20
  context 'use cases' do
@@ -33,12 +33,14 @@ describe LightOperations::Core do
33
33
  context 'dependency usage' do
34
34
  subject do
35
35
  subject_factory do
36
- def execute
36
+ def execute(_params)
37
37
  dependency(:login_service)
38
38
  end
39
39
  end
40
40
  end
41
41
 
42
+ before { subject.on(success: -> (_subject) {}) }
43
+
42
44
  it 'is allowed when is initialized correctly' do
43
45
  expect { subject.run }.not_to raise_error
44
46
  end
@@ -46,7 +48,7 @@ describe LightOperations::Core do
46
48
  context 'raise_error' do
47
49
  let(:dependencies) { {} }
48
50
  it 'when dependency missing' do
49
- expect { subject.run }.to raise_error(LightOperations::Core::MissingDependency)
51
+ expect { subject.run }.to raise_error(described_class::MissingDependency)
50
52
  end
51
53
  end
52
54
  end
@@ -54,49 +56,55 @@ describe LightOperations::Core do
54
56
  # error handling
55
57
 
56
58
  context '.rescue_from specific error' do
59
+ TestError = Class.new(StandardError)
60
+
61
+ before { subject.on(success: -> (_subject) {}) }
62
+
57
63
  context 'by block' do
58
64
  subject do
59
65
  subject_factory do
60
- rescue_from StandardError do |_exception|
61
- fail 'execute block instead original error'
66
+ rescue_from TestError do |exception|
67
+ fail "execute block instead original #{exception.class}"
62
68
  end
63
69
 
64
- def execute
65
- fail StandardError, 'What now'
70
+ def execute(_params)
71
+ fail TestError, 'What now'
66
72
  end
67
73
  end
68
74
  end
69
75
 
70
76
  it 'call' do
71
- expect { subject.run }.to raise_error('execute block instead original error')
77
+ expect { subject.run }.to raise_error('execute block instead original TestError')
72
78
  end
73
79
  end
74
80
 
75
81
  context 'by defined method' do
76
82
  subject do
77
83
  subject_factory do
78
- rescue_from StandardError, with: :rescue_me
84
+ rescue_from TestError, with: :rescue_me
79
85
 
80
- def rescue_me
81
- fail 'execute rescue_me method instead original error'
86
+ def rescue_me(exception)
87
+ fail "execute rescue_me method instead original #{exception.class}"
82
88
  end
83
89
 
84
- def execute
85
- fail StandardError, 'What now'
90
+ def execute(_params)
91
+ fail TestError, 'What now'
86
92
  end
87
93
  end
88
94
  end
89
95
 
90
96
  it 'execute' do
91
- expect { subject.run }.to raise_error('execute rescue_me method instead original error')
97
+ expect { subject.run }.to raise_error('execute rescue_me method instead original TestError')
92
98
  end
93
99
  end
94
100
  end
95
101
 
102
+ # on actions success/fail
103
+
96
104
  context 'when operation is successful' do
97
105
  subject do
98
106
  subject_factory do
99
- def execute
107
+ def execute(_params)
100
108
  :success
101
109
  end
102
110
  end
@@ -105,46 +113,44 @@ describe LightOperations::Core do
105
113
  context '#on_success' do
106
114
  it 'when bind_with and send_method is used' do
107
115
  expect(binding_object).to receive(:success_action).with(:success)
108
- subject.run.bind_with(binding_object).on_success(:success_action)
116
+ subject.on_success(:success_action).bind_with(binding_object).run
109
117
  end
110
118
 
111
- it 'when block is userd' do
112
- subject.run.on_success do |result|
113
- expect(result).to eq(:success)
114
- end
119
+ it 'when block is used' do
120
+ subject.on_success { |result| expect(result).to eq(:success) }.run
115
121
  end
116
122
  end
117
123
 
118
124
  context '#on_fail' do
119
125
  it 'when bind_with and send_method is used' do
120
126
  expect(binding_object).not_to receive(:error_action)
121
- subject.run.bind_with(binding_object).on_fail(:error_action)
127
+ subject.bind_with(binding_object).on_fail(:error_action).run
122
128
  end
123
129
 
124
- it 'when block is userd' do
130
+ it 'when block is used' do
125
131
  block_to_exec = -> () {}
126
132
  expect(block_to_exec).not_to receive(:call)
127
- subject.run.on_fail(&block_to_exec)
133
+ subject.on_fail(&block_to_exec).run
128
134
  end
129
135
  end
130
136
 
131
137
  it '#errors' do
132
- expect(subject.run.errors).to eq([])
138
+ expect(subject.on_success(:success_action).run.errors).to eq([])
133
139
  end
134
140
 
135
141
  it '#fail?' do
136
- expect(subject.run.fail?).to eq(false)
142
+ expect(subject.on_success(:success_action).run.fail?).to eq(false)
137
143
  end
138
144
 
139
145
  it '#success?' do
140
- expect(subject.run.success?).to eq(true)
146
+ expect(subject.on_success(:success_action).run.success?).to eq(true)
141
147
  end
142
148
  end
143
149
 
144
150
  context 'when operation is fail' do
145
151
  subject do
146
152
  subject_factory do
147
- def execute
153
+ def execute(_params)
148
154
  fail!([email: :unknown])
149
155
  :fail
150
156
  end
@@ -154,41 +160,78 @@ describe LightOperations::Core do
154
160
  context '#on_success' do
155
161
  it 'when bind_with and send_method is used' do
156
162
  expect(binding_object).not_to receive(:success_action)
157
- subject.run.bind_with(binding_object).on_success(:success_action)
163
+ subject.bind_with(binding_object).on_success(:success_action).run
158
164
  end
159
165
 
160
- it 'when block is userd' do
166
+ it 'when block is used' do
161
167
  block_to_exec = -> () {}
162
168
  expect(block_to_exec).not_to receive(:call)
163
- subject.run.on_success(&block_to_exec)
169
+ subject.on_success(&block_to_exec).run
164
170
  end
165
171
  end
166
172
 
167
173
  context '#on_fail' do
168
174
  it 'when bind_with and send_method is used' do
169
175
  expect(binding_object).to receive(:error_action).with(:fail, [email: :unknown])
170
- subject.run.bind_with(binding_object).on_fail(:error_action)
176
+ subject.bind_with(binding_object).on_fail(:error_action).run
171
177
  end
172
178
 
173
- it 'when block is userd' do
174
- subject.run.on_fail do |result, errors|
179
+ it 'when block is used' do
180
+ subject.on_fail do |result, errors|
175
181
  expect(result).to eq(:fail)
176
182
  expect(errors).to eq([email: :unknown])
177
183
  end
184
+ subject.run
178
185
  end
179
186
  end
180
187
 
181
188
  it '#errors' do
182
- expect(subject.run.errors).to eq([email: :unknown])
189
+ expect(subject.on_fail(:error_action).run.errors).to eq([email: :unknown])
183
190
  end
184
191
 
185
192
  it '#fail?' do
186
- expect(subject.run.fail?).to eq(true)
193
+ expect(subject.on_fail(:error_action).run.fail?).to eq(true)
187
194
  end
188
195
 
189
196
  it '#success?' do
190
- expect(subject.run.success?).to eq(false)
197
+ expect(subject.on_fail(:error_action).run.success?).to eq(false)
191
198
  end
192
199
  end
193
200
  end
201
+
202
+ context 'prepare operation to reuse or simply clear' do
203
+ it '#unbind!' do
204
+ subject.bind_with(:some_object)
205
+ expect { subject.unbind! }.to change { subject.bind_object }
206
+ .from(:some_object)
207
+ .to(nil)
208
+ end
209
+
210
+ it '#clear_actions!' do
211
+ subject.on(success: :abc, fail: :def)
212
+ expect { subject.clear_actions! }.to change { subject.send(:actions) }
213
+ .from(success: :abc, fail: :def)
214
+ .to({})
215
+ end
216
+
217
+ it '#clear_subject_with_errors!' do
218
+ %w{ subject fail_errors errors }.each do |variable|
219
+ subject.instance_variable_set("@#{variable}", variable)
220
+ end
221
+ expect(subject.subject).to eq('subject')
222
+ expect(subject.instance_variable_get('@errors')).to eq('errors')
223
+ expect(subject.instance_variable_get('@fail_errors')).to eq('fail_errors')
224
+ subject.clear_subject_with_errors!
225
+ expect(subject.subject).to be_nil
226
+ expect(subject.instance_variable_get('@errors')).to be_nil
227
+ expect(subject.instance_variable_get('@fail_errors')).to be_nil
228
+ end
229
+
230
+ it '#clear!' do
231
+ expect(subject).to receive(:unbind!)
232
+ expect(subject).to receive(:clear_actions!)
233
+ expect(subject).to receive(:clear_subject_with_errors!)
234
+ subject.clear!
235
+ end
236
+ end
194
237
  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.2
4
+ version: 0.0.3
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-26 00:00:00.000000000 Z
11
+ date: 2015-02-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport