lev 4.1.0 → 4.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- NmIyODE2NzY1ZTAwZjA4YzZmY2NmYzYyNzFiZmVmOGJhMWE4MzNkYw==
4
+ MTgxYzgyOGI5NmQwYzE2MWM0NGI2NjZiMTIzM2U5NDk5ZGZiZjZkYw==
5
5
  data.tar.gz: !binary |-
6
- MWE4YzcxOWFkYmU2ZTQ5ZjllMDg0MWQ3NTdkZTVhNjZjYjFjZWUzMA==
6
+ ZGIyNGMyNDdmZGY1NjM0NmZmY2E5NTMwODU2MDJmMTU1YTYwYjM4OQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- ZTExN2U3Njg2MmM2OTQ0NTk0NDZmYjk2NDU4NmE1NjBmOTIwNDJlMzY3MGRk
10
- MTc5NmRiMjJhNTljMjc3Yzg2OGE2NmU1Y2IwMDQ1ZDlkYTA4MjI4N2QzODA0
11
- NzZlMGMyYWYzMzEyM2QyM2UyN2EzY2NlN2FjYjdlMjQyYWU0NjA=
9
+ N2FhODUwZmE3ZTM3NDJlMGM2N2JlM2YxNWExNDIxMDI0MzdlNmZlZGFmODFj
10
+ ZmM3NjY3ZWQ2M2FkMjFkMjI4NWY3MDFkNzJmYzc1MWY3YTMyN2VjZWZlYzky
11
+ MWIyZDlhMTA1MjY5YTQ2MDc1ZjhjMGM1OGU0ZGQyZThhZWJiMzc=
12
12
  data.tar.gz: !binary |-
13
- MzA2YjM3ZTRiOWRlNzJiZWNjZDRhZWE5NWU1MjI5OGQ0OWZhMDdlZmJkNjFi
14
- ZTE5YWRlNjljNDE4NTQ2ZWU3OTIzN2FhZTYyZGM2ODk0N2MyZjBkOGZhODRh
15
- NzJhNTk5NDUyYjk3MGE0NGZkYTM0OGViNDA4MTA4MmNhNzhjYjM=
13
+ MmJkMjMwZDhhNjk1ZjRiYThiMzkzYjBkMmFlZmVjZGU1NGYyYjNlOGJhYjM4
14
+ MDU2YzI4NTg2OTQ0ZjdhYmM3OTkzZTU2Y2FjZTc0ZWRmYWVmZmMyZjdmNWNl
15
+ MTI0ODYwMDE1YzI0NWIzZGQzYTkxNDMzOTkzNGU3ZWZkOTdmMWQ=
data/README.md CHANGED
@@ -63,6 +63,16 @@ Here's an example setting an error and an output:
63
63
  end
64
64
  end
65
65
 
66
+ If you'd like the `fatal_error` to raise a `StandardError` immediately instead of bubbling up to the `Lev::Routine#errors` object, you must configure it:
67
+
68
+ ```ruby
69
+ Lev.configure do |config|
70
+ config.raise_fatal_errors = true
71
+ end
72
+ ```
73
+
74
+ So if you `fatal_error(name: :is_blank)` it will raise `StandardError: "name is blank"`, or `fatal_error(thing: :is_broken, and: :messed_up)` it will raise `StandardError: "thing is broken - and messed up"`
75
+
66
76
  Additionally, see below for a discussion on how to transfer errors from ActiveRecord models.
67
77
 
68
78
  Any `StandardError` raised within a routine will be caught and transformed into a fatal error with `:kind` set to `:exception`. The caller of this routine can choose to reraise this exception by calling `reraise_exception!` on the returned errors object:
@@ -376,6 +386,27 @@ Routine class have access to a few other methods:
376
386
  2. a `topmost_runner` accessor which points to the highest routine in the calling
377
387
  hierarchy (that routine whose 'runner' is nil)
378
388
 
