reform 2.1.0 → 2.2.0.rc1
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/.gitignore +0 -1
- data/.travis.yml +4 -12
- data/CHANGES.md +8 -0
- data/README.md +36 -743
- data/Rakefile +1 -31
- data/gemfiles/{Gemfile.rails-3.1 → Gemfile.disposable-0.3} +1 -2
- data/lib/reform.rb +0 -9
- data/lib/reform/contract.rb +5 -1
- data/lib/reform/form.rb +1 -4
- data/lib/reform/form/composition.rb +1 -2
- data/lib/reform/form/dry.rb +29 -16
- data/lib/reform/form/module.rb +15 -3
- data/lib/reform/form/validate.rb +1 -1
- data/lib/reform/validation.rb +3 -3
- data/lib/reform/version.rb +1 -1
- data/reform.gemspec +3 -10
- data/test/coercion_test.rb +7 -7
- data/test/composition_test.rb +5 -1
- data/test/contract_test.rb +10 -4
- data/test/deserialize_test.rb +3 -3
- data/test/errors_test.rb +48 -28
- data/test/form_option_test.rb +3 -1
- data/test/form_test.rb +19 -14
- data/test/module_test.rb +51 -11
- data/test/populate_test.rb +21 -7
- data/test/reform_test.rb +24 -20
- data/test/save_test.rb +10 -4
- data/test/skip_if_test.rb +5 -3
- data/test/test_helper.rb +3 -43
- data/test/validate_test.rb +34 -14
- data/test/validation/dry_test.rb +60 -0
- data/test/validation/dry_validation_test.rb +65 -43
- data/test/validation/errors.yml +4 -0
- metadata +16 -192
- data/database.sqlite3 +0 -0
- data/gemfiles/Gemfile.rails-3.2 +0 -7
- data/gemfiles/Gemfile.rails-4.0 +0 -8
- data/gemfiles/Gemfile.rails-4.1 +0 -8
- data/gemfiles/Gemfile.rails-4.2 +0 -8
- data/lib/reform/active_record.rb +0 -4
- data/lib/reform/form/active_model.rb +0 -87
- data/lib/reform/form/active_model/form_builder_methods.rb +0 -48
- data/lib/reform/form/active_model/model_reflections.rb +0 -46
- data/lib/reform/form/active_model/model_validations.rb +0 -110
- data/lib/reform/form/active_model/validations.rb +0 -107
- data/lib/reform/form/active_record.rb +0 -30
- data/lib/reform/form/lotus.rb +0 -59
- data/lib/reform/form/multi_parameter_attributes.rb +0 -48
- data/lib/reform/form/validation/unique_validator.rb +0 -54
- data/lib/reform/rails.rb +0 -13
- data/test/active_model_custom_validation_translations_test.rb +0 -75
- data/test/active_model_test.rb +0 -207
- data/test/active_model_validation_for_property_named_format_test.rb +0 -18
- data/test/active_record_test.rb +0 -273
- data/test/builder_test.rb +0 -32
- data/test/custom_validation_test.rb +0 -47
- data/test/dummy/Rakefile +0 -7
- data/test/dummy/app/controllers/albums_controller.rb +0 -18
- data/test/dummy/app/controllers/application_controller.rb +0 -4
- data/test/dummy/app/controllers/musician_controller.rb +0 -5
- data/test/dummy/app/forms/album_form.rb +0 -18
- data/test/dummy/app/helpers/application_helper.rb +0 -2
- data/test/dummy/app/models/album.rb +0 -4
- data/test/dummy/app/models/song.rb +0 -3
- data/test/dummy/app/views/albums/new.html.erb +0 -28
- data/test/dummy/app/views/layouts/application.html.erb +0 -14
- data/test/dummy/config.ru +0 -4
- data/test/dummy/config/application.rb +0 -20
- data/test/dummy/config/boot.rb +0 -10
- data/test/dummy/config/database.yml +0 -22
- data/test/dummy/config/environment.rb +0 -5
- data/test/dummy/config/environments/development.rb +0 -16
- data/test/dummy/config/environments/production.rb +0 -46
- data/test/dummy/config/environments/test.rb +0 -33
- data/test/dummy/config/locales/en.yml +0 -14
- data/test/dummy/config/routes.rb +0 -4
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/form_builder_test.rb +0 -138
- data/test/lotus/Gemfile +0 -5
- data/test/lotus/lotus_test.rb +0 -31
- data/test/lotus_test.rb +0 -150
- data/test/model_reflections_test.rb +0 -138
- data/test/model_validations_test.rb +0 -82
- data/test/mongoid_test.rb +0 -313
- data/test/multi_parameter_attributes_test.rb +0 -50
- data/test/rails/integration_test.rb +0 -54
- data/test/unique_test.rb +0 -135
- data/test/validation/activemodel_validation_test.rb +0 -252
data/database.sqlite3
DELETED
Binary file
|
data/gemfiles/Gemfile.rails-3.2
DELETED
data/gemfiles/Gemfile.rails-4.0
DELETED
data/gemfiles/Gemfile.rails-4.1
DELETED
data/gemfiles/Gemfile.rails-4.2
DELETED
data/lib/reform/active_record.rb
DELETED
@@ -1,87 +0,0 @@
|
|
1
|
-
require "reform/form/active_model/model_validations"
|
2
|
-
require "reform/form/active_model/form_builder_methods"
|
3
|
-
require "uber/delegates"
|
4
|
-
|
5
|
-
module Reform::Form::ActiveModel
|
6
|
-
def self.included(base)
|
7
|
-
base.class_eval do
|
8
|
-
extend ClassMethods
|
9
|
-
register_feature ActiveModel
|
10
|
-
|
11
|
-
extend Uber::Delegates
|
12
|
-
delegates :model, *[:persisted?, :to_key, :to_param, :id] # Uber::Delegates
|
13
|
-
|
14
|
-
def to_model # this is called somewhere in FormBuilder and ActionController.
|
15
|
-
self
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
|
21
|
-
module ClassMethods
|
22
|
-
# this module is only meant to extend (not include). # DISCUSS: is this a sustainable concept?
|
23
|
-
def self.extended(base)
|
24
|
-
base.class_eval do
|
25
|
-
extend Uber::InheritableAttribute
|
26
|
-
inheritable_attr :model_options
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
# DISCUSS: can we achieve that somehow via features in build_inline?
|
31
|
-
def property(*)
|
32
|
-
super.tap do |dfn|
|
33
|
-
return dfn unless dfn[:nested]
|
34
|
-
_name = dfn[:name]
|
35
|
-
dfn[:nested].instance_eval do
|
36
|
-
@_name = _name.singularize.camelize
|
37
|
-
# this adds Form::name for AM::Validations and I18N.
|
38
|
-
def name
|
39
|
-
@_name
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
|
46
|
-
# Set a model name for this form if the infered is wrong.
|
47
|
-
#
|
48
|
-
# class CoverSongForm < Reform::Form
|
49
|
-
# model :song
|
50
|
-
#
|
51
|
-
# or we can setup a isolated namespace model ( which defined in isolated rails egine )
|
52
|
-
#
|
53
|
-
# class CoverSongForm < Reform::Form
|
54
|
-
# model "api/v1/song", namespace: "api"
|
55
|
-
def model(main_model, options={})
|
56
|
-
self.model_options = [main_model, options]
|
57
|
-
end
|
58
|
-
|
59
|
-
def model_name
|
60
|
-
if model_options
|
61
|
-
form_name = model_options.first.to_s.camelize
|
62
|
-
namespace = model_options.last[:namespace].present? ? model_options.last[:namespace].to_s.camelize.constantize : nil
|
63
|
-
else
|
64
|
-
if name
|
65
|
-
form_name = name.sub(/(::)?Form$/, "") # Song::Form => "Song"
|
66
|
-
namespace = nil
|
67
|
-
else # anonymous forms. let's drop AM and forget about all this.
|
68
|
-
form_name = "reform"
|
69
|
-
namespace = nil
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
active_model_name_for(form_name, namespace)
|
74
|
-
end
|
75
|
-
|
76
|
-
private
|
77
|
-
def active_model_name_for(string, namespace=nil)
|
78
|
-
return ::ActiveModel::Name.new(OpenStruct.new(:name => string)) if Reform.rails3_0?
|
79
|
-
::ActiveModel::Name.new(self, namespace, string)
|
80
|
-
end
|
81
|
-
end # ClassMethods
|
82
|
-
|
83
|
-
|
84
|
-
def model_name(*args)
|
85
|
-
self.class.model_name(*args)
|
86
|
-
end
|
87
|
-
end
|
@@ -1,48 +0,0 @@
|
|
1
|
-
module Reform::Form::ActiveModel
|
2
|
-
# Including FormBuilderMethods will allow using form instances with form_for, simple_form, etc.
|
3
|
-
# in Rails. It will further try to translate Rails' suboptimal songs_attributes weirdness
|
4
|
-
# back to normal `songs: ` naming in +#valiate+.
|
5
|
-
module FormBuilderMethods
|
6
|
-
def self.included(base)
|
7
|
-
base.extend ClassMethods # ::model_name
|
8
|
-
end
|
9
|
-
|
10
|
-
module ClassMethods
|
11
|
-
private
|
12
|
-
|
13
|
-
# TODO: add that shit in Form#present, not by overriding ::property.
|
14
|
-
def property(name, options={}, &block)
|
15
|
-
super.tap do |definition|
|
16
|
-
add_nested_attribute_compat(name) if definition[:nested] # TODO: fix that in Rails FB#1832 work.
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
# The Rails FormBuilder "detects" nested attributes (which is what we want) by checking existance of a setter method.
|
21
|
-
def add_nested_attribute_compat(name)
|
22
|
-
define_method("#{name}_attributes=") {} # this is why i hate respond_to? in Rails.
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
# Modify the incoming Rails params hash to be representable compliant.
|
27
|
-
def deserialize!(params)
|
28
|
-
# this only happens in a Hash environment. other engines have to overwrite this method.
|
29
|
-
schema.each do |dfn|
|
30
|
-
rename_nested_param_for!(params, dfn)
|
31
|
-
end
|
32
|
-
|
33
|
-
super(params)
|
34
|
-
end
|
35
|
-
|
36
|
-
private
|
37
|
-
def rename_nested_param_for!(params, dfn)
|
38
|
-
name = dfn[:name]
|
39
|
-
nested_name = "#{name}_attributes"
|
40
|
-
return unless params.has_key?(nested_name)
|
41
|
-
|
42
|
-
value = params["#{name}_attributes"]
|
43
|
-
value = value.values if dfn[:collection]
|
44
|
-
|
45
|
-
params[name] = value
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
@@ -1,46 +0,0 @@
|
|
1
|
-
# ModelReflections will be the interface between the form object and form builders like simple_form.
|
2
|
-
#
|
3
|
-
# This module is meant to collect all dependencies simple_form needs in addition to the ActiveModel ones.
|
4
|
-
# Goal is to collect all methods and define a reflection API so simple_form works with all ORMs and Reform
|
5
|
-
# doesn't have to "guess" what simple_form and other form helpers need.
|
6
|
-
class Reform::Form < Reform::Contract
|
7
|
-
module ActiveModel::ModelReflections
|
8
|
-
def self.included(base)
|
9
|
-
base.extend ClassMethods
|
10
|
-
base.send :register_feature, self # makes it work in nested forms.
|
11
|
-
end
|
12
|
-
|
13
|
-
module ClassMethods
|
14
|
-
# Delegate reflect_on_association to the model class to support simple_form's
|
15
|
-
# association input.
|
16
|
-
def reflect_on_association(*args)
|
17
|
-
model_name.to_s.constantize.reflect_on_association(*args)
|
18
|
-
end
|
19
|
-
|
20
|
-
# this is needed in simpleform to infer required fields.
|
21
|
-
def validators_on(*args)
|
22
|
-
validation_groups.collect { |k, group| group.instance_variable_get(:@validations).validators_on(*args) }.flatten
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
# Delegate column for attribute to the model to support simple_form's
|
27
|
-
# attribute type interrogation.
|
28
|
-
def column_for_attribute(name)
|
29
|
-
model_for_property(name).column_for_attribute(name)
|
30
|
-
end
|
31
|
-
|
32
|
-
def has_attribute?(name)
|
33
|
-
model_for_property(name).has_attribute?(name)
|
34
|
-
end
|
35
|
-
|
36
|
-
def defined_enums
|
37
|
-
return model.defined_enums unless is_a?(Reform::Form::Composition)
|
38
|
-
|
39
|
-
mapper.each.with_object({}) { |m,h| h.merge! m.defined_enums }
|
40
|
-
end
|
41
|
-
|
42
|
-
# this should also contain to_param and friends as this is used by the form helpers.
|
43
|
-
end
|
44
|
-
|
45
|
-
ModelReflections = ActiveModel::ModelReflections
|
46
|
-
end
|
@@ -1,110 +0,0 @@
|
|
1
|
-
module Reform::Form::ActiveModel
|
2
|
-
module ModelValidations
|
3
|
-
# TODO: extract Composition behaviour.
|
4
|
-
# reduce code in Mapping.
|
5
|
-
|
6
|
-
class ValidationCopier
|
7
|
-
|
8
|
-
def self.copy(form_class, mapping, models)
|
9
|
-
if models.is_a?(Hash)
|
10
|
-
models.each do |model_name, model|
|
11
|
-
new(form_class, mapping, model, model_name).copy
|
12
|
-
end
|
13
|
-
else
|
14
|
-
new(form_class, mapping, models).copy
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def initialize(form_class, mapping, model, model_name=nil)
|
19
|
-
@form_class = form_class
|
20
|
-
@mapping = mapping
|
21
|
-
@model = model
|
22
|
-
@model_name = model_name
|
23
|
-
end
|
24
|
-
|
25
|
-
def copy
|
26
|
-
@model.validators.each(&method(:add_validator))
|
27
|
-
end
|
28
|
-
|
29
|
-
private
|
30
|
-
|
31
|
-
def add_validator(validator)
|
32
|
-
if validator.respond_to?(:attributes)
|
33
|
-
add_native_validator validator
|
34
|
-
else
|
35
|
-
add_custom_validator validator
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def add_native_validator validator
|
40
|
-
attributes = inverse_map_attributes(validator.attributes)
|
41
|
-
if attributes.any?
|
42
|
-
@form_class.validates(*attributes, {validator.kind => validator.options})
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
def add_custom_validator validator
|
47
|
-
@form_class.validates(nil, {validator.kind => validator.options})
|
48
|
-
end
|
49
|
-
|
50
|
-
def inverse_map_attributes(attributes)
|
51
|
-
@mapping.inverse_image(create_attributes(attributes))
|
52
|
-
end
|
53
|
-
|
54
|
-
def create_attributes(attributes)
|
55
|
-
attributes.map do |attribute|
|
56
|
-
[@model_name, attribute].compact
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
end
|
61
|
-
|
62
|
-
class Mapping
|
63
|
-
def self.from_representable_attrs(attrs)
|
64
|
-
new.tap do |mapping|
|
65
|
-
attrs.each do |dfn|
|
66
|
-
from = dfn[:name].to_sym
|
67
|
-
to = [dfn[:on], (dfn[:private_name] || dfn[:name])].compact.map(&:to_sym)
|
68
|
-
mapping.add(from, to)
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
def initialize
|
74
|
-
@forward_map = {}
|
75
|
-
@inverse_map = {}
|
76
|
-
end
|
77
|
-
|
78
|
-
# from is a symbol attribute
|
79
|
-
# to is an 1 or 2 element array, depending on whether the attribute is 'namespaced', as it is with composite forms.
|
80
|
-
# eg, add(:phone_number, [:person, :phone])
|
81
|
-
def add(from, to)
|
82
|
-
raise 'Mapping is not one-to-one' if @forward_map.has_key?(from) || @inverse_map.has_key?(to)
|
83
|
-
@forward_map[from] = to
|
84
|
-
@inverse_map[to] = from
|
85
|
-
end
|
86
|
-
|
87
|
-
def forward_image(attrs)
|
88
|
-
@forward_map.values_at(*attrs).compact
|
89
|
-
end
|
90
|
-
|
91
|
-
def forward(attr)
|
92
|
-
@forward_map[attr]
|
93
|
-
end
|
94
|
-
|
95
|
-
def inverse_image(attrs)
|
96
|
-
@inverse_map.values_at(*attrs).compact
|
97
|
-
end
|
98
|
-
|
99
|
-
def inverse(attr)
|
100
|
-
@inverse_map[attr]
|
101
|
-
end
|
102
|
-
|
103
|
-
end
|
104
|
-
|
105
|
-
def copy_validations_from(models)
|
106
|
-
ValidationCopier.copy(self, Mapping.from_representable_attrs(definitions), models)
|
107
|
-
end
|
108
|
-
|
109
|
-
end
|
110
|
-
end
|
@@ -1,107 +0,0 @@
|
|
1
|
-
require "active_model"
|
2
|
-
require "uber/delegates"
|
3
|
-
|
4
|
-
module Reform::Form::ActiveModel
|
5
|
-
# AM::Validations for your form.
|
6
|
-
# Provides ::validates, ::validate, #validate, and #valid?.
|
7
|
-
#
|
8
|
-
# Most of this file contains unnecessary wiring to make ActiveModel's error message magic work.
|
9
|
-
# Since Rails still thinks it's a good idea to do things like object.class.human_attribute_name,
|
10
|
-
# we have some hacks in here to provide that. If it doesn't work for you, don't blame us.
|
11
|
-
module Validations
|
12
|
-
def self.included(includer)
|
13
|
-
includer.instance_eval do
|
14
|
-
include Reform::Form::ActiveModel
|
15
|
-
|
16
|
-
class << self
|
17
|
-
extend Uber::Delegates
|
18
|
-
# # Hooray! Delegate translation back to Reform's Validator class which contains AM::Validations.
|
19
|
-
delegates :active_model_really_sucks, :human_attribute_name, :lookup_ancestors, :i18n_scope # Rails 3.1.
|
20
|
-
|
21
|
-
def validation_group_class
|
22
|
-
Group
|
23
|
-
end
|
24
|
-
|
25
|
-
# this is to allow calls like Form::human_attribute_name (note that this is on the CLASS level) to be resolved.
|
26
|
-
# those calls happen when adding errors in a custom validation method, which is defined on the form (as an instance method).
|
27
|
-
def active_model_really_sucks
|
28
|
-
Class.new(Validator).tap do |v|
|
29
|
-
v.model_name = model_name
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end # ::included
|
34
|
-
end
|
35
|
-
|
36
|
-
def build_errors
|
37
|
-
Errors.new(self)
|
38
|
-
end
|
39
|
-
|
40
|
-
# The concept of "composition" has still not arrived in Rails core and they rely on 400 methods being
|
41
|
-
# available in one object. This is why we need to provide parts of the I18N API in the form.
|
42
|
-
def read_attribute_for_validation(name)
|
43
|
-
send(name)
|
44
|
-
end
|
45
|
-
|
46
|
-
class Group
|
47
|
-
def initialize
|
48
|
-
@validations = Class.new(Reform::Form::ActiveModel::Validations::Validator)
|
49
|
-
end
|
50
|
-
|
51
|
-
extend Uber::Delegates
|
52
|
-
delegates :@validations, :validates, :validate, :validates_with, :validate_with
|
53
|
-
|
54
|
-
def call(fields, errors, form) # FIXME.
|
55
|
-
validator = @validations.new(form)
|
56
|
-
validator.valid?
|
57
|
-
|
58
|
-
validator.errors.each do |name, error| # TODO: handle with proper merge, or something. validator.errors is ALWAYS AM::Errors.
|
59
|
-
errors.add(name, error)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
|
65
|
-
# Validator is the validatable object. On the class level, we define validations,
|
66
|
-
# on instance, it exposes #valid?.
|
67
|
-
require "delegate"
|
68
|
-
class Validator < SimpleDelegator
|
69
|
-
# current i18n scope: :activemodel.
|
70
|
-
include ActiveModel::Validations
|
71
|
-
|
72
|
-
class << self
|
73
|
-
def model_name
|
74
|
-
@_active_model_sucks ||= ActiveModel::Name.new(Reform::Form, nil, "Reform::Form")
|
75
|
-
end
|
76
|
-
|
77
|
-
def model_name=(name)
|
78
|
-
@_active_model_sucks = name
|
79
|
-
end
|
80
|
-
|
81
|
-
def validates(*args, &block)
|
82
|
-
super(*Declarative::DeepDup.(args), &block)
|
83
|
-
end
|
84
|
-
|
85
|
-
# Prevent AM:V from mutating the validator class
|
86
|
-
def attr_reader(*)
|
87
|
-
end
|
88
|
-
|
89
|
-
def attr_writer(*)
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
def initialize(form)
|
94
|
-
super(form)
|
95
|
-
self.class.model_name = form.model_name # one of the many reasons why i will drop support for AM::V in 2.1. or maybe a bit later.
|
96
|
-
end
|
97
|
-
|
98
|
-
def method_missing(m, *args, &block)
|
99
|
-
__getobj__.send(m, *args, &block) # send all methods to the form, even privates.
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
class Errors < ActiveModel::Errors
|
105
|
-
include Reform::Form::Errors::Merge
|
106
|
-
end
|
107
|
-
end
|