lev 9.0.3 → 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: 7956e474a01a96f138b7dd2e926d77ecd28df3f9f9d3da82f924714c08a60a5e
4
- data.tar.gz: f43d74f9eaf88f5bb13ad669cf1e9bc8583f9f4e70708878062cb9565596ec17
3
+ metadata.gz: 6a420c65f32a06c875e0e5069340f8cf1cc38505d9862a9ecbe20aede96059a2
4
+ data.tar.gz: 51f9c9746e69f4fc818f8e0d0c33e22a0c62d1a4f87c14b37d3b5645118a40ca
5
5
  SHA512:
6
- metadata.gz: c2ab5a852630a17de13e57cc038a90409e3c472013eac10206c865938df076b5b19111a0a979aace04e4d4cdb7c134c1f91945fcecd862bbccea193d72ac4341
7
- data.tar.gz: fc864a9f83b3c3c7417f8b1e508fa5288ecdef4ce8cd66d5dae6cb21088db9bf41798c2836e8506893e618e2ede1803d0487ba5ac3b4f648b993e2f0d1646553
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.3"
2
+ VERSION = '11.0.0'
3
3
  end
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- RSpec.describe 'BetterActiveModelErrors' do
3
+ RSpec.describe 'ActiveModelErrors' do
4
4
  class DummyModel
5
5
  def self.human_attribute_name(attr, default='')
6
6
  return attr.capitalize
@@ -8,10 +8,11 @@ RSpec.describe 'BetterActiveModelErrors' do
8
8
  end
9
9
 
10
10
  let(:test_model) { DummyModel.new }
11
- let(:errors) { Lev::BetterActiveModelErrors.new(test_model) }
11
+ let(:errors) { ActiveModel::Errors.new(test_model) }
12
12
 
13
13
  it 'can record errors' do
14
- errors[:foo] = 'bar'
14
+ errors.add(:foo, 'bar')
15
+ expect(errors[:foo]).to eq ['bar']
15
16
  expect(errors.any?).to be(true)
16
17
  end
17
18
 
@@ -24,13 +25,13 @@ RSpec.describe 'BetterActiveModelErrors' do
24
25
  it 'duplicates when copy called' do
25
26
  model = OpenStruct.new
26
27
 
27
- error = Lev::BetterActiveModelErrors.new(model)
28
- error.set(:code, 'error')
29
- expect(error.get(:code)).to eq 'error'
28
+ error = ActiveModel::Errors.new(model)
29
+ error.add(:code, 'error')
30
+ expect(error[:code]).to eq ['error']
30
31
 
31
- other = Lev::BetterActiveModelErrors.new(model)
32
- other.set(:code, 'warning')
32
+ other = ActiveModel::Errors.new(model)
33
+ other.add(:code, 'warning')
33
34
  error.copy!(other)
34
- expect(error.get(:code)).to eq 'warning'
35
+ expect(error[:code]).to eq ['warning']
35
36
  end
