lev 9.0.2 → 11.0.0

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
  SHA256:
3
- metadata.gz: cecc3ef6f720b2c99f7a96c32b90d8354ca441d6d97af238d4186602d1dcf5e5
4
- data.tar.gz: ea7a54c1f4e8fdad398ad5403f84a8e13c706a1a5792b0188885857b1c8d0731
3
+ metadata.gz: 6a420c65f32a06c875e0e5069340f8cf1cc38505d9862a9ecbe20aede96059a2
4
+ data.tar.gz: 51f9c9746e69f4fc818f8e0d0c33e22a0c62d1a4f87c14b37d3b5645118a40ca
5
5
  SHA512:
6
- metadata.gz: e99acf68431e0bff2ab8ad0f374bb06371b3bad7cfc0a70276677ad61577555759a5e81da961c68318bae3f7f3e5871c3e8a224d0123e564f570785df5d820fb
7
- data.tar.gz: 29d9667e29a094876fbdb4cbd1253e42de6099bcf2b62d795397840bea23a6bc5ef334cec74e8b1f1468b793905afec0e2193220459e9b3ed6a0a51126dc10aa
6
+ metadata.gz: dd09071d958ae2590c604f2e6708b0942b06ecc97ff50be00d6a37863945543e56393a58f3a15b4664463104ba1064216cd66937bee00c1a3acdadf41596bfa3
7
+ data.tar.gz: 668e9e10ea0285ad953b3080484360017d487484f8771e7af1eb235ccdb1e1244ec7f563c1ed59df8148926a5284d2e89b7016c5376b702335ad2de9b88aea4a
data/lib/lev.rb CHANGED
@@ -9,7 +9,6 @@ require "lev/version"
9
9
  require "lev/object"
10
10
  require "lev/utilities"
11
11
  require "lev/exceptions"
12
- require "lev/better_active_model_errors"
13
12
  require "lev/term_mapper"
14
13
  require "lev/outputs"
15
14
  require "lev/routine"
@@ -13,7 +13,7 @@ module Lev
13
13
  end
14
14
 
15
15
  def perform_later(*args, &block)
16
- Lev::ActiveJob::Base.new.perform_later(routine_class, options, *args, &block)
16
+ routine_class.job_class.new.perform_later(routine_class, options, *args, &block)
17
17
  end
18
18
  end
19
19
  end
@@ -5,16 +5,16 @@ module Lev
5
5
  def self.transfer(source, target_routine, input_mapper, fail_if_errors=false)
6
6
  case source
7
7
  when ActiveRecord::Base, Lev::Paramifier
8
- source.errors.each_with_type_and_message do |attribute, type, message|
8
+ source.errors.each do |error|
9
9
  target_routine.nonfatal_error(
10
- code: type,
10
+ code: error.type,
11
11
  data: {
12
12
  model: source,
13
- attribute: attribute
13
+ attribute: error.attribute
14
14
  },
15
15
  kind: :activerecord,
16
- message: message,
17
- offending_inputs: input_mapper.map(attribute)
16
+ message: error.message,
17
+ offending_inputs: input_mapper.map(error.attribute)
18
18
  )
19
19
  end
20
20
  when Lev::Errors
@@ -5,15 +5,14 @@ module Lev
5
5
  def self.translate(error)
6
6
  case error.kind
7
7
  when :activerecord
8
- model = error.data[:model]
9
8
  attribute = error.data[:attribute]
10
- # TODO error.message might always be populated now -- really need the other call after ||?
11
- message = error.message || Lev::BetterActiveModelErrors.generate_message(model, attribute, error.code)
12
- Lev::BetterActiveModelErrors.full_message(model, attribute, message)
9
+ message = error.message
10
+ model = error.data[:model]
11
+ ActiveModel::Error.full_message(attribute, message, model)
13
12
  else
14
13
  message = error.message.to_s
15
14
  message.empty? ? error.code.to_s : message
16
- end
15
+ end
17
16
  end
18
17
 
19
18
  end
@@ -1,7 +1,9 @@
1
1
  module Lev
2
2
  class FormBuilder < ActionView::Helpers::FormBuilder
3
3
 
