{"id":17723585,"url":"https://github.com/doriantaylor/rb-intertwingler","last_synced_at":"2025-08-21T03:32:52.695Z","repository":{"id":56891199,"uuid":"134652505","full_name":"doriantaylor/rb-intertwingler","owner":"doriantaylor","description":"Intertwingler is an application server for creating dense hypermedia networks.","archived":false,"fork":false,"pushed_at":"2025-07-24T15:54:00.000Z","size":2075,"stargazers_count":67,"open_issues_count":1,"forks_count":2,"subscribers_count":11,"default_branch":"main","last_synced_at":"2025-08-05T09:26:27.934Z","etag":null,"topics":["hypermedia","link-rot","rdf"],"latest_commit_sha":null,"homepage":"https://intertwingler.net","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/doriantaylor.png","metadata":{"files":{"readme":"README-OLD.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2018-05-24T02:51:13.000Z","updated_at":"2025-07-24T15:54:04.000Z","dependencies_parsed_at":"2023-11-19T20:28:57.649Z","dependency_job_id":"ff5bc2bd-eeea-4ba9-bca3-a701e02a9900","html_url":"https://github.com/doriantaylor/rb-intertwingler","commit_stats":null,"previous_names":["doriantaylor/rb-rdf-sak"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/doriantaylor/rb-intertwingler","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doriantaylor%2Frb-intertwingler","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doriantaylor%2Frb-intertwingler/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doriantaylor%2Frb-intertwingler/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doriantaylor%2Frb-intertwingler/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/doriantaylor","download_url":"https://codeload.github.com/doriantaylor/rb-intertwingler/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doriantaylor%2Frb-intertwingler/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271420540,"owners_count":24756589,"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","status":"online","status_checked_at":"2025-08-21T02:00:08.990Z","response_time":74,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["hypermedia","link-rot","rdf"],"created_at":"2024-10-25T15:43:24.627Z","updated_at":"2025-08-21T03:32:52.353Z","avatar_url":"https://github.com/doriantaylor.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Intertwingler — An Engine for Dense Hypermedia\n\nThis software is an _engine_ for creating _dense hypermedia_ networks.\nDense hypermedia is what the Web, out of the box, is _not_. The Web is\n_sparse_ hypermedia: big, long documents, with few links aside from\nthings like navigation and footers. Dense hypermedia is all about\n_short_ resources connected by _lots_ of links. One example of dense\nhypermedia are the personal knowledge management systems, colloquially\nknown as _tools for thought_. Another is _knowledge graphs_. The goal\nof this product is to support those categories of application, and\nothers — perhaps even art or literature.\n\n\u003e One important success criterion is to eliminate the mundane aspects\n\u003e of \"building a website\", and otherwise get out of the way.\n\nThis is very much a _speaking artifact_: Since the ultimate goal is to\ncreate better conditions for developing dense hypermedia on the Web by\nretrofitting it with the capabilities of systems that preceded it\n(real and imagined), there are a number of subsidiary problems that\nneed to be solved, and this system implements concrete ways to solve them.\n\n## Links are first-class citizens\n\nBefore we can do anything related to dense hypermedia, we have to\nsolve for [_link rot_](https://en.wikipedia.org/wiki/Link_rot). The\nmedian URL has a lifespan that can be measured in weeks. If you have\norders of magnitude more addressable resources under management than\nthe median, this kind of performance is a non-starter. Link rot\ndoesn't need to happen (at least, for now, up to keeping one's domain\nname bill paid), but what _does_ need to happen in order to fix it is\na radical rethinking of how Web-based software is made. This system\nshows how to do it.\n\n\u003e [My own site](https://doriantaylor.com/), which admittedly has only\n\u003e been on this system since I made the latter in 2018, nevertheless\n\u003e still serves every URL it has ever exposed, dating back to the\n\u003e summer of 2008. I also use it for my client extranets, and my book\n\u003e project, [The Nature of Software](https://the.natureof.software/).\n\nOne ongoing criticism of the Web by [Ted\nNelson](https://en.wikipedia.org/wiki/Ted_Nelson), who in 1960\ncoined the term _hypertext_ (not to mention what it means to\n[_intertwingle_](https://en.wikipedia.org/wiki/Intertwingularity)), is\nthat links only go in one direction: without extra apparatus, you\ncan't see what links _to_ you. Well, turns out the apparatus to\ndisplay backlinks is the same apparatus as the one for eliminating\nlink rot.\n\n## Links (_and_ resources) have different species\n\nThe Web has three kinds of links: the conventional arc that when\nactivating it (typically) completely replaces the representational state\n(both `\u003ca\u003e` and forms), what I would characterize as a \"naïve embed\" —\nimages, A/V, and `iframe` documents — and non-printing metadata.\nEarlier systems had all kinds of other links besides, like\n[stretchtext](https://en.wikipedia.org/wiki/StretchText), conditional\ndisplay, and proper, seamless\n[transclusion](https://en.wikipedia.org/wiki/Transclusion). These of\ncourse can all be done on the Web, but the solutions are suboptimal.\nIn particular, the embedded metadata that drives these capabilities\ntends to be ad-hoc and mutually incompatible, making it single-purpose\nfor some particular UI framework or other. Many content management\nsystems, moreover, have a concept of _content_ type, but few systems —\neven sophisticated PKM systems — have a concept of _link_ type (as in,\nprecisely what the link _means_). It's the link types in\nconjunction with the content types that make it possible to _derive_\nhow they ought to be rendered in the user interface.\n\n## Don't copy what you can reference\n\nOne perennial problem of informational content, whether on the Web or\neven digital at all, is keeping it up to date. A necessary condition\nfor keeping content up to date is ensuring that there is precisely\n_one_ authoritative copy of it.\n\n\u003e The key word here of course is _authoritative_. We will invariably\n\u003e need multiple copies for things like cache and backups, but having\n\u003e exactly _one_ copy that drives all the others is absolutely\n\u003e indispensable.\n\nThis principle can be extended to resources which can be modeled as\n_functions_ of other resources, for example the HTML that corresponds\nto a Markdown document, or a cropped and/or resized image. Explicitly\nmodeling these as transformations shrinks the footprint of original\ncontent to be managed.\n\nFinally, for content to be reusable it must be finely _addressable_,\nwith durable addresses at both the document and _sub_-document level.\n\n## Standard interfaces \u0026 data transparency\n\nWith this system we're trying to imagine what it means to be a \"model\ncitizen\" on the Web: a reliable source of clear, actionable\ninformation. This is not only entails everything already discussed,\nbut also:\n\n* structured, machine-actionable data is available for every resource,\n* interfaces are standard, so as not to require custom API adapters,\n* this includes data _semantics_ as well as syntax,\n* A user (with sufficient authority) should be able to export 100% of\n  the system's instance data, and furthermore that data should _mean\n  something_ to other systems.\n\n## Layered system, clear development targets\n\nThis system anticipates being situated in a heterogeneous operating\nenvironment, sharing space with other programming languages and\nframeworks. Indeed, this engine can be thought of as a \"language bus\",\nthat marshals all things Ruby. The design is intended to be copied to\nother programming languages, and these systems are expected to\ninteroperate in a daisy chain-like configuration.\n\nEvery component in this system, including the central piece that does\nthe routing, is implemented as a\n[Rack](https://rubydoc.info/gems/rack/) handler, which ultimately\ncould be run as a stand-alone microservice. The handlers subsequently\nsubdivide into two subspecies:\n\n* **Content handlers** that either originate information resources or\n  proxy them from somewhere else,\n* **transforms** that manipulate HTTP requests or responses in transit.\n\nSince every building block in the system is a potentially stand-alone\nRack component, the language spoken between them is nominally HTTP.\nThis not only makes for _extremely_ well-defined development targets —\nyou get a request and return a response — but the system anticipates\nfuture segmentation, including, as mentioned, across different\nprogramming languages, machines, and runtimes.\n\n\u003e I should note that HTTP communication within the process space of a\n\u003e particular runtime is simulated, so we don't waste resources\n\u003e unnecessarily re-parsing and serializing. I also have a rudimentary\n\u003e sub-protocol in the works for specific constraints on how these\n\u003e components, particularly the transforms, are expected to behave.\n\n# History\n\nThis module began life as a thing called `RDF::SAK`, or the Content\nSwiss Army Knife. After positing the notion of [content management\n_meta-system_](https://doriantaylor.com/content-management-meta-system),\nI made an initial cut in 2018, to support some work I was doing\nfor a client. It quickly became a breadboard and/or test environment\nfor developing what I just referred to as \"good ideas about Web\ncontent\", which I ultimately realized as a static website generator,\nin the same vein as [Jekyll](https://jekyllrb.com/) or\n[11ty](https://www.11ty.dev/). Since most of my work was around\ndurable addressing and embedded metadata, a live engine was not a high\npriority. Priorities have since changed.\n\nFive years prior to creating `RDF::SAK`, in 2013, I designed a\nprotocol to aid in the development of Semantic Web applications called\n[RDF-KV](https://doriantaylor.com/rdf-kv). It provides an\nextraordinarily simple mechanism for getting RDF statement deltas\n(i.e., commands to add and/or remove statements) from a Web client to\na graph database on the server, with a minimum of moving parts (i.e.,\nno JavaScript). To test the implementation, I needed a complete\nvocabulary, so I used [the IBIS\nvocabulary](https://vocab.methodandstructure.com/ibis#) I had written\na year earlier, and created a tool called\n[`App::IBIS`](https://github.com/doriantaylor/p5-app-ibis). This tool\n[turned out to be useful](https://ibis.makethingsmakesense.com/), but\nlimited in its capacity for expansion, because it was written in Perl,\nwhich does not have an RDF reasoner, a piece of software that is both\nhighly abstract and difficult to write (a rudimentary yet satisficing\none of which Ruby happens to possess). Without a reasoner, `App::IBIS`\nwas much too sclerotic to develop very far past the initial prototype.\n\n\u003e `App::IBIS` is unambiguously a dense hypermedia application, and\n\u003e developing it meant generating a lot of markup that was thick with\n\u003e embedded RDFa metadata. This led me to create a family of terse\n\u003e markup generators\n\u003e ([Perl](https://metacpan.org/pod/Role::Markup::XML),\n\u003e [Ruby](https://github.com/doriantaylor/rb-xml-mixup),\n\u003e [JavaScript](https://github.com/doriantaylor/js-markup-mixup)) with\n\u003e some nice advantages over their incumbents. Working extensively with\n\u003e RDFa helped develop technique for reusing the embedded metadata for\n\u003e directing presentation markup, as well as providing the basis for\n\u003e CSS selectors in both HTML and SVG.\n\nThe plan for `RDF::SAK` was always to turn it into a live engine that\ncould be accessed and updated online. Nevertheless, due to its\ndecidedly organic origins, it was (and still very much is) a huge mess\nthat needed (and still needs) several rounds of intense refactoring. I\nbegan this work in December of 2021 but suspended it a few weeks later\ndue to an injury, and this refactor had to take a back seat to other\npriorities for most of 2022. I decided early this year (2023) I was\ngoing to complete the overhaul no matter what, which, it later turned\nout, the [Summer of Protocols](https://summerofprotocols.com/)\norganizers have graciously elected to sponsor.\n\n\u003e I have also gotten some interest, beginning last year, in the use of\n\u003e IBIS as a planning tool. Part of the impetus for getting `RDF::SAK`\n\u003e to a state where it can take over from the torpid `App::IBIS` is\n\u003e that I have an [entire project planning\n\u003e framework](https://vocab.methodandstructure.com/process-model#)\n\u003e based on\n\u003e [IBIS](https://en.wikipedia.org/wiki/Issue-based_information_system)\n\u003e for which any tooling will need a more flexible substrate. I am also\n\u003e grateful for my clients who support this development.\n\nThis project also represents a confluence of over two decades of work\non the Web. What is now called `Intertwingler` closely tracks a design\nI sketched out back in 2006 for a \"Web substrate\", with the intent of\ndecoupling functionality that _generates_ content from that which\n_manipulates_ it, on the premise that separating the two would result\nin both ending up markedly simpler. This design drew on technique I\nhad developed at my first tech job back in 1999.\n\n\u003e During my night shifts in 1999-2000 as a baby system administrator,\n\u003e I had a lot of time to mess around with\n\u003e [`mod_perl`](https://perl.apache.org/), the Perl bindings for the\n\u003e Apache API. One thing you learn when you work directly with a server\n\u003e API is that almost all Web development happens in a tiny corner (the\n\u003e _response handler_ or _content handler_) of what _you_ are able to\n\u003e address. It turns out there are several other places one can\n\u003e manipulate both the request _and_ response (header twiddling, URL\n\u003e rewriting, access control, filters — albeit filters came a couple\n\u003e years later) that are orthogonal to the actual application. Indeed,\n\u003e many Web application frameworks recapitulate this structure within\n\u003e their own confines, and the result is undoubtedly a whole lot of\n\u003e redundant code.\n\nI had had a personal site from 1998 to about 2003, and by 2008 I was\nready to put one up again. It was around this time I had realized that\none could use XSLT (which I had picked up in 2001) to transform\n(X)HTML into _itself_, meaning it could be used _in the browser_ as an\nextremely lazy Web template engine that does its page composition at\nthe _network_ level. This means you can mix content sources on the\nserver side, which can be any mixture of static or dynamic content\nwritten in any programming language or framework you like, since all\ncommunication happens using standard protocols and data formats. This\nis a technique I have used and expanded on for the last 15 years.\n\n\u003e Specifically, I have written [an RDFa query\n\u003e engine](https://github.com/doriantaylor/xslt-rdfa) (2016) and\n\u003e [seamless transclusion\n\u003e mechanism](https://github.com/doriantaylor/xslt-transclusion)\n\u003e (2018). While XSLT is still actively developed and used in\n\u003e publishing _outside_ the Web, I am somewhat concerned about its\n\u003e future as a native capability in the browser. XML is irredeemably\n\u003e out of fashion in mainstream Web circles (despite ostensibly having\n\u003e been reinvented as \"custom elements\"), but in my opinion XSLT is,\n\u003e for reasons too numerous to articualate here, unparalleled in its\n\u003e ability to manipulate markup — which is why I continue to use\n\u003e it. Indeed, [a compact, easier-to-type XSLT\n\u003e syntax](https://doriantaylor.com/file/xslt-mockup) similar to\n\u003e [RelaxNG](https://www.oasis-open.org/committees/relax-ng/compact-20021121.html)\n\u003e may be enough to renew interest in it. I should note that the use of\n\u003e XSLT is not strictly necessary; you could probably acccomplish the\n\u003e same effect using (a lot more) JavaScript.\n\nWhen I went to put the site up in 2008, I was keenly interested in\ncreating _dense hypermedia_ (though I would coin that term much\nlater). I wanted to convey information without forcing the audience to\nread any more than they had to. The constraints were:\n\n1. that no page should be so long that it scrolls (on an average\n   desktop monitor),\n2. any digressions, footnotes or parenthetical remarks would be\n   hived off to their own page and linked,\n3. no `404` errors — URLs do not get exposed to the wild until there\n   is something at that location.\n\nThese constraints made it very difficult to operate. For one, having\nto stop and think up a URL for a page because you happened to digress\na bit in the page you were just writing (which, since URLs tend to\ntrack with titles, ultimately meant coming up with a title) is a\njarring context switch of considerable cognitive overhead. Moreover,\nthis would an exponential jump in workload, because the digressions\nwould invariably generate their own digressions, and since nothing\ncould ship until all of it was complete (or at least roughed in), it\nwould take forever to do anything. Notwithstanding, I got about 40\npages into what I called a [Resource Handling and Representation\nPolicy](https://doriantaylor.com/policy/resource-handling-and-representation)\ndone in this style, before I gave up and decided to just write essays.\n\n\u003e This policy manual actually worked out a number of design decisions\n\u003e that are still perfectly valid fifteen years later, and have made\n\u003e their way into the `Intertwingler`.\n\nThe experience of writing this policy promptly moved me to start\nthinking about a mechanism that would enable information resources to\nbe stored under canonical identifiers (specifically\n[UUIDs](https://datatracker.ietf.org/doc/html/rfc4122) and\n[cryptographic hashes](https://datatracker.ietf.org/doc/html/rfc6920))\nthat traded off legibility for being _durable_, and overlay\nhuman-friendly addresses on top. It would likewise track changes to\nthese addresses, try to fix errors, and ensure _all_ URLs on a domain\nthat have _ever_ been exposed to the wild route to _something_. I got\nthis subsystem to finally work in `RDF::SAK` in 2019, and it remains\npresent in `Intertwingler`.\n\n\u003e The need to solve the same problem for fragment identifiers led me\n\u003e ([apparently back in\n\u003e 2012](https://metacpan.org/pod/Data::UUID::NCName)) to invent [a\n\u003e compact UUID\n\u003e representation](https://datatracker.ietf.org/doc/html/draft-taylor-uuid-ncname)\n\u003e which I am (slowly) trying to get graduated into an RFC.\n\nThe state mechanism for this URL naming history is a [content\ninventory\nvocabulary](https://vocab.methodandstructure.com/content-inventory#) I\nbegan roughing in around 2010. This was originally conceived as a data\nstorage and exchange format for website content inventories, but has\nsince become a catch-all, including a structure for holding\nquantitative metrics to help content strategists apprehend the\ncontours of websites (developed in 2011), and a sophisticated\nset-theoretic mechanism for modeling audiences, and pairing (or\n_anti_-pairing) them with content (2019).\n\nAs I mentioned above, the `Intertwingler` engenders an ultimately\n_simpler_ system by decoupling the _generation_ of content from its\nsubsequent downstream _manipulation_. I had sketched out how this was\ngoing to work as far back as 2008, along with a couple ill-fated\nprototypes. It wasn't until a project in 2020 though that I completed\na [Transformation Functions\nOntology](https://vocab.methodandstructure.com/transformation#)\n(started in 2014) and concomitant infrastructure that would resolve\ntransformation functions, apply them to content, and cache their\nresults. This infrastructure depends on earlier work on\ncontent-addressable stores ([Perl in\n2013](https://github.com/doriantaylor/p5-store-digest), [Ruby in\n2019](https://github.com/doriantaylor/rb-store-digest)), that use\n[RFC6920](https://datatracker.ietf.org/doc/html/rfc6920) `ni:` URIs,\nmaking them compatible with RDF.\n\n\u003e On a similar tack, I also explored [creating a registry for query\n\u003e parameters](https://metacpan.org/pod/Params::Registry::Template)\n\u003e (2015), with the triple purpose of parsing and validating input,\n\u003e generating round-trip-stable query strings, and facilitating the\n\u003e creation of organization-wide policy for the names, types, and\n\u003e semantics of query parameters. I do not currently have a Ruby port\n\u003e of this particular software, but I will probably eventually make\n\u003e one, along with an RDF vocabulary as an extension to the\n\u003e transformation one for expressing the configuration.\n\nAnd so, the `Intertwingler` is an odyssey spanning over two decades,\nwhich is fitting, since its ultimate goal is to retrofit the Web with\nthe capabilities of its hypermedia predecessors.\n\n# Anatomy\n\nAs I have hopefully communicated, the `Intertwingler` is in a state of\nabsolute disarray, still undergoing its metamorphosis from the\nless-ambitious and much more organic `RDF::SAK`. I have tried to\noutline some of the more important modules; those I have left out are\neither not very interesting (such as the generated vocabularies under\n[`Intertwingler::Vocab`](lib/intertwingler/vocab.rb) or slated for\nremoval. Checkmarks on the bullet points indicate the modules are\ncomplete enough to use.\n\n## Essential components\n\n* [X] [`Intertwingler::GraphOps`](lib/intertwingler/graphops.rb) is a\n      mix-in that extends `RDF::Queryable` with the all-important\n      inferencing operations.\n* [X] [`Intertwingler::Resolver`](lib/intertwingler/resolver.rb) is\n      the _also_-all-important URI resolver.\n* [ ] [`Intertwingler::Representation`](lib/intertwingler/representation.rb)\n      is a cheap knockoff of a\n      [monad](https://en.wikipedia.org/wiki/Monad_(functional_programming))-like\n      structure that enables parsed, in-memory representations of content to\n      persist across successive transformations, so they don't get\n      unnecessarily serialized and reparsed.\n* [ ] [`Intertwingler::Document`](lib/intertwingler/document.rb)\n      houses (mostly) context-free markup generation (though may be\n      dissipated into other modules).\n\n## Engine components\n\nEverything in the engine, including the engine itself, is an\n[`Intertwingler::Handler`](lib/intertwingler/handler.rb) that accepts\na `Rack::Request` and returns a `Rack::Response`, plus an embedded\nadapter so it can be used directly as a stand-alone Rack application.\n\n### Handler\n\n* [ ] [`Intertwingler::Handler::Generated`](lib/intertwingler/handler/generated.rb)\n* [ ] [`Intertwingler::Handler::FileSystem`](lib/intertwingler/handler/filesystem.rb)\n* [ ] [`Intertwingler::Handler::CAS`](lib/intertwingler/handler/cas.rb)\n* [ ] [`Intertwingler::Handler::Proxy`](lib/intertwingler/handler/proxy.rb)\n\n### Transforms\n\nAn [`Intertwingler::Transform`](lib/intertwingler/transform.rb) is a\nspecialized [`Intertwingler::Handler`](lib/intertwingler/handler.rb)\nthat responds to `POST` requests to a single URI. I am still working\nout the details of a protocol but the general sense is you `POST` a\npayload and it returns the transformed payload back. When a transform\nis in the engine, this happens automatically by subrequest. Shortcuts\nare in place (via\n[`Intertwingler::Representation`](lib/intertwingler/representation.rb))\nfor transformations that happen in the same process space.\n\n* [ ] [`Intertwingler::Transform`](lib/intertwingler/transform.rb) is\n      the base which also includes\n      `Intertwingler::Transform::Harness`, which probably needs some\n      to bring it up to par with the rest of the system.\n* [ ] `Intertwingler::Transform::Tidy` for sanitizing/normalizing HTML\n      via [`tidy`](https://www.html-tidy.org/).\n* [ ] `Intertwingler::Transform::Nokogiri` for transforming HTML/XML\n      via `Nokogiri`;\n  * [ ] Transform Markdown into HTML\n  * [ ] Turn HTML into XHTML and vice versa\n  * [ ] Strip comments\n  * [ ] Reindent markup\n  * [ ] Repair/\"rehydrate\" RDFa\n    * [ ] Normalize RDFa prefixes\n  * [ ] Mangle `mailto:` addresses (by whatever house style) to prevent spam\n  * [ ] Insert stylesheet references\n  * [ ] Rewrite links\n  * [ ] Add backlinks\n  * [ ] Add secondary links (e.g. glossary entries)\n  * [ ] Add (e.g.) Amazon affiliate codes to `amazon.com` links\n  * [ ] Add social media metadata (Google, Facebook, Twitter, whoever…)\n* [ ] `Intertwingler::Transform::Vips` for images via `Vips`.\n  * [ ] Crop images\n  * [ ] Resize (downward only due to potential denial of resources)\n  * [ ] Desaturate\n  * [ ] Posterize\n  * [ ] etc…\n\n## \"Offline\" components\n\n\n* [ ] [`Intertwingler::CLI`](lib/intertwingler/cli.rb) is the command\n      line harness.\n* [ ] `Intertwingler::Static` is (to be) an \"end cap\" on the engine\n      that performs the legacy static site generator function.\n* [ ] [`Intertwingler::DocStats`](lib/intertwingler/docstats.rb)\n      gathers statistics about a corpus of documents.\n* [ ] [`Intertwingler::NLP`](lib/intertwingler/nlp.rb) is a _very_\n      rudimentary natural language processor for extracting terminology\n      (jargon, acronyms, proper nouns etc.) from a corpus of documents.\n* [ ] [`Intertwingler::URLRunner`](lib/intertwingler/urlrunner.rb) is\n      planned as a generic crawler (eventually with some kind of\n      `Handler` interface) for resolving link previews.\n\n# Documentation\n\nAPI documentation, for what it's worth at the moment, can be found [in\nthe usual place](https://rubydoc.info/github/doriantaylor/rb-intertwingler/main).\n\n# Installation\n\nFor now I recommend just running the library out of its source tree:\n\n```bash\n~$ git clone git@github.com/doriantaylor/rb-intertwingler.git intertwingler\n~$ cd intertwingler\n~/intertwingler$ bundle install\n```\n\n# Contributing\n\nBug reports and pull requests are welcome at\n[the GitHub repository](https://github.com/doriantaylor/rb-intertwingler).\n\n# Copyright \u0026 License\n\n©2018-2023 [Dorian Taylor](https://doriantaylor.com/)\n\nThis software is provided under\nthe [Apache License, 2.0](https://www.apache.org/licenses/LICENSE-2.0).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdoriantaylor%2Frb-intertwingler","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdoriantaylor%2Frb-intertwingler","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdoriantaylor%2Frb-intertwingler/lists"}