light_operations 0.0.2 → 0.0.3

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: 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