{"id":15293292,"url":"https://github.com/ronin-rb/ronin-support-web","last_synced_at":"2026-02-06T03:01:17.979Z","repository":{"id":197183742,"uuid":"698118058","full_name":"ronin-rb/ronin-support-web","owner":"ronin-rb","description":"A web support library for ronin-rb.","archived":false,"fork":false,"pushed_at":"2026-01-12T11:52:11.000Z","size":108,"stargazers_count":0,"open_issues_count":2,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-01-12T19:27:02.520Z","etag":null,"topics":["helpers-library","html","ronin-rb","ruby","web","websockets","xml"],"latest_commit_sha":null,"homepage":"https://ronin-rb.dev","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ronin-rb.png","metadata":{"files":{"readme":"README.md","changelog":"ChangeLog.md","contributing":null,"funding":null,"license":"COPYING.txt","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},"funding":{"open_collective":"ronin-rb","patreon":"roninrb"}},"created_at":"2023-09-29T07:40:36.000Z","updated_at":"2026-01-12T11:51:59.000Z","dependencies_parsed_at":"2023-11-23T22:32:06.245Z","dependency_job_id":"10075ec2-d3f1-4269-a05c-e634bdf7e4e9","html_url":"https://github.com/ronin-rb/ronin-support-web","commit_stats":{"total_commits":76,"total_committers":1,"mean_commits":76.0,"dds":0.0,"last_synced_commit":"110b6567362138e15ed34f29f8a2cd91ac4d7f45"},"previous_names":["ronin-rb/ronin-support-web"],"tags_count":15,"template":false,"template_full_name":null,"purl":"pkg:github/ronin-rb/ronin-support-web","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ronin-rb%2Fronin-support-web","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ronin-rb%2Fronin-support-web/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ronin-rb%2Fronin-support-web/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ronin-rb%2Fronin-support-web/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ronin-rb","download_url":"https://codeload.github.com/ronin-rb/ronin-support-web/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ronin-rb%2Fronin-support-web/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29147369,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-06T02:39:25.012Z","status":"ssl_error","status_checked_at":"2026-02-06T02:37:22.784Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5: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":["helpers-library","html","ronin-rb","ruby","web","websockets","xml"],"created_at":"2024-09-30T16:46:05.717Z","updated_at":"2026-02-06T03:01:17.956Z","avatar_url":"https://github.com/ronin-rb.png","language":"Ruby","funding_links":["https://opencollective.com/ronin-rb","https://patreon.com/roninrb"],"categories":[],"sub_categories":[],"readme":"# ronin-support-web\n\n[![CI](https://github.com/ronin-rb/ronin-support-web/actions/workflows/ruby.yml/badge.svg)](https://github.com/ronin-rb/ronin-support-web/actions/workflows/ruby.yml)\n[![Code Climate](https://codeclimate.com/github/ronin-rb/ronin-support-web.svg)](https://codeclimate.com/github/ronin-rb/ronin-support-web)\n\n* [Website](https://ronin-rb.dev/)\n* [Source](https://github.com/ronin-rb/ronin-support-web)\n* [Issues](https://github.com/ronin-rb/ronin-support-web/issues)\n* [Documentation](https://ronin-rb.dev/docs/ronin-support-web)\n* [Discord](https://discord.gg/6WAb3PsVX9) |\n  [Mastodon](https://infosec.exchange/@ronin_rb)\n\n## Description\n\nronin-support-web is a web support library for ronin-rb. ronin-support-web\nprovides many helper methods for parsing HTML/XML, fetching web pages, and\nWebSockets.\n\n## Features\n\n* Provides helper methods for parsing HTML/XML.\n  * Also provides additional extensions to [Nokogiri][nokogiri] using\n    [nokogiri-ext].\n* Provides helper methods for working with WebSockets.\n\n## Examples\n\n```ruby\nrequire 'ronin/support/web'\ninclude Ronin::Support::Web\n\nhtml_parse \"\u003chtml\u003e...\u003c/html\u003e\"\n# =\u003e #\u003cNokogiri::HTML::Document:...\u003e\n```\n\n### HTML\n\nParse an HTML string:\n\n```ruby\ndoc = html_parse(\"\u003chtml\u003e\\n  \u003cbody\u003e\\n    \u003cp\u003eHello world\u003c/p\u003e\\n  \u003c/body\u003e\\n\u003c/html\u003e\\n\")\n# =\u003e \n# #(Document:0x6ab8 {\n#   name = \"document\",\n#   children = [\n#     #(DTD:0x6be4 { name = \"html\" }),\n#     #(Element:0x6cd4 {\n#       name = \"html\",\n#       children = [\n#         #(Text \"\\n  \"),\n#         #(Element:0x6e64 {\n#           name = \"body\",\n#           children = [\n#             #(Text \"\\n    \"),\n#             #(Element:0x6ff4 { name = \"p\", children = [ #(Text \"Hello world\")] }),\n#             #(Text \"\\n  \")]\n#           }),\n#         #(Text \"\\n\")]\n#       })]\n#   })\n```\n\nParse a HTML file:\n\n```ruby\ndoc = html_open(\"index.html\")\n# =\u003e #\u003cNokogiri::HTML::Document:...\u003e\n```\n\nSearching an HTML document using [XPath] or CSS-path:\n\n```ruby\nnodes = doc.search('//div/p')\nnodes = doc.search('div p.class')\n# =\u003e [#\u003cNokogiri::HTML::Element:...\u003e, ...]\n\nnode = doc.at('#id')\n# =\u003e #\u003cNokogiri::HTML::Element:...\u003e\n```\n\nBuild a HTML document:\n\n```ruby\ndoc = html_build do\n  html {\n    head {\n      script(type: 'text/javascript', src: 'redirect.js')\n    }\n  }\nend\n\nputs doc.to_html\n# \u003c!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\"\u003e\n# \u003chtml\u003e\u003chead\u003e\u003cscript src=\"redirect.js\" type=\"text/javascript\"\u003e\u003c/script\u003e\u003c/head\u003e\u003c/html\u003e\n```\n\n### XML\n\nParse an XML response body:\n\n```ruby\nxml_parse(\"\u003c?xml version=\\\"1.0\\\"?\u003e\\n\u003cusers\u003e\\n  \u003cuser\u003e\\n    \u003cname\u003eadmin\u003c/name\u003e\\n    \u003cpassword\u003e0mni\u003c/password\u003e\\n  \u003cuser\u003e\\n\u003c/users\u003e\\n\")\n# =\u003e\n# #(Document:0xdebc {\n#   name = \"document\",\n#   children = [\n#     #(Element:0xdfe8 {\n#       name = \"users\",\n#       children = [\n#         #(Text \"\\n  \"),\n#         #(Element:0xe178 {\n#           name = \"user\",\n#           children = [\n#             #(Text \"\\n    \"),\n#             #(Element:0xe308 { name = \"name\", children = [ #(Text \"admin\")] }),\n#             #(Text \"\\n    \"),\n#             #(Element:0xe538 { name = \"password\", children = [ #(Text \"0mni\")] }),\n#             #(Text \"\\n  \"),\n#             #(Element:0xe768 { name = \"user\", children = [ #(Text \"\\n\")] }),\n#             #(Text \"\\n\")]\n#           })]\n#       })]\n#   })\n```\n\nParse a XML file:\n\n```ruby\ndoc = html_open(\"data.xml\")\n# =\u003e #\u003cNokogiri:XML:::Document:...\u003e\n```\n\nSearching an XML document using [XPath]:\n\n```ruby\nusers = doc.search('//user')\n# =\u003e [#\u003cNokogiri::XML::Element:...\u003e, ...]\n\nadmin = doc.at('//user[@name=\"admin\"]')\n# =\u003e #\u003cNokogiri::XML::Element:...\u003e\n```\n\nBuild a XML document:\n\n```ruby\ndoc = xml_build do\n  playlist {\n    mp3 {\n      file { text('02 THE WAIT.mp3') }\n      artist { text('Evil Nine') }\n      track { text('The Wait feat David Autokratz') }\n      duration { text('1000000000') }\n    }\n  }\nend\n\nputs doc.to_xml\n# \u003c?xml version=\"1.0\"?\u003e\n# \u003cplaylist\u003e\n#   \u003cmp3\u003e\n#     \u003cfile\u003e02 THE WAIT.mp3\u003c/file\u003e\n#     \u003cartist\u003eEvil Nine\u003c/artist\u003e\n#     \u003ctrack\u003eThe Wait feat David Autokratz\u003c/track\u003e\n#     \u003cduration\u003e1000000000\u003c/duration\u003e\n#   \u003c/mp3\u003e\n# \u003c/playlist\u003e\n```\n\n### Web Requests\n\nGets a URL and follows any redirects:\n\n```ruby\nget 'https://example.com/'\n# =\u003e #\u003cNet::HTTPResponse:...\u003e\n```\n\nGets a URL and parses the HTML response:\n\n```ruby\nget_html 'https://example.com/'\n# =\u003e #\u003cNokogiri::HTML::Document:...\u003e\n```\n\nGets a URL and parses the XML response:\n\n```ruby\nget_xml 'https://example.com/sitemap.xml'\n# =\u003e #\u003cNokogiri::XML::Document:...\u003e\n```\n\nGets a URL and parses the JSON response:\n\n```ruby\nget_json 'https://example.com/api/endpoint.json'\n# =\u003e {...}\n```\n\nPOSTs to a URL and follows any redirects:\n\n```ruby\npost 'https://example.com/form', form_data: {'foo' =\u003e 'bar'}\n# =\u003e #\u003cNet::HTTPResponse:...\u003e\n```\n\nPOSTs to a URL and parses the HTML response:\n\n```ruby\npost_html 'https://example.com/form', form_data: {'foo' =\u003e 'bar'}\n# =\u003e #\u003cNokogiri::HTML::Document:...\u003e\n```\n\nPOSTs to a URL and parses the XML response:\n\n```ruby\npost_xml 'https://example.com/form', form_data: {'foo' =\u003e 'bar'}\n# =\u003e #\u003cNokogiri::XML::Document:...\u003e\n```\n\nPOSTs to a URL and parses the JSON response:\n\n```ruby\npost_json 'https://example.com/api/endpoint.json', json: {foo: 'bar'}\n# =\u003e {...}\n```\n\n### WebSockets\n\nConnecting to a WebSocket:\n\n```ruby\nwebsocket = websocket_connect('ws://websocket-echo.com')\n\nwebsocket.send_frame(\"foo bar\")\n# =\u003e 13\nwebsocket.recv_frame\n# =\u003e \u003cWebSocket::Frame::Incoming::Client:0x000227a4 @decoded=true, @code=nil, @data=\"foo bar\", @version=13, @handler=#\u003cWebSocket::Frame::Handler::Handler07:0x00007f7d4c9b0ed0 @frame=\u003cWebSocket::Frame::Incoming::Client:0x000227a4 @decoded=true, @code=nil, @data=\"foo bar\", @version=13, @handler=#\u003cWebSocket::Frame::Handler::Handler07:0x00007f7d4c9b0ed0 ...\u003e, @type=:text\u003e, @application_data_buffer=nil\u003e, @type=:text\u003e\nwebsocket.send_frame(\"hello world\")\n# =\u003e 17\nwebsocket.recv\n# =\u003e \"hello world\"\n```\n\nStarting a WebSocket server and receiving connections:\n\n```ruby\nserver = websocket_server('ws://localhost:1337/')\nclient = server.accept\nclient.send(\"hello\")\nclient.recv\n# =\u003e \"good, how are you\"\n```\n```ruby\nclient = websocket_client('ws://localhost:1337/')\nclient.recv\n# =\u003e \"hello\"\nclient.send(\"good, how are you\")\n```\n\n## Requirements\n\n* [Ruby] \u003e= 3.0.0\n* [ronin-support] ~\u003e 1.1\n* [nokogiri] ~\u003e 1.4\n* [nokogiri-ext] ~\u003e 0.1\n* [websocket] ~\u003e 1.2\n\n## Install\n\n```shell\n$ gem install ronin-support-web\n```\n\n### Gemfile\n\n```ruby\ngem 'ronin-support-web', '~\u003e 0.1'\n```\n\n### gemspec\n\n```ruby\ngem.add_dependency 'ronin-support-web', '~\u003e 0.1'\n```\n\n## Development\n\n1. [Fork It!](https://github.com/ronin-rb/ronin-support-web/fork)\n2. Clone It!\n3. `cd ronin-support-web/`\n4. `bundle install`\n5. `git checkout -b my_feature`\n6. Code It!\n7. `bundle exec rake spec`\n8. `git push origin my_feature`\n\n## License\n\nronin-support-web - A web support library for ronin-rb.\n\nCopyright (c) 2023-2026 Hal Brodigan (postmodern.mod3@gmail.com)\n\nronin-support-web is free software: you can redistribute it and/or modify\nit under the terms of the GNU Lesser General Public License as published\nby the Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.\n\nronin-support-web is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU Lesser General Public License for more details.\n\nYou should have received a copy of the GNU Lesser General Public License\nalong with ronin-support-web.  If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n[Ruby]: https://www.ruby-lang.org\n[nokogiri]: https://nokogiri.org/\n[nokogiri-ext]: https://github.com/postmodern/nokogiri-ext#readme\n[ronin-support]: https://github.com/ronin-rb/ronin-support#readme\n[websocket]: https://github.com/imanel/websocket-ruby#readme\n\n[XPath]: https://developer.mozilla.org/en-US/docs/Web/XPath\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fronin-rb%2Fronin-support-web","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fronin-rb%2Fronin-support-web","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fronin-rb%2Fronin-support-web/lists"}