389
+ ### Calling routines as ActiveJobs
390
+
391
+ If `ActiveJob` is included in your project, you can invoke a routine to be run in the background. E.g. instead of saying
392
+
393
+ ```ruby
394
+ MyRoutine.call(arg1: 23, arg2: 'howdy')
395
+ ```
396
+
397
+ You can say
398
+
399
+ ```ruby
400
+ MyRoutine.perform_later(arg1: 23, arg2: 'howdy')
401
+ ```
402
+
403
+ By default jobs are placed in the `:default` queue, but you can override this in the `lev_routine` call:
404
+
405
+ ```ruby
406
+ class MyRoutine
407
+ lev_routine active_job_queue: :some_other_queue
408
+ end
409
+ ```
379
410
 
380
411
  ## Handlers
381
412
 
data/lib/lev.rb CHANGED
@@ -54,11 +54,13 @@ module Lev
54
54
  attr_accessor :form_error_class
55
55
  attr_accessor :security_transgression_error
56
56
  attr_accessor :illegal_argument_error
57
+ attr_accessor :raise_fatal_errors
57
58
 
58
59
  def initialize
59
60
  @form_error_class = 'error'
60
61
  @security_transgression_error = Lev::SecurityTransgression
61
62
  @illegal_argument_error = Lev::IllegalArgument
63
+ @raise_fatal_errors = false
62
64
  super
63
65
  end
64
66
  end
@@ -8,6 +8,10 @@ class Object
8
8
  options[:transaction] ||= Lev::TransactionIsolation.mysql_default.symbol
9
9
  @transaction_isolation = Lev::TransactionIsolation.new(options[:transaction])
10
10
 
11
+ @active_job_queue = options[:active_job_queue]
12
+
13
+ @raise_fatal_errors = options[:raise_fatal_errors]
14
+
11
15
  @delegates_to = options[:delegates_to]
12
16
  if @delegates_to
13
17
  uses_routine @delegates_to,
@@ -206,6 +206,34 @@ module Lev
206
206
  result.outputs.send(@express_output)
207
207
  end
208
208
 
209
+ if defined?(ActiveJob)
210
+ def active_job_class
211
+ @active_job_class ||= const_set("ActiveJob",
212
+ Class.new(ActiveJob::Base) do
213
+ queue_as do
214
+ parent_routine.active_job_queue
215
+ end
216
+
217
+ def parent_routine
218
+ self.class.parent
219
+ end
220
+
221
+ def perform(*args, &block)
222
+ parent_routine.call(*args, &block)
223
+ end
224
+ end
225
+ )
226
+ end
227
+
228
+ def perform_later(*args, &block)
229
+ active_job_class.perform_later(*args, &block)
230
+ end
231
+
232
+ def active_job_queue
233
+ @active_job_queue || :default
234
+ end
235
+ end
236
+
209
237
  # Called at a routine's class level to foretell which other routines will
210
238
  # be used when this routine executes. Helpful for figuring out ahead of
211
239
  # time what kind of transaction isolation level should be used.
@@ -239,6 +267,11 @@ module Lev
239
267
  @nested_routines ||= {}
240
268
  end
241
269
 
270
+ def raise_fatal_errors?
271
+ @raise_fatal_errors ||
272
+ (Lev.configuration.raise_fatal_errors && @raise_fatal_errors.nil?)
273
+ end
274
+
242
275
  def class_to_symbol(klass)
243
276
  klass.name.underscore.gsub('/','_').to_sym
244
277
  end
@@ -369,7 +402,11 @@ module Lev
369
402
  end
370
403
 
371
404
  def fatal_error(args={})
372
- errors.add(true, args)
405
+ if topmost_runner.class.raise_fatal_errors?
406
+ raise StandardError, args.to_a.map { |i| i.join(' ') }.join(' - ')
407
+ else
408
+ errors.add(true, args)
409
+ end
373
410
  end
374
411
 
