lev 9.0.2 → 11.0.0

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