4
- (field_helpers - %w(label check_box radio_button fields_for file_field)).each do |selector|
4
+ (
5
+ field_helpers.map(&:to_s) - %w(label check_box radio_button fields_for file_field)
6
+ ).each do |selector|
5
7
  class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
6
8
  def #{selector}(method, options = {}) # def text_field(method, options = {})
7
9
  set_value_if_available(method, options) # ... verbatim ...
@@ -26,11 +28,11 @@ module Lev
26
28
  def fields_for(record_name, record_object = nil, fields_options = {}, &block)
27
29
  raise "Didn't put fields_for into LevitateFormBuilder yet"
28
30
  end
29
-
31
+
30
32
  protected
31
33
 
32
34
  def get_form_params_entry(name)
33
- @options[:params].present? ?
35
+ @options[:params].present? ?
34
36
  (@options[:params][@object_name].present? ?
35
37
  @options[:params][@object_name][name] :
36
38
  nil) :
@@ -60,4 +62,4 @@ def lev_form_for(record_or_name_or_array, *args, &proc)
60
62
  options[:params] = params
61
63
  options[:errors] = handler_errors # @errors || (@handler_outcome ? @handler_outcome.errors : [])
62
64
  form_for(record_or_name_or_array, *(args << options.merge(:builder => Lev::FormBuilder)), &proc)
63
- end
65
+ end
data/lib/lev/object.rb CHANGED
@@ -8,6 +8,7 @@ class Object
8
8
  options[:transaction] ||= Lev::TransactionIsolation.mysql_default.symbol
9
9
  @transaction_isolation = Lev::TransactionIsolation.new(options[:transaction])
10
10
 
11
+ @job_class = options[:job_class]
11
12
  @active_job_enqueue_options = options[:active_job_enqueue_options]
12
13
 
13
14
  @raise_fatal_errors = options[:raise_fatal_errors]
data/lib/lev/routine.rb CHANGED
@@ -214,23 +214,26 @@ module Lev
214
214
  Lev::ActiveJob::ConfiguredJob.new(self, options)
215
215
  end
216
216
 
217
- def perform_later(*args, &block)
218
- # Delegate to a subclass of Lev::Routine::ActiveJob::Base
219
- Lev::ActiveJob::Base.new.perform_later(self, active_job_enqueue_options, *args, &block)
217
+ def job_class
218
+ @job_class || Lev::ActiveJob::Base
220
219
  end
221
220
 
222
221
  def active_job_enqueue_options
223
222
  @active_job_enqueue_options || { queue: :default }
224
223
  end
225
224
 
225
+ def perform_later(*args, &block)
226
+ # Delegate to a subclass of Lev::Routine::ActiveJob::Base
227
+ job_class.new.perform_later(self, active_job_enqueue_options, *args, &block)
228
+ end
229
+
226
230
  # Called at a routine's class level to foretell which other routines will
227
231
  # be used when this routine executes. Helpful for figuring out ahead of
228
232
  # time what kind of transaction isolation level should be used.
229
233
  def uses_routine(routine_class, options={})
230
234
  symbol = options[:as] || class_to_symbol(routine_class)
231
235
 
232
- raise Lev.configuration.illegal_argument_error, "Routine #{routine_class} has already been registered" \
233
- if nested_routines[symbol]
236
+ warn("Routine #{routine_class} has already been registered") if nested_routines[symbol]
234
237
 
