adequate_errors 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6292086a3e4cbd867447484ac68fbc5a68802a63
4
+ data.tar.gz: 862ebfd497a58618225482f232b05b08b31c09aa
5
+ SHA512:
6
+ metadata.gz: 5193cc15cf703b06b4fbe3e72b894ab25eb757b6defc60411cd5dd67f7518f2cd5aecb2ea3348505ce049d07d9413f1184b95cbfe4ba02c620099bf03081f3bf
7
+ data.tar.gz: 037de8bbfd563dfa981e5ab22ea581d51d8ba693717223674cb91bed5eec95d23812f75a3e671f57931c5ce2736eb6fb4c1528ab3977e30bc198f9054486179d
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.ruby-version
2
+ /.bundle/
3
+ /.yardoc
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --no-private
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in adequate_errors.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,37 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ adequate_errors (0.1.0)
5
+ activemodel
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ activemodel (5.1.4)
11
+ activesupport (= 5.1.4)
12
+ activesupport (5.1.4)
13
+ concurrent-ruby (~> 1.0, >= 1.0.2)
14
+ i18n (~> 0.7)
15
+ minitest (~> 5.1)
16
+ tzinfo (~> 1.1)
17
+ builder (3.2.3)
18
+ concurrent-ruby (1.0.5)
19
+ i18n (0.9.1)
20
+ concurrent-ruby (~> 1.0)
21
+ minitest (5.10.3)
22
+ rake (10.5.0)
23
+ thread_safe (0.3.6)
24
+ tzinfo (1.2.4)
25
+ thread_safe (~> 0.1)
26
+
27
+ PLATFORMS
28
+ ruby
29
+
30
+ DEPENDENCIES
31
+ adequate_errors!
32
+ builder (~> 3.2.3)
33
+ bundler (~> 1.16.a)
34
+ rake (~> 10.0)
35
+
36
+ BUNDLED WITH
37
+ 1.16.0.pre.3
data/README.md ADDED
@@ -0,0 +1,72 @@
1
+ # AdequateErrors
2
+
3
+ Overcoming limitation of Rails model errors API:
4
+
5
+ * more expressive `where` query
6
+ * turn off message's attribute prefix.
7
+ * lazy evaluation of messages
8
+
9
+ ## Introduction
10
+
11
+ Rails errors API is simple to use, but can be inadequate when coping with more complex requirements.
12
+
13
+ The existing API was originally a collection of message strings without much meta data, making it very restrictive. Though `details` hash was added later for storing meta information, many fundamental issues can not be fixed without altering the API and the architecture.
14
+
15
+ This gem redesigned the API, placing it in its own object, co-existing with existing Rails API. Thus nothing will break, allowing you to migrate the code one at a time.
16
+
17
+ ## Quick start
18
+
19
+ To access the AdequateErrors object, call:
20
+
21
+ model.errors.adequate
22
+
23
+ From this object, many convenience methods are provided, for example this would return an array of AdequateErrors::Error objects, which matches the where query.
24
+
25
+ model.errors.adequate.where(attribute:'title', :type => :too_short, length: 5)
26
+
27
+ The following prints out each error's full message one by one:
28
+
29
+ model.errors.adequate.each {|error| puts error.message }
30
+
31
+ The following returns an array of all message strings:
32
+
33
+ model.errors.adequate.messages
34
+
35
+ For full documentation, please see http://www.rubydoc.info/github/lulalala/adequate_errors
36
+
37
+ ## Key difference to Rails own errors API
38
+
39
+ Errors are stored as Ruby objects instead of message strings, this makes more fine-grained query possible.
40
+
41
+ Error messages are evaluated lazily, which means it can be rendered in a different locale at view rendering time.
42
+
43
+ The messages in the locale file are looked up in its own `adequate_errors` namespace, for example:
44
+
45
+ en:
46
+ adequate_errors:
47
+ messages:
48
+ invalid: "%{attribute} is invalid"
49
+
50
+ Note that each message by default has the `attribute` prefix. This allow easy removal of attribute prefix by overriding each message in the locale file. You no longer need to attach errors to `:base` for that purpose. This allows prefix to be changed per language.
51
+
52
+ Calls to Rails' API are synced to AdequateErrors object, but not the reverse.
53
+
54
+ ## Migration Note
55
+
56
+ Deprecated methods such as `[]=`, `get` and `set` are not supported, therefore calling those methods will not sync to AdequateErrors.
57
+
58
+ The gem is developed from ActiveModel 5.1, but it should work with earlier versions.
59
+
60
+ ## We want to hear your issues too
61
+
62
+ If you also have issues with exsting API, share it by filing that issue here.
63
+
64
+ We collect use cases in issues and analyze the problem in wiki (publicly editable):
65
+
66
+ [So come to our wiki, see what's going on, and join us!](https://github.com/lulalala/adequate_errors/wiki)
67
+
68
+ ---
69
+
70
+ This repo was called Rails Error API Redesign Initiative.
71
+ This is a fan project and is not affiliated to Rails Core team,
72
+ but my wish is that one day this can be adapted into Rails too.
data/Rakefile ADDED
@@ -0,0 +1,25 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ dir = File.dirname(__FILE__)
5
+
6
+ task default: :test
7
+
8
+ task :package
9
+
10
+ Rake::TestTask.new do |t|
11
+ t.libs << "test"
12
+ t.test_files = Dir.glob("#{dir}/test/cases/**/*_test.rb")
13
+ t.warning = true
14
+ t.verbose = true
15
+ t.ruby_opts = ["--dev"] if defined?(JRUBY_VERSION)
16
+ end
17
+
18
+ namespace :test do
19
+ task :isolated do
20
+ Dir.glob("#{dir}/test/**/*_test.rb").all? do |file|
21
+ sh(Gem.ruby, "-w", "-I#{dir}/lib", "-I#{dir}/test", file)
22
+ end || raise("Failures")
23
+ end
24
+ end
25
+
@@ -0,0 +1,27 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "adequate_errors/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "adequate_errors"
8
+ spec.version = AdequateErrors::VERSION
9
+ spec.authors = ["lulalala"]
10
+ spec.email = ["mark@goodlife.tw"]
11
+
12
+ spec.summary = %q{Object-Oriented ActiveModel errors interface}
13
+ spec.homepage = "https://github.com/lulalala/adequate_errors/"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
16
+ f.match(%r{^(test|spec|features)/})
17
+ end
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_dependency "activemodel"
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.16.a"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "builder", "~> 3.2.3"
27
+ end
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "adequate_errors"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,129 @@
1
+ module AdequateErrors
2
+ # Represents one single error
3
+ # @!attribute [r] base
4
+ # @return [ActiveModel::Base] the object which the error belongs to
5
+ # @!attribute [r] attribute
6
+ # @return [Symbol] attribute of the object which the error belongs to
7
+ # @!attribute [r] type
8
+ # @return [Symbol] error's type
9
+ # @!attribute [r] options
10
+ # @return [Hash] additional options
11
+ class Error
12
+ def initialize(base, attribute, type, options = {})
13
+ @base = base
14
+ @attribute = attribute
15
+ @type = type
16
+ @options = options
17
+ end
18
+
19
+ attr_reader :base, :attribute, :type, :options
20
+
21
+ # Full message of the error.
22
+ #
23
+ # === Key differences to Rails vanilla errors
24
+ #
25
+ # ==== 1. Flexible positioning of attribute name interpolation
26
+ #
27
+ # In Rails, errors' full messages are always prefixed with attribute name,
28
+ # and if prefix is not wanted, developer often adds error to the `base' attribute instead.
29
+ # This can be unreasonable in different languages or special business requirements.
30
+ #
31
+ # AdequateErrors leaves the attribute placement to the developer.
32
+ # For each error message in the locale file, the %{attribute} indicates placement of the attribute.
33
+ # The same error can have prefix in English, and be prefix-less in Russian.
34
+ # If no prefix is needed, one should update the locale file accordingly,
35
+ #
36
+ # ==== 2. Message evaluated lazily
37
+ #
38
+ # In Rails, error message is evaluated during the `add` call.
39
+ # AdequateErrors evaluates message lazily at `message` call instead,
40
+ # so one can change message locale after model has been validated.
41
+ #
42
+ # === Order of I18n lookup:
43
+ #
44
+ # Error messages are first looked up in <tt>activemodel.adequate_errors.models.MODEL.attributes.ATTRIBUTE.MESSAGE</tt>,
45
+ # if it's not there, it's looked up in <tt>activemodel.adequate_errors.models.MODEL.MESSAGE</tt> and if
46
+ # that is not there also, it returns the translation of the default message
47
+ # (e.g. <tt>activemodel.errors.messages.MESSAGE</tt>). The translated model
48
+ # name, translated attribute name and the value are available for
49
+ # interpolation.
50
+ #
51
+ # When using inheritance in your models, it will check all the inherited
52
+ # models too, but only if the model itself hasn't been found. Say you have
53
+ # <tt>class Admin < User; end</tt> and you wanted the translation for
54
+ # the <tt>:blank</tt> error message for the <tt>title</tt> attribute,
55
+ # it looks for these translations:
56
+ #
57
+ # * <tt>activemodel.adequate_errors.models.admin.attributes.title.blank</tt>
58
+ # * <tt>activemodel.adequate_errors.models.admin.blank</tt>
59
+ # * <tt>activemodel.adequate_errors.models.user.attributes.title.blank</tt>
60
+ # * <tt>activemodel.adequate_errors.models.user.blank</tt>
61
+ # * any default you provided through the +options+ hash (in the <tt>activemodel.adequate_errors</tt> scope)
62
+ # * <tt>activemodel.adequate_errors.messages.blank</tt>
63
+ # * <tt>adequate_errors.attributes.title.blank</tt>
64
+ # * <tt>adequate_errors.messages.blank</tt>
65
+ def message
66
+ if @options[:message].is_a?(Symbol)
67
+ type = @options.delete(:message)
68
+ else
69
+ type = @type
70
+ end
71
+
72
+ if @base.class.respond_to?(:i18n_scope)
73
+ i18n_scope = @base.class.i18n_scope.to_s
74
+ defaults = @base.class.lookup_ancestors.flat_map do |klass|
75
+ [ :"#{i18n_scope}.adequate_errors.models.#{klass.model_name.i18n_key}.attributes.#{attribute}.#{type}",
76
+ :"#{i18n_scope}.adequate_errors.models.#{klass.model_name.i18n_key}.#{type}" ]
77
+ end
78
+ defaults << :"#{i18n_scope}.adequate_errors.messages.#{type}"
79
+ else
80
+ defaults = []
81
+ end
82
+
83
+ defaults << :"adequate_errors.attributes.#{attribute}.#{type}"
84
+ defaults << :"adequate_errors.messages.#{type}"
85
+
86
+ key = defaults.shift
87
+ defaults = @options.delete(:message) if @options[:message]
88
+ value = (attribute != :base ? @base.send(:read_attribute_for_validation, attribute) : nil)
89
+
90
+ i18n_options = {
91
+ default: defaults,
92
+ model: @base.model_name.human,
93
+ attribute: humanized_attribute,
94
+ value: value,
95
+ object: @base
96
+ }.merge!(@options)
97
+
98
+ I18n.translate(key, i18n_options)
99
+ end
100
+
101
+ # @param (see Errors#where)
102
+ # @return [Boolean] whether error matches the params
103
+ def match?(params)
104
+ if params.key?(:attribute) && @attribute != params[:attribute]
105
+ return false
106
+ end
107
+
108
+ if params.key?(:type) && @type != params[:type]
109
+ return false
110
+ end
111
+
112
+ (params.keys - [:attribute, :type]).each do |key|
113
+ if @options[key] != params[key]
114
+ return false
115
+ end
116
+ end
117
+
118
+ true
119
+ end
120
+
121
+ private
122
+
123
+ def humanized_attribute
124
+ default = @attribute.to_s.tr(".", "_").humanize
125
+ @base.class.human_attribute_name(@attribute, default: default)
126
+ end
127
+
128
+ end
129
+ end
@@ -0,0 +1,95 @@
1
+ require 'active_model/errors'
2
+ require 'forwardable'
3
+ require 'adequate_errors/error'
4
+
5
+ module AdequateErrors
6
+ # Collection of {Error} objects.
7
+ # Provides convenience methods to access these errors.
8
+ # It is accessed via +model.errors.adequate+
9
+ class Errors
10
+ include Enumerable
11
+
12
+ extend Forwardable
13
+ def_delegators :@errors, :each, :size, :clear, :blank?, :empty?, *Enumerable.instance_methods(false)
14
+
15
+ # @param base [ActiveModel::Base]
16
+ def initialize(base)
17
+ @base = base
18
+ @errors = []
19
+ end
20
+
21
+ # Delete errors of attribute
22
+ def delete(attribute)
23
+ @errors.delete_if do |error|
24
+ error.attribute == attribute
25
+ end
26
+ end
27
+
28
+ # Adds error.
29
+ # More than one error can be added to the same `attribute`.
30
+ # If no `type` is supplied, `:invalid` is assumed.
31
+ #
32
+ # @param attribute [Symbol] attribute that the error belongs to
33
+ # @param type [Symbol] error's type, defaults to `:invalid`.
34
+ # As convenience, if type is String/Proc/Lambda,
35
+ # it will be moved to `options[:message]`,
36
+ # and type itself will be changed to the default `:invalid`.
37
+ # @param options [Hash] extra conditions such as interpolated value
38
+ def add(attribute, type = :invalid, options = {})
39
+
40
+ if !type.is_a? Symbol
41
+ options[:message] = type
42
+ type = :invalid
43
+ end
44
+
45
+ @errors.append(::AdequateErrors::Error.new(@base, attribute, type, options))
46
+ end
47
+
48
+ # @return [Array(String)] all error messages
49
+ def messages
50
+ @errors.map(&:message)
51
+ end
52
+
53
+ # Convenience method to fetch error messages filtered by where condition.
54
+ # @param params [Hash] filter condition, see {#where} for details.
55
+ # @return [Array(String)] error messages
56
+ def messages_for(params)
57
+ where(params).map(&:message)
58
+ end
59
+
60
+ # @param params [Hash]
61
+ # filter condition
62
+ # The most common keys are +:attribute+ and +:type+,
63
+ # but other custom keys given during {#add} can also be used.
64
+ # If params is empty, all errors are returned.
65
+ # @option params [Symbol] :attribute Filtering on attribute the error belongs to
66
+ # @option params [Symbol] :type Filter on type of error
67
+ #
68
+ # @return [Array(AdequateErrors::Error)] matching {Error}.
69
+ def where(params)
70
+ return @errors.dup if params.blank?
71
+
72
+ @errors.select {|error|
73
+ error.match?(params)
74
+ }
75
+ end
76
+
77
+ # @return [Boolean] whether the given attribute contains error.
78
+ def include?(attribute)
79
+ @errors.any?{|error| error.attribute == attribute }
80
+ end
81
+
82
+ # @return [Hash] attributes with their error messages
83
+ def to_hash
84
+ hash = {}
85
+ @errors.each do |error|
86
+ if hash.has_key?(error.attribute)
87
+ hash[error.attribute] << error.message
88
+ else
89
+ hash[error.attribute] = [error.message]
90
+ end
91
+ end
92
+ hash
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,54 @@
1
+ require "active_model/errors"
2
+ require "adequate_errors/errors"
3
+
4
+ module AdequateErrors
5
+ # Wraps around Rails' errors object, intercepting its state changes
6
+ # in order to update AdequateErrors' errors object.
7
+ module Interceptor
8
+ def initialize(base)
9
+ rails_errors = super
10
+ @adequate_errors = ::AdequateErrors::Errors.new(base)
11
+ rails_errors
12
+ end
13
+
14
+ def clear
15
+ super
16
+ @adequate_errors.clear
17
+ end
18
+
19
+ def delete(key)
20
+ super
21
+ @adequate_errors.delete(key)
22
+ end
23
+
24
+ def add(attribute, message = :invalid, options = {})
25
+ adequate_options = options.dup
26
+
27
+ messages = super
28
+
29
+ if options.has_key?(:message) && !options[:message].is_a?(Symbol)
30
+ adequate_options[:message] = full_message(attribute, messages.last)
31
+ end
32
+
33
+ adequate_message = if !message.is_a?(Symbol)
34
+ full_message(attribute, messages.last)
35
+ else
36
+ message
37
+ end
38
+
39
+ @adequate_errors.add(attribute, adequate_message, adequate_options)
40
+ end
41
+
42
+ # Accessor
43
+ def adequate
44
+ @adequate_errors
45
+ end
46
+ end
47
+ end
48
+
49
+ # @private
50
+ module ActiveModel
51
+ class Errors
52
+ prepend AdequateErrors::Interceptor
53
+ end
54
+ end
@@ -0,0 +1,33 @@
1
+ en:
2
+ adequate_errors:
3
+ # The values :model, :attribute and :value are always available for interpolation
4
+ # The value :count is available when applicable. Can be used for pluralization.
5
+ messages:
6
+ model_invalid: "Validation failed: %{errors}"
7
+ inclusion: "%{attribute} is not included in the list"
8
+ exclusion: "%{attribute} is reserved"
9
+ invalid: "%{attribute} is invalid"
10
+ confirmation: "%{attribute} doesn't match %{attribute}"
11
+ accepted: "%{attribute} must be accepted"
12
+ empty: "%{attribute} can't be empty"
13
+ blank: "%{attribute} can't be blank"
14
+ present: "%{attribute} must be blank"
15
+ too_long:
16
+ one: "%{attribute} is too long (maximum is 1 character)"
17
+ other: "%{attribute} is too long (maximum is %{count} characters)"
18
+ too_short:
19
+ one: "%{attribute} is too short (minimum is 1 character)"
20
+ other: "%{attribute} is too short (minimum is %{count} characters)"
21
+ wrong_length:
22
+ one: "%{attribute} is the wrong length (should be 1 character)"
23
+ other: "%{attribute} is the wrong length (should be %{count} characters)"
24
+ not_a_number: "%{attribute} is not a number"
25
+ not_an_integer: "%{attribute} must be an integer"
26
+ greater_than: "%{attribute} must be greater than %{count}"
27
+ greater_than_or_equal_to: "%{attribute} must be greater than or equal to %{count}"
28
+ equal_to: "%{attribute} must be equal to %{count}"
29
+ less_than: "%{attribute} must be less than %{count}"
30
+ less_than_or_equal_to: "%{attribute} must be less than or equal to %{count}"
31
+ other_than: "%{attribute} must be other than %{count}"
32
+ odd: "%{attribute} must be odd"
33
+ even: "%{attribute} must be even"
@@ -0,0 +1,3 @@
1
+ module AdequateErrors
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,6 @@
1
+ require "adequate_errors/version"
2
+ require "adequate_errors/interceptor"
3
+
4
+ ActiveSupport.on_load(:i18n) do
5
+ I18n.load_path << File.dirname(__FILE__) + "/adequate_errors/locale/en.yml"
6
+ end
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: adequate_errors
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - lulalala
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-11-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activemodel
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 1.16.a
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 1.16.a
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: builder
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 3.2.3
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 3.2.3
69
+ description:
70
+ email:
71
+ - mark@goodlife.tw
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".yardopts"
78
+ - Gemfile
79
+ - Gemfile.lock
80
+ - README.md
81
+ - Rakefile
82
+ - adequate_errors.gemspec
83
+ - bin/console
84
+ - bin/setup
85
+ - lib/adequate_errors.rb
86
+ - lib/adequate_errors/error.rb
87
+ - lib/adequate_errors/errors.rb
88
+ - lib/adequate_errors/interceptor.rb
89
+ - lib/adequate_errors/locale/en.yml
90
+ - lib/adequate_errors/version.rb
91
+ homepage: https://github.com/lulalala/adequate_errors/
92
+ licenses: []
93
+ metadata: {}
94
+ post_install_message:
95
+ rdoc_options: []
96
+ require_paths:
97
+ - lib
98
+ required_ruby_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ requirements: []
109
+ rubyforge_project:
110
+ rubygems_version: 2.6.13
111
+ signing_key:
112
+ specification_version: 4
113
+ summary: Object-Oriented ActiveModel errors interface
114
+ test_files: []