CHANGES.md
representable dependency to < 4.disposable dependency to >= 0.5.0.Representable::Option to handle keyword_arguments forwarding :tada:representable and disposable dependencies which uses trailblazer-option over declarative-option.form as a separate positional argument. Make all callable (proc, method, Uber::Callable) signatures identical.Reform::Contract ?]
[* Move Form#deserializer to Form::deserializer]contract instead of schemaYou can upgrade from 2.2.0 without worries.
nil field values, sync {} and dry-validation.:populator classes marked with Uber::Callable.parse: false as a shortcut for deserialzer: { writeable: false}. Thanks to @pabloh for insisting on this handy change.::deserializer. This saves the inferal of a deserializing representer and speeds up following calls by 130%.validation :default, options: {}. New API: validation name: :default, **.Dry::Schema class for global schema configuration. Can be overridden via ::validation.Dry::Validation::Form with Schema which won't coerce values where it shouldn't.Group#call API now is: call(form, errors).Form#valid? - simply calls validate({}).:if for validation groups, you now get a hash of result objects, not just true/false.Compatibility with dry-validation with 1.x:
dry-schema anymore or better the same result is reached using the rule method:
Something like this:
validation do
def a_song?(value)
value == :really_cool_song
end
required(:songs).filled(:a_song?)
end
validation do
required(:songs).filled
rule(:songs) do
key.failure(:a_song?) unless value == :really_cool_song
end
end
dry-v so the inherit: option is NOT SUPPORTED for now. Also extend a schema: option using a block is NOT SUPPORTED for now. Possible workaround is to use reform module to compose different validations but this won't override existing validations but just merge themcall as a populator, no need to include Uber::Callable anymore. This is because we have only three types and don't need a is_a? or respond_to? check.declarative-option and loosen uber dependency.Form#call as an alias for validate and the Result object.uber dependency.Contract::Properties. Thanks @simonc. <3reform/rails. This is now handled via the reform-rails gem which you have to bundle.type: Types::Form::Bool.nilify: true option.####### TODO: fix Module and coercion Types::*
You should be able to upgrade from 2.0 without any code changes.
:populator for scalar properties, too. This allows "parsing code" per property which is super helpful to structure your deserialization.:populator can be a method name, as in populator: :populate_authors!.skip!. Learn more here.populator: ->(options) or ->(fragment:, model:, **o) where we only receive one hash instead of a varying number or arguments. This is pretty cool and should be listed under Awesomeness.ActiveModel::Validator prevents Rails from adding methods to it. This makes acceptance and confirmation validations work properly.ActiveModel::Validations from 2.2 onwards. Don't worry, it will still work, but we don't want to work with it anymore.ActiveModel::Validator now delegates all methods properly to the form. It used to crashed with properties called format or other private Object methods.ModelReflections::validators_on).:default option is not copied into the deserializer anymore from the schema. This requires disposable 0.1.11.#sync and #save with block now provide HashWithIndifferentAccess in Rails.Form#valid? is private now. Sorry for the inconvenience, but this has never been documented as public. Reason is that the only entry point for validation is #validate to give the form as less public API as possible and minimize misunderstandings.
The idea is that you set up the object graph before/while #validate and then invoke the validators once.
Fixed AM to find proper i18n for error messages. This happens by injecting the form's model_name into the Validator object in ActiveModel.
unique: true validation in combination with Composition.:pass_options anymore.ActiveModel::Validations where translations in custom validations would error. This is now handled by delegating back to the Validator object in Reform.The ::reform_2_0! is no longer there. Guess why.
Again: :empty doesn't exist anymore. You can choose from :readable, :writeable and :virtual.
When using :populator the API to work against the form has changed.
populator: lambda { |fragment, index, args|
songs[index] or songs[index] = args.binding[:form].new(Song.new)
}
is now
populator: lambda { |fragment, index, args|
songs[index] or songs.insert(index) = Song.new
}
You don't need to know about forms anymore, the twin handles that using the Twin API..
:as option removed. Use :from.
With Composition included, Form#model would give you a composition object. You can grab that using Form#mapper now.
Form#update! is deprecated. It still works but will remind you to override #present! or use pre-populators as described here and in the Trailblazer book, chapter "Nested Forms".
Forms do not include ActiveModel::Validations anymore. This has polluted the entire gem and is not encapsulated in Validator. Consider using Lotus Validations instead.
Validation inheritance with ActiveModel::Validations is broken with Rails 3.2 and 4.0. Update Rails or use the Lotus validations.
model_name.:prepopulate to fill out form properties for presentation. Note that you need to call Form#prepopulate! to trigger the prepopulation.Form#options_for to have access to all property options.Added Form#readonly? to find out whether a field is set to writeable. This is helpful for simple_form to display a disabled input field.
property :title, writeable: false
In the view, you can then use something along the following code.
f.input :title, readonly: @form.readonly?(:title)
Make ModelReflections work with simple_form 3.1.0. (#176). It also provides defined_enums and ::reflect_on_association now.
nil values passed into #validate will now be written to the model in #sync (#175). Formerly, only blank strings and values evaluating to true were considered when syncing. This allows blanking fields of the model as follows.
form.validate(title: nil)
Calling Form::reform_2_0! will now properly inherit to nested forms.
uber to allow subclassing reform_2_0! forms.rescue block in Form#validate.ActiveModel forms with form builder support wouldn't deserialize properly. A million Thanks to @karolsarnacki for finding this and providing an exemplary failing test. <3Due to countless bugs we no longer include support for simple_form's type interrogation automatically. This allows using forms with non-AM objects. If you want full support for simple_form do as follows.
class SongForm < Reform::Form
include ModelReflections
Including this module will add #column_for_attribute and other methods need by form builders to automatically guess the type of a property.
Form#save no longer passed self to the block. You've been warned long enough. ;)
:as to :from to be in line with Representable/Roar, Disposable and Cells. Thanks to @bethesque for pushing this.:empty is now :virtual and :virtual is writeable: false. It was too confusing and sucked. Thanks to @bethesque, again, for her moral assistance.Form#save with Composition now returns true only if all composite models saved.Form::copy_validations_from allows copying custom validators now.::properties. Instead of an array, it's now properties :title, :genre.pass_options: true.In #validate, you can ignore properties now using :skip_if.
property :hit, skip_if: lambda { |fragment, *| fragment["title"].blank? }
This works for both properties and nested forms. The property will simply be ignored when deserializing, as if it had never been in the incoming hash/document.
For nested properties you can use :skip_if: :all_blank as a macro to ignore a nested form if all values are blank.
You can now specify validations right in the ::property call.
property :title, validates: {presence: true}
Thanks to @zubin for this brillant idea!
Reform now tracks which attributes have changed after #validate. You can check that using form.changed?(:title).
When including Sync::SkipUnchanged, the form won't try to assign unchanged values anymore in #sync. This is extremely helpful when handling file uploads and the like.
Both #sync and #save can be configured dynamically now.
When syncing, you can run a lambda per property.
property :title, sync: lambda { |value, options| model.set_title(value) }
This won't run Reform's built-in sync for this property.
You can also provide the sync lambda at run-time.
form.sync(title: lambda { |value, options| form.model.title = "HOT: #{value}" })
This block is run in the caller's context allowing you to access environment variables.
Note that the dynamic sync happens before save, so the model id may unavailable.
You can do the same for saving.
form.save(title: lambda { |value, options| form.model.title = "#{form.model.id} --> #{value}" })
Again, this block is run in the caller's context.
The two features are an excellent way to handle file uploads without ActiveRecord's horrible callbacks.
Adding generic :base errors now works. Thanks to @bethesque.
errors.add(:base, "You are too awesome!")
This will prefix the error with :base.
Need your form to parse JSON? Include Reform::Form::JSON, the #validate method now expects a JSON string and will deserialize and populate the form from the JSON document.
Added Form::schema to generate a pure representer from the form's representer.
Added :readable and :writeable option which allow to skip reading or writing to the model when false.
::properties which modified the options hash while iterating properties.Form#save now returns the result of the model.save invocation.#update!. The deserialize/update run now grabs the actual nested form instances directly from fields.Errors#to_s is now delegated to messages.to_s. This is used in Trailblazer::Operation.save { |hash| }. You already got the form instance when calling form.save so there's no need to pass it into the block.#validate does not touch any model anymore. Both single values and collections are written to the model after #sync or #save.#validate, only.Reform::Form::Module to improve reusability.:inherit now works properly.Form::ActiveModel::ModelValidations to copy validations from model classes. Thanks to @cameron-martin for this fine addition.Form#model, members via Form#model[:member_name].Reverting what I did in 1.0.3. Leave your code as it is. You may override a writers like #title= to sanitize or filter incoming data, as in
def title=(v)
super(v.strip)
end
This setter will only be called in #validate.
Readers still work the same, meaning that
def title
super.downcase
end
will result in lowercased title when rendering the form (and only then).
The reason for this confusion is that you don't blog enough about Reform. Only after introducing all those deprecation warnings, people started to contact me to ask what's going on. This gave me the feedback I needed to decide what's the best way for filtering incoming data.
fields when saving the form. This avoids calling presentational readers that might have been defined by the user.The following property names are reserved and will raise an exception: [:model, :aliased_model, :fields, :mapper]
You get warned now when overriding accessors for your properties:
property :title
def title
super.upcase
end
This is because in Reform 1.1, those accessors will only be used when rendering the form, e.g. when doing = @form.title. If you override the accessors for presentation, only, you're fine. Add presentation_accessors: true to any property, the warnings will be suppressed and everything's gonna work. You may remove presentation_accessors: true in 1.1, but it won't affect the form.
However, if you used to override #title or #title= to manipulate incoming data, this is no longer working in 1.1. The reason for this is to make Reform cleaner. You will get two options :validate_processor and :sync_processor in order to filter data when calling #validate and when syncing data back to the model with #sync or #save.
Deprecated model readers for Composition and ActiveModel. Consider the following setup.
class RecordingForm < Reform::Form
include Composition
property :title, on: :song
end
Before, Reform would allow you to do form.song which returned the song model. You can still do this (but you shouldn't) with form.model[:song].
This allows having composed models and properties with the same name. Until 1.1, you have to use skip_accessors: true to advise Reform not to create the deprecated accessor.
Also deprecated is the alias accessor as found with ActiveModel.
class RecordingForm < Reform::Form
include Composition
include ActiveModel
model :hit, on: :song
end
Here, an automatic reader Form#hit was created. This is deprecated as
This is gonna be removed in 1.1.
Form::DSL in favour of Form::Composition.validates :songs, :length => {:minimum => 1}
validates :hit, :presence => true
#validate.save gets called on all nested forms automatically, disable with save: false.as: now works everywhere.Composition work everywhere.Contract.:populate_if_empty in #validate.#from_hash and #to_hash.#sync and make #save less smart.Song::Form.HitForm < SongForm < Reform::Form the HitForm class will contain SongForm's properties in addition to its own fields.::model is now inherited properly.title and title=) can now be overridden in the form and call super. This is extremely helpful if you wanna do "manual coercion" since the accessors are invoked in #validate. Thanks to @cj for requesting this.ActiveModel queries the class name to compute translation keys. If you're not happy with it, use ::model.#form_for now properly recognizes a nested form when declared using :form (instead of an inline form).property_name(1i) or the like, it's compiled into a Date. That happens in MultiParameterAttributes. If a component (year/month/day) is missing, the date is considered nil.form.save do .. end would call model.save even though a block was given. This no longer happens, if there's a block to #save, you have to manually save data (ActiveRecord environment, only).#validate doesn't blow up anymore when input data is missing for a nested property or collection.form: SongForm to specify an explicit form class instead of using an inline form for nested properties.ActiveRecord::i18n_scope now returns activerecord.Form#save now calls save on the model in ActiveRecord context.Form#model is public now.:empty to have empty fields that are accessible for validation and processing, only.:virtual for read-only fields the are like :empty but initially read from the decorated model.Composition form.setup and save logic into respective representer classes. This might break your code in case you overwrite private reform classes.has_one and has_many relationships. . Note that this currently works only 1-level deep.Reform::Form::DSL to Reform::Form::Composition and deprecated DSL.require 'reform' now automatically requires Rails stuff in a Rails environment. Mainly, this is the FormBuilder compatibility layer that is injected into Form. If you don't want that, only require 'reform/form'.Form.new now accepts one argument, only: the model/composition. If you want to create your own representer, inject it by overriding Form#mapper. Note that this won't create property accessors for you.Form::ActiveModel no longer creates accessors to your represented models, e.g. having property :title, on: :song doesn't allow form.song anymore. This is because the actual model and the form's state might differ, so please use form.title directly.reform/rails to conditionally load ActiveRecord code and created reform/active_record.Form#to_model is now delegated to model.reform/rails that requires everything you need (even in other frameworks :).Form::ActiveRecord that gives you validates_uniqueness_with. Note that this is strongly coupled to your database, thou.