Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Split locals and assigns, delegate more responsibilities to the handler

  • Loading branch information...
commit 41fbb83a3c7a2e1595ff8052819691c4e50c1055 1 parent f184917
José Valim authored September 09, 2012
44  lib/dynamo/view.ex
@@ -31,42 +31,34 @@ defmodule Dynamo.View do
31 31
   @doc """
32 32
   Renders the given template with the given assigns.
33 33
   """
34  
-  def render(template, assigns) do
35  
-    Dynamo.View.Renderer.render(template, Keyword.put(assigns, :template, template))
  34
+  def render(template, locals, assigns) do
  35
+    Dynamo.View.Renderer.render(template, locals, assigns)
36 36
   end
37 37
 
38 38
   @doc """
39 39
   Compiles the given set of `templates` into a module
40 40
   given by `name`. It returns the module binary,
41 41
   """
42  
-  def compile_module(name, templates) do
43  
-    { _, binary, _ } = defmodule name do
44  
-      Enum.reduce templates, 0, fn(template, i) ->
45  
-        template = template.ref({ name, :"template_#{i}" })
46  
-        def :find, [template.key], [], do: Macro.escape(template)
47  
-        i + 1
48  
-      end
49  
-
50  
-      def find(_) do
51  
-        nil
52  
-      end
53  
-
54  
-      args = quote hygiene: false, do: [assigns]
55  
-
56  
-      Enum.reduce templates, 0, fn(template, i) ->
57  
-        source = Dynamo.View.Handler.get!(template.handler).compile(template)
58  
-
59  
-        source = quote hygiene: false do
60  
-          _ = assigns
61  
-          unquote(source)
  42
+  def compile_module(name, templates, locals) do
  43
+    { _, binary, _ } =
  44
+      defmodule name do
  45
+        Enum.reduce templates, 0, fn(template, i) ->
  46
+          template = template.ref({ name, :"template_#{i}" })
  47
+          def :find, [template.key], [], do: Macro.escape(template)
  48
+          i + 1
62 49
         end
63 50
 
64  
-        @file template.identifier
65  
-        def :"template_#{i}", args, [], do: source
  51
+        def find(_) do
  52
+          nil
  53
+        end
66 54
 
67  
-        i + 1
  55
+        Enum.reduce templates, 0, fn(template, i) ->
  56
+          { args, source } = template.handler.compile(template, locals)
  57
+          @file template.identifier
  58
+          def :"template_#{i}", args, [], do: source
  59
+          i + 1
  60
+        end
68 61
       end
69  
-    end
70 62
 
71 63
     binary
72 64
   end
2  lib/dynamo/view/finder.ex
@@ -72,7 +72,7 @@ defmodule Dynamo.View.PathFinder do
72 72
       key: key,
73 73
       updated_at: File.stat!(path).mtime,
74 74
       identifier: path,
75  
-      handler: extname(path),
  75
+      handler: Dynamo.View.Handler.get!(extname(path)),
76 76
       format: extname(File.rootname(path)),
77 77
       source: File.read!(path)
78 78
     ]
36  lib/dynamo/view/handler.ex
@@ -9,13 +9,22 @@ defmodule Dynamo.View.Handler do
9 9
   @doc """
10 10
   A template handler must simply implement
11 11
   compile, receiving a Dynamo.View.Template
12  
-  record.
  12
+  record. It must return the arguments and
  13
+  a source, which will then be compiled to
  14
+  a function.
13 15
 
14 16
   A template handler must be necessarily
15 17
   named as Dynamo.View.EXTHandler where
16 18
   EXT is the handler extension.
17 19
   """
18  
-  defcallback compile(template)
  20
+  defcallback compile(template, locals)
  21
+
  22
+  @doc """
  23
+  Receives a module and function the compiled
  24
+  template is stored plus the locals and assigns
  25
+  to be used on dispatch.
  26
+  """
  27
