adequate_errors 0.1.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 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: []