{"id":17766466,"url":"https://github.com/dmitry/model_form","last_synced_at":"2026-02-05T09:37:29.807Z","repository":{"id":6085189,"uuid":"7311862","full_name":"dmitry/model_form","owner":"dmitry","description":"Model Form","archived":false,"fork":false,"pushed_at":"2016-04-28T12:23:02.000Z","size":11,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-07-25T18:08:09.947Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"h5bp/Front-end-Developer-Interview-Questions","license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dmitry.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2012-12-24T22:10:56.000Z","updated_at":"2016-04-28T12:23:03.000Z","dependencies_parsed_at":"2022-09-22T07:12:00.511Z","dependency_job_id":null,"html_url":"https://github.com/dmitry/model_form","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/dmitry/model_form","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmitry%2Fmodel_form","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmitry%2Fmodel_form/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmitry%2Fmodel_form/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmitry%2Fmodel_form/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dmitry","download_url":"https://codeload.github.com/dmitry/model_form/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmitry%2Fmodel_form/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29118562,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-05T05:31:32.482Z","status":"ssl_error","status_checked_at":"2026-02-05T05:31:29.075Z","response_time":65,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-26T20:30:04.048Z","updated_at":"2026-02-05T09:37:29.791Z","avatar_url":"https://github.com/dmitry.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# model_form\n\nIn a big or even medium project fat controllers and models can become a real problem.\nWith that gem you can move specific form parts of your logic to a `forms`.\n\n\n## Features\n\n* callbacks and validations\n* virtual attributes\n* nested attributes (can be replacements for a `accepts_nested_attributes_for` in your model)\n * with correct error messages mapped to the nested attributes\n * without `_attributes` postfix\n* transaction safe (same as autosave)\n\n## Future support\n\n* multi-form - one form may consists of \u003e 1 model\n* orm agnostic: activerecord, mongoid\n* multiparameter attributes\n\n## Inspiration\n\nRelated: PORO, DAO, Service Object\n\nFrameworks:\n\n* Django - ModelForms\n* Symfony - Forms (http://symfony.com/doc/current/book/forms.html#index-15)\n\nArticles:\n\n* http://pivotallabs.com/users/jdean/blog/articles/1706-form-backing-objects-for-fun-and-profit\n* http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/\n* http://www.devcaffeine.com/2012/06/20/isolating-validations-in-activemodel/\n* http://fredwu.me/post/54009567748/datamappify-a-new-take-on-decoupling-domain-form-and\n* http://brainspec.com/blog/2012/07/09/activerecord-inheritance-contexts/ - context technique\n* http://rubyflow.ru/items/1481 - in russian language about `form_logic` gem\n\nVideos:\n\n* http://railscasts.com/episodes/16-virtual-attributes\n* http://railscasts.com/episodes/398-service-objects\n\nGems:\n\n* https://github.com/orgsync/active_interaction - prefer that over everything else\n* https://github.com/cypriss/mutations - more or less that gem would be\n* https://github.com/fredwu/datamappify\n* https://github.com/hookercookerman/form_model\n* https://github.com/apotonick/reform\n* https://github.com/ClearFit/redtape - nested attributes\n* https://github.com/MSch/activemodel-form\n* https://github.com/saratovsource/form_object\n* https://github.com/joevandyk/cat_forms\n* https://github.com/brycesenz/freeform\n* https://github.com/braintree/curator\n* https://github.com/nulogy/edr\n* https://github.com/joakimk/minimapper\n* https://github.com/GCorbel/activeform-rails\n* https://github.com/shlima/form_logic\n\nAdditional readings:\n\n* https://github.com/rails/rails/pull/8189 - rails pull request for refactoring multi-parameter to a controller; nice idea to have it in this gem\n\n## Concept example\n    \n```ruby\nclass Article \u003c ActiveRecord::Base\n  has_many :photos\nend\n\nclass Photo \u003c ActiveRecord::Base\n  # belongs_to :article\nend\n\n\nclass ArticleForm \u003c ModelForm\n  attr_accessible :header, :description, :photos\n\n  attribute :accepted, Boolean\n  \n  after_assign do\n    self.description = translate(description, :en, :ru)\n    \n    # triggers after_assign in nested attribute\n  end\n  \n  before_validaiton do\n    # runs after_assign\n  end\n  \n  validates :name, presence: true\n  \n  # no photos_attributes - think about it, because ActiveRecord and ActionPack have some references on _attributes\n  nested_attributes :photos\n  \n  def accepted=(accepted)\n    self.accepted_at = accepted ? Time.now : nil\n  end\n  \n  class PhotoForm # \u003c NestedForm\n    \n  end\n  \n  private\n  \n  def translate(text, from, to)\n    # translation logic here\n  end\nend\n\narticle_form = ArticleForm.new(header: 'Header name', description: 'Some description here', photos: [{id: 1, name: 'some name'}, {name: 'new'}])\narticle_form.assign_attributes header: 'Update header name'\narticle_form.save # or save!\narticle_form.valid?\narticle_form.invalid?\narticle_form.errors\n```\n\n## API\n\n### Callbacks\n\n##### assign_attributes\n\n`before_assign`\n\n`after_assign`\n\n##### save\n\n`before_validaiton`\n\n`before_save`\n\n`after_save`\n\n`after_commit`\n\n### Class\n\n`attr_accessible` may take block or list of attributes to whitelist\n\n`attribute(name, type)` add virtual attribute to a form with specific type\n\n[`nested_attributes(association_name, options={})`](#nested_attributes) add ability to set nested attributes for some association\n\n### Instance\n\n`assign_attributes`\n\n`valid?`\n\n`invalid?`\n\n`save`\n\n`save!`\n\n`model`\n\n`params`\n\n`errors` - TODO think how it should be: nested or plain!?\n\n## Extended API description\n\n##### nested_attributes\n\n* `association_name`: association name that used in one of association types\n* `options` by default is `{}`\n * `class_name` by default \"#{attribute_name.to_s.singularize.camelcase}Form\", but can be a model or other ModelForm\n * `reject_if`\n * `allow_destroy`\n\n## What else?!\n\nFrom the beginning I've tried to develop this gem with `RDD` (Readme Driven Development - http://tom.preston-werner.com/2010/08/23/readme-driven-development.html)\nAs you can see, it not works yet, but the idea lives.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdmitry%2Fmodel_form","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdmitry%2Fmodel_form","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdmitry%2Fmodel_form/lists"}