+  defcallback render(module, function, locals, assigns)
19 28
 
20 29
   @doc """
21 30
   Get the template handler for the given extension.
@@ -46,7 +55,26 @@ defmodule Dynamo.View.EEXHandler do
46 55
   @moduledoc false
47 56
   @behaviour Dynamo.View.Handler
48 57
 
49  
-  def compile(Dynamo.View.Template[source: source, identifier: identifier]) do
50  
-    EEx.compile_string(source, file: identifier)
  58
+  def compile(Dynamo.View.Template[source: source, identifier: identifier], locals) do
  59
+    locals = [:assigns|locals]
  60
+    match  = match(locals)
  61
+    source = EEx.compile_string(source, file: identifier)
  62
+
  63
+    { args(locals), quote do
  64
+      __block__ unquote(match)
  65
+      unquote(source)
  66
+    end }
  67
+  end
  68
+
  69
+  def render(module, function, locals, assigns) do
  70
+    apply module, function, [assigns|Keyword.values(locals)]
  71
+  end
  72
+
  73
+  defp args(locals) do
  74
+    lc name inlist locals, do: { name, 0, nil }
  75
+  end
  76
+
  77
+  defp match(locals) do
  78
+    lc name inlist locals, do: { :=, 0, [{ :_, 0, nil }, { name, 0 , nil }] }
51 79
   end
52 80
 end
34  lib/dynamo/view/renderer.ex
@@ -35,15 +35,17 @@ defmodule Dynamo.View.Renderer do
35 35
   The on demand mode needs to be explicitly enabled
36 36
   by calling start_link/0.
37 37
   """
38  
-  def render(Template[ref: { mod, fun }], assigns) do
39  
-    apply mod, fun, [assigns]
  38
+  def render(Template[ref: { mod, fun }, handler: handler], locals, assigns) do
  39
+    handler.render(mod, fun, locals, assigns)
40 40
   end
41 41
 
42  
-  def render(template, assigns) do
  42
+  def render(Template[handler: handler] = template, locals, assigns) do
43 43
     module =
44  
-      get_cached(template) || compile(template) || raise_too_busy(template)
  44
+      get_cached(template) ||
  45
+      compile(template, Keyword.keys(locals)) ||
  46
+      raise_too_busy(template)
45 47
 
46  
-    module.render(assigns)
  48
+    handler.render(module, :render, locals, assigns)
47 49
   end
48 50
 
49 51
   ## Callbacks
@@ -66,8 +68,8 @@ defmodule Dynamo.View.Renderer do
66 68
     end
67 69
   end
68 70
 
69  
-  def handle_call({ :register, identifier, updated_at, compiled }, _from, dict) do
70  
-    if module = generate_module(compiled, identifier, 0) do
  71
+  def handle_call({ :register, identifier, updated_at, args, source }, _from, dict) do
  72
+    if module = generate_module(args, source, identifier, 0) do
71 73
       { :reply, module, Dict.put(dict, identifier, { module, updated_at }) }
72 74
     else
73 75
       { :reply, nil, dict }
@@ -101,9 +103,9 @@ defmodule Dynamo.View.Renderer do
101 103
     :gen_server.call(__MODULE__, { :get_cached, identifier, updated_at })
102 104
   end
103 105
 
104  
-  defp compile(Template[handler: handler, identifier: identifier, updated_at: updated_at] = template) do
105  
-    compiled = Dynamo.View.Handler.get!(handler).compile(template)
106  
-    :gen_server.call(__MODULE__, { :register, identifier, updated_at, compiled })
  106
+  defp compile(Template[handler: handler, identifier: identifier, updated_at: updated_at] = template, locals) do
  107
+    { args, source } = handler.compile(template, locals)
  108
+    :gen_server.call(__MODULE__, { :register, identifier, updated_at, args, source })
107 109
   end
108 110
 
