项目作者: stepnivlk

项目描述 :
Bidirectional Pusher client in Elixir
高级语言: Elixir
项目地址: git://github.com/stepnivlk/pushest.git
创建时间: 2018-03-25T18:00:54Z
项目社区:https://github.com/stepnivlk/pushest

开源协议:MIT License

下载


Pushest

Pushest is bidirectional Pusher client leveraging Elixir/OTP to combine server and client-side
Pusher features together in one library. Pushest communicates both via WebSockets and REST API.
You can trigger on any channel, subscribe to channels, handle events using callbacks or
keep track of presence.

Build Status Ebert

TODO

  • Event scoping
  • Presence
  • Unsubscribe method
  • Channels list method
  • Auth token generated only for private/presence channels
  • Missing tests
  • Handle pusher:error
  • Generate documentation
  • :gun.conn monitoring
  • start_link/3 - opts to Pushest
  • Named process option
  • Propagate app version to url
  • Overall error handling
  • Publish to hex.pm
  • Fallback to REST when triggering on a public channel
  • Test recovery from :gun_down / EXIT
  • expose auth function to generate a token for client-side libraries.
  • trigger batching
  • Push notifications
  • Subscribe to a list of channels after startup
  • Full recovery after network outage, exit, etc. Buffer needed.
  • Refactor :gun.conn PID handling.
  • Add a support for testing of modules using Pushest

Usage

A simple implementation in an OTP application would be:

  1. # Add necessary pusher configuration to your application config:
  2. # simple_client/config/config.exs
  3. config :simple_client, SimpleClient,
  4. pusher_app_id: System.get_env("PUSHER_APP_ID"),
  5. pusher_key: System.get_env("PUSHER_APP_KEY"),
  6. pusher_secret: System.get_env("PUSHER_SECRET"),
  7. pusher_cluster: System.get_env("PUSHER_CLUSTER"),
  8. pusher_encrypted: true
  9. # simple_client/simple_client.ex
  10. defmodule SimpleClient do
  11. use Pushest, otp_app: :simple_client
  12. # Subscribe to these channels right after application startup.
  13. def init_channels do
  14. [
  15. [name: "public-init-channel", user_data: %{}],
  16. [name: "private-init-channel", user_data: %{}],
  17. [name: "presence-init-channel", user_data: %{user_id: 123}],
  18. ]
  19. end
  20. # handle_event/2 is user-defined callback which is triggered whenever an event
  21. # occurs on the channel.
  22. def handle_event({:ok, "public-init-channel", "some-event"}, frame) do
  23. # do something with public-init-channel frame
  24. end
  25. def handle_event({:ok, "public-channel", "some-event"}, frame) do
  26. # do something with public-channel frame
  27. end
  28. def handle_event({:ok, "private-channel", "some-other-event"}, frame) do
  29. # do something with private-channel frame
  30. end
  31. # We can also catch errors.
  32. def handle_event({:error, msg}, frame) do
  33. # do something with error
  34. end
  35. end
  36. # Now you can start your application with Pushest as a part of your supervision tree:
  37. # simple_client/lib/simple_client/application.ex
  38. def start(_type, _args) do
  39. children = [
  40. {SimpleClient, []}
  41. ]
  42. opts = [strategy: :one_for_one, name: Sup.Supervisor]
  43. Supervisor.start_link(children, opts)
  44. end
  1. config = %{
  2. app_id: System.get_env("PUSHER_APP_ID"),
  3. key: System.get_env("PUSHER_APP_KEY"),
  4. secret: System.get_env("PUSHER_SECRET"),
  5. cluster: System.get_env("PUSHER_CLUSTER"),
  6. encrypted: true
  7. }
  8. {:ok, pid} = SimpleClient.start_link(config)

Now you can use various functions injected in your module

  1. SimpleClient.channels()
  2. # => %{
  3. "channels" => %{
  4. "presence-init-channel" => %{},
  5. "private-init-channel" => %{},
  6. "public-init-channel" => %{}
  7. }
  8. # ...
  9. SimpleClient.subscribe("public-channel")
  10. :ok
  11. # ...
  12. SimpleClient.subscribe("private-channel")
  13. :ok
  14. # ...
  15. SimpleClient.subscribe("presence-channel", %{user_id: "1", user_info: %{name: "Tomas"}})
  16. :ok
  17. # ...
  18. SimpleClient.presence()
  19. %Pushest.Data.Presence{
  20. count: 2,
  21. hash: %{"1" => %{"name" => "Tomas"}, "2" => %{"name" => "Jose"}},
  22. ids: ["1", "2"],
  23. me: %{user_id: "1", user_info: %{name: "Tomas"}}
  24. }
  25. # ...
  26. SimpleClient.trigger("private-channel", "first-event", %{message: "Ahoj"})
  27. :ok
  28. # ...
  29. SimpleClient.subscribed_channels()
  30. ["presence-channel", "private-channel", "public-channel",
  31. "presence-init-channel", "private-init-channel", "public-init-channel"]
  32. # ...
  33. SimpleClient.unsubscribe("public-channel")
  34. :ok

Functions list

subscribe/1

Subscribes to public or private channel

  1. SimpleClient.subscribe("public-channel")
  2. :ok

subscribe/2

Subscribes to private or presence channel with user data as second parameter.
User data has to contain user_id key with unique identifier for current user.
Can optionally contain user_info field with map of additional informations about user.

  1. user_data = %{user_id: 123, user_info: %{name: "Tomas", email: "secret@secret.com"}}
  2. SimpleClient.subscribe("presence-channel", user_data)
  3. :ok
  4. # ...
  5. SimpleClient.subscribe("private-channel", user_data)
  6. :ok

trigger/3

Triggers on given channel and event with given data payload. Pushest sends data by
default to REST API endpoint of Pusher, however when subscribed to private or presence channel
it sends data to Pusher via WebSockets.

  1. SimpleClient.trigger("public-channel", "event", %{message: "message"})
  2. :ok
  3. # ..
  4. SimpleClient.trigger("private-channel", "event", %{message: "message"})
  5. :ok

trigger/4

Same as trigger/3 but lets you force trigger over the REST API (so it never triggers via WebSockets).

  1. SimpleClient.trigger("private-channel", "event", %{message: "message"}, force_api: true)

channels/0

Returns map of all the active channels which are being used in your Pusher application.
Can contain informations about subscribed users.

  1. SimpleClient.channels()
  2. %{"channels" => %{"public-channel" => %{}, "private-channel" => %{}}}

subscribed_channels/0

Returns list of all the subscribed channels for current instance.

  1. SimpleClient.subscribed_channels()
  2. ["private-channel"]

presence/0

Returns information about all the users subscribed to a presence channel.

  1. SimpleClient.presence()
  2. %Pushest.Data.Presence{
  3. count: 2,
  4. hash: %{"1" => %{"name" => "Tomas"}, "2" => %{"name" => "Jose"}},
  5. ids: ["1", "2"],
  6. me: %{user_id: "2", user_info: %{name: "Jose"}}
  7. }

unsubscribe/1

Unsubscribes from given channel

  1. SimpleClient.unsubscribe("public-channel")

Overridable functions

These functions are meant to be overridden in a module using Pushest

handle_event/2

Callback being triggered when there is a WebSocket event on a subscribed channel.

  1. defmodule MyApp.MyModule
  2. use Pushest, otp_app: :my_app
  3. def handle_event({:ok, "my-channel", "my-event"}, frame) do
  4. IO.inspect frame
  5. end
  6. end

init_channels/0

Subscribes to given list of channels right after application startup.
Each element has to be a keyword list in exact format of: [name: String.t(), user_data: map]

  1. defmodule MyApp.MyModule
  2. use Pushest, otp_app: :my_app
  3. def init_channels do
  4. [
  5. [name: "public-init-channel", user_data: %{}],
  6. [name: "private-init-channel", user_data: %{}],
  7. [name: "presence-init-channel", user_data: %{user_id: 123}],
  8. ]
  9. end
  10. end

frame example

frame is a Pushest.Socket.Data.Frame or Pushest.Api.Data.Frame struct with data payload as a map.

  1. %Pushest.Data.Frame{
  2. channel: "private-channel",
  3. data: %{"name" => "John", "message" => "Hello"},
  4. event: "second-event"
  5. }

Installation

The package can be installed by adding pushest to your list of dependencies in mix.exs:

  1. def deps do
  2. [
  3. {:pushest, "~> 0.2.2"}
  4. ]
  5. end

Documentation

Documentation can be be found at https://hexdocs.pm/pushest.