375
412
  def nonfatal_error(args={})
@@ -1,3 +1,3 @@
1
1
  module Lev
2
- VERSION = "4.1.0"
2
+ VERSION = "4.2.0"
3
3
  end
@@ -0,0 +1,27 @@
1
+ require 'active_job'
2
+ require 'spec_helper'
3
+
4
+ ActiveJob::Base.queue_adapter = :test
5
+ ActiveJob::Base.logger = ::Logger.new(nil)
6
+
7
+ class LaterRoutine
8
+ lev_routine active_job_queue: :something_else
9
+
10
+ protected
11
+ def exec
12
+ end
13
+ end
14
+
15
+ RSpec.describe 'ActiveJob routines' do
16
+ it 'can perform routines later' do
17
+ expect {
18
+ LaterRoutine.perform_later
19
+ }.to change { ActiveJob::Base.queue_adapter.enqueued_jobs.count }.by(1)
20
+ end
21
+
22
+ it 'can have the default queue overridden' do
23
+ LaterRoutine.perform_later
24
+ queue_name = ActiveJob::Base.queue_adapter.enqueued_jobs.first[:queue]
25
+ expect(queue_name).to eq('something_else')
26
+ end
27
+ end
@@ -4,16 +4,16 @@ describe Lev::Routine do
4
4
 
5
5
  before do
6
6
  stub_const 'RaiseError', Class.new
7
- RaiseError.class_eval {
8
- lev_routine
7
+ RaiseError.class_eval {
8
+ lev_routine
9
9
  def exec
10
10
  raise 'error message'
11
11
  end
12
12
  }
13
13
 
14
14
  stub_const 'RaiseStandardError', Class.new
15
- RaiseStandardError.class_eval {
16
- lev_routine
15
+ RaiseStandardError.class_eval {
16
+ lev_routine
17
17
  def exec
18
18
  unknown_method_call
19
19
  end
@@ -32,4 +32,88 @@ describe Lev::Routine do
32
32
  }.to raise_error(NameError)
33
33
  end
34
34
 
35
+ it 'allows not raising fatal errors to be overridden' do
36
+ stub_const 'NestedFatalError', Class.new
37
+ NestedFatalError.class_eval {
38
+ lev_routine
39
+
40
+ def exec
41
+ fatal_error(code: :its_broken)
42
+ end
43
+ }
44
+
45
+ stub_const 'SpecialFatalErrorOption', Class.new
46
+ SpecialFatalErrorOption.class_eval {
47
+ lev_routine raise_fatal_errors: true, delegates_to: NestedFatalError
48
+ }
49
+
50
+ stub_const 'NoFatalErrorOption', Class.new
51
+ NoFatalErrorOption.class_eval {
52
+ lev_routine
53
+ def exec
54
+ fatal_error(code: :no_propagate)
55
+ end
56
+ }
57
+
58
+ Lev.configure { |c| c.raise_fatal_errors = false }
59
+
60
+ expect {
61
+ SpecialFatalErrorOption.call
62
+ }.to raise_error
63
+
64
+ expect {
65
+ NoFatalErrorOption.call
66
+ }.not_to raise_error
67
+ end
68
+
69
+ it 'allows raising fatal errors config to be overridden' do
70
+ stub_const 'SpecialNoFatalErrorOption', Class.new
71
+ SpecialNoFatalErrorOption.class_eval {
72
+ lev_routine raise_fatal_errors: false
73
+ def exec
74
+ fatal_error(code: :its_broken)
75
+ end
76
+ }
77
+
78
+ Lev.configure { |c| c.raise_fatal_errors = true }
79
+
80
+ expect {
81
+ SpecialNoFatalErrorOption.call
82
+ }.not_to raise_error
83
+ end
84
+
85
+ context 'when raise_fatal_errors is configured true' do
86
+ before do
87
+ Lev.configure do |config|
88
+ config.raise_fatal_errors = true
89
+ end
90
+
91
+ stub_const 'RaiseFatalError', Class.new
92
+ RaiseFatalError.class_eval {
93
+ lev_routine
94
+ def exec
95
+ fatal_error(code: :broken, such: :disaster)
96
+ end
97
+ }
98
+ end
99
+
100
+ after do
101
+ Lev.configure do |config|
102
+ config.raise_fatal_errors = false
103
+ end
104
+ end
105
+
106
+ it 'raises an exception on fatal_error if configured' do
107
+ expect {
108
+ RaiseFatalError.call
109
+ }.to raise_error
110
+
111
+ begin
112
+ RaiseFatalError.call
113
+ rescue => e
114
+ expect(e.message).to eq('code broken - such disaster')
115
+ end
116
+ end
117
+ end
118
+
35
119
  end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ unless ActiveRecord::Migration.table_exists?(:models)
4
+ ActiveRecord::Migration.create_table(:models)
5
+ end
6
+
7
+ class Model < ActiveRecord::Base; end
8
+
9
+ RSpec.describe 'Transactions' do
10
+ before do
11
+ stub_const 'RollBackTransactions', Class.new
12
+ stub_const 'NestedRoutine', Class.new
13
+
14
+ NestedRoutine.class_eval do
15
+ lev_routine
16
+
17
+ def exec
18
+ raise 'Rolled back'
19
+ end
20
+ end
21
+
22
+ RollBackTransactions.class_eval do
23
+ lev_routine
24
+
25
+ uses_routine NestedRoutine
26
+
27
+ def exec
28
+ Model.create!
29
+ run(:nested_routine)
30
+ end
31
+ end
32
+ end
33
+
34
+ context 'in nested routines' do
35
+ it 'rolls back on exceptions' do
36
+ expect {
37
+ RollBackTransactions.call
38
+ }.to raise_error
39
+
40
+ expect(Model.count).to eq(0)
41
+ end
42
+ end
43
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lev
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.1.0
4
+ version: 4.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - JP Slavinsky
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-16 00:00:00.000000000 Z
11
+ date: 2015-06-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -178,6 +178,20 @@ dependencies:
178
178
  - - ! '>='
179
179
  - !ruby/object:Gem::Version
180
180
  version: '0'
181
+ - !ruby/object:Gem::Dependency
182
+ name: activejob
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ! '>='
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ! '>='
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
181
195
  - !ruby/object:Gem::Dependency
182
196
  name: rails
183
197
  requirement: !ruby/object:Gem::Requirement
@@ -221,6 +235,7 @@ files:
221
235
  - lib/lev/transaction_isolation.rb
222
236
  - lib/lev/utilities.rb
223
237
  - lib/lev/version.rb
238
+ - spec/active_job_routines_spec.rb
224
239
  - spec/create_sprocket_spec.rb
225
240
  - spec/deep_merge_spec.rb
226
241
  - spec/delegates_to_spec.rb
@@ -238,6 +253,7 @@ files:
238
253
  - spec/support/paramify_handler_b.rb
239
254
  - spec/support/sprocket.rb
240
255
  - spec/support/sprocket_handler.rb
256
+ - spec/transaction_spec.rb
241
257
  homepage: http://github.com/lml/lev
242
258
  licenses:
243
259
  - MIT
@@ -263,6 +279,7 @@ signing_key:
263
279
  specification_version: 4
264
280
  summary: Ride the rails but don't touch them.
265
281
  test_files:
282
+ - spec/active_job_routines_spec.rb
266
283
  - spec/create_sprocket_spec.rb
267
284
  - spec/deep_merge_spec.rb
268
285
  - spec/delegates_to_spec.rb
@@ -280,3 +297,4 @@ test_files:
280
297
  - spec/support/paramify_handler_b.rb
281
298
  - spec/support/sprocket.rb
282
299
  - spec/support/sprocket_handler.rb
300
+ - spec/transaction_spec.rb