Permalink
Browse files

Tidy up view handling, Dynamo.Views -> Dynamo.View

  • Loading branch information...
1 parent 1d1c52f commit 7b9f5776403658a49ee4827ddc73e35c9f499777 @josevalim josevalim committed Sep 7, 2012
View
@@ -176,6 +176,9 @@ defmodule Dynamo.App do
view = dynamo[:view_paths]
view = Enum.reduce view, [], fn(path, acc) -> expand_paths(path, root) ++ acc end
+ # Remove views that eventually end up on source
+ source = source -- view
+
quote do
config :dynamo,
view_paths: unquote(view),
@@ -1,4 +1,4 @@
-defmodule Dynamo.Views do
+defmodule Dynamo.View do
defrecord Template, identifier: nil, format: nil, handler: nil, source: nil, ref: nil, updated_at: nil
defexception TemplateNotFound, query: nil, view_paths: nil do
@@ -18,6 +18,6 @@ defmodule Dynamo.Views do
Renders the given template with the given assigns.
"""
def render(template, assigns) do
- Dynamo.Views.Renderer.render(template, Keyword.put(assigns, :template, template))
+ Dynamo.View.Renderer.render(template, Keyword.put(assigns, :template, template))
end
end
@@ -0,0 +1,62 @@
+defmodule Dynamo.View.Finder do
+ use Behaviour
+
+ @doc """
+ Initializes the finder with the relevant
+ information.
+ """
+ defcallback new(info)
+
+ @doc """
+ Returns all templates for this finder.
+
+ This is used for eager template
+ compilation in production. In case
+ eager compilation is not supported,
+ it should simply return nil.
+ """
+ defcallback all(self)
+
+ @doc """
+ Attempts to find a template given by
+ `query` in the current finder `self`.
+
+ Returns a `Dynamo.View.Template` or
+ nil in case a template can't be found.
+ """
+ defcallback find(query, self)
+end
+
+defmodule Dynamo.View.PathFinder do
+ @behaviour Dynamo.View.Finder
+
+ def new(path) do
+ { __MODULE__, File.expand_path(path) }
+ end
+
+ def all({ __MODULE__, _path }) do
+ nil
+ end
+
+ def find(template, { __MODULE__, path }) do
+ query = File.join(path, template <> ".*")
+ tmpl = Enum.first File.wildcard(query)
+
+ if tmpl do
+ Dynamo.View.Template[
+ updated_at: File.stat!(tmpl).mtime,
+ identifier: tmpl,
+ handler: extname(tmpl),
+ format: extname(File.rootname(tmpl)),
+ source: File.read!(tmpl)
+ ]
+ end
+ end
+
+ defp extname(path) do
+ case File.extname(path) do
+ "." <> ext -> ext
+ ext -> ext
+ end
+ end
+end
@@ -0,0 +1,51 @@
+defmodule Dynamo.View.Handler do
+ @moduledoc """
+ A module that specifies the handler API and
+ small conveniences around it.
+ """
+
+ use Behaviour
+
+ @doc """
+ A template handler must simply implement
+ compile, receiving a Dynamo.View.Template
+ record.
+
+ A template handler must be necessarily
+ named as Dynamo.View.EXTHandler where
+ EXT is the handler extension.
+ """
+ defcallback compile(template)
+
+ @doc """
+ Get the template handler for the given extension.
+ """
+ def get!(extension) do
+ module = Module.concat(Dynamo.View, upcase(extension) <> "Handler")
+ if Code.ensure_loaded?(module) do
+ module
+ else
+ raise "Could not find handler for #{extension}"
+ end
+ end
+
+ defp upcase(<<h, t | :binary>>) when h in ?a..?z do
+ <<h - 32, upcase(t) | :binary>>
+ end
+
+ defp upcase(<<h, t | :binary>>) do
+ <<h, upcase(t) | :binary>>
+ end
+
+ defp upcase(<<>>) do
+ <<>>
+ end
+end
+
+defmodule Dynamo.View.EEXHandler do
+ @behaviour Dynamo.View.Handler
+
+ def compile(Dynamo.View.Template[source: source, identifier: identifier]) do
+ EEx.compile_string(source, file: identifier)
+ end
+end
@@ -1,21 +1,21 @@
-defmodule Dynamo.Views.Renderer do
+defmodule Dynamo.View.Renderer do
@moduledoc false
@slots 1_000_000
@max_attempts 1_000
use GenServer.Behaviour
- alias Dynamo.Views.Template, as: Template
+ alias Dynamo.View.Template, as: Template
@doc """
- Starts the `Dynamo.Views.Renderer` server.
+ Starts the `Dynamo.View.Renderer` server.
Usually called internally by Dynamo.
"""
def start_link do
:gen_server.start({ :local, __MODULE__ }, __MODULE__, [], [])
end
@doc """
- Stops the `Dynamo.Views.Renderer` server.
+ Stops the `Dynamo.View.Renderer` server.
"""
def stop do
:gen_server.call(__MODULE__, :stop)
@@ -86,22 +86,17 @@ defmodule Dynamo.Views.Renderer do
end
defp compile(Template[handler: handler, identifier: identifier, updated_at: updated_at] = template) do
- compiled = get_handler(handler).compile(template)
+ compiled = Dynamo.View.Handler.get!(handler).compile(template)
:gen_server.call(__MODULE__, { :register, identifier, updated_at, compiled })
end
defp raise_too_busy(Template[identifier: identifier]) do
raise "Compiling template #{inspect identifier} exceeded the max number of attempts #{@max_attemps}. What gives?"
end
- # TODO: Remove hardcoded handler.
- defp get_handler("eex") do
- Dynamo.Views.EEXHandler
- end
-
defp generate_module(source, identifier, attempts) when attempts < @max_attemps do
random = :random.uniform(@slots)
- module = Module.concat(Dynamo.Views, "Template#{random}")
+ module = Module.concat(Dynamo.View, "Template#{random}")
if :code.is_loaded(module) do
generate_module(source, identifier, attempts + 1)
@@ -1,31 +0,0 @@
-
-
-defmodule Dynamo.Views.PathFinder do
- # TODO: Define handler behaviour
-
- def new(path) do
- { __MODULE__, File.expand_path(path) }
- end
-
- def find(template, { __MODULE__, path }) do
- query = File.join(path, template <> ".*")
- tmpl = Enum.first File.wildcard(query)
-
- if tmpl do
- Dynamo.Views.Template[
- updated_at: File.stat!(tmpl).mtime,
- identifier: tmpl,
- handler: extname(tmpl),
- format: extname(File.rootname(tmpl)),
- source: File.read!(tmpl)
- ]
- end
- end
-
- defp extname(path) do
- case File.extname(path) do
- "." <> ext -> ext
- ext -> ext
- end
- end
-end
@@ -1,7 +0,0 @@
-defmodule Dynamo.Views.EEXHandler do
- # TODO: Define handler behaviour
-
- def compile(Dynamo.Views.Template[source: source, identifier: identifier]) do
- EEx.compile_string(source, file: identifier)
- end
-end
@@ -13,7 +13,9 @@ defmodule Dynamo.App.ConfigTest do
config :dynamo,
public_root: :app,
public_route: "/public",
- root: File.expand_path("../../../fixtures", __FILE__)
+ root: File.expand_path("../../../fixtures", __FILE__),
+ view_paths: ["foo"],
+ source_path: ["foo"]
config :linq, adapter: :pg
@@ -62,4 +64,8 @@ defmodule Dynamo.App.ConfigTest do
test "gets config from environment" do
assert App.config[:from_dev][:other] == "config"
end
+
+ test "removes views from source paths" do
+ assert App.config[:dynamo][:source_paths] == []
+ end
end
@@ -1,15 +1,15 @@
Code.require_file "../../../test_helper.exs", __FILE__
-defmodule Dynamo.Views.PathFinderTest do
+defmodule Dynamo.View.PathFinderTest do
use ExUnit.Case, async: true
@view_path File.expand_path("../../../fixtures/views", __FILE__)
- @finder Dynamo.Views.PathFinder.new(@view_path)
+ @finder Dynamo.View.PathFinder.new(@view_path)
test "finds available template" do
path = File.join(@view_path, "hello.html.eex")
- assert Dynamo.Views.Template[identifier: ^path,
+ assert Dynamo.View.Template[identifier: ^path,
handler: "eex", format: "html"] = @finder.find "hello.html"
end
@@ -0,0 +1,15 @@
+Code.require_file "../../../test_helper.exs", __FILE__
+
+defmodule Dynamo.View.HandlerTest do
+ use ExUnit.Case, async: true
+
+ test "gets a handler by extension" do
+ assert Dynamo.View.Handler.get!("eex") == Dynamo.View.EEXHandler
+ end
+
+ test "raises on invalid handler" do
+ assert_raise RuntimeError, fn ->
+ Dynamo.View.Handler.get!("unknown")
+ end
+ end
+end
@@ -1,16 +1,16 @@
Code.require_file "../../test_helper.exs", __FILE__
-defmodule Dynamo.ViewsTest do
+defmodule Dynamo.ViewTest do
use ExUnit.Case, async: true
- @view_paths [Dynamo.Views.PathFinder.new(File.expand_path("../../fixtures/views", __FILE__))]
+ @view_paths [Dynamo.View.PathFinder.new(File.expand_path("../../fixtures/views", __FILE__))]
def setup(_) do
- Dynamo.Views.Renderer.start_link
+ Dynamo.View.Renderer.start_link
end
def teardown(_) do
- Dynamo.Views.Renderer.stop
+ Dynamo.View.Renderer.stop
end
test "renders a template" do
@@ -26,13 +26,17 @@ defmodule Dynamo.ViewsTest do
assert module == cached
template = File.expand_path("../../fixtures/views/module.html.eex", __FILE__)
- File.touch!(template, { { 2030, 1, 1 }, { 0, 0, 0 } })
- not_cached = render "module.html"
- assert module != not_cached
+ try do
+ File.touch!(template, { { 2030, 1, 1 }, { 0, 0, 0 } })
+ not_cached = render "module.html"
+ assert module != not_cached
+ after
+ File.touch!(template, :erlang.universaltime)
+ end
end
defp render(query) do
- Dynamo.Views.render Dynamo.Views.find(query, @view_paths), []
+ Dynamo.View.render Dynamo.View.find(query, @view_paths), []
end
end

0 comments on commit 7b9f577

Please sign in to comment.