Unpoly - a framework like Turbolinks

Whooo welcome! Didn’t expect you here. ^.^

I…may push it around a lot here, I really find it’s API to be well designed (though I do wish I could feed it through my rollup packaging system, it doesn’t follow the JS module spec). ^.^;

Yep plugs and an api for querying and setting things. I’ve made enough of what I’ve needed in my own project. I’ve intended to pull it out for a while now and document it, but it is so super easy for anyone to make theirselves that I’ve not really gotten around to it yet… ^.^;

My file as it stands, I don’t use ‘all’ of it so I cannot claim it all to be tested, and of course it’s missing a lot of obvious helpers that I otherwise do randomly in my code (bad form I know) so it does need some work on it (it’s an internal file…), but this is what I have so far in an unpoly.ex file:

defmodule Unpoly do


  def up?(conn), do: target(conn) !== nil
  # defdelegate unpoly?, to: up?


  def target(conn), do: List.keyfind(conn.req_headers, "x-up-target", 0, nil)


  def target?(conn, tested_target) when is_binary(tested_target) do
    if up?(conn) do
      case target(conn) do
        ^tested_target -> true
        "html" -> true
        "body" -> not (tested_target in ["head", "title", "meta"])
        _ -> false
      end
    else
      true
    end
  end


  def validate?(conn), do: validate_name(conn) !== nil


  def validate_name(conn), do: List.keyfind(conn.req_headers, "x-up-validate", 0, nil)


  def set_title(conn, new_title) when is_binary(new_title), do: Plug.Conn.put_resp_header(conn, "x-up-title", new_title)


  defmodule Plugs do
    defmodule RequestMethodCookie do
      @cookie_name "_up_method"

      def init(options), do: options

      def call(conn, params) do
        case conn.method do
          "GET" -> Plug.Conn.delete_resp_cookie(conn, @cookie_name, List.wrap(params[:cookie_opts]))
          method -> Plug.Conn.put_resp_cookie(conn, @cookie_name, method, List.wrap(params[:cookie_opts]))
        end
      end
    end


    defmodule RequestEchoHeaders do
      def init(options), do: options

      def call(conn, params) do
        conn
        # |> Plug.Conn.put_resp_header("x-up-location", conn.request_path)
        # |> Plug.Conn.put_resp_header("x-up-method", conn.method)
      end
    end


    defmodule AddRedirectHeaderAfter do
      def init(options), do: options

      def call(conn, params) do
        case Plug.Conn.get_resp_header(conn, "location") do
          location when is_binary(location) -> Plug.Conn.put_resp_header(conn, "x-up-location", location)
        end
      end
    end
  end

end

To package it you just run mix new unpoly and follow the steps, the replace the pre-built lib/unpoly.ex with the above (edited to add documentation and all such too of course).

For testing you would want to inlcude the Plug library as a testing only dependency and use it’s test helpers to test that things go through properly as expected either in doctests, or the dedicated test file(s) in the test/* directory. I’m sure someone here could create a whole scaffold project if you want. :slight_smile:

Once it’s made you’ll want to include ex_doc as a dev-only dependency then you’ll be able to publish it to the hex package system with documentation and all (and of course you can keep the whole project directory inside the unpoly project if you preferred too). :slight_smile:

15 Likes