instructure-active_model-better_errors 1.6.3.rails2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/.document +5 -0
  2. data/.gitmodules +3 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +3 -0
  5. data/Gemfile +18 -0
  6. data/LICENSE.txt +20 -0
  7. data/README.md +183 -0
  8. data/Rakefile +57 -0
  9. data/VERSION +1 -0
  10. data/active_model-better_errors.gemspec +110 -0
  11. data/lib/active_model/better_errors.rb +17 -0
  12. data/lib/active_model/error_collecting/array_reporter.rb +9 -0
  13. data/lib/active_model/error_collecting/core_ext.rb +6 -0
  14. data/lib/active_model/error_collecting/emulation.rb +65 -0
  15. data/lib/active_model/error_collecting/error_collection.rb +86 -0
  16. data/lib/active_model/error_collecting/error_message.rb +88 -0
  17. data/lib/active_model/error_collecting/error_message_set.rb +33 -0
  18. data/lib/active_model/error_collecting/errors.rb +52 -0
  19. data/lib/active_model/error_collecting/hash_reporter.rb +9 -0
  20. data/lib/active_model/error_collecting/human_array_reporter.rb +9 -0
  21. data/lib/active_model/error_collecting/human_hash_reporter.rb +15 -0
  22. data/lib/active_model/error_collecting/human_message_formatter.rb +72 -0
  23. data/lib/active_model/error_collecting/human_message_reporter.rb +32 -0
  24. data/lib/active_model/error_collecting/machine_array_reporter.rb +19 -0
  25. data/lib/active_model/error_collecting/machine_hash_reporter.rb +22 -0
  26. data/lib/active_model/error_collecting/message_reporter.rb +17 -0
  27. data/lib/active_model/error_collecting/reporter.rb +14 -0
  28. data/lib/active_model/error_collecting.rb +49 -0
  29. data/spec/lib/active_model/better_errors_spec.rb +7 -0
  30. data/spec/lib/active_model/error_collecting/emulation_spec.rb +45 -0
  31. data/spec/lib/active_model/error_collecting/error_collection_spec.rb +205 -0
  32. data/spec/lib/active_model/error_collecting/error_message_set_spec.rb +96 -0
  33. data/spec/lib/active_model/error_collecting/error_message_spec.rb +293 -0
  34. data/spec/lib/active_model/error_collecting/errors_spec.rb +95 -0
  35. data/spec/lib/active_model/error_collecting/human_array_reporter_spec.rb +33 -0
  36. data/spec/lib/active_model/error_collecting/human_hash_reporter_spec.rb +32 -0
  37. data/spec/lib/active_model/error_collecting/human_message_formatter_spec.rb +22 -0
  38. data/spec/lib/active_model/error_collecting/human_message_reporter_spec.rb +61 -0
  39. data/spec/lib/active_model/error_collecting/machine_array_reporter_spec.rb +40 -0
  40. data/spec/lib/active_model/error_collecting/machine_hash_reporter_spec.rb +40 -0
  41. data/spec/lib/active_model/error_collecting_spec.rb +22 -0
  42. data/spec/spec_helper.rb +30 -0
  43. data/spec/support/models.rb +7 -0
  44. data/test/integration.rb +10 -0
  45. metadata +308 -0
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.gitmodules ADDED
@@ -0,0 +1,3 @@
1
+ [submodule "vendor/rails"]
2
+ path = vendor/rails
3
+ url = git://github.com/rails/rails.git
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
data/Gemfile ADDED
@@ -0,0 +1,18 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem "activerecord", "~> 2.3.11"
4
+
5
+ group :development, :test do
6
+ gem "rake"
7
+ gem "rspec", "~> 2.12.0"
8
+ gem "pry"
9
+ gem "yard"
10
+ gem "rdoc"
11
+ gem "bundler"
12
+ gem "jeweler"
13
+ gem "simplecov"
14
+ gem 'bcrypt-ruby', '~> 3.0.0'
15
+ gem 'mocha', '>= 0.12.1'
16
+ gem 'sqlite3'
17
+ gem 'debugger'
18
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2013 Aaron Qian
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,183 @@
1
+ # ActiveModel Better Errors
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/active_model-better_errors.png)](http://badge.fury.io/rb/active_model-better_errors)
4
+ [![Build Status](https://travis-ci.org/aq1018/active_model-better_errors.png?branch=master)](https://travis-ci.org/aq1018/active_model-better_errors)
5
+ [![Dependency Status](https://gemnasium.com/aq1018/active_model-better_errors.png)](https://gemnasium.com/aq1018/active_model-better_errors)
6
+ [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/aq1018/active_model-better_errors)
7
+
8
+ `active_model-better_errors` is a `ActiveModel::Errors` compatible library to help you
9
+ customize the presentation of your error messages.
10
+
11
+ ## Why Better Errors
12
+
13
+ In `ActiveModel::Errors`, when an error is added, it is pre translated into human readable string.
14
+ However, this becomes less ideal when output error messages in JSON or XML in your API.
15
+ Most of the times, we want error codes instead of human readable strings. This will allow
16
+ API consumers to translate them in their own application and provide more flexibility for your
17
+ API.
18
+
19
+ ## How Better Errors Work
20
+
21
+ `active_model-better_errors` works by sperating the concerns of error message storage and presentation.
22
+ `ActiveModel::Errors` really handles four tasks in a single class:
23
+
24
+ * error data storage
25
+ * presenting human readable errors using `#full_messages`
26
+ * presenting errors in Array / XML format with `#to_a` and `#to_xml`
27
+ * presenting errors in Hash / JSON format with `#to_hash` and `#as_json`
28
+
29
+ This gem seperates these concerns and allows users to plugin various reporters for different presentation needs.
30
+
31
+ ## Setup
32
+
33
+ ### With Bundler
34
+
35
+ ```ruby
36
+ # in Gemfile
37
+ gem 'active_model-better_errors', '>= 1.4.0'
38
+ ```
39
+
40
+ ```
41
+ # in console
42
+ bundle install
43
+ ```
44
+
45
+ ### With RubyGems
46
+
47
+ ```
48
+ gem install active_model-better_errors
49
+ ```
50
+
51
+ ## Configure
52
+
53
+ By default, `active_model-better_errors` is a drop-in replacement for `ActiveModel::Errors`
54
+ and will mostly function without modifications. This is because the default reporters are set like the following:
55
+
56
+ ```ruby
57
+ ActiveModel::ErrorCollecting.set_reporter :message, :human
58
+ ActiveModel::ErrorCollecting.set_reporter :array, :human
59
+ ActiveModel::ErrorCollecting.set_reporter :hash, :human
60
+ ```
61
+
62
+ If you want to output API friendly JSON / XML by default, you can use the following config.
63
+
64
+ ```ruby
65
+ ActiveModel::ErrorCollecting.set_reporter :message, :human
66
+ ActiveModel::ErrorCollecting.set_reporter :array, :machine
67
+ ActiveModel::ErrorCollecting.set_reporter :hash, :machine
68
+ ```
69
+
70
+ Note: The configurations above determines *default* reporters to use, you can always switch to
71
+ any reporters you want during runtime by invoking `errors.set_reporter`
72
+
73
+ ## Usage
74
+
75
+ ```ruby
76
+ user.errors # returns an instance of ActiveModel::ErrorCollecting::Errors
77
+
78
+ # Use MachineArrayReporter to output API friendly error messages
79
+ user.errors.set_reporter(:array, :machine)
80
+ user.errors.to_a # API Friendly
81
+ user.errors.to_xml # API Friendly
82
+
83
+ # Use HumanArrayReporter to output Human friendly error messages.
84
+ # This is ActiveModel::Errors behavior
85
+ user.errors.set_reporter(:array, :human)
86
+ user.errors.to_a # Human Friendly
87
+ user.errors.to_xml # Human Friendly
88
+
89
+ # Use MachineHashReporter to output API friendly error messages
90
+ user.errors.set_reporter(:hash, :machine)
91
+ user.errors.to_hash # API Friendly
92
+ user.errors.as_json # API Friendly
93
+ user.errors.to_json # API Friendly
94
+
95
+
96
+ # Use HumanHashReporter to output Human friendly error messages.
97
+ # This is ActiveModel::Errors behavior
98
+ user.errors.set_reporter(:hash, :human)
99
+ user.errors.to_hash # Human Friendly
100
+ user.errors.as_json # Human Friendly
101
+ user.errors.to_json # Human Friendly
102
+
103
+ # Use MyHashReporter to output Human friendly error messages.
104
+ # This is ActiveModel::Errors behavior
105
+ user.errors.set_reporter(:hash, MyHashReporter)
106
+ user.errors.to_hash # custom behavior
107
+ user.errors.as_json # custom behavior
108
+ user.errors.to_json # custom behavior
109
+
110
+ ```
111
+
112
+ ## In Depth
113
+
114
+ This library creates three types of reporters to take care of
115
+ some presentation aspects of `ActiveModel::Errors`, and they are:
116
+
117
+ **Message Reporters**
118
+
119
+ This is responsible for handling the following methods in `ActiveModel::Errors`
120
+
121
+ * `#full_messages`
122
+ * `#full_message`
123
+ * `#generate_message`
124
+
125
+ Due to the conventional use of these methods, they are mostly intended for human consumption.
126
+
127
+ This library implements the following reporters of this type:
128
+
129
+ * `HumanMessageReporter`
130
+
131
+ **Array Reporters**
132
+
133
+ This is responsible for handling the following methods in `ActiveModel::Errors`
134
+
135
+ * `#to_a`
136
+
137
+ This library implements the following reporters of this type:
138
+
139
+ * `HumanArrayReporter`
140
+ * `MachineArrayReporter`
141
+
142
+ **Hash Reporters**
143
+
144
+ This is responsible for handling the following methods in `ActiveModel::Errors`
145
+
146
+ * `#to_hash`
147
+
148
+ This library implements the following reporters of this type:
149
+
150
+ * `HumanHashReporter`
151
+ * `MachineHashReporter`
152
+
153
+
154
+ ## Creating Custom Reporters
155
+
156
+ Creating a custom reporter is easy. Here is an example to create a hash reporter
157
+
158
+ ```ruby
159
+ class MyHashReporter < ActiveModel::ErrorCollecting::HashReporter
160
+ def to_hash
161
+ # you have access to #collection and #base
162
+ end
163
+ end
164
+
165
+ # set it to use it.
166
+ ActiveModel::ErrorCollecting.set_reporter :hash, MyHashReporter
167
+ ```
168
+
169
+ ## Contributing to active_model-better_errors
170
+
171
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
172
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
173
+ * Fork the project.
174
+ * Start a feature/bugfix branch.
175
+ * Commit and push until you are happy with your contribution.
176
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
177
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
178
+
179
+ ## Copyright
180
+
181
+ Copyright (c) 2013 Aaron Qian. See LICENSE.txt for
182
+ further details.
183
+
data/Rakefile ADDED
@@ -0,0 +1,57 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "instructure-active_model-better_errors"
18
+ gem.homepage = "http://github.com/codekitchen/active_model-better_errors"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{API consumable error messages with ActiveModel::Errors drop-in compatibility.}
21
+ gem.description = %Q{ActiveModel::Errors class is not designed for API consumption. This gem allows for that while keeping compatibility with existing rails API.}
22
+ gem.email = ["aq1018@gmail.com", "byronanderson32@gmail.com", "brianp@instructure.com"]
23
+ gem.authors = ["Aaron Qian", "Byron Anderson", "Brian Palmer"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rspec/core'
29
+ require 'rspec/core/rake_task'
30
+ RSpec::Core::RakeTask.new(:spec) do |spec|
31
+ spec.pattern = FileList['spec/**/*_spec.rb']
32
+ end
33
+
34
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
35
+ spec.pattern = 'spec/**/*_spec.rb'
36
+ spec.rcov = true
37
+ end
38
+
39
+ dir = File.dirname(__FILE__)
40
+
41
+ require 'rake/testtask'
42
+
43
+ test_files = []
44
+ test_files << "test/integration"
45
+ test_files += Dir.glob("#{dir}/vendor/rails/activemodel/test/cases/**/*_test.rb").sort
46
+
47
+ Rake::TestTask.new("test:integration") do |t|
48
+ t.libs << "vendor/rails/activemodel/test"
49
+ t.test_files = test_files
50
+ t.warning = true
51
+ end
52
+
53
+
54
+ task :default => [ :spec ]
55
+
56
+ require 'yard'
57
+ YARD::Rake::YardocTask.new
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.6.3.rails2
@@ -0,0 +1,110 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "instructure-active_model-better_errors"
8
+ s.version = "1.6.3.rails2"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Aaron Qian", "Byron Anderson", "Brian Palmer"]
12
+ s.date = "2013-11-08"
13
+ s.description = "ActiveModel::Errors class is not designed for API consumption. This gem allows for that while keeping compatibility with existing rails API."
14
+ s.email = ["aq1018@gmail.com", "byronanderson32@gmail.com", "brianp@instructure.com"]
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.md"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitmodules",
22
+ ".rspec",
23
+ ".travis.yml",
24
+ "Gemfile",
25
+ "LICENSE.txt",
26
+ "README.md",
27
+ "Rakefile",
28
+ "VERSION",
29
+ "active_model-better_errors.gemspec",
30
+ "lib/active_model/better_errors.rb",
31
+ "lib/active_model/error_collecting.rb",
32
+ "lib/active_model/error_collecting/array_reporter.rb",
33
+ "lib/active_model/error_collecting/core_ext.rb",
34
+ "lib/active_model/error_collecting/emulation.rb",
35
+ "lib/active_model/error_collecting/error_collection.rb",
36
+ "lib/active_model/error_collecting/error_message.rb",
37
+ "lib/active_model/error_collecting/error_message_set.rb",
38
+ "lib/active_model/error_collecting/errors.rb",
39
+ "lib/active_model/error_collecting/hash_reporter.rb",
40
+ "lib/active_model/error_collecting/human_array_reporter.rb",
41
+ "lib/active_model/error_collecting/human_hash_reporter.rb",
42
+ "lib/active_model/error_collecting/human_message_formatter.rb",
43
+ "lib/active_model/error_collecting/human_message_reporter.rb",
44
+ "lib/active_model/error_collecting/machine_array_reporter.rb",
45
+ "lib/active_model/error_collecting/machine_hash_reporter.rb",
46
+ "lib/active_model/error_collecting/message_reporter.rb",
47
+ "lib/active_model/error_collecting/reporter.rb",
48
+ "spec/lib/active_model/better_errors_spec.rb",
49
+ "spec/lib/active_model/error_collecting/emulation_spec.rb",
50
+ "spec/lib/active_model/error_collecting/error_collection_spec.rb",
51
+ "spec/lib/active_model/error_collecting/error_message_set_spec.rb",
52
+ "spec/lib/active_model/error_collecting/error_message_spec.rb",
53
+ "spec/lib/active_model/error_collecting/errors_spec.rb",
54
+ "spec/lib/active_model/error_collecting/human_array_reporter_spec.rb",
55
+ "spec/lib/active_model/error_collecting/human_hash_reporter_spec.rb",
56
+ "spec/lib/active_model/error_collecting/human_message_formatter_spec.rb",
57
+ "spec/lib/active_model/error_collecting/human_message_reporter_spec.rb",
58
+ "spec/lib/active_model/error_collecting/machine_array_reporter_spec.rb",
59
+ "spec/lib/active_model/error_collecting/machine_hash_reporter_spec.rb",
60
+ "spec/lib/active_model/error_collecting_spec.rb",
61
+ "spec/spec_helper.rb",
62
+ "spec/support/models.rb",
63
+ "test/integration.rb"
64
+ ]
65
+ s.homepage = "http://github.com/codekitchen/active_model-better_errors"
66
+ s.licenses = ["MIT"]
67
+ s.require_paths = ["lib"]
68
+ s.rubygems_version = "1.8.24"
69
+ s.summary = "API consumable error messages with ActiveModel::Errors drop-in compatibility."
70
+
71
+ if s.respond_to? :specification_version then
72
+ s.specification_version = 3
73
+
74
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
75
+ s.add_development_dependency(%q<rake>, [">= 0"])
76
+ s.add_development_dependency(%q<rspec>, ["~> 2.12.0"])
77
+ s.add_development_dependency(%q<pry>, [">= 0"])
78
+ s.add_development_dependency(%q<yard>, [">= 0"])
79
+ s.add_development_dependency(%q<rdoc>, [">= 0"])
80
+ s.add_development_dependency(%q<bundler>, [">= 0"])
81
+ s.add_development_dependency(%q<jeweler>, [">= 0"])
82
+ s.add_development_dependency(%q<simplecov>, [">= 0"])
83
+ s.add_development_dependency(%q<bcrypt-ruby>, ["~> 3.0.0"])
84
+ s.add_development_dependency(%q<mocha>, [">= 0.12.1"])
85
+ else
86
+ s.add_dependency(%q<rake>, [">= 0"])
87
+ s.add_dependency(%q<rspec>, ["~> 2.12.0"])
88
+ s.add_dependency(%q<pry>, [">= 0"])
89
+ s.add_dependency(%q<yard>, [">= 0"])
90
+ s.add_dependency(%q<rdoc>, [">= 0"])
91
+ s.add_dependency(%q<bundler>, [">= 0"])
92
+ s.add_dependency(%q<jeweler>, [">= 0"])
93
+ s.add_dependency(%q<simplecov>, [">= 0"])
94
+ s.add_dependency(%q<bcrypt-ruby>, ["~> 3.0.0"])
95
+ s.add_dependency(%q<mocha>, [">= 0.12.1"])
96
+ end
97
+ else
98
+ s.add_dependency(%q<rake>, [">= 0"])
99
+ s.add_dependency(%q<rspec>, ["~> 2.12.0"])
100
+ s.add_dependency(%q<pry>, [">= 0"])
101
+ s.add_dependency(%q<yard>, [">= 0"])
102
+ s.add_dependency(%q<rdoc>, [">= 0"])
103
+ s.add_dependency(%q<bundler>, [">= 0"])
104
+ s.add_dependency(%q<jeweler>, [">= 0"])
105
+ s.add_dependency(%q<simplecov>, [">= 0"])
106
+ s.add_dependency(%q<bcrypt-ruby>, ["~> 3.0.0"])
107
+ s.add_dependency(%q<mocha>, [">= 0.12.1"])
108
+ end
109
+ end
110
+
@@ -0,0 +1,17 @@
1
+ require 'forwardable'
2
+
3
+ require 'active_support/core_ext'
4
+
5
+ require 'active_model/error_collecting'
6
+
7
+ require 'active_record'
8
+ require 'active_record/base'
9
+ require 'active_record/validations'
10
+
11
+ module ActiveRecord
12
+ module Validations
13
+ def errors
14
+ @errors ||= ActiveModel::ErrorCollecting::Errors.new(self)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ module ActiveModel
2
+ module ErrorCollecting
3
+ class ArrayReporter < Reporter
4
+ def to_a
5
+ raise "abstract method"
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,6 @@
1
+ class String
2
+ def ==(other)
3
+ return super other.to_s if other.is_a? ActiveModel::ErrorCollecting::ErrorMessage
4
+ super
5
+ end
6
+ end
@@ -0,0 +1,65 @@
1
+ #
2
+ # Allows included class to emulate ActiveModel::Errors class
3
+ # by defining a set of methods to delegate to facilities
4
+ # in this gem.
5
+ #
6
+ module ActiveModel
7
+ module ErrorCollecting
8
+ module Emulation
9
+ MODEL_METHODS = [
10
+ :clear, :include?, :get, :set, :delete, :[], :[]=,
11
+ :each, :size, :values, :keys, :count, :empty?, :any?,
12
+ :added?, :entries
13
+ ]
14
+
15
+ MESSAGE_REPORTER_METHODS = [
16
+ :full_messages, :full_message, :generate_message
17
+ ]
18
+
19
+ def self.included(base)
20
+ base.class_eval do
21
+ extend Forwardable
22
+ def_delegators :error_collection, *MODEL_METHODS
23
+ def_delegators :message_reporter, *MESSAGE_REPORTER_METHODS
24
+ def_delegators :hash_reporter, :to_hash
25
+ def_delegators :array_reporter, :to_a
26
+
27
+ alias_method :blank?, :empty?
28
+ alias_method :has_key?, :include?
29
+ end
30
+ end
31
+
32
+ def add_on_empty(attributes, options = {})
33
+ [attributes].flatten.each do |attribute|
34
+ value = @base.send(:read_attribute, attribute)
35
+ is_empty = value.respond_to?(:empty?) ? value.empty? : false
36
+ add(attribute, :empty, options) if value.nil? || is_empty
37
+ end
38
+ end
39
+
40
+ def add_on_blank(attributes, options = {})
41
+ [attributes].flatten.each do |attribute|
42
+ value = @base.send(:read_attribute, attribute)
43
+ add(attribute, :blank, options) if value.blank?
44
+ end
45
+ end
46
+
47
+ def add(attribute, message=nil, options = {})
48
+ if options[:strict]
49
+ error = ErrorMessage.build(attribute, message, options)
50
+ message = HumanMessageFormatter.new(@base, error).format_message
51
+ raise ActiveModel::StrictValidationFailed, full_message(attribute, message)
52
+ end
53
+ error_collection.add attribute, message, options
54
+ end
55
+
56
+ def to_xml(options={})
57
+ to_a.to_xml options.reverse_merge(:root => "errors", :skip_types => true)
58
+ end
59
+
60
+ def as_json(options=nil)
61
+ to_hash
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,86 @@
1
+ module ActiveModel
2
+ module ErrorCollecting
3
+ class ErrorCollection
4
+ include Enumerable
5
+
6
+ attr_reader :base
7
+ def initialize(base)
8
+ @base = base
9
+ @collection = {}
10
+ end
11
+
12
+ def clear
13
+ @collection.clear
14
+ end
15
+
16
+ def include?(attribute)
17
+ ( v = @collection[attribute] ) && v.any?
18
+ end
19
+
20
+ def get(attribute)
21
+ @collection[attribute]
22
+ end
23
+
24
+ def set(attribute, errors)
25
+ return delete attribute if errors.nil?
26
+ @collection[attribute] = ErrorMessageSet.new(base, attribute, errors)
27
+ end
28
+
29
+ def delete(attribute)
30
+ @collection.delete attribute
31
+ end
32
+
33
+ def [](attribute)
34
+ get(attribute.to_sym) || set(attribute.to_sym, [])
35
+ end
36
+
37
+ def []=(attribute, error)
38
+ self[attribute] << error
39
+ end
40
+
41
+ def each
42
+ @collection.each_key do |attribute|
43
+ self[attribute].each { |error_message| yield attribute, error_message }
44
+ end
45
+ end
46
+
47
+ def size
48
+ values.inject(0){ |sum, set| sum += set.size }
49
+ end
50
+ alias_method :count, :size
51
+
52
+ def values
53
+ @collection.values
54
+ end
55
+
56
+ def keys
57
+ @collection.keys
58
+ end
59
+
60
+ def to_a
61
+ array = []
62
+ @collection.each_key do |attribute|
63
+ self[attribute].each { |error_message| array << error_message }
64
+ end
65
+
66
+ array
67
+ end
68
+
69
+ def to_hash
70
+ @collection.dup
71
+ end
72
+
73
+ def empty?
74
+ size == 0
75
+ end
76
+
77
+ def add(attribute, message, options = {})
78
+ self[attribute] << [ message, options ]
79
+ end
80
+
81
+ def added?(attribute, message = nil, options = {})
82
+ self[attribute].include? ErrorMessage.build(base, attribute, message, options)
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,88 @@
1
+ module ActiveModel
2
+ module ErrorCollecting
3
+ class ErrorMessage
4
+ include Comparable
5
+ CALLBACKS_OPTIONS = [:if, :unless, :on, :allow_nil, :allow_blank, :strict]
6
+
7
+ # return the message either as nil, symbol, or string
8
+ def self.normalize(message)
9
+ case message
10
+ when nil
11
+ nil
12
+ when Symbol
13
+ message
14
+ when Proc
15
+ message.call
16
+ else
17
+ message.to_s
18
+ end
19
+ end
20
+
21
+ def self.identify(message, override)
22
+ symbol = string = nil
23
+
24
+ message = normalize message
25
+ override = normalize override
26
+
27
+ symbol = message if message.is_a?(Symbol)
28
+ symbol = override if override.is_a?(Symbol)
29
+
30
+ string = message if message.is_a?(String)
31
+ string = override if override.is_a?(String)
32
+
33
+ string = nil if string.blank?
34
+
35
+ [symbol, string]
36
+ end
37
+
38
+ def self.build(base, attribute, message, options=nil)
39
+ options = options ? options : {}
40
+ options = options.except(*CALLBACKS_OPTIONS)
41
+
42
+ symbol, string = identify message, options.delete(:message)
43
+
44
+ new(base, attribute, symbol, string, options)
45
+ end
46
+
47
+ attr_reader :base, :attribute, :type, :message, :options
48
+
49
+ def initialize(base, attribute, type, message, options = {})
50
+ @base = base
51
+ @attribute = attribute
52
+ @type = type
53
+ @message = message
54
+ @options = options
55
+ end
56
+
57
+ def <=> (other)
58
+ to_hash <=> other.to_hash
59
+ end
60
+
61
+ def to_hash
62
+ {
63
+ attribute: attribute,
64
+ type: type,
65
+ message: message,
66
+ options: options
67
+ }
68
+ end
69
+
70
+ def hash
71
+ to_hash.hash
72
+ end
73
+
74
+ def to_s
75
+ HumanMessageFormatter.new(base, self).format_message
76
+ end
77
+
78
+ def inspect
79
+ to_s.inspect
80
+ end
81
+
82
+ def ==(other)
83
+ return type == other if other.is_a?(Symbol)
84
+ to_s == other.to_s
85
+ end
86
+ end
87
+ end
88
+ end