Last active
July 28, 2017 18:48
-
-
Save Woody88/b0407081b6145ee24b49ab1384c6751d to your computer and use it in GitHub Desktop.
Dygraph Genserver
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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) }) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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