{"id":21587453,"url":"https://github.com/nakajima/sinatras-hat","last_synced_at":"2026-03-09T23:03:09.255Z","repository":{"id":437487,"uuid":"59135","full_name":"nakajima/sinatras-hat","owner":"nakajima","description":"lol","archived":false,"fork":false,"pushed_at":"2009-03-08T00:24:55.000Z","size":341,"stargazers_count":139,"open_issues_count":6,"forks_count":15,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-10-11T08:19:04.358Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nakajima.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2008-10-03T08:30:19.000Z","updated_at":"2025-05-25T08:24:57.000Z","dependencies_parsed_at":"2022-07-04T16:09:30.186Z","dependency_job_id":null,"html_url":"https://github.com/nakajima/sinatras-hat","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/nakajima/sinatras-hat","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nakajima%2Fsinatras-hat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nakajima%2Fsinatras-hat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nakajima%2Fsinatras-hat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nakajima%2Fsinatras-hat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nakajima","download_url":"https://codeload.github.com/nakajima/sinatras-hat/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nakajima%2Fsinatras-hat/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30315982,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-09T20:05:46.299Z","status":"ssl_error","status_checked_at":"2026-03-09T19:57:04.425Z","response_time":61,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: 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-11-24T15:16:42.568Z","updated_at":"2026-03-09T23:03:09.184Z","avatar_url":"https://github.com/nakajima.png","language":"Ruby","funding_links":[],"categories":["Writing APIs"],"sub_categories":[],"readme":"# Sinatra's Hat\n\nEasy REST-ful apps with Sinatra.\n\nUsing Sinatra's Hat is centered around the `mount` method, which is\nadded to `Sinatra::Base`. It takes at bare minimum a model class. This\nclass will be mounted as a REST-ful resource, giving you all the CRUD\nactions, as well as `new` and `edit` actions. Let's look at some code:\n\n\u003cpre\u003e\nmount Article\n\u003c/pre\u003e\n\nNow you've basically got the same functionality as you'll get in Rails\nby running `script/generate scaffold Article`. The views for your `Article`\nwill live in `views/articles`, and be named `index.erb`, `show.erb`, etc.\n\nYou can look at my [Hatter](http://github.com/nakajima/hatter/tree/master)\nproject, or the `examples/` directory in this one to see this in action.\n\nGo ahead, try it.\n\n## ORM agnostic? Or ORM atheist?\n\nBy default, Sinatra's Hat works with ActiveRecord. That means that going\nto `/articles` will simply call `Article.all` to populate the `@articles`\ninstance variable. Going to `/articles/2` will call `Article.find_by_id(2)`\nto populate the `@article` instance variable.\n\nWe call `find_by_id` instead of `find` because the `record` option should\nsimply return `nil` when the record can't be found.\n\nNot every class is an ActiveRecord though (especially if you're not using\nActiveRecord). That's why you can use the `finder` and `record` options.\n\nThis example will show you how to use DataMapper with Sinatra's Hat:\n\n\u003cpre\u003e\nmount Article do\n  finder { |model, params| model.all }\n  record { |model, params| model.first(:id =\u003e params[:id]) }\nend\n\u003c/pre\u003e\n\nAs you can see, both `finder` and `record` take a block, which will get\npassed the \"model\" and `params` for each request. The reason you should use\nthe \"model\" argument instead of referencing the class directly is that\nwhen you start nesting mounted models, then Sinatra's Hat will attempt to\npass the association proxy as the model argument instead of the class itself.\n\n\"Nested mounted models?\" you ask?\n\n## Nested mounted models.\n\nYou don't want to have to expose your entire application at the top level\nof URL paths. That wouldn't be very RESTful, and more importantly, it'd be\ndamn ugly. So Sinatra's Hat allows you to nest resources:\n\n\u003cpre\u003e\nmount Article do\n  mount Comment\nend\n\u003c/pre\u003e\n\nWith this example, you'd get `/articles/1/comments`, `/articles/1/comments/1`\nand all the rest of the actions you get for articles, just nested. As long\nas your `Article` model supports a `comments` association proxy, then the `finder`\nand `record` options for `Comment` will automatically scope their results by\nthe parent `Article`.\n\n## Limiting routes\n\nBy default, Sinatra's Hat creates seven routes for each mounted model (the\nfour ones for \u003cacronym title=\"Create|Read|Update|Destroy\"\u003eCRUD\u003c/acronym\u003e\nactions plus the routes for index, new and edit action), but you can reduce\nthe number of available routes with `only`:\n\n\u003cpre\u003e\nmount Article do\n  only :index, :show\nend\n\u003c/pre\u003e\n\nOnly the listed actions will return valid responses; requests for the\n\"missing\" routes will produce 404 \"Not Found\" HTTP responses.\n\n## Basic Auth\n\nTo protect actions using basic authentication, you can use the `protect` method.\n\n\u003cpre\u003e\nmount Article do\n  protect :create, :update, :destroy, :username =\u003e \"foo\", :password =\u003e \"bar\", :realm =\u003e \"BLOGZ\"\nend\n\u003c/pre\u003e\n\nThe above snippet will protect your \u003cacronym title=\"Create|Update|Destroy\"\u003eCUD\u003c/acronym\u003e\nactions with basic auth, using the username \"foo\" and password \"bar\". The realm\nfor the basic auth prompt will say \"BLOGZ\".\n\nIf you want to protect all of your actions, you cay say `protect :all`.\n\n## `.xml`, `.json`, `.yaml`, and whatever else you want\n\nIf a request has a format extensions, then Sinatra's Hat will first check\nto see if it has a custom way of serializing that format. To specify a \ncustom formatter, you can use the `formats` hash:\n\n\u003cpre\u003e\nmount Article do\n  formats[:ruby] = { |data| data.inspect }\nend\n\u003c/pre\u003e\n\nWith that custom formatter, a request to `/articles.ruby` will return\nthe equivalent of `Article.all.inspect`.\n\n### Automatic formatters\n\nIf you don't specify a custom formatter, then Sinatra's Hat will try to\ncall `to_#{format}` on the record object. That means that with most ORMs,\nthings like `to_xml`, `to_json`, and `to_yaml` will be supported right out\nof the box.\n\nRequests for unknow formats will produce 406 \"Not Acceptable\" HTTP responses.\n\n## Default Flows\n\nSinatra's Hat has some default flows:\n\n### After the `create` action\n\n**On Success**: If a record is successfully created, Sinatra's Hat will redirect to that\nrecord's show page.\n\n**On Failure**: If a record cannot be saved, Sinatra's Hat will render the `new` action.\n\n### After the `Update` action\n\n**On Success**: If a record is successfully updated, Sinatra's Hat will redirect to that\nrecord's show page.\n\n**On Failure**: If a record cannot be updated, Sinatra's Hat will render the `edit` action.\n\n## Custom Flows\n\nTo specify custom flows for your actions, you can use the `after` method.\n\nLet's say that after a user creates an Article, you want to render the\narticle's edit action, and if it can't be created, you want to redirect\nback to the articles index.\n\n\u003cpre\u003e\nmount Article do\n  after :create do |on|\n    on.success { |record| render(:edit) }\n    on.failure { |record| redirect(:index) }\n  end\nend\n\u003c/pre\u003e\n\nOnly `:create` and `:update` actions allow to handle success and failure\ndifferently; for the other actions you can customize only the `success` result\nand if something goes wrong (i.e., when a record cannot be found) they will\nsimply return a 404 \"Not Found\" HTTP response.\n\n### `redirect` options\n\nWhen specifying a custom redirect, you can pass one of a few things:\n\n#### A String\n\nWhen you pass `redirect` a string, the redirect will go to that string.\n\n\u003cpre\u003e\nafter :create do |on|\n  on.success { |record| redirect(\"/articles/#{record.to_param}\") }\nend\n\u003c/pre\u003e\n\n#### A Record\n\nWhen you pass `redirect` a record, the redirect will go to the show\naction for that record.\n\n\u003cpre\u003e\nafter :create do |on|\n  on.success { |record| redirect(record) }\nend\n\u003c/pre\u003e\n\n#### A symbol\n\nIf you pass `redirect` the name of an action as a symbol (like `:index`),\nthen the redirect will go to the correct path for that option:\n\n\u003cpre\u003e\nafter :create do |on|\n  on.success { redirect(:index) }\nend\n\u003c/pre\u003e\n\nWhen the action requires a record (like `:show`), then just pass the\nrecord as a second argument:\n\n\u003cpre\u003e\nafter :create do |on|\n  on.success { |record| redirect(:show, record) }\nend\n\u003c/pre\u003e\n\n### Responding with a `render`\n\nWhen you want your response to just render a template, just call `render`\nwith the name of the template:\n\n\u003cpre\u003e\nafter :create do |on|\n  on.failure { |record| render(:new) }\nend\n\u003c/pre\u003e\n\n## Todo\n\n* Make `last_modified` calls more efficient\n* Investigate other forms of caching\n\n## Other Info\n\n* [View the Lighthouse Project](http://nakajima.lighthouseapp.com/projects/24609-sinatras-hat/overview)\n* [View the CI build](http://ci.patnakajima.com/sinatra-s-hat)\n* Thanks a ton to the [Sinatra team](http://github.com/sinatra) for such an\n  awesome framework.\n\n(c) Copyright 2008-2009 Pat Nakajima. All Rights Reserved.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnakajima%2Fsinatras-hat","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnakajima%2Fsinatras-hat","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnakajima%2Fsinatras-hat/lists"}