109 111
   defp raise_too_busy(Template[identifier: identifier]) do
@@ -115,21 +117,15 @@ defmodule Dynamo.View.Renderer do
115 117
     :code.delete(module)
116 118
   end
117 119
 
118  
-  defp generate_module(source, identifier, attempts) when attempts < @max_attemps do
  120
+  defp generate_module(args, source, identifier, attempts) when attempts < @max_attemps do
119 121
     random = :random.uniform(@slots)
120 122
     module = Module.concat(Dynamo.View, "Template#{random}")
121 123
 
122 124
     if :code.is_loaded(module) do
123  
-      generate_module(source, identifier, attempts + 1)
  125
+      generate_module(args, source, identifier, attempts + 1)
124 126
     else
125  
-      source = quote hygiene: false do
126  
-        _ = assigns
127  
-        unquote(source)
128  
-      end
129  
-
130 127
       defmodule module do
131 128
         @file identifier
132  
-        args = quote hygiene: false, do: [assigns]
133 129
         def :render, args, [], do: source
134 130
       end
135 131
 
@@ -137,7 +133,7 @@ defmodule Dynamo.View.Renderer do
137 133
     end
138 134
   end
139 135
 
140  
-  defp generate_module(_, _, _) do
  136
+  defp generate_module(_, _, _, _) do
141 137
     nil
142 138
   end
143 139
 end
2  lib/mix/tasks/compile.dynamo.ex
@@ -115,7 +115,7 @@ defmodule Mix.Tasks.Compile.Dynamo do
115 115
                    view_path.eager?,
116 116
                    template inlist view_path.all, do: template
117 117
 
118  
-    binary = Dynamo.View.compile_module(name, templates)
  118
+    binary = Dynamo.View.compile_module(name, templates, [:conn])
119 119
     File.write! File.join(compile_path, "#{name}.beam"), binary
120 120
 
121 121
     Mix.shell.info "Generated #{inspect name}"
2  test/dynamo/view/finder_test.exs
@@ -10,7 +10,7 @@ defmodule Dynamo.View.PathFinderTest do
10 10
     path = File.join(@fixture_path, "hello.html.eex")
11 11
 
12 12
     assert Dynamo.View.Template[identifier: ^path, key: "hello.html",
13  
-      handler: "eex", format: "html"] = @path_finder.find "hello.html"
  13
+      handler: Dynamo.View.EEXHandler, format: "html"] = @path_finder.find "hello.html"
14 14
   end
15 15
 
16 16
   test "returns all templates" do
6  test/dynamo/view_test.exs
@@ -52,19 +52,19 @@ defmodule Dynamo.ViewTest do
52 52
   end
53 53
 
54 54
   test "compiles a module with the given templates" do
55  
-    Dynamo.View.compile_module(CompileTest.Sample0, @path_finder.all)
  55
+    Dynamo.View.compile_module(CompileTest.Sample0, @path_finder.all, [])
56 56
 
57 57
     path     = File.join(@fixture_path, "hello.html.eex")
58 58
     template = CompileTest.Sample0.find "hello.html"
59 59
 
60 60
     assert Dynamo.View.Template[identifier: ^path, key: "hello.html",
61  
-      handler: "eex", format: "html", ref: { CompileTest.Sample0, _ }] = template
  61
+      handler: Dynamo.View.EEXHandler, format: "html", ref: { CompileTest.Sample0, _ }] = template
62 62
 
63 63
     { mod, fun } = template.ref
64 64
     assert apply(mod, fun, [[]]) == "HELLO!"
65 65
   end
66 66
 
67 67
   defp render(query) do
68  
-    Dynamo.View.render Dynamo.View.find(query, @view_paths), []
  68
+    Dynamo.View.render Dynamo.View.find(query, @view_paths), [], []
69 69
   end
70 70
 end

0 notes on commit 41fbb83

Please sign in to comment.
Something went wrong with that request. Please try again.