Skip to content

Instantly share code, notes, and snippets.

@Woody88
Last active July 28, 2017 18:48
Show Gist options
  • Save Woody88/b0407081b6145ee24b49ab1384c6751d to your computer and use it in GitHub Desktop.
Save Woody88/b0407081b6145ee24b49ab1384c6751d to your computer and use it in GitHub Desktop.
Dygraph Genserver
defmodule PlotEx.Dygraph do
@derive [Poison.Encoder]
@default_options %{strokeWidth: 1, drawPoints: true, includeZero: true, gridLineColor: "#ddd", drawGrid: false, title: ""}
@options [:strokWidth, :drawPoints, :includeZero, :gridLineColor,
:drawGrid, :series, :dataHandler, :axes, :plotter, :animatedZooms,
:showInRangeSelector, :file, :showRangeSelector, :maxNumberWidth,
:gridLineWidth, :drawAxis, :yLabelWidth, :y2label, :ylabel, :xLabelHeight,
:xlabel, :titleHeight, :title, :panEdgeFraction, :displayAnnotations, :errorBars, :colorValue,
:sigma, :showRoller, :labels, :drawAxesAtZero, :legend, :visibility, :strokeBorderColor,
:logscale, :axis, :width, :height, :delimiter, :rollPeriod, :color, :colors, :connectSeparatedPoints,
:height, :drawGapEdgePoints, :pointSize]
defstruct [layout: :none, callback: :none, data: :none, options: @default_options]
use GenServer
alias __MODULE__
def new(name) when is_atom(name) do
GenServer.start_link(__MODULE__, name, [name: name])
end
def new() do
name = generate_name
GenServer.start_link(__MODULE__, name, [name: name])
end
def init(name) do
options = %Dygraph{}
|> Map.from_struct
|> Kernel.put_in([:options, :title], name )
{:ok, %Dygraph{options: options}}
end
def data(dygraph, coords) when is_list(coords), do: GenServer.call(dygraph, {:set_data, coords})
def info(dygraph), do: GenServer.call(dygraph, :info)
def get_options(dygraph) when is_pid(dygraph) or is_atom(dygraph), do: GenServer.call(dygraph, :get_options)
def show(dygraph) when is_pid(dygraph) or is_atom(dygraph), do: GenServer.cast(dygraph, :show_graph)
def options(dygraph, options) when is_list(options), do: GenServer.call(dygraph, {:set_options, options})
def handle_call(:info, _from, state) do
IO.puts "hello"
{:reply, state, state}
end
def handle_cast(:show_graph, state) do
{:registered_name, graph_id} = Process.info(self(), :registered_name)
File.cd(System.cwd <> "/electron")
System.cmd("npm", ["start", graph_id])
{:noreply, state}
end
def handle_call({:set_data, data}, _from, state) do
updated_state = data
|> validate_data_format
|> (fn d -> %{state | data: d} end).()
{:reply, updated_state, updated_state}
end
def handle_call({:set_options, options}, _from, state) do
updated_state = options
|> validate_options
|> insert_options(state)
{:reply, updated_state, updated_state}
end
def handle_call(:get_options, _from, state) do
{:reply, state, state}
end
defp insert_options(options, state) do
Enum.into(options, state, fn {key, value}, update_state -> update_state |> Map.put(key, value) end)
end
def validate_data_format(data) do
data
end
defp validate_keys(options) do
for key <- Keyword.keys(options), do:
unless Enum.member?(@options, key), do: raise(PlotEx.Dygraph.NotValidOption)
options
end
defp validate_options(options), do:
if !Keyword.keyword?(options), do: raise(PlotEx.Dygraph.NotKeywordList), else: options |> validate_keys
defp generate_name do
"dygraph__"
|> Kernel.<>(:crypto.strong_rand_bytes(10) |> Base.url_encode64 |> binary_part(0, 10))
|> String.to_atom
end
def option_keys, do: @options
end
defmodule PlotEx.Dygraph.NotKeywordList do
defexception message: "accepts only keyword list as an option."
end
defmodule PlotEx.Dygraph.NotValidOption do
defexception message: "not a valid option key."
end
# [debug] INCOMING "dygraph:get_initial_graph" on "plotly:lobby" to PlotEx.Web.PlotlyChannel
# Transport: Phoenix.Transports.WebSocket
# Parameters: %{"graph_id" => "dygraph__D-Bj4kZss1"}
# [error] GenServer #PID<0.347.0> terminating
# ** (stop) exited in: GenServer.call(:"dygraph__D-Bj4kZss1", :info, 5000)
# ** (EXIT) time out
# (elixir) lib/gen_server.ex:737: GenServer.call/3
# (plot_ex) lib/plot_ex/web/channels/plotly_channel.ex:35: PlotEx.Web.PlotlyChannel.handle_in/3
# (phoenix) lib/phoenix/channel/server.ex:226: anonymous fn/4 in Phoenix.Channel.Server.handle_info/2
# (plot_ex) lib/plot_ex/web/endpoint.ex:1: PlotEx.Web.Endpoint.instrument/4
# (stdlib) gen_server.erl:601: :gen_server.try_dispatch/4
# (stdlib) gen_server.erl:667: :gen_server.handle_msg/5
# (stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3
# Last message: %Phoenix.Socket.Message{event: "dygraph:get_initial_graph", payload: %{"graph_id" => "dygraph__D-Bj4kZss1"}, ref: "2", topic: "plotly:lobby"}
# State: %Phoenix.Socket{assigns: %{}, channel: PlotEx.Web.PlotlyChannel, channel_pid: #PID<0.347.0>, endpoint: PlotEx.Web.Endpoint, handler: PlotEx.Web.Socket, id: nil, join_ref: "1", joined: true, private: %{log_handle_in: :debug, log_join: :info}, pubsub_server: PlotEx.PubSub, ref: nil, serializer: Phoenix.Transports.WebSocketSerializer, topic: "plotly:lobby", transport: Phoenix.Transports.WebSocket, transport_name: :websocket, transport_pid: #PID<0.344.0>}
# hello
# iex(4)>
// NOTE: The contents of this file will only be executed if
// you uncomment its entry in "web/static/js/app.js".
// To use Phoenix channels, the first step is to import Socket
// and connect at the socket path in "lib/my_app/endpoint.ex":
//import {Socket} from "phoenix"
const Socket = require('phoenix-socket').Socket
let socket = new Socket("ws://localhost:4000/socket")
socket.connect()
// Now that you are connected, you can join channels with a topic:
let channel = socket.channel("plotly:lobby", {})
let graph_id = document.getElementById('graph').dataset.graph_id;
channel.join()
.receive("ok", resp => {
console.log("Joined successfully", resp)
channel.push("dygraph:get_initial_graph", {"graph_id": graph_id})
.receive("ok", function (msgData){
console.log(msgData);
});
})
.receive("error", resp => { console.log("Unable to join", resp) })
defmodule PlotEx.Web.PlotlyChannel do
use PlotEx.Web, :channel
alias PlotEx.Dygraph
def join("plotly:lobby", payload, socket) do
if authorized?(payload) do
# :timer.send_interval(5000, {:after_join, "ping"})
{:ok, socket}
else
{:error, %{reason: "unauthorized"}}
end
end
def handle_info({:after_join, msg}, socket) do
#push socket, "new:msg", %{message: msg}
{:noreply, socket}
end
def handle_info({:new_plot, dygraph}, socket) do
push socket, "new:plot", dygraph
{:noreply, socket}
end
# Channels can be used in a request/response fashion
# by sending replies to requests from the client
def handle_in({:ping, payload}, socket) do
push socket, "new:msg", %{message: payload}
{:noreply, socket}
end
# It is also common to receive messages from the client and
# broadcast to everyone in the current topic (plotly:lobby).
def handle_in("dygraph:get_initial_graph", %{"graph_id" => graph}, socket) do
graph_data = graph |> String.to_atom |> Dygraph.info()
{:reply, {:ok, graph_data}, socket}
end
def handle_in("shout", payload, socket) do
broadcast socket, "shout", payload
{:noreply, socket}
end
# Add authorization logic here as required.
defp authorized?(_payload) do
true
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment