[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-7962":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":16,"stars7d":16,"stars30d":16,"stars90d":16,"forks30d":16,"starsTrendScore":16,"compositeScore":17,"rankGlobal":10,"rankLanguage":10,"license":18,"archived":19,"fork":19,"defaultBranch":20,"hasWiki":21,"hasPages":19,"topics":22,"createdAt":10,"pushedAt":10,"updatedAt":23,"readmeContent":24,"aiSummary":25,"trendingCount":16,"starSnapshotCount":16,"syncStatus":26,"lastSyncTime":27,"discoverSource":28},7962,"reform","trailblazer\u002Freform","trailblazer","Form objects decoupled from models.","https:\u002F\u002Ftrailblazer.to\u002F2.1\u002Fdocs\u002Freform.html",null,"Ruby",2492,181,40,36,0,28.78,"MIT License",false,"master",true,[],"2026-06-12 02:01:47","# Reform\n\n[![Gitter Chat](https:\u002F\u002Fbadges.gitter.im\u002Ftrailblazer\u002Fchat.svg)](https:\u002F\u002Fgitter.im\u002Ftrailblazer\u002Fchat)\n[![TRB Newsletter](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FTRB-newsletter-lightgrey.svg)](http:\u002F\u002Ftrailblazer.to\u002Fnewsletter\u002F)\n[![Build\nStatus](https:\u002F\u002Ftravis-ci.org\u002Ftrailblazer\u002Freform.svg)](https:\u002F\u002Ftravis-ci.org\u002Ftrailblazer\u002Freform)\n[![Gem Version](https:\u002F\u002Fbadge.fury.io\u002Frb\u002Freform.svg)](http:\u002F\u002Fbadge.fury.io\u002Frb\u002Freform)\n\n_Form objects decoupled from your models._\n\nReform gives you a form object with validations and nested setup of models. It is completely framework-agnostic and doesn't care about your database.\n\nAlthough reform can be used in any Ruby framework, it comes with [Rails support](#rails-integration), works with simple_form and other form gems, allows nesting forms to implement has_one and has_many relationships, can [compose a form](#compositions) from multiple objects and gives you coercion.\n\n## Full Documentation\n\nReform is part of the [Trailblazer](http:\u002F\u002Ftrailblazer.to) framework. [Full documentation](http:\u002F\u002Ftrailblazer.to\u002F2.1\u002Fdocs\u002Freform.html) is available on the project site.\n\n## Reform 2.2\n\nTemporary note: Reform 2.2 does **not automatically load Rails files** anymore (e.g. `ActiveModel::Validations`). You need the `reform-rails` gem, see [Installation](#installation).\n\n## Defining Forms\n\nForms are defined in separate classes. Often, these classes partially map to a model.\n\n```ruby\nclass AlbumForm \u003C Reform::Form\n  property :title\n  validates :title, presence: true\nend\n```\n\nFields are declared using `::property`. Validations work exactly as you know it from Rails or other frameworks. Note that validations no longer go into the model.\n\n\n## The API\n\nForms have a ridiculously simple API with only a handful of public methods.\n\n1. `#initialize` always requires a model that the form represents.\n2. `#validate(params)` updates the form's fields with the input data (only the form, _not_ the model) and then runs all validations. The return value is the boolean result of the validations.\n3. `#errors` returns validation messages in a classic ActiveModel style.\n4. `#sync` writes form data back to the model. This will only use setter methods on the model(s).\n5. `#save` (optional) will call `#save` on the model and nested models. Note that this implies a `#sync` call.\n6. `#prepopulate!` (optional) will run pre-population hooks to \"fill out\" your form before rendering.\n\nIn addition to the main API, forms expose accessors to the defined properties. This is used for rendering or manual operations.\n\n\n## Setup\n\nIn your controller or operation you create a form instance and pass in the models you want to work on.\n\n```ruby\nclass AlbumsController\n  def new\n    @form = AlbumForm.new(Album.new)\n  end\n```\n\nThis will also work as an editing form with an existing album.\n\n```ruby\ndef edit\n  @form = AlbumForm.new(Album.find(1))\nend\n```\n\nReform will read property values from the model in setup. In our example, the `AlbumForm` will call `album.title` to populate the `title` field.\n\n## Rendering Forms\n\nYour `@form` is now ready to be rendered, either do it yourself or use something like Rails' `#form_for`, `simple_form` or `formtastic`.\n\n```haml\n= form_for @form do |f|\n  = f.input :title\n```\n\nNested 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.\n\nOptionally, you might want to use the `#prepopulate!` method to pre-populate fields and prepare the form for rendering.\n\n\n## Validation\n\nAfter form submission, you need to validate the input.\n\n```ruby\nclass SongsController\n  def create\n    @form = SongForm.new(Song.new)\n\n    #=> params: {song: {title: \"Rio\", length: \"366\"}}\n\n    if @form.validate(params[:song])\n```\n\nThe `#validate` method first updates the values of the form - the underlying model is still treated as immutuable and *remains unchanged*. It then runs all validations you provided in the form.\n\nIt's the only entry point for updating the form. This is per design, as separating writing and validation doesn't make sense for a form.\n\nThis allows rendering the form after `validate` with the data that has been submitted. However, don't get confused, the model's values are still the old, original values and are only changed after a `#save` or `#sync` operation.\n\n\n## Syncing Back\n\nAfter validation, you have two choices: either call `#save` and let Reform sort out the rest. Or call `#sync`, which will write all the properties back to the model. In a nested form, this works recursively, of course.\n\nIt's then up to you what to do with the updated models - they're still unsaved.\n\n\n## Saving Forms\n\nThe easiest way to save the data is to call `#save` on the form.\n\n```ruby\nif @form.validate(params[:song])\n  @form.save  #=> populates album with incoming data\n              #   by calling @form.album.title=.\nelse\n  # handle validation errors.\nend\n```\n\nThis will sync the data to the model and then call `album.save`.\n\nSometimes, you need to do saving manually.\n\n## Default values\n\nReform allows default values to be provided for properties.\n\n```ruby\nclass AlbumForm \u003C Reform::Form\n  property :price_in_cents, default: 9_95\nend\n```\n\n## Saving Forms Manually\n\nCalling `#save` with a block will provide a nested hash of the form's properties and values. This does **not call `#save` on the models** and allows you to implement the saving yourself.\n\nThe block parameter is a nested hash of the form input.\n\n```ruby\n  @form.save do |hash|\n    hash      #=> {title: \"Greatest Hits\"}\n    Album.create(hash)\n  end\n```\n\nYou can always access the form's model. This is helpful when you were using populators to set up objects when validating.\n\n```ruby\n  @form.save do |hash|\n    album = @form.model\n\n    album.update_attributes(hash[:album])\n  end\n```\n\n\n## Nesting\n\nReform provides support for nested objects. Let's say the `Album` model keeps some associations.\n\n```ruby\nclass Album \u003C ActiveRecord::Base\n  has_one  :artist\n  has_many :songs\nend\n```\n\nThe implementation details do not really matter here, as long as your album exposes readers and writes like `Album#artist` and `Album#songs`, this allows you to define nested forms.\n\n\n```ruby\nclass AlbumForm \u003C Reform::Form\n  property :title\n  validates :title, presence: true\n\n  property :artist do\n    property :full_name\n    validates :full_name, presence: true\n  end\n\n  collection :songs do\n    property :name\n  end\nend\n```\n\nYou can also reuse an existing form from elsewhere using `:form`.\n\n```ruby\nproperty :artist, form: ArtistForm\n```\n\n## Nested Setup\n\nReform will wrap defined nested objects in their own forms. This happens automatically when instantiating the form.\n\n```ruby\nalbum.songs #=> [\u003CSong name:\"Run To The Hills\">]\n\nform = AlbumForm.new(album)\nform.songs[0] #=> \u003CSongForm model: \u003CSong name:\"Run To The Hills\">>\nform.songs[0].name #=> \"Run To The Hills\"\n```\n\n### Nested Rendering\n\nWhen rendering a nested form you can use the form's readers to access the nested forms.\n\n```haml\n= text_field :title,         @form.title\n= text_field \"artist[name]\", @form.artist.name\n```\n\nOr use something like `#fields_for` in a Rails environment.\n\n```haml\n= form_for @form do |f|\n  = f.text_field :title\n\n  = f.fields_for :artist do |a|\n    = a.text_field :name\n```\n\n## Nested Processing\n\n`validate` will assign values to the nested forms. `sync` and `save` work analogue to the non-nested form, just in a recursive way.\n\nThe block form of `#save` would give you the following data.\n\n```ruby\n@form.save do |nested|\n  nested #=> {title:  \"Greatest Hits\",\n         #    artist: {name: \"Duran Duran\"},\n         #    songs: [{title: \"Hungry Like The Wolf\"},\n         #            {title: \"Last Chance On The Stairways\"}]\n         #   }\n  end\n```\n\nThe manual saving with block is not encouraged. You should rather check the Disposable docs to find out how to implement your manual tweak with the official API.\n\n\n## Populating Forms\n\nVery often, you need to give Reform some information how to create or find nested objects when `validate`ing. This directive is called _populator_ and [documented here](http:\u002F\u002Ftrailblazer.to\u002F2.1\u002Fdocs\u002Freform.html#reform-populators).\n\n## Installation\n\nAdd this line to your Gemfile:\n\n```ruby\ngem \"reform\"\n```\n\nReform works fine with Rails 3.1-5.0. However, inheritance of validations with `ActiveModel::Validations` is broken in Rails 3.2 and 4.0.\n\nSince Reform 2.2, you have to add the `reform-rails` gem to your `Gemfile` to automatically load ActiveModel\u002FRails files.\n\n```ruby\ngem \"reform-rails\"\n```\n\nSince 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).\n\nTo use ActiveModel (not recommended because very out-dated).\n\n```ruby\nrequire \"reform\u002Fform\u002Factive_model\u002Fvalidations\"\nReform::Form.class_eval do\n  include Reform::Form::ActiveModel::Validations\nend\n```\n\nTo use dry-validation (recommended).\n\n```ruby\nrequire \"reform\u002Fform\u002Fdry\"\nReform::Form.class_eval do\n  feature Reform::Form::Dry\nend\n```\n\nPut this in an initializer or on top of your script.\n\n\n## Compositions\n\nReform allows to map multiple models to one form. The [complete documentation](https:\u002F\u002Fgithub.com\u002Fapotonick\u002Fdisposable#composition) is here, however, this is how it works.\n\n```ruby\nclass AlbumForm \u003C Reform::Form\n  include Composition\n\n  property :id,    on: :album\n  property :title, on: :album\n  property :songs, on: :cd\n  property :cd_id, on: :cd, from: :id\nend\n```\nWhen initializing a composition, you have to pass a hash that contains the composees.\n\n```ruby\nAlbumForm.new(album: album, cd: CD.find(1))\n```\n\n## More\n\nReform comes many more optional features, like hash fields, coercion, virtual fields, and so on. Check the [full documentation here](http:\u002F\u002Ftrailblazer.to\u002F2.1\u002Fdocs\u002Freform.html).\n\n[![](http:\u002F\u002Ftrailblazer.to\u002Fimages\u002F3dbuch-freigestellt.png)](https:\u002F\u002Fleanpub.com\u002Ftrailblazer)\n\nReform is part of the [Trailblazer project](http:\u002F\u002Ftrailblazer.to). Please [buy my book](https:\u002F\u002Fleanpub.com\u002Ftrailblazer) to support the development and learn everything about Reform - there's two chapters dedicated to Reform!\n\n\n## Security And Strong_parameters\n\nBy explicitly defining the form layout using `::property` there is no more need for protecting from unwanted input. `strong_parameter` or `attr_accessible` become obsolete. Reform will simply ignore undefined incoming parameters.\n\n## This is not Reform 1.x!\n\nTemporary note: This is the README and API for Reform 2. On the public API, only a few tiny things have changed. Here are the [Reform 1.2 docs](https:\u002F\u002Fgithub.com\u002Ftrailblazer\u002Freform\u002Fblob\u002Fv1.2.6\u002FREADME.md).\n\nAnyway, please upgrade and _report problems_ and do not simply assume that we will magically find out what needs to get fixed. When in trouble, join us on [Gitter](https:\u002F\u002Fgitter.im\u002Ftrailblazer\u002Fchat).\n\n[Full documentation for Reform](http:\u002F\u002Ftrailblazer.to\u002F2.1\u002Fdocs\u002Freform.html) is available online, or support us and grab the [Trailblazer book](https:\u002F\u002Fleanpub.com\u002Ftrailblazer). There is an [Upgrading Guide](http:\u002F\u002Ftrailblazer.to\u002F2.1\u002Fdocs\u002Freform.html#reform-upgrading-guide) to help you migrate through versions.\n\n### Attributions!!!\n\nGreat thanks to [Blake Education](https:\u002F\u002Fgithub.com\u002Fblake-education) for giving us the freedom and time to develop this project in 2013 while working on their project.\n","Reform 是一个用于创建与模型解耦的表单对象的 Ruby 库。它提供了表单验证和嵌套模型设置的功能，支持任何形式的 Ruby 框架而不依赖特定数据库。Reform 的核心特性包括对 Rails 的集成支持、与 simple_form 等表单库兼容、能够处理 has_one 和 has_many 关系的嵌套表单定义以及从多个对象组合表单的能力。此外，Reform 还支持数据类型转换。该工具非常适合需要在保持业务逻辑清晰的同时构建复杂用户输入界面的应用场景，特别是那些已经采用或计划采用 Trailblazer 架构风格的项目。",2,"2026-06-11 03:15:23","top_language"]