From 8fa7eaaea3007dba95388091c1b9963fb07990e3 Mon Sep 17 00:00:00 2001
From: Jesse Dijkstra <mail@jessedijkstra.nl>
Date: Fri, 24 Nov 2017 15:21:37 +0100
Subject: [PATCH] Use CellJS adapter

---
 lib/ex_cell/adapters/cell_js.ex        |  12 ++-
 lib/ex_cell/base.ex                    |  44 +---------
 lib/ex_cell/ex_cell.ex                 |  33 +++++++
 test/ex_cell/adapters/cell_js_test.exs | 114 ++++++++++++++++++++-----
 test/ex_cell/base_test.exs             |  35 --------
 test/ex_cell/cell_test.exs             |  71 +++++----------
 test/ex_cell/ex_cell_test.exs          |  30 +++++++
 7 files changed, 183 insertions(+), 156 deletions(-)
 delete mode 100644 test/ex_cell/base_test.exs

diff --git a/lib/ex_cell/adapters/cell_js.ex b/lib/ex_cell/adapters/cell_js.ex
index fb974e1..743953f 100644
--- a/lib/ex_cell/adapters/cell_js.ex
+++ b/lib/ex_cell/adapters/cell_js.ex
@@ -6,11 +6,8 @@ defmodule ExCell.Adapters.CellJS do
   def data_attribute(name, data \\ [], params \\ %{})
   def data_attribute(name, data, params) when is_nil(data), do:
     data_attribute(name, [], params)
-
-  def data_attribute(name, data, params) when is_list(data) do
-    data
-    |> Keyword.merge(cell: name, cell_params: Poison.encode!(params))
-  end
+  def data_attribute(name, data, params) when is_list(data), do:
+    Keyword.merge(data, cell: name, cell_params: Poison.encode!(params))
 
   def attributes(name, attributes \\ [], params \\ %{}) do
     Keyword.put(
@@ -24,10 +21,11 @@ defmodule ExCell.Adapters.CellJS do
     name: name,
     attributes: attributes,
     params: params,
-    tag: tag,
-    closing_tag: closing_tag,
     content: content
   }) do
+    {tag, attributes} = Keyword.pop(attributes, :tag, :div)
+    {closing_tag, attributes} = Keyword.pop(attributes, :closing_tag, true)
+
     attributes = attributes(name, attributes, params)
 
     case closing_tag do
diff --git a/lib/ex_cell/base.ex b/lib/ex_cell/base.ex
index 2c77c2b..3ba0dfc 100644
--- a/lib/ex_cell/base.ex
+++ b/lib/ex_cell/base.ex
@@ -1,27 +1,7 @@
 defmodule ExCell.Base do
-  @moduledoc false
-  @dialyzer [{:no_match, relative_name: 2}]
-
-  def relative_name(module, namespace) do
-    parts = case namespace do
-      nil -> Module.split(module)
-      _ -> ExCell.module_relative_to(module, namespace)
-    end
-
-    Enum.join(parts, "-")
-  end
-
-  def class_attribute(name, class) do
-    [name, class]
-    |> List.flatten
-    |> Enum.reject(&is_nil/1)
-    |> Enum.join(" ")
-  end
-
   defmacro __using__(opts \\ []) do
     quote do
       import ExCell.View
-      import ExCell.Base
 
       @adapter unquote(opts[:adapter] || ExCell.Adapters.CellJS)
       @namespace unquote(opts[:namespace])
@@ -38,7 +18,8 @@ defmodule ExCell.Base do
         iex(1)> User.AvatarCell.name()
         "User-AvatarCell"
       """
-      def name, do: relative_name(__MODULE__, @namespace)
+      def __adapter__, do: @adapter
+      def name, do: ExCell.relative_name(__MODULE__, @namespace)
 
       @doc """
       Generates the CSS class name based on the cell name. Can be overriden
@@ -135,26 +116,7 @@ defmodule ExCell.Base do
       def container(%{} = params, options) when is_list(options), do:
         container(params, options, [do: nil])
       def container(%{} = params, options, [do: content]) when is_list(options), do:
-        @adapter.container(adapter_options(params, options, content))
-
-      def adapter_options(params \\ %{}, attributes \\ [], content \\ nil) do
-        {tag, attributes} = Keyword.pop(attributes, :tag, :div)
-        {closing_tag, attributes} = Keyword.pop(attributes, :closing_tag, true)
-        {cell_name, attributes} =
-          Keyword.pop(attributes, :cell_name, cell_name())
-
-        class_attribute =
-          class_attribute(class_name(), Keyword.get(attributes, :class))
-
-        %{
-          name: cell_name,
-          attributes: Keyword.put(attributes, :class, class_attribute),
-          params: params(params),
-          tag: tag,
-          closing_tag: closing_tag,
-          content: content
-        }
-      end
+        ExCell.container(__MODULE__, params, options, [do: content])
 
       defoverridable [class_name: 0, cell_name: 0, params: 0]
     end
