{"id":13858728,"url":"https://github.com/lyokato/sinatra-formkeeper","last_synced_at":"2025-07-14T01:31:39.683Z","repository":{"id":5162285,"uuid":"6332556","full_name":"lyokato/sinatra-formkeeper","owner":"lyokato","description":"Sinatra Extension which support stuff around HTML forms","archived":false,"fork":false,"pushed_at":"2013-05-02T05:27:23.000Z","size":221,"stargazers_count":50,"open_issues_count":1,"forks_count":3,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-06-27T05:13:32.751Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"720kb/angular-datepicker","license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lyokato.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2012-10-22T09:33:57.000Z","updated_at":"2022-10-12T14:51:28.000Z","dependencies_parsed_at":"2022-07-05T22:01:47.747Z","dependency_job_id":null,"html_url":"https://github.com/lyokato/sinatra-formkeeper","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/lyokato/sinatra-formkeeper","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lyokato%2Fsinatra-formkeeper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lyokato%2Fsinatra-formkeeper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lyokato%2Fsinatra-formkeeper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lyokato%2Fsinatra-formkeeper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lyokato","download_url":"https://codeload.github.com/lyokato/sinatra-formkeeper/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lyokato%2Fsinatra-formkeeper/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265230950,"owners_count":23731488,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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-08-05T03:02:18.973Z","updated_at":"2025-07-14T01:31:39.302Z","avatar_url":"https://github.com/lyokato.png","language":"Ruby","funding_links":[],"categories":["Ruby"],"sub_categories":[],"readme":"# Sinatra::FormKeeper\n\nThis module provides you a easy way for form-validation and fill-in-form on your sinatra application\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n    gem 'sinatra-formkeeper'\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install sinatra-formkeeper\n\n## Usage\n\n### Synopsis\n\n```ruby\nrequire 'sinatra/formkeeper'\n\nget '/sign_up' do\n  form do\n    filters :strip, :my_filter\n    field :username, :present =\u003e true, :length =\u003e 4..8\n    field :age, :present =\u003e true, :int =\u003e { :gte =\u003e 18 }\n    field :password01, :present =\u003e true, :length =\u003e 4..8\n    field :password02, :present =\u003e true, :length =\u003e 4..8\n    same :same_password, [:password01, :password02]\n  end\n  if form.failed?\n    \"signup failed\"\n  else\n    \"singup success \" + form[:username]\n  end\nend\n```\n\n### 0: Preparation\n\nAt your application file's header, add `require` line for this library.\n\n```ruby\nrequire 'sinatra/formkeeper'\n```\n\nAnd if your application is `Sinatra::Base` inheritance type, register `Sinatra::FormKeeper`\n\n```ruby\nclass MyApp \u003c Sinatra::Base\n  register Sinatra::FormKeeper\n  #...\nend\n```\n\n### 1: Building rules\n\nIn your routing block, you should build a form-rule at first, \nlike following\n\n```ruby\npost '/entry' do \n  form do\n    filters :strip\n    field :title,   :present =\u003e true, :length =\u003e 4..20\n    field :message, :present =\u003e true, :length =\u003e 0..200\n  end\n  #...\nend\n```\n\nCalling `form` with block which includes rule-setting,\nyou can build a form-rule.\nThere are some DSL-method to build rules. In this example, `filters` and `field` are written.\n\n#### filters\n\nYou can set `filters`. All input parameters are filtered by indicated filtering feature\nThe filtering process is executed before validation.\n\n```ruby\nform do\n  filters :strip\n  #...\nend\n```\n\nYou can set multiple filters at once\n\n```ruby\nform do\n  filters :strip, :downcase\n  #...\nend\n```\n\nAll preset filters are described at [8: Preset Filters](#8-preset-filters)\n\n#### field\n\nYou can add a setting for each field\n\n```ruby\nform do\n  field :field_name, :present =\u003e true, length =\u003e 0..10\n  #...\nend\n```\n\nThis constraint works for an input form named as `field_name`, for instance\n\n```html\n\u003cinput type=\"text\" name=\"field_name\" /\u003e\n```\n\nAnd key-value pares are following the field name.\nThey are constraints set for the field.\nYou can add your favorite constraints here.\n\nAll preset constraints are described at [9: Preset Constraints](#9-preset-constraints)\nRead the chapter for more detail.\n\n`:present` is a special constraint. if parameter not found for the field which\nset `:present` constraint, the field will be marked as *not present*,\nand other validation for rest constraints won't be executed.\n\nYou also can set :default\n\n```ruby\nform do\n  field :field_name, :default =\u003e 'Default Value', :length =\u003e 0..10\n  #...\nend\n```\n\nWhen it's set, if parameter not found, the indicated value will be set\nand other validation for rest constraints won't be executed.\n\nYou aren't allowed to set both *:present* and *:default* at same time.\n\nAnd you can set filters here,\nif you don't want to filter all the parameters included in the request.\nThis filtering setting only affets on `:field_name`.\n\n```ruby\nform do\n  field :field_name, :present =\u003e true, filters =\u003e [:strip, :downcase]\n  #...\nend\n```\n\nYou can set as just one single symbol, if you don't need multiple filters.\n\n```ruby\nform do\n  field :field_name, :present =\u003e true, filters =\u003e :strip\n  #...\nend\n```\n\n#### selection\n\nYou also can set the rule like this.\n\n```ruby\nform do\n  selection :field_name, :count =\u003e 1..3, int =\u003e true\n  #...\nend\n```\n\nThis is just for field which has multiple values.\nFor instance,\n\n```html\n\u003cinput type=\"checkbox\" name=\"field_name[]\" value=\"1\" checked\u003e\n\u003clabel\u003echeck1\u003c/label\u003e\n\u003cinput type=\"checkbox\" name=\"field_name[]\" value=\"2\" checked\u003e\n\u003clabel\u003echeck2\u003c/label\u003e\n\u003cinput type=\"checkbox\" name=\"field_name[]\" value=\"3\" checked\u003e\n\u003clabel\u003echeck3\u003c/label\u003e\n```\n\nOr\n\n```html\n\u003cselect name=\"favorite[]\" multiple\u003e\n  \u003coption value=\"1\" selected=\"selected\"\u003ewhite\u003c/option\u003e\n  \u003coption value=\"2\"\u003eblack\u003c/option\u003e\n  \u003coption value=\"3\"\u003eblue\u003c/option\u003e\n\u003c/select\u003e\n```\n\nRack request handle such type of name (exp: field_name[]) as Array.\nFor this type of input, use `selection` method.\nIn this case, you must use `:count` constraints instead of `:present`.\n\n#### combination\n\nThere is another special rule, *Combination*\n\n```ruby\nform do\n  combination :same_address, :fields =\u003e [\"email01\", \"email02\"], :same =\u003e true\n  combination :favorite_color, :fields =\u003e [\"white\", \"black\", \"blue\"], :any =\u003e true\nend\n```\n\nSet rule-name as a first argument.\nAnd you should set multiple target fields.\nAnd one constraint like (:same =\u003e true), or (:any =\u003e true).\n\n`:same` and `:any` are called as *Combination Constraint*\nFor this purpose, formkeeper provides you a simple way to do same things.\n\n```ruby\nform do\n  same :same_address, [\"email01\", \"email02\"]\n  any :favorite_color, [\"white\", \"black\", \"blue\"]\nend\n```\n\nYou can call a name of *Combination Constraints* as a method.\nFollowed by rule-name and target-fields.\n\nAll preset constraints are described at [10: Preset Combination Constraints](#10-preset-combination-constraints)\n\n### 2: Check if user's input is valid or not\n\n`form.failed?` can be used to judge if user's input is valid for the rule you build.\n\n```ruby\npost '/entry' do \n  form do\n    #...\n  end\n  if form.failed?\n    # user's input is invalid\n  else\n    # user's input is valid!\n  end\nend\n```\n\n### 3: Pick up valid data\n\nAfter validation is proccessed without any failure,\nyou can implement your domain logic with valid parameters.\n\n`form[:field_name]` can be used to pick up a valid data.\nThis data you can obtain through this method is a filtered data\naccording to the rule you build (if you set a `filters` rule).\n\n```ruby\npost '/entry' do \n  form do\n    #...\n  end\n  if form.failed?\n    #...\n  else\n    # do something with valid data\n    Database.insert( :title =\u003e form[:field], :message =\u003e form[:message] )\n  end\nend\n```\n\n### 4: Check if what field has failed?\n\nWhen validation is failed, you might want to provide user\nsame form again, with error message that describes what fields was invalid.\nFor this purpose, use `failed_on?` method.\n\n```ruby\npost '/entry' do \n  form do\n    #...\n  end\n  if form.failed?\n    erb :entry\n  else\n    #...\n  end\nend\n__END__\n@@ entry\n\u003chtml\u003e\n\u003chead\u003e\u003ctitle\u003eEntry\u003c/title\u003e\u003c/head\u003e\n\u003cbody\u003e\n\u003c% if form.failed? %\u003e\n  \u003c% if form.failed_on?(:title) %\u003e\n  \u003cp\u003eTitle is invalid\u003c/p\u003e\n  \u003c% end %\u003e\n  \u003c% if form.failed_on?(:message) %\u003e\n  \u003cp\u003eMessage is invalid\u003c/p\u003e\n  \u003c% end %\u003e\n\u003c% end %\u003e\n  \u003cform action=\"/entry\" method=\"post\"\u003e\n  \u003clabel\u003eTitle\u003c/label\u003e\u003cinput type=\"text\" name=\"title\"\u003e\u003cbr /\u003e\n  \u003clabel\u003eMessage\u003c/label\u003e\u003ctextarea name=\"message\"\u003e\u003c/textarea\u003e\n  \u003cinput type=\"submit\" value=\"Post this entry\"\u003e \n  \u003c/form\u003e \n\u003c/body\u003e\n\u003c/html\u003e\n```\n\n### 5: Check if what field and constraint has failed?\n\nYou can pass constraint-type to `failed_on?` as a second argument.\nThis provides you a way to show detailed error-messages.\n\n```ruby\npost '/entry' do \n  form do\n    #...\n  end\n  if form.failed?\n    erb :entry\n  else\n    #...\n  end\nend\n__END__\n@@ entry\n\u003chtml\u003e\n\u003chead\u003e\u003ctitle\u003eEntry\u003c/title\u003e\u003c/head\u003e\n\u003cbody\u003e\n\u003c% if form.failed? %\u003e\n  \u003c% if form.failed_on?(:title, :present) %\u003e\n    \u003cp\u003eTitle not found\u003c/p\u003e\n  \u003c% end %\u003e\n  \u003c% if form.failed_on?(:title, :length) %\u003e\n    \u003cp\u003eTitle's length is invalid \u003c/p\u003e\n  \u003c% end %\u003e\n  \u003c% if form.failed_on?(:message, :present) %\u003e\n    \u003cp\u003eMessage not found\u003c/p\u003e\n  \u003c% end %\u003e\n\u003c% end %\u003e\n  \u003cform action=\"/entry\" method=\"post\"\u003e\n  \u003clabel\u003eTitle\u003c/label\u003e\u003cinput type=\"text\" name=\"title\"\u003e\u003cbr /\u003e\n  \u003clabel\u003eMessage\u003c/label\u003e\u003ctextarea name=\"message\"\u003e\u003c/textarea\u003e\n  \u003cinput type=\"submit\" value=\"Post this entry\"\u003e \n  \u003c/form\u003e \n\u003c/body\u003e\n\u003c/html\u003e\n```\n\n### 6: Fill in form\n\nIn many case you might want to fill in form with user's last input.\nDo like following. `fill_in_form` automatically fill the fields with `params`\n\n```ruby\npost '/entry' do \n  form do\n    #...\n  end\n  if form.failed?\n    output = erb :entry\n    fill_in_form(output)\n  else\n    #...\n  end\nend\n```\n\n### 7: Message Handling\n\nYou can aggregate a error messages into external yaml file.\n\n```yaml\n--- messages.yaml\nlogin:\n  username:\n    present: input name!\n    length: intput name (length should be between 0 and 10)\n  email:\n    DEFAULT: input correct email address\npost_entry:\n  title:\n    present: Title not found\nDEFAULT:\n  username:\n    present: username not found\n-- ... \n```\n\n`DEFAULT` is a special type. If it can't find setting for indicated validation-type, it uses message set for `DEFAULT`.\nAfter you prepare a yaml file, load it.\n\n```ruby\nform_messages File.expand_path(File.join(File.dirname(__FILE__), 'config', 'form_messages.yaml'))\npost '/entry' do \n  #...\nend\n```\n\nYou can show messages bound to indicated action-name you set in yaml.\n\n```html\n\u003chtml\u003e\n  \u003chead\u003e\u003ctitle\u003eEntry\u003c/title\u003e\u003c/head\u003e\n  \u003cbody\u003e\n  \u003c% if form.failed? %\u003e \n    \u003cul\u003e\n    \u003c% form.messages(:post_entry).each do |message|  %\u003e\n      \u003cli\u003e\u003c%= message %\u003e\u003c/li\u003e\n    \u003c% end %\u003e\n    \u003c/ul\u003e\n  \u003c% end %\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\nIf you want to show messages for each field, separately, of course you can.\n\n```html\n\u003chtml\u003e\n  \u003chead\u003e\u003ctitle\u003eEntry\u003c/title\u003e\u003c/head\u003e\n  \u003cbody\u003e\n  \u003cform\u003e\n  \u003c% if form.failed? %\u003e \n    \u003cul\u003e\n    \u003c% form.messages(:login, :username).each do |message|  %\u003e\n      \u003cli\u003e\u003c%= message %\u003e\u003c/li\u003e\n    \u003c% end %\u003e\n    \u003c/ul\u003e\n  \u003c% end %\u003e\n  \u003clabel\u003eusername\u003c/label\u003e\u003cinput type=\"text\" name=\"username\"\u003e\n  \u003c% if form.failed? %\u003e \n    \u003cul\u003e\n    \u003c% form.messages(:login, :password).each do |message|  %\u003e\n      \u003cli\u003e\u003c%= message %\u003e\u003c/li\u003e\n    \u003c% end %\u003e\n    \u003c/ul\u003e\n  \u003c% end %\u003e\n  \u003clabel\u003epassword\u003c/label\u003e\u003cinput type=\"text\" name=\"password\"\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\n### 8: Preset Filters\n\n#### strip\n#### downcase\n#### upcase\n\n### 9: Preset Constraints\n\n#### length\n\ncalculate length. this constraint use String#length internally\nYou can set integer.\n\n```ruby\npost '/entry' do \n  form do\n    field :field01, :present =\u003e true, :length =\u003e 10\n  end\n  #...\nend\n```\n\nOr as range\n\n```ruby\npost '/entry' do \n  form do\n    field :field01, :present =\u003e true, :length =\u003e 4..10\n  end\n  #...\nend\n```\n\n#### bytesize\n\nCalculate byte size. this constraint use String#bytesize internally\nYou can set integer.\n\n```ruby\npost '/entry' do \n  form do\n    field :field01, :present =\u003e true, :bytesize =\u003e 10\n  end\n  #...\nend\n```\n\nOr as range\n\n```ruby\npost '/entry' do \n  form do\n    field :field01, :present =\u003e true, :bytesize =\u003e 4..10\n  end\n  #...\nend\n```\n\n#### ascii\n\n```ruby\npost '/entry' do \n  form do\n    field :field01, :present =\u003e true, :ascii =\u003e true\n  end\n  #...\nend\n```\n\n#### regexp\n\n```ruby\npost '/entry' do \n  form do\n    field :field01, :present =\u003e true, :regexp =\u003e %r{regexp}\n  end\n  #...\nend\n```\n\n#### int\n\n```ruby\npost '/entry' do \n  form do\n    field :field01, :present =\u003e true, :int =\u003e true\n  end\n  #...\nend\n```\n\nFore more detailed constraint,\nYou can use following options as a hash.\n\n* gt: This means \u003e\n* gte: This means \u003e=\n* lt: This means \u003c\n* lte: This means \u003c=\n* between: Or you can set Range object\n\n```ruby\npost '/entry' do \n  form do\n    field :field01, :present =\u003e true, :int =\u003e { :gt =\u003e 5, :lt =\u003e 10 }\n  end\n  #...\nend\n```\n\n```ruby\npost '/entry' do \n  form do\n    field :field01, :present =\u003e true, :int =\u003e { :gte =\u003e 5, :lte =\u003e 10 }\n  end\n  #...\nend\n```\n\n```ruby\npost '/entry' do \n  form do\n    field :field01, :present =\u003e true, :int =\u003e { :between =\u003e 5..10 }\n  end\n  #...\nend\n```\n\n#### uint\n\nUnsined integer. This doesn't allow lass than zero.\nExcept for that, it behaves same as integer\n\n```ruby\npost '/entry' do \n  form do\n    field :field01, :present =\u003e true, :uint =\u003e { :between =\u003e 5..10 }\n  end\n  #...\nend\n```\n\n#### alpha\n\nAlphabet\n\n#### alpha_space\n\nAlphabet and Space\n\n#### alnum\n\nAlphabet and Number\n\n#### alnum_space\n\nAlphabet, Number and Space\n\n#### email\n\nEmail-Address\n\n```ruby\npost '/entry' do \n  form do\n    field :your_address, :present =\u003e true, :email =\u003e true, :bytesize =\u003e 10..255\n  end\n  #...\nend\n```\n\n#### uri\n\nLimit a scheme as Array\n\n```ruby\npost '/entry' do \n  form do\n    field :your_address, :present =\u003e true, :uri =\u003e [:http, :https], :bytesize =\u003e 10..255\n  end\n  #...\nend\n```\n\n```ruby\npost '/entry' do \n  form do\n    field :your_address, :present =\u003e true, :uri =\u003e [:http], :bytesize =\u003e 10..255\n  end\n  #...\nend\n```\n\nIf your scheme option is only one.\nYou can set as a String.\n\n```ruby\npost '/entry' do \n  form do\n    field :your_address, :present =\u003e true, :uri =\u003e :http, :bytesize =\u003e 10..255\n  end\n  #...\nend\n```\n\n### 10: Preset Combination Constraints\n\n#### same\n#### any\n#### date\n#### time\n#### datetime\n\n### 11: Utilize Plugins\n\n```ruby\nrequire 'formkeeper/japanese' \n\npost '/entry' do\n  form do\n    filters :zenkaku2hankaku\n  end\nend\n```\n\n### 12: Custom Filter\n\n```ruby\nform_filter :my_capitalize_filter do |value|\n  value.capitalize\nend\n\npost '/entry' do\n  form do\n    filters :my_capitalize_filter\n  end\nend\n```\n\n### 13: Custom Constraint\n\n## See Also\n\n* https://github.com/lyokato/formkeeper/\n* https://github.com/lyokato/formkeeper-japanese/\n\n## Contributing\n\n1. Fork it\n2. Create your feature branch (`git checkout -b my-new-feature`)\n3. Commit your changes (`git commit -am 'Add some feature'`)\n4. Push to the branch (`git push origin my-new-feature`)\n5. Create new Pull Request\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flyokato%2Fsinatra-formkeeper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flyokato%2Fsinatra-formkeeper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flyokato%2Fsinatra-formkeeper/lists"}