235
238
  nested_routines[symbol] = {
236
239
  routine_class: routine_class,
@@ -377,8 +380,7 @@ module Lev
377
380
  input_mapper = new_term_mapper(options[:translations][:inputs]) ||
378
381
  new_term_mapper({ scope: symbol })
379
382
 
380
- output_mapper = new_term_mapper(options[:translations][:outputs]) ||
381
- new_term_mapper({ scope: symbol })
383
+ output_mapper = new_term_mapper(options[:translations][:outputs])
382
384
 
383
385
  #
384
386
  # Set up the ignored errors in the routine instance
@@ -397,7 +399,7 @@ module Lev
397
399
 
398
400
  run_result.outputs.transfer_to(outputs) do |name|
399
401
  output_mapper.map(name)
400
- end
402
+ end unless output_mapper.nil?
401
403
 
402
404
  options[:errors_are_fatal] = true if !options.has_key?(:errors_are_fatal)
403
405
  transfer_errors_from(run_result.errors, input_mapper, options[:errors_are_fatal])
@@ -458,8 +460,9 @@ module Lev
458
460
  end
459
461
 
460
462
  def result
461
- @result ||= Result.new(Outputs.new,
462
- Errors.new(status, topmost_runner.class.raise_fatal_errors?))
463
+ @result ||= Result.new(
464
+ Outputs.new, Errors.new(status, topmost_runner.class.raise_fatal_errors?)
465
+ )
463
466
  end
464
467
 
465
468
  def reset_result!
data/lib/lev/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Lev
2
- VERSION = "9.0.2"
2
+ VERSION = '11.0.0'
3
3
  end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe 'ActiveModelErrors' do
4
+ class DummyModel
5
+ def self.human_attribute_name(attr, default='')
6
+ return attr.capitalize
7
+ end
8
+ end
9
+
10
+ let(:test_model) { DummyModel.new }
11
+ let(:errors) { ActiveModel::Errors.new(test_model) }
12
+
13
+ it 'can record errors' do
14
+ errors.add(:foo, 'bar')
15
+ expect(errors[:foo]).to eq ['bar']
16
+ expect(errors.any?).to be(true)
17
+ end
18
+
19
+ it 'can add using strings' do
20
+ errors.add('crash', 'is a bad bad value')
21
+ expect(errors[:crash]).to eq ['is a bad bad value']
22
+ expect(errors.include?('crash')).to be true
23
+ end
24
+
25
+ it 'duplicates when copy called' do
26
+ model = OpenStruct.new
27
+
28
+ error = ActiveModel::Errors.new(model)
29
+ error.add(:code, 'error')
30
+ expect(error[:code]).to eq ['error']
31
+
32
+ other = ActiveModel::Errors.new(model)
33
+ other.add(:code, 'warning')
34
+ error.copy!(other)
35
+ expect(error[:code]).to eq ['warning']
36
+ end
37
+ end
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe CreateSprocket do
4
-
3
+ RSpec.describe CreateSprocket do
4
+
5
5
  it "should transfer errors appropriately" do
6
6
  result = CreateSprocket.call(1,"42")
7
7
  errors = result.errors.collect { |error| error.translate }
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Lev::Utilities do
3
+ RSpec.describe Lev::Utilities do
4
4
 
5
5
  it "should merge properly" do
6
6
  default_options = {
@@ -38,4 +38,4 @@ describe Lev::Utilities do
38
38
  expect(merged).to eq expected
39
39
  end
40
40
 
41
- end
41
+ end
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe DelegatingRoutine do
3
+ RSpec.describe DelegatingRoutine do
4
4
 
5
5
  it "should delegate" do
6
6
  result = DelegatingRoutine.call(1,8)
data/spec/outputs_spec.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Lev::Outputs do
3
+ RSpec.describe Lev::Outputs do
4
4
 
5
5
  let(:outputs) { Lev::Outputs.new }
6
6
 
@@ -60,7 +60,7 @@ describe Lev::Outputs do
60
60
  :y
61
61
  end
62
62
 
63
- other_outputs.add(:y, 6)
63
+ other_outputs.add(:y, 6)
64
64
  expect(other_outputs.y).to eq [4,5,6]
65
65
  end
66
66
 
@@ -72,9 +72,9 @@ describe Lev::Outputs do
72
72
 
73
73
  outputs.transfer_to(other_outputs)
74
74
 
75
- other_outputs.add(:y, 6)
75
+ other_outputs.add(:y, 6)
76
76
  expect(other_outputs.x).to eq [4,5]
77
77
  expect(other_outputs.y).to eq 6
78
78
  end
79
79
 
80
- end
80
+ end
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe ParamifyHandlerA do
3
+ RSpec.describe ParamifyHandlerA do
4
4
  it 'should error out on badly formatted params' do
5
5
  result = ParamifyHandlerA.handle(params: {terms: {type: 'blah'}})
6
6
  errors = result.errors.collect { |error| error.translate }
@@ -8,7 +8,7 @@ describe ParamifyHandlerA do
8
8
  end
9
9
  end
10
10
 
11
- describe ParamifyHandlerB do
11
+ RSpec.describe ParamifyHandlerB do
12
12
  it 'should error out on badly formatted ungrouped params' do
13
13
  result = ParamifyHandlerB.handle(params: {type: 'blah'})
14
14
  errors = result.errors.collect { |error| error.translate }
data/spec/routine_spec.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Lev::Routine do
3
+ RSpec.describe Lev::Routine do
4
4
 
5
5
  before do
6
6
  stub_const 'RaiseRuntimeError', Class.new
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe SprocketHandler do
3
+ RSpec.describe SprocketHandler do
4
4
  it 'should return fatal error messages' do
5
5
  allow_any_instance_of(SprocketHandler).to(
6
6
  receive(:params).and_return({
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper.rb'
2
2
 
3
- describe Sprocket do
3
+ RSpec.describe Sprocket do
4
4
 
5
5
  it "should not be valid with bad inputs" do
6
6
  sprocket = Sprocket.new(integer_gt_2: 1, text_only_letters: 'abcd4')
@@ -8,4 +8,4 @@ describe Sprocket do
8
8
  expect(sprocket.errors.count).to eq 2
9
9
  end
10
10
 
11
- end
11
+ 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: 9.0.2
4
+ version: 11.0.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: 2019-08-02 00:00:00.000000000 Z
11
+ date: 2021-02-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '4.2'
19
+ version: '6.1'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '4.2'
26
+ version: '6.1'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activerecord
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -178,20 +178,6 @@ dependencies:
178
178
  - - ">="
179
179
  - !ruby/object:Gem::Version
180
180
  version: '0'
181
- - !ruby/object:Gem::Dependency
182
- name: byebug
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'
195
181
  - !ruby/object:Gem::Dependency
196
182
  name: jobba
197
183
  requirement: !ruby/object:Gem::Requirement
@@ -206,20 +192,6 @@ dependencies:
206
192
  - - ">="
207
193
  - !ruby/object:Gem::Version
208
194
  version: '1.5'
209
- - !ruby/object:Gem::Dependency
210
- name: rails
211
- requirement: !ruby/object:Gem::Requirement
212
- requirements:
213
- - - ">="
214
- - !ruby/object:Gem::Version
215
- version: '0'
216
- type: :development
217
- prerelease: false
218
- version_requirements: !ruby/object:Gem::Requirement
219
- requirements:
220
- - - ">="
221
- - !ruby/object:Gem::Version
222
- version: '0'
223
195
  description: Ride the rails but don't touch them.
224
196
  email:
225
197
  - jps@kindlinglabs.com
@@ -234,7 +206,6 @@ files:
234
206
  - lib/lev/active_job.rb
235
207
  - lib/lev/active_job/base.rb
236
208
  - lib/lev/active_job/configured_job.rb
237
- - lib/lev/better_active_model_errors.rb
238
209
  - lib/lev/delegate_to_routine.rb
239
210
  - lib/lev/error.rb
240
211
  - lib/lev/error_transferer.rb
@@ -255,7 +226,7 @@ files:
255
226
  - lib/lev/utilities.rb
256
227
  - lib/lev/version.rb
257
228
  - spec/active_job_routines_spec.rb
258
- - spec/better_active_model_errors_spec.rb
229
+ - spec/active_model_errors_spec.rb
259
230
  - spec/create_sprocket_spec.rb
260
231
  - spec/deep_merge_spec.rb
261
232
  - spec/delegates_to_spec.rb
@@ -293,28 +264,28 @@ required_rubygems_version: !ruby/object:Gem::Requirement
293
264
  - !ruby/object:Gem::Version
294
265
  version: '0'
295
266
  requirements: []
296
- rubygems_version: 3.0.3
267
+ rubygems_version: 3.2.7
297
268
  signing_key:
298
269
  specification_version: 4
299
270
  summary: Ride the rails but don't touch them.
300
271
  test_files:
301
- - spec/spec_helper.rb
302
- - spec/paramify_handler_spec.rb
303
- - spec/sprocket_handler_spec.rb
304
272
  - spec/create_sprocket_spec.rb
305
- - spec/delegates_to_spec.rb
306
- - spec/better_active_model_errors_spec.rb
273
+ - spec/paramify_handler_spec.rb
274
+ - spec/outputs_spec.rb
307
275
  - spec/transaction_spec.rb
308
- - spec/deep_merge_spec.rb
276
+ - spec/support/paramify_handler_a.rb
277
+ - spec/support/delegated_routine.rb
309
278
  - spec/support/paramify_handler_b.rb
279
+ - spec/support/create_sprocket.rb
310
280
  - spec/support/sprocket_handler.rb
311
- - spec/support/paramify_handler_a.rb
312
281
  - spec/support/delegating_routine.rb
313
- - spec/support/create_sprocket.rb
314
- - spec/support/delegated_routine.rb
315
282
  - spec/support/sprocket.rb
316
- - spec/sprocket_spec.rb
317
- - spec/outputs_spec.rb
318
- - spec/routine_spec.rb
319
- - spec/active_job_routines_spec.rb
320
283
  - spec/statused_routines_spec.rb
284
+ - spec/active_model_errors_spec.rb
285
+ - spec/delegates_to_spec.rb
286
+ - spec/active_job_routines_spec.rb
287
+ - spec/deep_merge_spec.rb
288
+ - spec/routine_spec.rb
289
+ - spec/sprocket_handler_spec.rb
290
+ - spec/spec_helper.rb
291
+ - spec/sprocket_spec.rb
@@ -1,413 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- require 'active_support/core_ext/array/wrap'
4
- require 'active_support/core_ext/array/conversions'
5
- require 'active_support/core_ext/string/inflections'
6
- require 'active_support/core_ext/object/blank'
7
- require 'active_support/core_ext/hash/reverse_merge'
8
- require 'active_support/ordered_hash'
9
-
10
- module Lev
11
- # == Better Errors
12
- #
13
- # Same as ActiveModel::Errors but also retains error types
14
- #
15
- # Provides a modified +OrderedHash+ that you can include in your object
16
- # for handling error messages and interacting with Action Pack helpers.
17
- #
18
- # A minimal implementation could be:
19
- #
20
- # class Person
21
- #
22
- # # Required dependency for ActiveModel::Errors
23
- # extend ActiveModel::Naming
24
- #
25
- # def initialize
26
- # @errors = ActiveModel::Errors.new(self)
27
- # end
28
- #
29
- # attr_accessor :name
30
- # attr_reader :errors
31
- #
32
- # def validate!
33
- # errors.add(:name, "can not be nil") if name == nil
34
- # end
35
- #
36
- # # The following methods are needed to be minimally implemented
37
- #
38
- # def read_attribute_for_validation(attr)
39
- # send(attr)
40
- # end
41
- #
42
- # def Person.human_attribute_name(attr, options = {})
43
- # attr
44
- # end
45
- #
46
- # def Person.lookup_ancestors
47
- # [self]
48
- # end
49
- #
50
- # end
51
- #
52
- # The last three methods are required in your object for Errors to be
53
- # able to generate error messages correctly and also handle multiple
54
- # languages. Of course, if you extend your object with ActiveModel::Translation
55
- # you will not need to implement the last two. Likewise, using
56
- # ActiveModel::Validations will handle the validation related methods
57
- # for you.
58
- #
59
- # The above allows you to do:
60
- #
61
- # p = Person.new
62
- # p.validate! # => ["can not be nil"]
63
- # p.errors.full_messages # => ["name can not be nil"]
64
- # # etc..
65
- class BetterActiveModelErrors
66
- include Enumerable
67
-
68
- CALLBACKS_OPTIONS = [:if, :unless, :on, :allow_nil, :allow_blank, :strict]
69
-
70
- attr_reader :messages
71
- attr_reader :types
72
-
73
- # Pass in the instance of the object that is using the errors object.
74
- #
75
- # class Person
76
- # def initialize
77
- # @errors = ActiveModel::Errors.new(self)
78
- # end
79
- # end
80
- def initialize(base)
81
- @base = base
82
- @types = ActiveSupport::OrderedHash.new
83
- @messages = ActiveSupport::OrderedHash.new
84
- end
85
-
86
- def initialize_dup(other)
87
- @types = other.types.dup
88
- @messages = other.messages.dup
89
- end
90
-
91
- # Backport dup from 1.9 so that #initialize_dup gets called
92
- unless Object.respond_to?(:initialize_dup, true)
93
- def dup # :nodoc:
94
- copy = super
95
- copy.initialize_dup(self)
96
- copy
97
- end
98
- end
99
-
100
- # Clear the messages
101
- def clear
102
- types.clear
103
- messages.clear
104
- end
105
-
106
- # Do the error messages include an error with key +error+?
107
- def include?(error)
108
- (v = messages[error.to_sym]) && v.any?
109
- end
110
- alias :has_key? :include?
111
-
112
- # Get messages for +key+
113
- def get(key)
114
- messages[key.to_sym]
115
- end
116
-
117
- def get_type(key)
118
- types[key.to_sym]
119
- end
120
-
121
- # Set messages for +key+ to +value+
122
- def set(key, value)
123
- types[key.to_sym] = (value == [] ? [] : (value.is_a?(Symbol) ? value : nil))
124
- messages[key.to_sym] = value
125
- end
126
-
127
- # Delete messages for +key+
128
- def delete(key)
129
- key = key.to_sym
130
- types.delete(key)
131
- messages.delete(key)
132
- end
133
-
134
- # When passed a symbol or a name of a method, returns an array of errors
135
- # for the method.
136
- #
137
- # p.errors[:name] # => ["can not be nil"]
138
- # p.errors['name'] # => ["can not be nil"]
139
- def [](attribute)
140
- get(attribute.to_sym) || set(attribute.to_sym, [])
141
- end
142
-
143
- # Adds to the supplied attribute the supplied error message.
144
- #
145
- # p.errors[:name] = "must be set"
146
- # p.errors[:name] # => ['must be set']
147
- def []=(attribute, error)
148
- self[attribute] << error
149
- end
150
-
151
- # Iterates through each error key, value pair in the error messages hash.
152
- # Yields the attribute and the error for that attribute. If the attribute
153
- # has more than one error message, yields once for each error message.
154
- #
155
- # p.errors.add(:name, "can't be blank")
156
- # p.errors.each do |attribute, errors_array|
157
- # # Will yield :name and "can't be blank"
158
- # end
159
- #
160
- # p.errors.add(:name, "must be specified")
161
- # p.errors.each do |attribute, errors_array|
162
- # # Will yield :name and "can't be blank"
163
- # # then yield :name and "must be specified"
164
- # end
165
- def each
166
- messages.each_key do |attribute|
167
- self[attribute].each { |error| yield attribute, error }
168
- end
169
- end
170
-
171
- def each_with_type_and_message
172
- types.each_key do |attribute|
173
- for ii in 0..self.types[attribute].size-1
174
- yield attribute, self.types[attribute][ii], self.messages[attribute][ii]
175
- end
176
- end
177
- end
178
-
179
- # Returns the number of error messages.
180
- #
181
- # p.errors.add(:name, "can't be blank")
182
- # p.errors.size # => 1
183
- # p.errors.add(:name, "must be specified")
184
- # p.errors.size # => 2
185
- def size
186
- values.flatten.size
187
- end
188
-
189
- # Returns all message values
190
- def values
191
- messages.values
192
- end
193
-
194
- # Returns all message keys
195
- def keys
196
- messages.keys
197
- end
198
-
199
- # Returns an array of error messages, with the attribute name included
200
- #
201
- # p.errors.add(:name, "can't be blank")
202
- # p.errors.add(:name, "must be specified")
203
- # p.errors.to_a # => ["name can't be blank", "name must be specified"]
204
- def to_a
205
- full_messages
206
- end
207
-
208
- # Returns the number of error messages.
209
- # p.errors.add(:name, "can't be blank")
210
- # p.errors.count # => 1
211
- # p.errors.add(:name, "must be specified")
212
- # p.errors.count # => 2
213
- def count
214
- to_a.size
215
- end
216
-
217
- # Returns true if no errors are found, false otherwise.
218
- # If the error message is a string it can be empty.
219
- def empty?
220
- all? { |k, v| v && v.empty? && !v.is_a?(String) }
221
- end
222
- alias_method :blank?, :empty?
223
-
224
- # Returns an xml formatted representation of the Errors hash.
225
- #
226
- # p.errors.add(:name, "can't be blank")
227
- # p.errors.add(:name, "must be specified")
228
- # p.errors.to_xml
229
- # # =>
230
- # # <?xml version=\"1.0\" encoding=\"UTF-8\"?>
231
- # # <errors>
232
- # # <error>name can't be blank</error>
233
- # # <error>name must be specified</error>
234
- # # </errors>
235
- def to_xml(options={})
236
- to_a.to_xml options.reverse_merge(:root => "errors", :skip_types => true)
237
- end
238
-
239
- # Returns an ActiveSupport::OrderedHash that can be used as the JSON representation for this object.
240
- def as_json(options=nil)
241
- to_hash
242
- end
243
-
244
- def to_hash
245
- messages.dup
246
- end
247
-
248
- def to_s
249
- inspect
250
- end
251
-
252
- # Adds +message+ to the error messages on +attribute+. More than one error can be added to the same
253
- # +attribute+.
254
- # If no +message+ is supplied, <tt>:invalid</tt> is assumed.
255
- #
256
- # If +message+ is a symbol, it will be translated using the appropriate scope (see +translate_error+).
257
- # If +message+ is a proc, it will be called, allowing for things like <tt>Time.now</tt> to be used within an error.
258
- def add(attribute, message = nil, options = {})
259
- normalized_message = normalize_message(attribute, message, options)
260
- if options[:strict]
261
- raise ActiveModel::StrictValidationFailed, full_message(attribute, normalized_message)
262
- end
263
-
264
- self[attribute] << normalized_message
265
- self.types[attribute.to_sym] << message.try(:to_sym)
266
- end
267
-
268
- # Will add an error message to each of the attributes in +attributes+ that is empty.
269
- def add_on_empty(attributes, options = {})
270
- [attributes].flatten.each do |attribute|
271
- value = @base.send(:read_attribute_for_validation, attribute)
272
- is_empty = value.respond_to?(:empty?) ? value.empty? : false
273
- add(attribute, :empty, options) if value.nil? || is_empty
274
- end
275
- end
276
-
277
- # Will add an error message to each of the attributes in +attributes+ that is blank (using Object#blank?).
278
- def add_on_blank(attributes, options = {})
279
- [attributes].flatten.each do |attribute|
280
- value = @base.send(:read_attribute_for_validation, attribute)
281
- add(attribute, :blank, options) if value.blank?
282
- end
283
- end
284
-
285
- # Returns true if an error on the attribute with the given message is present, false otherwise.
286
- # +message+ is treated the same as for +add+.
287
- # p.errors.add :name, :blank
288
- # p.errors.added? :name, :blank # => true
289
- def added?(attribute, message = nil, options = {})
290
- message = normalize_message(attribute, message, options)
291
- self[attribute].include? message
292
- end
293
-
294
- # Returns all the full error messages in an array.
295
- #
296
- # class Company
297
- # validates_presence_of :name, :address, :email
298
- # validates_length_of :name, :in => 5..30
299
- # end
300
- #
301
- # company = Company.create(:address => '123 First St.')
302
- # company.errors.full_messages # =>
303
- # ["Name is too short (minimum is 5 characters)", "Name can't be blank", "Email can't be blank"]
304
- def full_messages
305
- map { |attribute, message| full_message(attribute, message) }
306
- end
307
-
308
- # Returns a full message for a given attribute.
309
- #
310
- # company.errors.full_message(:name, "is invalid") # =>
311
- # "Name is invalid"
312
- def full_message(attribute, message)
313
- self.class.full_message(@base, attribute, message)
314
- end
315
-
316
- def self.full_message(model, attribute, message)
317
- return message if attribute == :base
318
- attr_name = attribute.to_s.gsub('.', '_').humanize
319
- attr_name = model.class.human_attribute_name(attribute, default: attr_name)
320
- I18n.t(:"errors.format", {
321
- default: "%{attribute} %{message}",
322
- attribute: attr_name,
323
- message: message
324
- })
325
- end
326
-
327
- # Translates an error message in its default scope
328
- # (<tt>activemodel.errors.messages</tt>).
329
- #
330
- # Error messages are first looked up in <tt>models.MODEL.attributes.ATTRIBUTE.MESSAGE</tt>,
331
- # if it's not there, it's looked up in <tt>models.MODEL.MESSAGE</tt> and if that is not
332
- # there also, it returns the translation of the default message
333
- # (e.g. <tt>activemodel.errors.messages.MESSAGE</tt>). The translated model name,
334
- # translated attribute name and the value are available for interpolation.
335
- #
336
- # When using inheritance in your models, it will check all the inherited
337
- # models too, but only if the model itself hasn't been found. Say you have
338
- # <tt>class Admin < User; end</tt> and you wanted the translation for
339
- # the <tt>:blank</tt> error message for the <tt>title</tt> attribute,
340
- # it looks for these translations:
341
- #
342
- # * <tt>activemodel.errors.models.admin.attributes.title.blank</tt>
343
- # * <tt>activemodel.errors.models.admin.blank</tt>
344
- # * <tt>activemodel.errors.models.user.attributes.title.blank</tt>
345
- # * <tt>activemodel.errors.models.user.blank</tt>
346
- # * any default you provided through the +options+ hash (in the <tt>activemodel.errors</tt> scope)
347
- # * <tt>activemodel.errors.messages.blank</tt>
348
- # * <tt>errors.attributes.title.blank</tt>
349
- # * <tt>errors.messages.blank</tt>
350
- #
351
- def generate_message(attribute, type = :invalid, options = {})
352
- self.class.generate_message(@base, attribute, type, options)
353
- end
354
-
355
- # TODO maybe don't need this method split out any more?
356
- def self.generate_message(model, attribute, type = :invalid, options = {})
357
- type = options.delete(:message) if options[:message].is_a?(Symbol)
358
- attribute = attribute.to_sym
359
- if model.class.respond_to?(:i18n_scope)
360
- defaults = model.class.lookup_ancestors.map do |klass|
361
- [ :"#{model.class.i18n_scope}.errors.models.#{klass.model_name.i18n_key}.attributes.#{attribute}.#{type}",
362
- :"#{model.class.i18n_scope}.errors.models.#{klass.model_name.i18n_key}.#{type}" ]
363
- end
364
- else
365
- defaults = []
366
- end
367
-
368
- defaults << options.delete(:message)
369
- defaults << :"#{model.class.i18n_scope}.errors.messages.#{type}" if model.class.respond_to?(:i18n_scope)
370
- defaults << :"errors.attributes.#{attribute}.#{type}"
371
- defaults << :"errors.messages.#{type}"
372
-
373
- defaults.compact!
374
- defaults.flatten!
375
-
376
- key = defaults.shift
377
- value = (attribute != :base ? model.send(:read_attribute_for_validation, attribute) : nil)
378
-
379
- options = {
380
- :default => defaults,
381
- :model => model.class.model_name.human,
382
- :attribute => model.class.human_attribute_name(attribute),
383
- :value => value
384
- }.merge(options)
385
-
386
- I18n.translate(key, options)
387
- end
388
-
389
- private
390
- def normalize_message(attribute, message, options)
391
- message ||= :invalid
392
-
393
- if message.is_a?(Symbol)
394
- generate_message(attribute, message, options.except(*CALLBACKS_OPTIONS))
395
- elsif message.is_a?(Proc)
396
- message.call
397
- else
398
- message
399
- end
400
- end
401
- end
402
-
403
- class StrictValidationFailed < StandardError
404
- end
405
- end
406
-
407
- module ActiveModel
408
- module Validations
409
- def errors
410
- @errors ||= Lev::BetterActiveModelErrors.new(self)
411
- end
412
- end
413
- end
@@ -1,25 +0,0 @@
1
- require 'spec_helper'
2
-
3
- RSpec.describe 'Better active model errors' do
4
-
5
- class DummyModel
6
- def self.human_attribute_name(attr, default='')
7
- return attr.capitalize
8
- end
9
- end
10
-
11
- let(:test_model) { DummyModel.new }
12
- let(:errors) { Lev::BetterActiveModelErrors.new(test_model) }
13
-
14
- it 'can record errors' do
15
- errors[:foo] = 'bar'
16
- expect(errors.any?).to be(true)
17
- end
18
-
19
- it 'can add using strings' do
20
- errors.add('crash', 'is a bad bad value')
21
- expect(errors[:crash]).to eq ['is a bad bad value']
22
- expect(errors.include?('crash')).to be true
23
- end
24
-
25
- end