36
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.3
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-09-03 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,421 +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
- # copy & details are needed to match `ActiveModel::Errors` interface
87
- def copy!(other)
88
- initialize_dup(other)
89
- end
90
- def details
91
- {}
92
- end
93
-
94
- def initialize_dup(other)
95
- @types = other.types.dup
96
- @messages = other.messages.dup
97
- end
98
-
99
- # Backport dup from 1.9 so that #initialize_dup gets called
100
- unless Object.respond_to?(:initialize_dup, true)
101
- def dup # :nodoc:
102
- copy = super
103
- copy.initialize_dup(self)
104
- copy
105
- end
106
- end
107
-
108
- # Clear the messages
109
- def clear
110
- types.clear
111
- messages.clear
112
- end
113
-
114
- # Do the error messages include an error with key +error+?
115
- def include?(error)
116
- (v = messages[error.to_sym]) && v.any?
117
- end
118
- alias :has_key? :include?
119
-
120
- # Get messages for +key+
121
- def get(key)
122
- messages[key.to_sym]
123
- end
124
-
125
- def get_type(key)
126
- types[key.to_sym]
127
- end
128
-
129
- # Set messages for +key+ to +value+
130
- def set(key, value)
131
- types[key.to_sym] = (value == [] ? [] : (value.is_a?(Symbol) ? value : nil))
132
- messages[key.to_sym] = value
133
- end
134
-
135
- # Delete messages for +key+
136
- def delete(key)
137
- key = key.to_sym
138
- types.delete(key)
139
- messages.delete(key)
140
- end
141
-
142
- # When passed a symbol or a name of a method, returns an array of errors
143
- # for the method.
144
- #
145
- # p.errors[:name] # => ["can not be nil"]
146
- # p.errors['name'] # => ["can not be nil"]
147
- def [](attribute)
148
- get(attribute.to_sym) || set(attribute.to_sym, [])
149
- end
150
-
151
- # Adds to the supplied attribute the supplied error message.
152
- #
153
- # p.errors[:name] = "must be set"
154
- # p.errors[:name] # => ['must be set']
155
- def []=(attribute, error)
156
- self[attribute] << error
157
- end
158
-
159
- # Iterates through each error key, value pair in the error messages hash.
160
- # Yields the attribute and the error for that attribute. If the attribute
161
- # has more than one error message, yields once for each error message.
162
- #
163
- # p.errors.add(:name, "can't be blank")
164
- # p.errors.each do |attribute, errors_array|
165
- # # Will yield :name and "can't be blank"
166
- # end
167
- #
168
- # p.errors.add(:name, "must be specified")
169
- # p.errors.each do |attribute, errors_array|
170
- # # Will yield :name and "can't be blank"
171
- # # then yield :name and "must be specified"
172
- # end
173
- def each
174
- messages.each_key do |attribute|
175
- self[attribute].each { |error| yield attribute, error }
176
- end
177
- end
178
-
179
- def each_with_type_and_message
180
- types.each_key do |attribute|
181
- for ii in 0..self.types[attribute].size-1
182
- yield attribute, self.types[attribute][ii], self.messages[attribute][ii]
183
- end
184
- end
185
- end
186
-
187
- # Returns the number of error messages.
188
- #
189
- # p.errors.add(:name, "can't be blank")
190
- # p.errors.size # => 1
191
- # p.errors.add(:name, "must be specified")
192
- # p.errors.size # => 2
193
- def size
194
- values.flatten.size
195
- end
196
-
197
- # Returns all message values
198
- def values
199
- messages.values
200
- end
201
-
202
- # Returns all message keys
203
- def keys
204
- messages.keys
205
- end
206
-
207
- # Returns an array of error messages, with the attribute name included
208
- #
209
- # p.errors.add(:name, "can't be blank")
210
- # p.errors.add(:name, "must be specified")
211
- # p.errors.to_a # => ["name can't be blank", "name must be specified"]
212
- def to_a
213
- full_messages
214
- end
215
-
216
- # Returns the number of error messages.
217
- # p.errors.add(:name, "can't be blank")
218
- # p.errors.count # => 1
219
- # p.errors.add(:name, "must be specified")
220
- # p.errors.count # => 2
221
- def count
222
- to_a.size
223
- end
224
-
225
- # Returns true if no errors are found, false otherwise.
226
- # If the error message is a string it can be empty.
227
- def empty?
228
- all? { |k, v| v && v.empty? && !v.is_a?(String) }
229
- end
230
- alias_method :blank?, :empty?
231
-
232
- # Returns an xml formatted representation of the Errors hash.
233
- #
234
- # p.errors.add(:name, "can't be blank")
235
- # p.errors.add(:name, "must be specified")
236
- # p.errors.to_xml
237
- # # =>
238
- # # <?xml version=\"1.0\" encoding=\"UTF-8\"?>
239
- # # <errors>
240
- # # <error>name can't be blank</error>
241
- # # <error>name must be specified</error>
242
- # # </errors>
243
- def to_xml(options={})
244
- to_a.to_xml options.reverse_merge(:root => "errors", :skip_types => true)
245
- end
246
-
247
- # Returns an ActiveSupport::OrderedHash that can be used as the JSON representation for this object.
248
- def as_json(options=nil)
249
- to_hash
250
- end
251
-
252
- def to_hash
253
- messages.dup
254
- end
255
-
256
- def to_s
257
- inspect
258
- end
259
-
260
- # Adds +message+ to the error messages on +attribute+. More than one error can be added to the same
261
- # +attribute+.
262
- # If no +message+ is supplied, <tt>:invalid</tt> is assumed.
263
- #
264
- # If +message+ is a symbol, it will be translated using the appropriate scope (see +translate_error+).
265
- # If +message+ is a proc, it will be called, allowing for things like <tt>Time.now</tt> to be used within an error.
266
- def add(attribute, message = nil, options = {})
267
- normalized_message = normalize_message(attribute, message, options)
268
- if options[:strict]
269
- raise ActiveModel::StrictValidationFailed, full_message(attribute, normalized_message)
270
- end
271
-
272
- self[attribute] << normalized_message
273
- self.types[attribute.to_sym] << message.try(:to_sym)
274
- end
275
-
276
- # Will add an error message to each of the attributes in +attributes+ that is empty.
277
- def add_on_empty(attributes, options = {})
278
- [attributes].flatten.each do |attribute|
279
- value = @base.send(:read_attribute_for_validation, attribute)
280
- is_empty = value.respond_to?(:empty?) ? value.empty? : false
281
- add(attribute, :empty, options) if value.nil? || is_empty
282
- end
283
- end
284
-
285
- # Will add an error message to each of the attributes in +attributes+ that is blank (using Object#blank?).
286
- def add_on_blank(attributes, options = {})
287
- [attributes].flatten.each do |attribute|
288
- value = @base.send(:read_attribute_for_validation, attribute)
289
- add(attribute, :blank, options) if value.blank?
290
- end
291
- end
292
-
293
- # Returns true if an error on the attribute with the given message is present, false otherwise.
294
- # +message+ is treated the same as for +add+.
295
- # p.errors.add :name, :blank
296
- # p.errors.added? :name, :blank # => true
297
- def added?(attribute, message = nil, options = {})
298
- message = normalize_message(attribute, message, options)
299
- self[attribute].include? message
300
- end
301
-
302
- # Returns all the full error messages in an array.
303
- #
304
- # class Company
305
- # validates_presence_of :name, :address, :email
306
- # validates_length_of :name, :in => 5..30
307
- # end
308
- #
309
- # company = Company.create(:address => '123 First St.')
310
- # company.errors.full_messages # =>
311
- # ["Name is too short (minimum is 5 characters)", "Name can't be blank", "Email can't be blank"]
312
- def full_messages
313
- map { |attribute, message| full_message(attribute, message) }
314
- end
315
-
316
- # Returns a full message for a given attribute.
317
- #
318
- # company.errors.full_message(:name, "is invalid") # =>
319
- # "Name is invalid"
320
- def full_message(attribute, message)
321
- self.class.full_message(@base, attribute, message)
322
- end
323
-
324
- def self.full_message(model, attribute, message)
325
- return message if attribute == :base
326
- attr_name = attribute.to_s.gsub('.', '_').humanize
327
- attr_name = model.class.human_attribute_name(attribute, default: attr_name)
328
- I18n.t(:"errors.format", {
329
- default: "%{attribute} %{message}",
330
- attribute: attr_name,
331
- message: message
332
- })
333
- end
334
-
335
- # Translates an error message in its default scope
336
- # (<tt>activemodel.errors.messages</tt>).
337
- #
338
- # Error messages are first looked up in <tt>models.MODEL.attributes.ATTRIBUTE.MESSAGE</tt>,
339
- # if it's not there, it's looked up in <tt>models.MODEL.MESSAGE</tt> and if that is not
340
- # there also, it returns the translation of the default message
341
- # (e.g. <tt>activemodel.errors.messages.MESSAGE</tt>). The translated model name,
342
- # translated attribute name and the value are available for interpolation.
343
- #
344
- # When using inheritance in your models, it will check all the inherited
345
- # models too, but only if the model itself hasn't been found. Say you have
346
- # <tt>class Admin < User; end</tt> and you wanted the translation for
347
- # the <tt>:blank</tt> error message for the <tt>title</tt> attribute,
348
- # it looks for these translations:
349
- #
350
- # * <tt>activemodel.errors.models.admin.attributes.title.blank</tt>
351
- # * <tt>activemodel.errors.models.admin.blank</tt>
352
- # * <tt>activemodel.errors.models.user.attributes.title.blank</tt>
353
- # * <tt>activemodel.errors.models.user.blank</tt>
354
- # * any default you provided through the +options+ hash (in the <tt>activemodel.errors</tt> scope)
355
- # * <tt>activemodel.errors.messages.blank</tt>
356
- # * <tt>errors.attributes.title.blank</tt>
357
- # * <tt>errors.messages.blank</tt>
358
- #
359
- def generate_message(attribute, type = :invalid, options = {})
360
- self.class.generate_message(@base, attribute, type, options)
361
- end
362
-
363
- # TODO maybe don't need this method split out any more?
364
- def self.generate_message(model, attribute, type = :invalid, options = {})
365
- type = options.delete(:message) if options[:message].is_a?(Symbol)
366
- attribute = attribute.to_sym
367
- if model.class.respond_to?(:i18n_scope)
368
- defaults = model.class.lookup_ancestors.map do |klass|
369
- [ :"#{model.class.i18n_scope}.errors.models.#{klass.model_name.i18n_key}.attributes.#{attribute}.#{type}",
370
- :"#{model.class.i18n_scope}.errors.models.#{klass.model_name.i18n_key}.#{type}" ]
371
- end
372
- else
373
- defaults = []
374
- end
375
-
376
- defaults << options.delete(:message)
377
- defaults << :"#{model.class.i18n_scope}.errors.messages.#{type}" if model.class.respond_to?(:i18n_scope)
378
- defaults << :"errors.attributes.#{attribute}.#{type}"
379
- defaults << :"errors.messages.#{type}"
380
-
381
- defaults.compact!
382
- defaults.flatten!
383
-
384
- key = defaults.shift
385
- value = (attribute != :base ? model.send(:read_attribute_for_validation, attribute) : nil)
386
-
387
- options = {
388
- :default => defaults,
389
- :model => model.class.model_name.human,
390
- :attribute => model.class.human_attribute_name(attribute),
391
- :value => value
392
- }.merge(options)
393
-
394
- I18n.translate(key, options)
395
- end
396
-
397
- private
398
- def normalize_message(attribute, message, options)
399
- message ||= :invalid
400
-
401
- if message.is_a?(Symbol)
402
- generate_message(attribute, message, options.except(*CALLBACKS_OPTIONS))
403
- elsif message.is_a?(Proc)
404
- message.call
405
- else
406
- message
407
- end
408
- end
409
- end
410
-
411
- class StrictValidationFailed < StandardError
412
- end
413
- end
414
-
415
- module ActiveModel
416
- module Validations
417
- def errors
418
- @errors ||= Lev::BetterActiveModelErrors.new(self)
419
- end
420
- end
421
- end