reform 2.0.0.rc1 → 2.0.0.rc2
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 +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
|