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.
Files changed (89) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -1
  3. data/.travis.yml +4 -12
  4. data/CHANGES.md +8 -0
  5. data/README.md +36 -743
  6. data/Rakefile +1 -31
  7. data/gemfiles/{Gemfile.rails-3.1 → Gemfile.disposable-0.3} +1 -2
  8. data/lib/reform.rb +0 -9
  9. data/lib/reform/contract.rb +5 -1
  10. data/lib/reform/form.rb +1 -4
  11. data/lib/reform/form/composition.rb +1 -2
  12. data/lib/reform/form/dry.rb +29 -16
  13. data/lib/reform/form/module.rb +15 -3
  14. data/lib/reform/form/validate.rb +1 -1
  15. data/lib/reform/validation.rb +3 -3
  16. data/lib/reform/version.rb +1 -1
  17. data/reform.gemspec +3 -10
  18. data/test/coercion_test.rb +7 -7
  19. data/test/composition_test.rb +5 -1
  20. data/test/contract_test.rb +10 -4
  21. data/test/deserialize_test.rb +3 -3
  22. data/test/errors_test.rb +48 -28
  23. data/test/form_option_test.rb +3 -1
  24. data/test/form_test.rb +19 -14
  25. data/test/module_test.rb +51 -11
  26. data/test/populate_test.rb +21 -7
  27. data/test/reform_test.rb +24 -20
  28. data/test/save_test.rb +10 -4
  29. data/test/skip_if_test.rb +5 -3
  30. data/test/test_helper.rb +3 -43
  31. data/test/validate_test.rb +34 -14
  32. data/test/validation/dry_test.rb +60 -0
  33. data/test/validation/dry_validation_test.rb +65 -43
  34. data/test/validation/errors.yml +4 -0
  35. metadata +16 -192
  36. data/database.sqlite3 +0 -0
  37. data/gemfiles/Gemfile.rails-3.2 +0 -7
  38. data/gemfiles/Gemfile.rails-4.0 +0 -8
  39. data/gemfiles/Gemfile.rails-4.1 +0 -8
  40. data/gemfiles/Gemfile.rails-4.2 +0 -8
  41. data/lib/reform/active_record.rb +0 -4
  42. data/lib/reform/form/active_model.rb +0 -87
  43. data/lib/reform/form/active_model/form_builder_methods.rb +0 -48
  44. data/lib/reform/form/active_model/model_reflections.rb +0 -46
  45. data/lib/reform/form/active_model/model_validations.rb +0 -110
  46. data/lib/reform/form/active_model/validations.rb +0 -107
  47. data/lib/reform/form/active_record.rb +0 -30
  48. data/lib/reform/form/lotus.rb +0 -59
  49. data/lib/reform/form/multi_parameter_attributes.rb +0 -48
  50. data/lib/reform/form/validation/unique_validator.rb +0 -54
  51. data/lib/reform/rails.rb +0 -13
  52. data/test/active_model_custom_validation_translations_test.rb +0 -75
  53. data/test/active_model_test.rb +0 -207
  54. data/test/active_model_validation_for_property_named_format_test.rb +0 -18
  55. data/test/active_record_test.rb +0 -273
  56. data/test/builder_test.rb +0 -32
  57. data/test/custom_validation_test.rb +0 -47
  58. data/test/dummy/Rakefile +0 -7
  59. data/test/dummy/app/controllers/albums_controller.rb +0 -18
  60. data/test/dummy/app/controllers/application_controller.rb +0 -4
  61. data/test/dummy/app/controllers/musician_controller.rb +0 -5
  62. data/test/dummy/app/forms/album_form.rb +0 -18
  63. data/test/dummy/app/helpers/application_helper.rb +0 -2
  64. data/test/dummy/app/models/album.rb +0 -4
  65. data/test/dummy/app/models/song.rb +0 -3
  66. data/test/dummy/app/views/albums/new.html.erb +0 -28
  67. data/test/dummy/app/views/layouts/application.html.erb +0 -14
  68. data/test/dummy/config.ru +0 -4
  69. data/test/dummy/config/application.rb +0 -20
  70. data/test/dummy/config/boot.rb +0 -10
  71. data/test/dummy/config/database.yml +0 -22
  72. data/test/dummy/config/environment.rb +0 -5
  73. data/test/dummy/config/environments/development.rb +0 -16
  74. data/test/dummy/config/environments/production.rb +0 -46
  75. data/test/dummy/config/environments/test.rb +0 -33
  76. data/test/dummy/config/locales/en.yml +0 -14
  77. data/test/dummy/config/routes.rb +0 -4
  78. data/test/dummy/db/test.sqlite3 +0 -0
  79. data/test/form_builder_test.rb +0 -138
  80. data/test/lotus/Gemfile +0 -5
  81. data/test/lotus/lotus_test.rb +0 -31
  82. data/test/lotus_test.rb +0 -150
  83. data/test/model_reflections_test.rb +0 -138
  84. data/test/model_validations_test.rb +0 -82
  85. data/test/mongoid_test.rb +0 -313
  86. data/test/multi_parameter_attributes_test.rb +0 -50
  87. data/test/rails/integration_test.rb +0 -54
  88. data/test/unique_test.rb +0 -135
  89. data/test/validation/activemodel_validation_test.rb +0 -252
Binary file
@@ -1,7 +0,0 @@
1
- source "http://rubygems.org"
2
-
3
- # Specify your gem's dependencies in reform.gemspec
4
- gemspec :path => '../'
5
-
6
- gem 'railties', '~> 3.2.0'
7
- gem 'activerecord', '~> 3.2.0'
@@ -1,8 +0,0 @@
1
- source "http://rubygems.org"
2
-
3
- # Specify your gem's dependencies in reform.gemspec
4
- gemspec :path => '../'
5
-
6
- gem 'railties', '~> 4.0.0'
7
- gem 'activerecord', '~> 4.0.0'
8
- gem 'minitest', '~> 4.2'
@@ -1,8 +0,0 @@
1
- source "http://rubygems.org"
2
-
3
- # Specify your gem's dependencies in reform.gemspec
4
- gemspec :path => '../'
5
-
6
- gem 'railties', '~> 4.1.0'
7
- gem 'activerecord', '~> 4.1.0'
8
- gem 'minitest'
@@ -1,8 +0,0 @@
1
- source "http://rubygems.org"
2
-
3
- # Specify your gem's dependencies in reform.gemspec
4
- gemspec :path => '../'
5
-
6
- gem 'railties', '~> 4.2.0'
7
- gem 'activerecord', '~> 4.2.0'
8
- gem 'minitest'
@@ -1,4 +0,0 @@
1
- require "reform/form/active_model"
2
- require "reform/form/orm"
3
- require "reform/form/active_record"
4
- require "reform/form/active_model/model_reflections" # only load this in AR context as simple_form currently is bound to AR.
@@ -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