diff --git a/lib/ex_cell/ex_cell.ex b/lib/ex_cell/ex_cell.ex
index becc733..b8036ce 100644
--- a/lib/ex_cell/ex_cell.ex
+++ b/lib/ex_cell/ex_cell.ex
@@ -1,5 +1,6 @@
 defmodule ExCell do
   @moduledoc false
+  @dialyzer [{:no_match, relative_name: 2}]
 
   def module_relative_to(module, relative_to) do
     module
@@ -12,4 +13,36 @@ defmodule ExCell do
     |> Application.get_env(__MODULE__, [])
     |> Keyword.get(keyword, fallback)
   end
+
+
+  def relative_name(module, namespace) do
+    parts = case namespace do
+      nil -> Module.split(module)
+      _ -> ExCell.module_relative_to(module, namespace)
+    end
+
+    Enum.join(parts, "-")
+  end
+
+  def class_name(name, classes) do
+    [name, classes]
+    |> List.flatten
+    |> Enum.reject(&is_nil/1)
+    |> Enum.join(" ")
+  end
+
+  def container(module, params, attributes, [do: content]) do
+    options(module, params, attributes, content)
+    |> module.__adapter__().container()
+  end
+
+  def options(module, params, attributes, content) do
+    %{
+      name: module.cell_name(),
+      params: module.params(params),
+      attributes: Keyword.put(attributes, :class,
+        class_name(module.class_name(), Keyword.get(attributes, :class))),
+      content: content
+    }
+  end
 end
diff --git a/test/ex_cell/adapters/cell_js_test.exs b/test/ex_cell/adapters/cell_js_test.exs
index 1884c33..b20347f 100644
--- a/test/ex_cell/adapters/cell_js_test.exs
+++ b/test/ex_cell/adapters/cell_js_test.exs
@@ -4,13 +4,8 @@ defmodule ExCell.Adapters.CellJSTest do
   import Phoenix.HTML
   alias Phoenix.HTML.Tag
 
-  alias ExCell.Cell
   alias ExCell.Adapters.CellJS
 
-  defmodule MockCell do
-    use Cell, namespace: ExCell.Adapters.CellJSTest
-  end
-
   describe "attributes/3" do
     test "it rejects nil values" do
       assert CellJS.attributes(nil, [], %{}) ==
@@ -55,48 +50,121 @@ defmodule ExCell.Adapters.CellJSTest do
 
   describe "container/1" do
     test "defaults to a :div as tag" do
-      assert safe_to_string(CellJS.container(MockCell.adapter_options()))
+      attributes = %{
+        name: "MockCell",
+        attributes: [
+          class: "MockCell"
+        ],
+        params: %{},
+        content: nil
+      }
+
+      assert safe_to_string(CellJS.container(attributes))
         == "<div class=\"MockCell\" data-cell=\"MockCell\" data-cell-params=\"{}\"></div>"
     end
 
     test "custom tag" do
-      assert safe_to_string(CellJS.container(MockCell.adapter_options(%{}, tag: :p)))
+      attributes = %{
+        name: "MockCell",
+        attributes: [
+          tag: :p,
+          class: "MockCell"
+        ],
+        params: %{},
+        content: nil
+      }
+
+      assert safe_to_string(CellJS.container(attributes))
         == "<p class=\"MockCell\" data-cell=\"MockCell\" data-cell-params=\"{}\"></p>"
     end
 
     test "content" do
-      assert safe_to_string(CellJS.container(MockCell.adapter_options(%{}, [], "TestContent")))
+      attributes = %{
+        name: "MockCell",
+        attributes: [
+          class: "MockCell"
+        ],
+        params: %{},
+        content: "TestContent"
+      }
+
+      assert safe_to_string(CellJS.container(attributes))
         == "<div class=\"MockCell\" data-cell=\"MockCell\" data-cell-params=\"{}\">TestContent</div>"
     end
 
     test "unsafe content" do
-      assert safe_to_string(
-        CellJS.container(MockCell.adapter_options(%{}, [], Tag.content_tag(:div, "TestContent")))
-      ) == "<div class=\"MockCell\" data-cell=\"MockCell\" data-cell-params=\"{}\"><div>TestContent</div></div>"
+      attributes = %{
+        name: "MockCell",
+        attributes: [
+          class: "MockCell"
+        ],
+        params: %{},
+        content: Tag.content_tag(:div, "TestContent")
+      }
+
+      assert safe_to_string(CellJS.container(attributes))
+        == "<div class=\"MockCell\" data-cell=\"MockCell\" data-cell-params=\"{}\"><div>TestContent</div></div>"
     end
 
     test "cell params" do
