reform 2.0.0.rc1 → 2.0.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +6 -4
- data/CHANGES.md +3 -0
- data/Gemfile +1 -1
- data/README.md +47 -3
- data/database.sqlite3 +0 -0
- data/gemfiles/Gemfile.rails-4.1 +8 -0
- data/gemfiles/Gemfile.rails-4.2 +8 -0
- data/lib/reform/active_record.rb +1 -0
- data/lib/reform/contract.rb +7 -11
- data/lib/reform/contract/errors.rb +1 -15
- data/lib/reform/contract/validate.rb +13 -18
- data/lib/reform/form/active_model.rb +5 -0
- data/lib/reform/form/active_model/validations.rb +66 -0
- data/lib/reform/form/active_record.rb +2 -23
- data/lib/reform/form/lotus.rb +55 -0
- data/lib/reform/form/mongoid.rb +37 -0
- data/lib/reform/form/orm.rb +26 -0
- data/lib/reform/form/validation/unique_validator.rb +5 -0
- data/lib/reform/mongoid.rb +4 -0
- data/lib/reform/rails.rb +7 -3
- data/lib/reform/version.rb +1 -1
- data/reform.gemspec +2 -2
- data/test/active_model_test.rb +13 -12
- data/test/active_record_test.rb +0 -3
- data/test/errors_test.rb +9 -7
- data/test/form_option_test.rb +1 -1
- data/test/lotus_test.rb +150 -0
- data/test/mongoid_test.rb +311 -0
- data/test/reform_test.rb +14 -11
- data/test/test_helper.rb +13 -0
- data/test/{uniqueness_test.rb → unique_test.rb} +0 -0
- data/test/validate_test.rb +2 -11
- metadata +38 -27
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a87860e43b6e321c13ae641903aa1a6b098c2ec1
|
4
|
+
data.tar.gz: 50d863fbf7d3e5ba9ea7eb0ea2d46a073e7cf458
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6a3171c4d74d852e77a88d817b886515fbe2c0f469e9f54abb3e53efd7b237013010d4b1c404b382c5e068294bd5747c9bd61061fdc511c18db8507bfff5630c
|
7
|
+
data.tar.gz: 12f688ec7d485fafd29924d91c5dd59862ff3fb6f23c463a3b4e6be2aaddaa6d7381e706f318029841509bfd52571d4b462d6eead389c976a3437085b6053f84
|
data/.travis.yml
CHANGED
@@ -1,15 +1,17 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
3
|
- 2.2.0
|
4
|
-
- 2.1.5
|
5
|
-
- 2.0.0
|
6
4
|
- 1.9.3
|
5
|
+
services:
|
6
|
+
- mongodb
|
7
7
|
gemfile:
|
8
|
+
- gemfiles/Gemfile.rails-4.2
|
9
|
+
- gemfiles/Gemfile.rails-4.1
|
8
10
|
- gemfiles/Gemfile.rails-4.0
|
9
11
|
- gemfiles/Gemfile.rails-3.2
|
10
12
|
- gemfiles/Gemfile.rails-3.1
|
11
|
-
- gemfiles/Gemfile.rails-3.0
|
13
|
+
# - gemfiles/Gemfile.rails-3.0
|
12
14
|
matrix:
|
13
15
|
fast_finish: true
|
14
16
|
allow_failures:
|
15
|
-
- rvm:
|
17
|
+
- rvm: 1.9.3
|
data/CHANGES.md
CHANGED
@@ -14,6 +14,9 @@ you don't need to know about forms anymore, the twin handles that using #insert.
|
|
14
14
|
* With `Composition` included, `Form#model` would give you a composition object. You can grab that using `Form#mapper` now.
|
15
15
|
* `Form#update!` is deprecated. It still works but will remind you to override `#present!` or use pre-populators as [described here](http://trailblazerb.org/gems/reform/prepopulator.html) and in the Trailblazer book, chapter "Nested Forms".
|
16
16
|
|
17
|
+
* Forms do not `include ActiveModel::Validations` anymore. This has polluted the entire gem and is not encapsulated in `Validator`. Consider using Lotus Validations instead.
|
18
|
+
* Validation inheritance with `ActiveModel::Validations` is broken with Rails 3.2 and 4.0. Update Rails or use the `Lotus` validations.
|
19
|
+
|
17
20
|
## 1.2.6
|
18
21
|
|
19
22
|
* Added `:prepopulate` to fill out form properties for presentation. Note that you need to call `Form#prepopulate!` to trigger the prepopulation.
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -40,7 +40,7 @@ Forms have a ridiculously simple API with only a handful of public methods.
|
|
40
40
|
3. `#errors` returns validation messages in a classic ActiveModel style.
|
41
41
|
4. `#sync` writes form data back to the model. This will only use setter methods on the model(s).
|
42
42
|
5. `#save` (optional) will call `#save` on the model and nested models. Note that this implies a `#sync` call.
|
43
|
-
6. `#
|
43
|
+
6. `#prepopulate!` (optional) will run pre-population hooks to "fill out" your form before rendering.
|
44
44
|
|
45
45
|
In addition to the main API, forms expose accessors to the defined properties. This is used for rendering or manual operations.
|
46
46
|
|
@@ -77,7 +77,7 @@ Your `@form` is now ready to be rendered, either do it yourself or use something
|
|
77
77
|
|
78
78
|
Nested forms and collections can be easily rendered with `fields_for`, etc. Note that you no longer pass the model to the form builder, but the Reform instance.
|
79
79
|
|
80
|
-
Optionally, you might want to use the
|
80
|
+
Optionally, you might want to use the `#prepopulate!` method to pre-populate fields and prepare the form for rendering.
|
81
81
|
|
82
82
|
|
83
83
|
## Validation
|
@@ -283,6 +283,30 @@ Add this line to your Gemfile:
|
|
283
283
|
gem 'reform'
|
284
284
|
```
|
285
285
|
|
286
|
+
Reform works fine with Rails 3.1-4.2. However, inheritance of validations with `ActiveModel::Validations` is broken in Rails 3.2 and 4.0.
|
287
|
+
|
288
|
+
Since Reform 2.0 you need to specify which **validation backend** you want to use (unless you're in a Rails environment where ActiveModel will be used).
|
289
|
+
|
290
|
+
To use ActiveModel (not recommended as it doesn't support removing validations).
|
291
|
+
|
292
|
+
```ruby
|
293
|
+
require "reform/form/active_model/validations"
|
294
|
+
Reform::Form.class_eval do
|
295
|
+
include Reform::Form::ActiveModel::Validations
|
296
|
+
end
|
297
|
+
```
|
298
|
+
|
299
|
+
To use Lotus validations (recommended).
|
300
|
+
|
301
|
+
```ruby
|
302
|
+
require "reform/form/lotus"
|
303
|
+
Reform::Form.class_eval do
|
304
|
+
include Reform::Form::Lotus
|
305
|
+
end
|
306
|
+
```
|
307
|
+
|
308
|
+
Put this in an initializer or on top of your script.
|
309
|
+
|
286
310
|
|
287
311
|
## Compositions
|
288
312
|
|
@@ -723,8 +747,10 @@ Rails and Reform work together out-of-the-box.
|
|
723
747
|
|
724
748
|
However, you should know about two things.
|
725
749
|
|
726
|
-
1. In case you explicitely _don't_ want to have automatic support for `ActiveRecord` and form builder: `require reform/form`, only.
|
750
|
+
1. In case you explicitely _don't_ want to have automatic support for `ActiveRecord` or `Mongoid` and form builder: `require reform/form`, only.
|
727
751
|
2. In some setups around Rails 4 the `Form::ActiveRecord` module is not loaded properly, usually triggering a `NoMethodError` saying `undefined method 'model'`. If that happened to you, `require 'reform/rails'` manually at the bottom of your `config/application.rb`.
|
752
|
+
3. Mongoid form gets loaded with the gem if `Mongoid` constant is defined.
|
753
|
+
|
728
754
|
|
729
755
|
## ActiveRecord Compatibility
|
730
756
|
|
@@ -741,6 +767,24 @@ class SongForm < Reform::Form
|
|
741
767
|
include Reform::Form::ActiveRecord
|
742
768
|
```
|
743
769
|
|
770
|
+
## Mongoid Compatibility
|
771
|
+
|
772
|
+
Reform provides the following `Mongoid` specific features. They're mixed in automatically in a Rails/Mongoid setup.
|
773
|
+
|
774
|
+
* Uniqueness validations. Use `validates_uniqueness_of` in your form.
|
775
|
+
|
776
|
+
You may want to include the module manually then.
|
777
|
+
|
778
|
+
```ruby
|
779
|
+
class SongForm < Reform::Form
|
780
|
+
include Reform::Form::Mongoid
|
781
|
+
```
|
782
|
+
|
783
|
+
## Uniqueness Validation
|
784
|
+
|
785
|
+
Both ActiveRecord and Mongoid modules will support "native" uniqueness support from the model class when you use `validates_uniqueness_of`. They will provide options like `:scope`, etc.
|
786
|
+
|
787
|
+
You're encouraged to use Reform's non-writing `unique: true` validation, though. [Learn more](http://trailblazerb.org/gems/reform/validation.html)
|
744
788
|
|
745
789
|
## ActiveModel Compliance
|
746
790
|
|
data/database.sqlite3
CHANGED
Binary file
|
data/lib/reform/active_record.rb
CHANGED
data/lib/reform/contract.rb
CHANGED
@@ -27,7 +27,7 @@ module Reform
|
|
27
27
|
end
|
28
28
|
|
29
29
|
if validates_options = options[:validates]
|
30
|
-
validates name, validates_options.dup # .dup for RAils 3.x
|
30
|
+
validates name, validates_options.dup # .dup for RAils 3.x.
|
31
31
|
end
|
32
32
|
|
33
33
|
super
|
@@ -39,20 +39,18 @@ module Reform
|
|
39
39
|
args.each { |name| property(name, options) }
|
40
40
|
end
|
41
41
|
|
42
|
-
# FIXME: make AM optional.
|
43
|
-
require 'active_model'
|
44
|
-
include ActiveModel::Validations
|
45
|
-
|
46
42
|
require 'reform/contract/validate'
|
47
43
|
include Reform::Contract::Validate
|
48
44
|
|
49
|
-
|
50
|
-
|
45
|
+
|
46
|
+
module ValidatesWarning
|
47
|
+
def validates(*)
|
48
|
+
raise "[Reform] Please include either Reform::Form::ActiveModel::Validations or Reform::Form::Lotus in your form class."
|
49
|
+
end
|
51
50
|
end
|
51
|
+
extend ValidatesWarning
|
52
52
|
|
53
53
|
private
|
54
|
-
attr_writer :errors # only used in top form. (is that true?)
|
55
|
-
|
56
54
|
# DISCUSS: can we achieve that somehow via features in build_inline?
|
57
55
|
# TODO: check out if that is needed with Lotus::Validations and make it a AM feature.
|
58
56
|
def self.process_inline!(mod, definition)
|
@@ -92,5 +90,3 @@ module Reform
|
|
92
90
|
extend Reform::Schema
|
93
91
|
end
|
94
92
|
end
|
95
|
-
|
96
|
-
require 'reform/contract/errors'
|
@@ -1,25 +1,11 @@
|
|
1
1
|
# The Errors class is planned to replace AM::Errors. It provides proper nested error messages.
|
2
2
|
class Reform::Contract::Errors < ActiveModel::Errors
|
3
|
-
def messages
|
4
|
-
return super unless Reform.rails3_0?
|
5
|
-
self
|
6
|
-
end
|
7
|
-
|
8
|
-
# def each
|
9
|
-
# messages.each_key do |attribute|
|
10
|
-
# self[attribute].each { |error| yield attribute, Array.wrap(error) }
|
11
|
-
# end
|
12
|
-
# end
|
13
|
-
|
14
3
|
def merge!(errors, prefix)
|
15
|
-
# TODO: merge into AM.
|
16
4
|
errors.messages.each do |field, msgs|
|
17
5
|
unless field.to_sym == :base
|
18
6
|
field = (prefix+[field]).join(".").to_sym # TODO: why is that a symbol in Rails?
|
19
7
|
end
|
20
8
|
|
21
|
-
msgs = [msgs] if Reform.rails3_0? # DISCUSS: fix in #each?
|
22
|
-
|
23
9
|
msgs.each do |msg|
|
24
10
|
next if messages[field] and messages[field].include?(msg)
|
25
11
|
add(field, msg)
|
@@ -28,7 +14,7 @@ class Reform::Contract::Errors < ActiveModel::Errors
|
|
28
14
|
end
|
29
15
|
|
30
16
|
def valid? # TODO: test me in unit test.
|
31
|
-
|
17
|
+
empty?
|
32
18
|
end
|
33
19
|
|
34
20
|
def to_s
|
@@ -1,36 +1,31 @@
|
|
1
1
|
module Reform::Contract::Validate
|
2
2
|
def validate
|
3
|
-
|
3
|
+
validate!(errs=build_errors, [])
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
self.errors = errs # if the AM valid? API wouldn't use a "global" variable this would be better.
|
8
|
-
|
9
|
-
errors.valid?
|
5
|
+
@errors = errs
|
6
|
+
errors.empty?
|
10
7
|
end
|
11
8
|
|
12
|
-
def validate!(
|
13
|
-
|
9
|
+
def validate!(errors, prefix)
|
10
|
+
validate_nested!(nested_errors = build_errors, prefix) # call valid? recursively and collect nested errors.
|
14
11
|
|
15
|
-
|
12
|
+
valid? # calls AM/Lotus validators and invokes self.errors=.
|
16
13
|
|
17
|
-
|
14
|
+
errors.merge!(self.errors, prefix) # local errors.
|
15
|
+
errors.merge!(nested_errors, []) #
|
16
|
+
end
|
18
17
|
|
19
|
-
|
18
|
+
def errors
|
19
|
+
@errors ||= build_errors
|
20
20
|
end
|
21
21
|
|
22
22
|
private
|
23
23
|
|
24
24
|
# runs form.validate! on all nested forms
|
25
|
-
def validate_nested!(
|
25
|
+
def validate_nested!(errors, prefixes)
|
26
26
|
schema.each(twin: true) do |dfn|
|
27
|
-
property_options = options.dup
|
28
|
-
|
29
|
-
property_options[:prefix] = options[:prefix].dup # TODO: implement Options#dup.
|
30
|
-
property_options[:prefix] << dfn.name
|
31
|
-
|
32
27
|
# recursively call valid? on nested form.
|
33
|
-
Disposable::Twin::PropertyProcessor.new(dfn, self).() { |form| form.validate!(
|
28
|
+
Disposable::Twin::PropertyProcessor.new(dfn, self).() { |form| form.validate!(errors, prefixes+[dfn.name]) }
|
34
29
|
end
|
35
30
|
end
|
36
31
|
end
|
@@ -99,5 +99,10 @@ module Reform::Form::ActiveModel
|
|
99
99
|
return ::ActiveModel::Name.new(OpenStruct.new(:name => string)) if Reform.rails3_0?
|
100
100
|
::ActiveModel::Name.new(self, namespace, string)
|
101
101
|
end
|
102
|
+
end # ClassMethods
|
103
|
+
|
104
|
+
|
105
|
+
def model_name(*args)
|
106
|
+
self.class.model_name(*args)
|
102
107
|
end
|
103
108
|
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require "active_model"
|
2
|
+
require "reform/contract/errors"
|
3
|
+
|
4
|
+
module Reform::Form::ActiveModel
|
5
|
+
# AM::Validations for your form.
|
6
|
+
#
|
7
|
+
# Note: The preferred way for validations should be Lotus::Validations, as ActiveModel::Validation's implementation is
|
8
|
+
# old, very complex given that it needs to do a simple thing, and it's using globals like @errors.
|
9
|
+
#
|
10
|
+
# Implements ::validates and friends, and #valid?.
|
11
|
+
module Validations
|
12
|
+
def self.included(includer)
|
13
|
+
includer.instance_eval do
|
14
|
+
extend Uber::InheritableAttr
|
15
|
+
inheritable_attr :validator
|
16
|
+
self.validator = Class.new(Validator)
|
17
|
+
|
18
|
+
class << self
|
19
|
+
extend Uber::Delegates
|
20
|
+
delegates :validator, :validates, :validate, :validates_with, :validate_with
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def build_errors
|
26
|
+
Reform::Contract::Errors.new(self)
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
# Validators is the validatable object. On the class level, we define validations,
|
31
|
+
# on instance, it exposes #valid?.
|
32
|
+
class Validator
|
33
|
+
include ActiveModel::Validations
|
34
|
+
|
35
|
+
def initialize(form)
|
36
|
+
@form = form
|
37
|
+
end
|
38
|
+
|
39
|
+
def method_missing(method_name, *args, &block)
|
40
|
+
@form.send(method_name, *args, &block)
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.name # FIXME: this is only needed for i18n, it seems.
|
44
|
+
"ba"
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.clone
|
48
|
+
Class.new(self)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
def valid?
|
54
|
+
validator = self.class.validator.new(self)
|
55
|
+
validator.valid? # run the Validations object's validator with the form as context. this won't pollute anything in the form.
|
56
|
+
|
57
|
+
|
58
|
+
#errors.merge!(validator.errors, "")
|
59
|
+
validator.errors.each do |name, error| # TODO: handle with proper merge, or something. validator.errors is ALWAYS AM::Errors.
|
60
|
+
errors.add(name, error)
|
61
|
+
end
|
62
|
+
|
63
|
+
errors.empty?
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -3,6 +3,7 @@ module Reform::Form::ActiveRecord
|
|
3
3
|
base.class_eval do
|
4
4
|
register_feature Reform::Form::ActiveRecord
|
5
5
|
include Reform::Form::ActiveModel
|
6
|
+
include Reform::Form::ORM
|
6
7
|
extend ClassMethods
|
7
8
|
end
|
8
9
|
end
|
@@ -18,28 +19,6 @@ module Reform::Form::ActiveRecord
|
|
18
19
|
end
|
19
20
|
|
20
21
|
class UniquenessValidator < ::ActiveRecord::Validations::UniquenessValidator
|
21
|
-
|
22
|
-
def validate(form)
|
23
|
-
property = attributes.first
|
24
|
-
|
25
|
-
# here is the thing: why does AM::UniquenessValidator require a filled-out record to work properly? also, why do we need to set
|
26
|
-
# the class? it would be way easier to pass #validate a hash of attributes and get back an errors hash.
|
27
|
-
# the class for the finder could either be infered from the record or set in the validator instance itself in the call to ::validates.
|
28
|
-
record = form.model_for_property(property)
|
29
|
-
record.send("#{property}=", form.send(property))
|
30
|
-
|
31
|
-
@klass = record.class # this is usually done in the super-sucky #setup method.
|
32
|
-
super(record).tap do |res|
|
33
|
-
form.errors.add(property, record.errors.first.last) if record.errors.present?
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
# TODO: this is no AR thing.
|
39
|
-
def model_for_property(name)
|
40
|
-
return model unless is_a?(Reform::Form::Composition) # i am too lazy for proper inheritance. there should be a ActiveRecord::Composition that handles this.
|
41
|
-
|
42
|
-
model_name = schema.representable_attrs.get(name)[:on]
|
43
|
-
model[model_name]
|
22
|
+
include Reform::Form::ORM::UniquenessValidator
|
44
23
|
end
|
45
24
|
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require "lotus/validations"
|
2
|
+
|
3
|
+
# Implements ::validates and friends, and #valid?.
|
4
|
+
module Reform::Form::Lotus
|
5
|
+
class Errors < Lotus::Validations::Errors
|
6
|
+
def merge!(errors, prefix)
|
7
|
+
errors.instance_variable_get(:@errors).each do |name, err|
|
8
|
+
field = (prefix+[name]).join(".")
|
9
|
+
add(field, *err) # TODO: use namespace feature in Lotus here!
|
10
|
+
end
|
11
|
+
# next if messages[field] and messages[field].include?(msg)
|
12
|
+
end
|
13
|
+
|
14
|
+
def inspect
|
15
|
+
@errors.to_s
|
16
|
+
end
|
17
|
+
|
18
|
+
def messages
|
19
|
+
self
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
def self.included(base)
|
25
|
+
# base.send(:include, Lotus::Validations)
|
26
|
+
base.extend(ClassMethods)
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
module ClassMethods
|
31
|
+
def validates(name, options)
|
32
|
+
validations.add(name, options)
|
33
|
+
end
|
34
|
+
|
35
|
+
def validate(name, *)
|
36
|
+
# DISCUSS: lotus does not support that?
|
37
|
+
# validations.add(name, options)
|
38
|
+
end
|
39
|
+
|
40
|
+
def validations
|
41
|
+
@validations ||= Lotus::Validations::ValidationSet.new
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
def valid?
|
47
|
+
# DISCUSS: by using @fields here, we avoid setters being called. win!
|
48
|
+
validator = Lotus::Validations::Validator.new(self.class.validations, @fields, errors)
|
49
|
+
validator.validate
|
50
|
+
end
|
51
|
+
|
52
|
+
def build_errors
|
53
|
+
Errors.new
|
54
|
+
end
|
55
|
+
end
|