{"id":13879517,"url":"https://github.com/pete/watts","last_synced_at":"2025-07-16T15:32:34.995Z","repository":{"id":928802,"uuid":"697968","full_name":"pete/watts","owner":"pete","description":"Resource-oriented, Rack-based, minimalist web framework.","archived":false,"fork":false,"pushed_at":"2024-07-13T19:18:03.000Z","size":64,"stargazers_count":37,"open_issues_count":1,"forks_count":2,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-09-14T16:56:54.428Z","etag":null,"topics":["microframework","rack","ruby"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pete.png","metadata":{"files":{"readme":"README","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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2010-06-01T20:29:31.000Z","updated_at":"2024-07-13T19:18:06.000Z","dependencies_parsed_at":"2024-07-13T20:30:29.622Z","dependency_job_id":null,"html_url":"https://github.com/pete/watts","commit_stats":null,"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pete%2Fwatts","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pete%2Fwatts/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pete%2Fwatts/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pete%2Fwatts/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pete","download_url":"https://codeload.github.com/pete/watts/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":226143895,"owners_count":17580245,"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":["microframework","rack","ruby"],"created_at":"2024-08-06T08:02:23.595Z","updated_at":"2024-11-24T08:31:25.795Z","avatar_url":"https://github.com/pete.png","language":"Ruby","funding_links":[],"categories":["Ruby"],"sub_categories":[],"readme":"# Watts\n\n## Intro\n\nWatts is a minimalist, Rack-based, resource-oriented web framework.  It\nhas fewer than 400 lines of code (including comments), no dependencies\n(besides Rack), and only does one thing: it maps resources.\n\nSee doc/LICENSE for the license.  See doc/examples if you're impatient.\n\n## Goals\n\nDead-simple web development.  I don't want a framework to take up so\nmuch of my brain that there's no space left for my application.\n\n## Resource Orientation\n\nIf you think of resources as first-class objects, and then think of HTTP\nmethods as operations on those objects, then this should probably make\nsense.\n\nMost of the web frameworks I have seen seem to be HTTP-method-oriented\nrather than resource-oriented.  It seems odd to me (not to mention\nrepetitive) that you would type \"A GET on /foo does this, a POST on /foo\ndoes that, ...\" rather than \"There's a resource at /foo.  That\nresource's GET does this, and its POST does that, ...\".\n\nAnd because the second one made more sense to me, that's how I wrote\nWatts.  Let me clarify if that was a bit vague, by showing how a Watts\napp comes into being:\n* You make a resource by sub-classing `Watts::Resource`.  \n* On this resource, you define some basic operations, in terms of HTTP\nmethods.\n* You create an app by sub-classing `Watts::App`.\n* You use the resource() method to tell Watts the path under which the\nresource can be found.\n\nThere are a few easy-to-read examples in doc/examples.\n\n## Pattern-matching\n\nThat resource() method mentioned above?  It does some pattern-matching\non the different components of the path.  They all arrive as strings, of\ncourse.  The priority for matches is that Watts will attempt to match a\nstring literally if possible.  Next it tries to match any regex patterns\nthat have been specified, and failing that, symbols are used as a\ncatch-all.  Here are a few patterns and the things they match:\n* '' matches \"/\"\n* 'foo' matches \"/foo\".\n* 'foo/bar' matches \"/foo/bar\".\n* [] matches \"/\".\n* ['foo'] matches \"/foo\".\n* ['foo', 'bar'] matches \"/foo/bar\".\n* ['foo', /^[0-9]+$/] matches \"/foo/\", followed by a string of digits.\nThe matching part of the path will be passed as an argument to the\nmethod on the resource when it is called.\n* ['foo', :arg] matches \"/foo/\" followed by anything.  Like with the\nregex, the argument is passed in.  The symbol's actual value doesn't\nreally matter to Watts; it is intended for documentation.\n\nSee doc/examples/matching.ru.\n\n## What methods return:\n\nWhen you define an HTTP method on a `Watts::Resource`, the best thing\nto return is an array in the format Rack expects for responses, namely:\n\n [status_code, {headers}, [body, ...]]\n\nFor the sake of convenience, Watts will attempt to do the right thing\nif you return a bare string (in which case, it is treated as `text/plain`).\nIf the return value is a `Rack::Response`, then Watts will use that.  If you\ncall `Watts::Resource#response`, a `Rack::Response` will be created if it\ndoes not exist, and that response will be used if the return value is nil.\n\nThe Rack API has changed a handful of times recently, so see SPEC.rdoc in\nthe Rack repo or use ri(1) or on the web:\nhttps://github.com/rack/rack/blob/main/SPEC.rdoc#label-The+Response .\n\nNote also that, although HTTP headers are case-insensitive,\n`Rack::Lint` has started throwing errors if you use the canonical case\n(e.g., \"User-Agent\", \"Content-Type\") rather than all lower-case (e.g.,\n\"user-agent\", \"content-type\").  The canonical case for HTTP headers is\nused in the RFCs, in nearly every web server and user agent since the\n1990s, as well as all of the documentation, including the IANA's\nheader registry:  http://www.iana.org/assignments/message-headers .\nAs Watts is designed to work with Rack, though, Watts now emits only\nlower-case header names in the few places where it does emit headers.\n\n`Rack::Lint` is of dubious utility and can be disabled without consequence.\n\nSee doc/examples/return.ru.\n\n* A string, in which case, \n\n## REST, RFC 2616, TL;DR\n\nThere's a lot of talk on the internets about what exactly REST is, why it's\nimportant, why we're doing it wrong, content-negotiation, discoverability,\navoiding out-of-band communication, and all of that stuff.  Watts makes it a\nlittle easier to comply with the spec than Rails or Sinatra if you know what the\nspec says, but it doesn't force it on you.  (You should definitely care, but\nWatts won't make you.)\n\n## Design\n\nLots of web frameworks move very quickly, have a large number of features, a\nlarge number of bugs resulting from the large number of features, and an onerous\nupgrade process when updating to a new version.  Watts has a much more\nstrict policy:  do not add a feature unless it is obviously correct.\n\nExcept for the earliest versions, every new feature that made it into Watts has\nbeen implemented in two or three Watts applications, and looks very different\nthan it would look if it had been implemented based on speculation.  (I've been\nhacking for a long time; this may seem obvious to older hackers, but you have to\nbuild something a few times before you understand it well enough to put it into\na framework.)\n\nIf you feel that Watts sorely misses some features, the codebase is very small,\nand as a result very amenable to extension.\n\n## Bugs\n\nI'm sure they're present.  You can email me about them, or ping me on\nGitHub, or send a patch.\n\nThere do not seem to be too many.  There is a test suite.\n\n## About the Name\n\nI named it after a character in a video game that I liked as a kid (and still\nlike).  It's also the name of a city not far from where I live.\n\nAlso:  joules per second.\n\nThe Watts in question:\n\n░░░█░░██▒█░░█░░ ███░██░░▓░██░██\n░░█▒██▓█▒▓██▒█░ ██░▓░░▒░▓▒░░▓░█\n░█▒██▓▓█▒▓▓██▒█ █░▓░░▒▒░▓▒▒░░▓░\n░█▒▒▒███▒███▒▒█ █░▓▓▓░░░▓░░░▓▓░\n░░█▒█▒▒▒▒▒▒█▒█░ ██░▓░▓▓▓▓▓▓░▓░█\n░░░█▒██████▒█░░ ███░▓░░░░░░▓░██\n░░█▒██▒██▒██▒█░ ██░▓░░▓░░▓░░▓░█\n░░░███▒██▒███░░ ███░░░▓░░▓░░░██\n░█▒▓█▒████▒█▓█░ █░▓▒░▓░░░░▓░▒░█\n█▒▒▓▓█▒▒▒▒█▓██░ ░▓▓▒▒░▓▓▓▓░▒░░█\n█▒▒█▓██▒▒█▓█▒▒█ ░▓▓░▒░░▓▓░▒░▓▓░\n█▒██▓█▓██▓▓█▒▒█ ░▓░░▒░▒░░▒▒░▓▓░\n░█░██▒████████░ █░█░░▓░░░░░░░░█\n░░░█▓▓▓▓█▓▓█░░░ ███░▒▒▒▒░▒▒░███\n░░░░█▓▓▓███░░░░ ████░▒▒▒░░░████\n░░████████████░ ██░░░░░░░░░░░░█\n\n\n## Author\n\nPete Elmore.  Feel free to email me using pete at debu dot gs.  I'm on\nGitHub at http://github.com/pete .  Also there's http://debu.gs/ , which\nruns Watts.\n\n### Acknowledgements\n\nThe commits coming just from me is a bit misleading!  Suggestions and tweaks\nhave been provided by friends and coworkers:\n\n* John Dewey ( http://bitbucket.org/retr0h )\n* Jim Radford ( http://github.com/radford )\n* Johnathon Britz ( http://github.com/johnathonbritz )\n* Justin George ( http://github.com/jaggederest )\n\nAnd, as with Hoshi ( http://github.com/pete/hoshi ), I think I'll continue the\ntradition of crediting the music I was blasting while writing out the first\ndraft:  Softball and Dance☆Man.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpete%2Fwatts","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpete%2Fwatts","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpete%2Fwatts/lists"}