-      assert safe_to_string(
-        CellJS.container(MockCell.adapter_options(%{ foo: "bar" }))
-      ) == "<div class=\"MockCell\" data-cell=\"MockCell\" data-cell-params=\"{&quot;foo&quot;:&quot;bar&quot;}\"></div>"
+      attributes = %{
+        name: "MockCell",
+        attributes: [
+          class: "MockCell"
+        ],
+        params: %{
+          foo: "bar"
+        },
+        content: nil
+      }
+
+      assert safe_to_string(CellJS.container(attributes))
+        == "<div class=\"MockCell\" data-cell=\"MockCell\" data-cell-params=\"{&quot;foo&quot;:&quot;bar&quot;}\"></div>"
     end
 
     test "attributes" do
-      assert safe_to_string(
-        CellJS.container(MockCell.adapter_options(%{}, foo: "bar"))
-      ) == "<div class=\"MockCell\" data-cell=\"MockCell\" data-cell-params=\"{}\" foo=\"bar\"></div>"
+      attributes = %{
+        name: "MockCell",
+        attributes: [
+          class: "MockCell",
+          foo: "bar"
+        ],
+        params: %{},
+        content: nil
+      }
+
+      assert safe_to_string(CellJS.container(attributes))
+        == "<div class=\"MockCell\" data-cell=\"MockCell\" data-cell-params=\"{}\" foo=\"bar\"></div>"
     end
 
     test "data attributes" do
-      assert safe_to_string(
-        CellJS.container(MockCell.adapter_options(%{}, data: [foo: "bar"]))
-      ) == "<div class=\"MockCell\" data-cell=\"MockCell\" data-cell-params=\"{}\" data-foo=\"bar\"></div>"
+      attributes = %{
+        name: "MockCell",
+        attributes: [
+          class: "MockCell",
+          data: [foo: "bar"]
+        ],
+        params: %{},
+        content: nil
+      }
+
+      assert safe_to_string(CellJS.container(attributes))
+        == "<div class=\"MockCell\" data-cell=\"MockCell\" data-cell-params=\"{}\" data-foo=\"bar\"></div>"
     end
 
     test "no closing tag" do
-      assert safe_to_string(
-        CellJS.container(MockCell.adapter_options(%{}, closing_tag: false, data: [foo: "bar"]))
-      ) == "<div class=\"MockCell\" data-cell=\"MockCell\" data-cell-params=\"{}\" data-foo=\"bar\">"
+      attributes = %{
+        name: "MockCell",
+        attributes: [
+          class: "MockCell",
+          closing_tag: false
+        ],
+        params: %{},
+        content: nil
+      }
+
+      assert safe_to_string(CellJS.container(attributes))
+        == "<div class=\"MockCell\" data-cell=\"MockCell\" data-cell-params=\"{}\">"
     end
   end
 end
diff --git a/test/ex_cell/base_test.exs b/test/ex_cell/base_test.exs
deleted file mode 100644
index cdab6a4..0000000
--- a/test/ex_cell/base_test.exs
+++ /dev/null
@@ -1,35 +0,0 @@
-defmodule ExCell.BaseTest do
-  use ExUnit.Case
-
-  alias ExCell.Base
-
-  describe "relative_name/2" do
-    test "returns the module name" do
-      assert Base.relative_name(MockCell, nil) == "MockCell"
-    end
-
-    test "returns the relative module name" do
-      assert Base.relative_name(Bar.MockCell, Bar) == "MockCell"
-    end
-
-    test "returns the relative module name with dashes for nested cells" do
-      assert Base.relative_name(Bar.MockCell, nil) == "Bar-MockCell"
-    end
-  end
-
-  describe "class_attribute/2" do
-    test "adds the name" do
-      assert Base.class_attribute("Hello", "World") ==
-        "Hello World"
-    end
-
-    test "allows lists" do
-      assert Base.class_attribute(["Hello", "World"], ["Foo", "Bar"]) ==
-        "Hello World Foo Bar"
-    end
-
-    test "rejects nil" do
-      assert Base.class_attribute(nil, nil) == ""
-    end
-  end
-end
diff --git a/test/ex_cell/cell_test.exs b/test/ex_cell/cell_test.exs
index 580a160..cf5e30a 100644
--- a/test/ex_cell/cell_test.exs
+++ b/test/ex_cell/cell_test.exs
@@ -2,16 +2,23 @@ defmodule ExCell.CellTest do
   use ExUnit.Case
   alias ExCell.Cell
 
