reform-rails 0.1.7 → 0.2.1
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 +5 -5
- data/.gitignore +4 -1
- data/.rubocop.yml +3 -0
- data/.rubocop_todo.yml +661 -0
- data/.travis.yml +11 -11
- data/CHANGES.md +23 -1
- data/Gemfile +30 -3
- data/LICENSE.txt +1 -1
- data/README.md +11 -2
- data/Rakefile +3 -1
- data/lib/reform/active_record.rb +1 -1
- data/lib/reform/form/active_model.rb +7 -4
- data/lib/reform/form/active_model/validations.rb +195 -70
- data/lib/reform/form/active_record.rb +0 -2
- data/lib/reform/form/mongoid.rb +26 -0
- data/lib/reform/form/multi_parameter_attributes.rb +5 -1
- data/lib/reform/form/orm.rb +4 -1
- data/lib/reform/form/validation/unique_validator.rb +18 -4
- data/lib/reform/mongoid.rb +4 -0
- data/lib/reform/rails.rb +1 -0
- data/lib/reform/rails/railtie.rb +22 -17
- data/lib/reform/rails/version.rb +1 -1
- data/reform-rails.gemspec +5 -12
- metadata +17 -126
- data/database.sqlite3 +0 -0
- data/gemfiles/Gemfile.rails-3.2 +0 -7
- data/gemfiles/Gemfile.rails-4.0 +0 -7
- data/gemfiles/Gemfile.rails-4.1 +0 -7
- data/gemfiles/Gemfile.rails-4.2 +0 -7
- data/log/development.log +0 -0
data/.travis.yml
CHANGED
@@ -1,18 +1,18 @@
|
|
1
1
|
language: ruby
|
2
2
|
cache: bundler
|
3
|
-
|
3
|
+
before_install:
|
4
|
+
- gem update --system
|
5
|
+
- gem install bundler
|
4
6
|
rvm:
|
5
|
-
|
6
|
-
|
7
|
-
|
7
|
+
- 2.5.5
|
8
|
+
- 2.6.3
|
9
|
+
- 2.7
|
8
10
|
services:
|
9
11
|
- mongodb
|
10
|
-
|
11
|
-
-
|
12
|
-
-
|
13
|
-
-
|
14
|
-
-
|
15
|
-
# - gemfiles/Gemfile.rails-3.1
|
16
|
-
# - gemfiles/Gemfile.rails-3.0
|
12
|
+
env:
|
13
|
+
- "RAILS_VERSION=6.0.0"
|
14
|
+
- "RAILS_VERSION=5.2.0"
|
15
|
+
- "RAILS_VERSION=5.1.0"
|
16
|
+
- "RAILS_VERSION=5.0.0"
|
17
17
|
matrix:
|
18
18
|
fast_finish: true
|
data/CHANGES.md
CHANGED
@@ -1,3 +1,25 @@
|
|
1
|
+
# 0.2.1
|
2
|
+
|
3
|
+
* Error's full_message with translation fixed thanks to [@marcelolx](https://github.com/trailblazer/reform-rails/pull/85)
|
4
|
+
|
5
|
+
# 0.2.0
|
6
|
+
|
7
|
+
* Needs Reform >= 2.3.0.
|
8
|
+
* make the inclusion of ActiveModel form builder modules optional when using dry-validation. This can be controlled via `config.reform.enable_active_model_builder_methods = true`.
|
9
|
+
* delegate `validates_each` method and allow it to be called outside a validation block.
|
10
|
+
* add `case_sensitive` option to Reform Uniqueness validation. Defaults to true.
|
11
|
+
* fix bug in uniqueness validation where form has different attribute name to column
|
12
|
+
* improve handling of persisted records in uniqueness validator
|
13
|
+
* remove params.merge! as it's deprecated in rails 5
|
14
|
+
* update to support reform 2.3, the new API means that `errors.add` is delegated to ActiveModel::Errors to support rails 5
|
15
|
+
* Fix nested form validation (#53)
|
16
|
+
* Errors supports symbol and string lookup (PR #77)
|
17
|
+
* Implement respond_to that delegates to AMV errors (PR #78)
|
18
|
+
* Drop support for activemodel before 5.0
|
19
|
+
|
20
|
+
# 0.1.8
|
21
|
+
* Drop support to mongoid < 4.
|
22
|
+
|
1
23
|
# 0.1.7 (0.1.6 Yanked)
|
2
24
|
|
3
25
|
* Fix a bug where requiring `form/active_model/validations` in a non-Rails environment wouldn't load all necessary files.
|
@@ -12,7 +34,7 @@
|
|
12
34
|
|
13
35
|
# 0.1.3
|
14
36
|
|
15
|
-
* Introduce a railtie to load either `ActiveModel::Validations` *or* `Dry::Validations`. This can be
|
37
|
+
* Introduce a railtie to load either `ActiveModel::Validations` *or* `Dry::Validations`. This can be controlled via `config.reform.validations = :dry`.
|
16
38
|
|
17
39
|
# 0.1.2
|
18
40
|
|
data/Gemfile
CHANGED
@@ -1,6 +1,33 @@
|
|
1
|
-
|
1
|
+
git_source(:github) do |repo_name|
|
2
|
+
repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
|
3
|
+
"https://github.com/#{repo_name}.git"
|
4
|
+
end
|
2
5
|
|
3
|
-
|
6
|
+
source "https://rubygems.org"
|
4
7
|
gemspec
|
5
8
|
|
6
|
-
gem
|
9
|
+
gem 'pry-byebug'
|
10
|
+
gem "minitest-line"
|
11
|
+
|
12
|
+
case ENV["GEMS_SOURCE"]
|
13
|
+
when "local"
|
14
|
+
gem "reform", path: "../reform"
|
15
|
+
when "github"
|
16
|
+
gem "reform", github: "trailblazer/reform"
|
17
|
+
end
|
18
|
+
|
19
|
+
rails_version = ENV.fetch("RAILS_VERSION", "6.0.0")
|
20
|
+
|
21
|
+
# bored of wrestling with rails...
|
22
|
+
|
23
|
+
gem("mongoid", "< 7.0") unless rails_version.include?('6.0')
|
24
|
+
|
25
|
+
|
26
|
+
gem "activerecord", "~> #{rails_version}"
|
27
|
+
gem "railties", "~> #{rails_version}"
|
28
|
+
if rails_version.include?('6.0')
|
29
|
+
gem "sqlite3", "~> 1.4"
|
30
|
+
else
|
31
|
+
gem "sqlite3", "~> 1.3", "< 1.4"
|
32
|
+
end
|
33
|
+
puts "Rails version #{rails_version}"
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -14,7 +14,7 @@ Simply don't include this gem if you don't want to use the conventional Reform/R
|
|
14
14
|
|
15
15
|
## Documentation
|
16
16
|
|
17
|
-
The [full documentation](
|
17
|
+
The [full documentation](https://trailblazer.to/2.0/gems/reform/rails.html) can be found on the Trailblazer page.
|
18
18
|
|
19
19
|
## Installation
|
20
20
|
|
@@ -24,7 +24,16 @@ Add this line to your application's Gemfile:
|
|
24
24
|
gem 'reform-rails'
|
25
25
|
```
|
26
26
|
|
27
|
-
Reform-rails needs Reform >= 2.
|
27
|
+
Reform-rails needs Reform >= 2.3.
|
28
|
+
|
29
|
+
## Contributing
|
30
|
+
|
31
|
+
By default your tests will run against rails 6.0.1.
|
32
|
+
Please ensure that you test your changes against all supported ruby and rails versions (see .travis.yml)
|
33
|
+
|
34
|
+
You can run tests for a specific version of rails by running the following:
|
35
|
+
|
36
|
+
`export RAILS_VERSION=5.0.0; bundle update; bundle exec rake test`
|
28
37
|
|
29
38
|
## License
|
30
39
|
|
data/Rakefile
CHANGED
data/lib/reform/active_record.rb
CHANGED
@@ -42,15 +42,19 @@ module Reform::Form::ActiveModel
|
|
42
42
|
|
43
43
|
# moved from reform as not applicable to dry
|
44
44
|
def validates(*args, &block)
|
45
|
-
validation(:default, inherit: true) { validates *args, &block }
|
45
|
+
validation(name: :default, inherit: true) { validates *args, &block }
|
46
46
|
end
|
47
47
|
|
48
48
|
def validate(*args, &block)
|
49
|
-
validation(:default, inherit: true) { validate *args, &block }
|
49
|
+
validation(name: :default, inherit: true) { validate *args, &block }
|
50
50
|
end
|
51
51
|
|
52
52
|
def validates_with(*args, &block)
|
53
|
-
validation(:default, inherit: true) { validates_with *args, &block }
|
53
|
+
validation(name: :default, inherit: true) { validates_with *args, &block }
|
54
|
+
end
|
55
|
+
|
56
|
+
def validates_each(*args, &block)
|
57
|
+
validation(name: :default, inherit: true) { validates_each *args, &block }
|
54
58
|
end
|
55
59
|
|
56
60
|
# Set a model name for this form if the infered is wrong.
|
@@ -85,7 +89,6 @@ module Reform::Form::ActiveModel
|
|
85
89
|
|
86
90
|
private
|
87
91
|
def active_model_name_for(string, namespace=nil)
|
88
|
-
return ::ActiveModel::Name.new(OpenStruct.new(:name => string)) if ::ActiveModel::VERSION::MAJOR == 3 and ::ActiveModel::VERSION::MINOR == 0 # TODO: remove when we drop rails 3.x.
|
89
92
|
::ActiveModel::Name.new(self, namespace, string)
|
90
93
|
end
|
91
94
|
end # ClassMethods
|
@@ -2,114 +2,239 @@ require "active_model"
|
|
2
2
|
require "reform/form/active_model"
|
3
3
|
require "uber/delegates"
|
4
4
|
|
5
|
-
module Reform
|
5
|
+
module Reform
|
6
|
+
module Form::ActiveModel
|
6
7
|
# AM::Validations for your form.
|
7
8
|
# Provides ::validates, ::validate, #validate, and #valid?.
|
8
9
|
#
|
9
10
|
# Most of this file contains unnecessary wiring to make ActiveModel's error message magic work.
|
10
11
|
# Since Rails still thinks it's a good idea to do things like object.class.human_attribute_name,
|
11
12
|
# we have some hacks in here to provide that. If it doesn't work for you, don't blame us.
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
13
|
+
module Validations
|
14
|
+
def self.included(includer)
|
15
|
+
includer.instance_eval do
|
16
|
+
include Reform::Form::ActiveModel
|
17
|
+
|
18
|
+
class << self
|
19
|
+
extend Uber::Delegates
|
20
|
+
# # Hooray! Delegate translation back to Reform's Validator class which contains AM::Validations.
|
21
|
+
delegates :active_model_really_sucks, :human_attribute_name, :lookup_ancestors, :i18n_scope # Rails 3.1.
|
22
|
+
|
23
|
+
def validation_group_class
|
24
|
+
Group
|
25
|
+
end
|
21
26
|
|
22
|
-
|
23
|
-
|
27
|
+
# this is to allow calls like Form::human_attribute_name (note that this is on the CLASS level) to be resolved.
|
28
|
+
# those calls happen when adding errors in a custom validation method, which is defined on the form (as an instance method).
|
29
|
+
def active_model_really_sucks
|
30
|
+
Class.new(Validator).tap do |v|
|
31
|
+
v.model_name = model_name
|
32
|
+
end
|
33
|
+
end
|
24
34
|
end
|
35
|
+
end # ::included
|
36
|
+
end
|
25
37
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
38
|
+
# The concept of "composition" has still not arrived in Rails core and they rely on 400 methods being
|
39
|
+
# available in one object. This is why we need to provide parts of the I18N API in the form.
|
40
|
+
def read_attribute_for_validation(name)
|
41
|
+
send(name)
|
42
|
+
end
|
43
|
+
|
44
|
+
def initialize(*)
|
45
|
+
super
|
46
|
+
@amv_errors = ActiveModel::Errors.new(self)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Problem with this method is, it's being used in two completely different contexts: Once to add errors in validations,
|
50
|
+
# and second to expose errors for presentation.
|
51
|
+
def errors(*args)
|
52
|
+
@amv_errors
|
53
|
+
end
|
54
|
+
|
55
|
+
def custom_errors
|
56
|
+
# required to keep update the ActiveModel::Errors#details used to test for
|
57
|
+
# added errors ActiveModel::Errors#added? and needs to be inside this block!
|
58
|
+
super.each do |custom_error|
|
59
|
+
errors = custom_error.errors
|
60
|
+
# CustomError build always the errors with an hash where the value is an array
|
61
|
+
errors.values.first.each do |value|
|
62
|
+
@amv_errors.add(errors.keys.first, value)
|
32
63
|
end
|
33
64
|
end
|
34
|
-
end
|
35
|
-
end
|
65
|
+
end
|
36
66
|
|
37
|
-
|
38
|
-
|
39
|
-
end
|
67
|
+
def validate!(params, pointers=[])
|
68
|
+
@amv_errors = ActiveModel::Errors.new(self)
|
40
69
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
end
|
70
|
+
super.tap do
|
71
|
+
# @fran: super ugly hack thanks to the shit architecture of AMV. let's drop it in 3.0 and move on!
|
72
|
+
all_errors = @result.to_results
|
73
|
+
nested_errors = @result.instance_variable_get(:@failure)
|
46
74
|
|
47
|
-
|
48
|
-
|
49
|
-
|
75
|
+
@result = Reform::Contract::Result.new(all_errors, [nested_errors].compact)
|
76
|
+
|
77
|
+
@amv_errors = Result::ResultErrors.new(@result, self, @result.success?, @amv_errors)
|
78
|
+
end
|
79
|
+
@result
|
50
80
|
end
|
51
81
|
|
52
|
-
|
53
|
-
|
82
|
+
class Group
|
83
|
+
def initialize(*)
|
84
|
+
@validations = Class.new(Reform::Form::ActiveModel::Validations::Validator)
|
85
|
+
end
|
86
|
+
|
87
|
+
extend Uber::Delegates
|
88
|
+
delegates :@validations, :validates, :validate, :validates_with, :validate_with, :validates_each
|
54
89
|
|
55
|
-
|
56
|
-
|
57
|
-
|
90
|
+
def call(form)
|
91
|
+
validator = @validations.new(form)
|
92
|
+
validator.instance_variable_set(:@errors, form.errors)
|
93
|
+
success = validator.valid? # run the validations.
|
58
94
|
|
59
|
-
|
60
|
-
errors.add(name, error)
|
95
|
+
Result.new(success, validator.errors.messages)
|
61
96
|
end
|
62
97
|
end
|
63
|
-
end
|
64
98
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
99
|
+
# The idea here to mimic Dry.RB's Result API.
|
100
|
+
class Result < Hash # FIXME; should this be AMV::Errors?
|
101
|
+
def initialize(success, hash)
|
102
|
+
super()
|
103
|
+
@success = success
|
104
|
+
hash.each { |k,v| self[k] = v }
|
105
|
+
end
|
71
106
|
|
72
|
-
|
73
|
-
|
74
|
-
@_active_model_sucks ||= ActiveModel::Name.new(Reform::Form, nil, "Reform::Form")
|
107
|
+
def success?
|
108
|
+
@success
|
75
109
|
end
|
76
110
|
|
77
|
-
def
|
78
|
-
|
111
|
+
def failure?
|
112
|
+
! success?
|
79
113
|
end
|
80
114
|
|
81
|
-
def
|
82
|
-
|
115
|
+
def messages
|
116
|
+
self
|
83
117
|
end
|
84
118
|
|
85
|
-
#
|
86
|
-
def
|
119
|
+
# DISCUSS @FRAN: not sure this is 100% compatible with AMV::Errors?
|
120
|
+
def errors
|
121
|
+
self
|
87
122
|
end
|
88
123
|
|
89
|
-
|
124
|
+
class ResultErrors < ::Reform::Contract::Result::Errors # to expose via #errors. i hate it.
|
125
|
+
def initialize(a, b, success, amv_errors)
|
126
|
+
super(a, b)
|
127
|
+
@success = success
|
128
|
+
@amv_errors = amv_errors
|
129
|
+
end
|
130
|
+
|
131
|
+
def empty?
|
132
|
+
@success
|
133
|
+
end
|
134
|
+
|
135
|
+
def [](k)
|
136
|
+
super(k.to_sym) || []
|
137
|
+
end
|
138
|
+
|
139
|
+
# rails expects this to return a stringified hash of the messages
|
140
|
+
def to_s
|
141
|
+
messages.to_s
|
142
|
+
end
|
143
|
+
|
144
|
+
def add(key, error_text)
|
145
|
+
# use rails magic to get the correct error_text and make sure we still update details and fields
|
146
|
+
text = @amv_errors.add(key, error_text)
|
147
|
+
|
148
|
+
# using error_text instead of text to either keep the symbol which will be
|
149
|
+
# magically replaced with the translate or directly the string - this is also
|
150
|
+
# required otherwise in the custom_errors method we will add the actual message in the
|
151
|
+
# ActiveModel::Errors#details which is not correct if a symbol was passed here
|
152
|
+
Reform::Contract::CustomError.new(key, error_text, @result.to_results)
|
153
|
+
|
154
|
+
# but since messages method is actually already defined in `Reform::Contract::Result::Errors
|
155
|
+
# we need to update the @dotted_errors instance variable to add or merge a new error
|
156
|
+
@dotted_errors.key?(key) ? @dotted_errors[key] |= text : @dotted_errors[key] = text
|
157
|
+
instance_variable_set(:@dotted_errors, @dotted_errors)
|
158
|
+
end
|
159
|
+
|
160
|
+
def method_missing(m, *args, &block)
|
161
|
+
@amv_errors.send(m, *args, &block) # send all methods to the AMV errors, even privates.
|
162
|
+
end
|
163
|
+
|
164
|
+
def respond_to?(method)
|
165
|
+
@amv_errors.respond_to?(method) ? true : super
|
166
|
+
end
|
167
|
+
|
168
|
+
def full_messages
|
169
|
+
base_errors = @amv_errors.full_messages
|
170
|
+
form_fields = @amv_errors.instance_variable_get(:@base).instance_variable_get(:@fields)
|
171
|
+
nested_errors = full_messages_for_nested_fields(form_fields)
|
172
|
+
|
173
|
+
[base_errors, nested_errors].flatten.compact
|
174
|
+
end
|
175
|
+
|
176
|
+
private
|
177
|
+
|
178
|
+
def full_messages_for_nested_fields(form_fields)
|
179
|
+
form_fields.map { |field| full_messages_for_twin(field[1]) }
|
180
|
+
end
|
181
|
+
|
182
|
+
def full_messages_for_twin(object)
|
183
|
+
return get_collection_errors(object) if object.is_a? Disposable::Twin::Collection
|
184
|
+
return get_amv_errors(object) if object.is_a? Disposable::Twin
|
185
|
+
|
186
|
+
nil
|
187
|
+
end
|
188
|
+
|
189
|
+
def get_collection_errors(twin_collection)
|
190
|
+
twin_collection.map { |twin| get_amv_errors(twin) }
|
191
|
+
end
|
192
|
+
|
193
|
+
def get_amv_errors(object)
|
194
|
+
object.instance_variable_get(:@amv_errors).full_messages
|
195
|
+
end
|
90
196
|
end
|
91
197
|
end
|
92
198
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
199
|
+
# Validator is the validatable object. On the class level, we define validations,
|
200
|
+
# on instance, it exposes #valid?.
|
201
|
+
require "delegate"
|
202
|
+
class Validator < SimpleDelegator
|
203
|
+
# current i18n scope: :activemodel.
|
204
|
+
include ActiveModel::Validations
|
205
|
+
|
206
|
+
class << self
|
207
|
+
def model_name
|
208
|
+
@_active_model_sucks ||= ActiveModel::Name.new(Reform::Form, nil, "Reform::Form")
|
209
|
+
end
|
210
|
+
|
211
|
+
def model_name=(name)
|
212
|
+
@_active_model_sucks = name
|
213
|
+
end
|
214
|
+
|
215
|
+
def validates(*args, &block)
|
216
|
+
super(*Declarative::DeepDup.(args), &block)
|
217
|
+
end
|
97
218
|
|
98
|
-
|
99
|
-
|
219
|
+
# Prevent AM:V from mutating the validator class
|
220
|
+
def attr_reader(*)
|
221
|
+
end
|
222
|
+
|
223
|
+
def attr_writer(*)
|
224
|
+
end
|
225
|
+
end
|
100
226
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
227
|
+
def initialize(form)
|
228
|
+
super(form)
|
229
|
+
self.class.model_name = form.model_name
|
230
|
+
end
|
105
231
|
|
106
|
-
|
107
|
-
|
232
|
+
def method_missing(m, *args, &block)
|
233
|
+
__getobj__.send(m, *args, &block) # send all methods to the form, even privates.
|
234
|
+
end
|
108
235
|
end
|
109
236
|
end
|
110
|
-
end
|
111
237
|
|
112
|
-
|
113
|
-
include Reform::Form::Errors::Merge
|
238
|
+
|
114
239
|
end
|
115
240
|
end
|