lev 9.0.3 → 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: 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