{"id":17336756,"url":"https://github.com/zampino/pastelli","last_synced_at":"2025-04-14T18:41:07.367Z","repository":{"id":30434886,"uuid":"33987955","full_name":"zampino/pastelli","owner":"zampino","description":"An Elixir Plug Adapter for Elli http server ","archived":false,"fork":false,"pushed_at":"2016-10-25T13:36:20.000Z","size":56,"stargazers_count":25,"open_issues_count":1,"forks_count":5,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-28T07:11:12.370Z","etag":null,"topics":["elixir","plug"],"latest_commit_sha":null,"homepage":"","language":"Elixir","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/zampino.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":"2015-04-15T10:38:34.000Z","updated_at":"2024-11-28T16:30:59.000Z","dependencies_parsed_at":"2022-09-15T12:51:41.379Z","dependency_job_id":null,"html_url":"https://github.com/zampino/pastelli","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zampino%2Fpastelli","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zampino%2Fpastelli/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zampino%2Fpastelli/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zampino%2Fpastelli/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zampino","download_url":"https://codeload.github.com/zampino/pastelli/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248938174,"owners_count":21186358,"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":["elixir","plug"],"created_at":"2024-10-15T15:32:21.430Z","updated_at":"2025-04-14T18:41:07.342Z","avatar_url":"https://github.com/zampino.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Pastelli ![travis](https://travis-ci.org/zampino/pastelli.svg)\n\n![alt](logo.png)\n\nPastelli is a colorful Plug adapter for [Elli](//github.com/knutin/elli)\nwith a focus on streaming over chunked\nconnections (read `EventSource`).\n\nFor the moment, this is quite alpha and\nit implements almost all (see below) of the `Plug.Conn.Adapter` behaviour.\n\n## Usage\nAs you would do with your beloved `Plug.Adapters.Cowboy`,\nyou'll type:\n\n```elixir\nPastelli.http MyPlug.Router, [], [port: 4001]\n```\nNow setup your router (or simpler plug) as usual.\nPastelli changes the semantics of EventSource chunked responses,\nin which it doesn't block your router dispatch:\n\n```elixir\ndefmodule MyPlug.Router do\n  use Plug.Router\n  plug :match\n  plug :dispatch\n\n  get \"/connections/:id\" do\n    put_resp_content_type(conn, \"text/event-stream\")\n    |\u003e send_chunked(200)\n    |\u003e register_stream(id)\n    # dispatch doesn't need to block execution,\n    # it enters a chunk loop just after pipeline resolution,\n    # waiting for chunk messages\n  end\n\n  defp register_stream(conn, id) do\n    {:ok, pid} = MyPlug.Connections.register id, conn\n    # usually a :simple_one_for_one supervised\n    # event manager\n\n    Process.link pid\n    # we link the process to the streaming manager!\n    # once the chunk is complete (client closes socket or crashes)\n    # pastelli handler will send a `chunk_complete` exit signal\n    # to the connection process.\n    # It is your responsibility to monitor the event manager and\n    # react on such exits\n    conn\n  end\nend\n```\n\n## A Streaming DSL\n`Pastelli.Router` wraps an extra [`stream`](//github.com/zampino/pastelli/blob/master/lib/pastelli/router.ex) macro around `Plug.Router` and\nimports `Pastelli.Conn`, a module with a few extra functions to manipulate\n`Plug.Conn` chunked-state structs.\n\n```elixir\ndefmodule MyPlug.Router do\n  use Pastelli.Router\n  plug :match\n  plug :dispatch\n\n  stream \"/connections\" do\n    init_chunk(conn, %{text: \"hallo!\"}, event: :handshake, retry: 6000, id: 1234)\n    # sends an initial chunk to event source as early as plug pipeline ends\n    |\u003e register_stream()\n  end\nend\n```\n\nfrom wherever you can access the connection struct,\n`Pastelli.Conn.event/2,3` serializes non-binary message body and\nmeta data.\n\n```elixir\nPastelli.Conn.event(conn, %{some: \"map\"}, event: \"message\", id: \"x4x3\", retry: 6000)\n```\n\n## Examples\nEvent Source based [remote control](https://github.com/zampino/plug_rc) backend\nfor remote controlling presentation slides.\n\n## Web Sockets\nPastelli upgrades to Web Sockets thanks to mmzeeman's [elli_websockets](https://github.com/mmzeeman/elli_websocket).\n\nPass an elli_websocket\n[handler](https://github.com/mmzeeman/elli_websocket#callback-module) in the private\nmap of your connection. This will receive the current connection as option argument.\n\n```elixir\n  get \"/ws\" do\n    put_private(conn, :upgrade, {:websocket, WebSocketHandler})\n  end\n\n  defmodule WebSocketHandler do\n    def websocket_init(request, conn: %Plug.Conn{} = conn) do\n      # ...\n    end\n    def websocket_handle() # handle callback\n    def websocket_info() # info callback\n  end\n```\n\n## Pastelli and Phoenix\n\nWith version `1.1.0` or newer of\n[Phoenix](htts://phoenixframework.org)\nwe can allow different servers other than Cowboy at the heart of the framework.\nIt's enough to provide a suitable\n[endpoint handler behaviour](https://github.com/phoenixframework/phoenix/blob/v1.1.0/lib/phoenix/endpoint/handler.ex).\n\nWith [`Pastelli.Phoenix`](https://github.com/zampino/pastelli_phoenix) you\ncan have Elli serve Phoenix. Check also this [example](https://github.com/zampino/phoenix-on-pastelli) out.\n\n## Pastelli tries to help!\n\nThe current built-in Plug cowboy adapter does not notify the\nconnection owner process of the EventSource client\nclosing the socket (or just crashing).\nMore precisely, Pastelli tries to address this [issue](https://github.com/elixir-lang/plug/issues/228).\n\n## `Plug.Conn.Adapter` behaviour currently covered by pastelli\n\n- [x] send_resp\n- [x] send_file\n- [x] send_chunked\n- [x] chunk\n- [x] read_req_body\n- [ ] parse_req_multipart\n\n## `Plug.Conn` extensions (`Pastelli.Conn`)\n\n- init_chunk/2, init_chunk/3\n- event/2, event/3\n- close_chunk/0\n\n## Agenda\n\n- [x] run http\n- [ ] run https\n- [x] websocket upgrade via mmzeeman/elli_websocket\n- [x] Plug.Conn extensions\n- [x] Pastelli.Router streaming DSL\n- [ ] ExDoc\n- [ ] hex package\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzampino%2Fpastelli","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzampino%2Fpastelli","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzampino%2Fpastelli/lists"}