+  defmodule MockAdapter do
+    def container(options), do: options
+  end
+
   defmodule Foo.MockCell do
-    use Cell, namespace: Foo
+    use Cell, namespace: Foo,
+              adapter: MockAdapter
   end
 
   defmodule Foo.Bar.MockCell do
-    use Cell, namespace: Foo
+    use Cell, namespace: Foo,
+              adapter: MockAdapter
   end
 
   defmodule Foo.MockCellWithOverridables do
-    use Cell, namespace: Foo
+    use Cell, namespace: Foo,
+              adapter: MockAdapter
 
     def params, do: %{foo: "Bar"}
     def class_name, do: "Bar-" <> name()
@@ -73,100 +80,64 @@ defmodule ExCell.CellTest do
     end
   end
 
-  describe "adapter_options/3" do
+  describe "container/3" do
     test "without arguments" do
-      assert MockCell.adapter_options() == %{
+      assert MockCell.container() == %{
         name: "MockCell",
         attributes: [
           class: "MockCell"
         ],
-        closing_tag: true,
         content: nil,
-        params: %{},
-        tag: :div
+        params: %{}
       }
     end
 
     test "with overrideables" do
-      assert MockCellWithOverridables.adapter_options() == %{
+      assert MockCellWithOverridables.container() == %{
         name: "World-MockCellWithOverridables",
         attributes: [
           class: "Bar-MockCellWithOverridables"
         ],
-        closing_tag: true,
         content: nil,
         params: %{
           foo: "Bar"
-        },
-        tag: :div
-      }
-    end
-
-    test "with custom tag" do
-      assert MockCell.adapter_options(%{}, tag: :p) == %{
-        name: "MockCell",
-        attributes: [
-          class: "MockCell"
-        ],
-        closing_tag: true,
-        content: nil,
-        params: %{},
-        tag: :p
+        }
       }
     end
 
     test "with content" do
-      assert MockCell.adapter_options(%{}, [], "TestContent") == %{
+      assert MockCell.container(%{}, [], [do: "TestContent"]) == %{
         name: "MockCell",
         attributes: [
           class: "MockCell"
         ],
-        closing_tag: true,
         content: "TestContent",
-        params: %{},
-        tag: :div
+        params: %{}
       }
     end
 
     test "with cell params" do
-      assert MockCell.adapter_options(%{ foo: "bar" }) == %{
+      assert MockCell.container(%{ foo: "bar" }) == %{
         name: "MockCell",
         attributes: [
           class: "MockCell"
         ],
-        closing_tag: true,
         content: nil,
         params: %{
           foo: "bar"
-        },
-        tag: :div
+        }
       }
     end
 
     test "with custom atttributes" do
-      assert MockCell.adapter_options(%{}, foo: "bar") == %{
+      assert MockCell.container(%{}, foo: "bar") == %{
         name: "MockCell",
         attributes: [
           class: "MockCell",
           foo: "bar"
         ],
-        closing_tag: true,
-        content: nil,
-        params: %{},
-        tag: :div
-      }
-    end
-
-    test "without closing tag" do
-      assert MockCell.adapter_options(%{}, closing_tag: false) == %{
-        name: "MockCell",
-        attributes: [
-          class: "MockCell"
-        ],
-        closing_tag: false,
         content: nil,
-        params: %{},
-        tag: :div
+        params: %{}
       }
     end
   end
diff --git a/test/ex_cell/ex_cell_test.exs b/test/ex_cell/ex_cell_test.exs
index a29d19c..d0278b8 100644
--- a/test/ex_cell/ex_cell_test.exs
+++ b/test/ex_cell/ex_cell_test.exs
@@ -24,4 +24,34 @@ defmodule ExCellTest do
       assert ExCell.config(:hello, :world) == :world
     end
   end
+
+  describe "relative_name/2" do
+    test "returns the module name" do
+      assert ExCell.relative_name(MockCell, nil) == "MockCell"
+    end
+
+    test "returns the relative module name" do
+      assert ExCell.relative_name(Bar.MockCell, Bar) == "MockCell"
+    end
+
+    test "returns the relative module name with dashes for nested cells" do
+      assert ExCell.relative_name(Bar.MockCell, nil) == "Bar-MockCell"
+    end
+  end
+
+  describe "class_name/2" do
+    test "adds the name" do
+      assert ExCell.class_name("Hello", "World") ==
+        "Hello World"
+    end
+
+    test "allows lists" do
+      assert ExCell.class_name(["Hello", "World"], ["Foo", "Bar"]) ==
+        "Hello World Foo Bar"
+    end
+
+    test "rejects nil" do
+      assert ExCell.class_name(nil, nil) == ""
+    end
+  end
 end