poirot 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,13 +2,27 @@ module Poirot
2
2
  module AssetHelper
3
3
  def template_include_tag(*sources)
4
4
  sources.collect do |source|
5
- template = File.open(_poirot_resolve_partial_path(source), "rb")
6
- content_tag :script, template.read.html_safe, :type => "text/mustache", :id => "#{source.parameterize.dasherize}-template"
5
+ template = File.open(resolve_partial_path(source), "rb")
6
+ content_tag :script, template.read.html_safe, :type => "text/mustache", :id => "#{template_name(source).parameterize.dasherize}-template"
7
7
  end.join("\n").html_safe
8
8
  end
9
-
10
- def _poirot_resolve_partial_path(source)
11
- if source.to_s =~ /^\// # absolute path to a different view folder
9
+
10
+ private
11
+
12
+ def is_absolute_path?(source)
13
+ source.to_s =~ /^\//
14
+ end
15
+
16
+ def template_name(source)
17
+ if is_absolute_path?(source)
18
+ source.to_s.split('/').last
19
+ else
20
+ source
21
+ end
22
+ end
23
+
24
+ def resolve_partial_path(source)
25
+ if is_absolute_path?(source)
12
26
  segments = source.to_s.split('/')
13
27
  partial_name = "_#{segments.pop}.html.mustache"
14
28
  segments << partial_name
@@ -17,6 +31,5 @@ module Poirot
17
31
  Rails.root.join('app/views', controller_name, "_#{source}.html.mustache")
18
32
  end
19
33
  end
20
-
21
34
  end
22
35
  end
@@ -6,6 +6,7 @@ module Poirot
6
6
  view_class = begin
7
7
  view_path.classify.constantize
8
8
  rescue NameError => e
9
+ Rails.logger.info ">>> #{e}"
9
10
  Poirot::View
10
11
  end
11
12
  "#{view_class}.new(self, '#{template.source.gsub(/'/, "\\\\'")}').render.html_safe"
@@ -1,3 +1,3 @@
1
1
  module Poirot
2
- VERSION = "0.0.3"
2
+ VERSION = "0.1.0"
3
3
  end
data/lib/poirot/view.rb CHANGED
@@ -3,18 +3,42 @@ module Poirot
3
3
 
4
4
  def initialize(view_context, template_source)
5
5
  @view_context = view_context
6
+ @params = view_context.params || {}
6
7
  self.template = template_source
7
8
  assign_variables!
8
9
  end
9
10
 
11
+ self.template_extension = 'html.mustache'
12
+
13
+ def self.inherited(view_class)
14
+ self.template_path = Rails.root.join('app', 'views', view_class.name.split('::').first.downcase)
15
+ end
16
+
10
17
  def respond_to?(method_sym, include_private = false)
11
- if view_context.respond_to?(method_sym)
18
+ if view_context.respond_to?(method_sym)
12
19
  true
13
20
  else
14
21
  super
15
22
  end
16
23
  end
17
24
 
25
+ def render_buffer(buffer)
26
+ self.render(buffer, context).html_safe
27
+ end
28
+
29
+ def include_partial(name)
30
+ path = ["app/views", controller_name]
31
+ path += name.split("/")
32
+ path.delete(nil)
33
+ path.last.replace("_#{path.last}.html.mustache")
34
+
35
+ filename = path.join("/")
36
+
37
+ Dir.chdir(Rails.root) do
38
+ render_buffer(File.read(filename))
39
+ end
40
+ end
41
+
18
42
  def method_missing(method_name, *args, &block)
19
43
  instance_var = instance_variable_get("@#{method_name}")
20
44
  if defined?(instance_var) && args.empty?
@@ -27,6 +51,7 @@ module Poirot
27
51
  private
28
52
 
29
53
  attr_reader :view_context
54
+ attr_reader :params
30
55
 
31
56
  def assign_variables!
32
57
  variables = view_context.instance_variable_names.select{|name| name =~ /^@[^_]/}
@@ -36,12 +61,14 @@ module Poirot
36
61
  self[name.tr('@','').to_sym] = instance_var
37
62
  end
38
63
 
39
- # get the locals from the view context, is there a better way?
40
- locals = view_context.send(:view_renderer).send(:_partial_renderer).instance_variable_get("@locals") || {}
64
+ if Rails.version >= "3.1"
65
+ # get the locals from the view context, is there a better way?
66
+ locals = view_context.send(:view_renderer).send(:_partial_renderer).instance_variable_get("@locals") || {}
41
67
 
42
- locals.each do |name, val|
43
- instance_variable_set("@#{name}", val)
44
- self[name] = val
68
+ locals.each do |name, val|
69
+ instance_variable_set("@#{name}", val)
70
+ self[name] = val
71
+ end
45
72
  end
46
73
  end
47
74
  end
@@ -5,6 +5,7 @@
5
5
  */
6
6
 
7
7
  var Mustache = function() {
8
+ var regexCache = {};
8
9
  var Renderer = function() {};
9
10
 
10
11
  Renderer.prototype = {
@@ -34,13 +35,22 @@ var Mustache = function() {
34
35
  }
35
36
  }
36
37
 
38
+ // get the pragmas together
37
39
  template = this.render_pragmas(template);
40
+
41
+ // render the template
38
42
  var html = this.render_section(template, context, partials);
39
- if(in_recursion) {
40
- return this.render_tags(html, context, partials, in_recursion);
43
+
44
+ // render_section did not find any sections, we still need to render the tags
45
+ if (html === false) {
46
+ html = this.render_tags(template, context, partials, in_recursion);
41
47
  }
42
48
 
43
- this.render_tags(html, context, partials, in_recursion);
49
+ if (in_recursion) {
50
+ return html;
51
+ } else {
52
+ this.sendLines(html);
53
+ }
44
54
  },
45
55
 
46
56
  /*
@@ -52,6 +62,15 @@ var Mustache = function() {
52
62
  }
53
63
  },
54
64
 
65
+ sendLines: function(text) {
66
+ if (text) {
67
+ var lines = text.split("\n");
68
+ for (var i = 0; i < lines.length; i++) {
69
+ this.send(lines[i]);
70
+ }
71
+ }
72
+ },
73
+
55
74
  /*
56
75
  Looks for %PRAGMAS
57
76
  */
@@ -62,11 +81,13 @@ var Mustache = function() {
62
81
  }
63
82
 
64
83
  var that = this;
65
- var regex = new RegExp(this.otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" +
66
- this.ctag, "g");
84
+ var regex = this.getCachedRegex("render_pragmas", function(otag, ctag) {
85
+ return new RegExp(otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" + ctag, "g");
86
+ });
87
+
67
88
  return template.replace(regex, function(match, pragma, options) {
68
89
  if(!that.pragmas_implemented[pragma]) {
69
- throw({message:
90
+ throw({message:
70
91
  "This implementation of mustache doesn't understand the '" +
71
92
  pragma + "' pragma"});
72
93
  }
@@ -99,45 +120,74 @@ var Mustache = function() {
99
120
  */
100
121
  render_section: function(template, context, partials) {
101
122
  if(!this.includes("#", template) && !this.includes("^", template)) {
102
- return template;
123
+ // did not render anything, there were no sections
124
+ return false;
103
125
  }
104
126
 
105
127
  var that = this;
106
- // CSW - Added "+?" so it finds the tighest bound, not the widest
107
- var regex = new RegExp(this.otag + "(\\^|\\#)\\s*(.+)\\s*" + this.ctag +
108
- "\n*([\\s\\S]+?)" + this.otag + "\\/\\s*\\2\\s*" + this.ctag +
109
- "\\s*", "mg");
128
+
129
+ var regex = this.getCachedRegex("render_section", function(otag, ctag) {
130
+ // This regex matches _the first_ section ({{#foo}}{{/foo}}), and captures the remainder
131
+ return new RegExp(
132
+ "^([\\s\\S]*?)" + // all the crap at the beginning that is not {{*}} ($1)
133
+
134
+ otag + // {{
135
+ "(\\^|\\#)\\s*(.+)\\s*" + // #foo (# == $2, foo == $3)
136
+ ctag + // }}
137
+
138
+ "\n*([\\s\\S]*?)" + // between the tag ($2). leading newlines are dropped
139
+
140
+ otag + // {{
141
+ "\\/\\s*\\3\\s*" + // /foo (backreference to the opening tag).
142
+ ctag + // }}
143
+
144
+ "\\s*([\\s\\S]*)$", // everything else in the string ($4). leading whitespace is dropped.
145
+
146
+ "g");
147
+ });
148
+
110
149
 
111
150
  // for each {{#foo}}{{/foo}} section do...
112
- return template.replace(regex, function(match, type, name, content) {
113
- var value = that.find(name, context);
114
- if(type == "^") { // inverted section
115
- if(!value || that.is_array(value) && value.length === 0) {
151
+ return template.replace(regex, function(match, before, type, name, content, after) {
152
+ // before contains only tags, no sections
153
+ var renderedBefore = before ? that.render_tags(before, context, partials, true) : "",
154
+
155
+ // after may contain both sections and tags, so use full rendering function
156
+ renderedAfter = after ? that.render(after, context, partials, true) : "",
157
+
158
+ // will be computed below
159
+ renderedContent,
160
+
161
+ value = that.find(name, context);
162
+
163
+ if (type === "^") { // inverted section
164
+ if (!value || that.is_array(value) && value.length === 0) {
116
165
  // false or empty list, render it
117
- return that.render(content, context, partials, true);
166
+ renderedContent = that.render(content, context, partials, true);
118
167
  } else {
119
- return "";
168
+ renderedContent = "";
120
169
  }
121
- } else if(type == "#") { // normal section
122
- if(that.is_array(value)) { // Enumerable, Let's loop!
123
- return that.map(value, function(row) {
124
- return that.render(content, that.create_context(row),
125
- partials, true);
170
+ } else if (type === "#") { // normal section
171
+ if (that.is_array(value)) { // Enumerable, Let's loop!
172
+ renderedContent = that.map(value, function(row) {
173
+ return that.render(content, that.create_context(row), partials, true);
126
174
  }).join("");
127
- } else if(that.is_object(value)) { // Object, Use it as subcontext!
128
- return that.render(content, that.create_context(value),
175
+ } else if (that.is_object(value)) { // Object, Use it as subcontext!
176
+ renderedContent = that.render(content, that.create_context(value),
129
177
  partials, true);
130
- } else if(typeof value === "function") {
178
+ } else if (typeof value === "function") {
131
179
  // higher order section
132
- return value.call(context, content, function(text) {
180
+ renderedContent = value.call(context, content, function(text) {
133
181
  return that.render(text, context, partials, true);
134
182
  });
135
- } else if(value) { // boolean section
136
- return that.render(content, context, partials, true);
183
+ } else if (value) { // boolean section
184
+ renderedContent = that.render(content, context, partials, true);
137
185
  } else {
138
- return "";
186
+ renderedContent = "";
139
187
  }
140
188
  }
189
+
190
+ return renderedBefore + renderedContent + renderedAfter;
141
191
  });
142
192
  },
143
193
 
@@ -148,9 +198,12 @@ var Mustache = function() {
148
198
  // tit for tat
149
199
  var that = this;
150
200
 
201
+
202
+
151
203
  var new_regex = function() {
152
- return new RegExp(that.otag + "(=|!|>|\\{|%)?([^\\/#\\^]+?)\\1?" +
153
- that.ctag + "+", "g");
204
+ return that.getCachedRegex("render_tags", function(otag, ctag) {
205
+ return new RegExp(otag + "(=|!|>|\\{|%)?([^\\/#\\^]+?)\\1?" + ctag + "+", "g");
206
+ });
154
207
  };
155
208
 
156
209
  var regex = new_regex();
@@ -216,10 +269,20 @@ var Mustache = function() {
216
269
  }
217
270
 
218
271
  var value;
219
- if(is_kinda_truthy(context[name])) {
220
- value = context[name];
221
- } else if(is_kinda_truthy(this.context[name])) {
222
- value = this.context[name];
272
+
273
+ // check for dot notation eg. foo.bar
274
+ if(name.match(/([a-z_]+)\./ig)){
275
+ var childValue = this.walk_context(name, context);
276
+ if(is_kinda_truthy(childValue)) {
277
+ value = childValue;
278
+ }
279
+ }
280
+ else{
281
+ if(is_kinda_truthy(context[name])) {
282
+ value = context[name];
283
+ } else if(is_kinda_truthy(this.context[name])) {
284
+ value = this.context[name];
285
+ }
223
286
  }
224
287
 
225
288
  if(typeof value === "function") {
@@ -232,6 +295,22 @@ var Mustache = function() {
232
295
  return "";
233
296
  },
234
297
 
298
+ walk_context: function(name, context){
299
+ var path = name.split('.');
300
+ // if the var doesn't exist in current context, check the top level context
301
+ var value_context = (context[path[0]] != undefined) ? context : this.context;
302
+ var value = value_context[path.shift()];
303
+ while(value != undefined && path.length > 0){
304
+ value_context = value;
305
+ value = value[path.shift()];
306
+ }
307
+ // if the value is a function, call it, binding the correct context
308
+ if(typeof value === "function") {
309
+ return value.apply(value_context);
310
+ }
311
+ return value;
312
+ },
313
+
235
314
  // Utility methods
236
315
 
237
316
  /* includes tag */
@@ -247,7 +326,6 @@ var Mustache = function() {
247
326
  return s.replace(/&(?!\w+;)|["'<>\\]/g, function(s) {
248
327
  switch(s) {
249
328
  case "&": return "&amp;";
250
- case "\\": return "\\\\";
251
329
  case '"': return '&quot;';
252
330
  case "'": return '&#39;';
253
331
  case "<": return "&lt;";
@@ -301,12 +379,31 @@ var Mustache = function() {
301
379
  }
302
380
  return r;
303
381
  }
382
+ },
383
+
384
+ getCachedRegex: function(name, generator) {
385
+ var byOtag = regexCache[this.otag];
386
+ if (!byOtag) {
387
+ byOtag = regexCache[this.otag] = {};
388
+ }
389
+
390
+ var byCtag = byOtag[this.ctag];
391
+ if (!byCtag) {
392
+ byCtag = byOtag[this.ctag] = {};
393
+ }
394
+
395
+ var regex = byCtag[name];
396
+ if (!regex) {
397
+ regex = byCtag[name] = generator(this.otag, this.ctag);
398
+ }
399
+
400
+ return regex;
304
401
  }
305
402
  };
306
403
 
307
404
  return({
308
405
  name: "mustache.js",
309
- version: "0.3.1-dev",
406
+ version: "0.4.0-dev",
310
407
 
311
408
  /*
312
409
  Turns a template and view into HTML
@@ -316,7 +413,7 @@ var Mustache = function() {
316
413
  if(send_fun) {
317
414
  renderer.send = send_fun;
318
415
  }
319
- renderer.render(template, view, partials);
416
+ renderer.render(template, view || {}, partials);
320
417
  if(!send_fun) {
321
418
  return renderer.buffer.join("\n");
322
419
  }
@@ -1,7 +1,14 @@
1
1
  var poirot = (function ($) {
2
2
 
3
+ var viewFactory = function (template, partials) {
4
+ return function (data) {
5
+ return $(Mustache.to_html(template, data, partials))
6
+ }
7
+ }
8
+
3
9
  var poirot = {
4
- partials: {}
10
+ partials: {},
11
+ _viewFactory: viewFactory
5
12
  }
6
13
 
7
14
  $(document).ready(function () {
@@ -13,9 +20,7 @@ var poirot = (function ($) {
13
20
 
14
21
  poirot.partials[methodName] = template
15
22
 
16
- poirot[methodName] = function (presenter) {
17
- return $(Mustache.to_html(template, presenter, poirot.partials))
18
- }
23
+ poirot[methodName] = poirot._viewFactory(template, poirot.partials)
19
24
  })
20
25
  })
21
26
 
metadata CHANGED
@@ -1,57 +1,46 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: poirot
3
- version: !ruby/object:Gem::Version
4
- prerelease: false
5
- segments:
6
- - 0
7
- - 0
8
- - 3
9
- version: 0.0.3
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
10
6
  platform: ruby
11
- authors:
7
+ authors:
12
8
  - Oliver Nightingale
13
9
  - Mark Evans
14
10
  autorequire:
15
11
  bindir: bin
16
12
  cert_chain: []
17
-
18
- date: 2011-08-15 00:00:00 +01:00
19
- default_executable:
20
- dependencies:
21
- - !ruby/object:Gem::Dependency
13
+ date: 2012-01-04 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
22
16
  name: rails
23
- prerelease: false
24
- requirement: &id001 !ruby/object:Gem::Requirement
25
- requirements:
26
- - - ">"
27
- - !ruby/object:Gem::Version
28
- segments:
29
- - 3
30
- version: "3"
17
+ requirement: &70170133189960 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>'
21
+ - !ruby/object:Gem::Version
22
+ version: '3'
31
23
  type: :runtime
32
- version_requirements: *id001
33
- - !ruby/object:Gem::Dependency
34
- name: mustache
35
24
  prerelease: false
36
- requirement: &id002 !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- segments:
41
- - 0
42
- version: "0"
25
+ version_requirements: *70170133189960
26
+ - !ruby/object:Gem::Dependency
27
+ name: mustache
28
+ requirement: &70170133189380 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
43
34
  type: :runtime
44
- version_requirements: *id002
35
+ prerelease: false
36
+ version_requirements: *70170133189380
45
37
  description: mustaches are cool
46
- email:
38
+ email:
47
39
  - oliver.nightingale1@gmail.com
48
40
  executables: []
49
-
50
41
  extensions: []
51
-
52
42
  extra_rdoc_files: []
53
-
54
- files:
43
+ files:
55
44
  - .gitignore
56
45
  - Gemfile
57
46
  - Gemfile.lock
@@ -67,35 +56,28 @@ files:
67
56
  - vendor/assets/javascripts/poirot/index.js
68
57
  - vendor/assets/javascripts/poirot/mustache.js
69
58
  - vendor/assets/javascripts/poirot/poirot.js
70
- has_rdoc: true
71
59
  homepage: http://rubygems.org/gems/poirot
72
60
  licenses: []
73
-
74
61
  post_install_message:
75
62
  rdoc_options: []
76
-
77
- require_paths:
63
+ require_paths:
78
64
  - lib
79
- required_ruby_version: !ruby/object:Gem::Requirement
80
- requirements:
81
- - - ">="
82
- - !ruby/object:Gem::Version
83
- segments:
84
- - 0
85
- version: "0"
86
- required_rubygems_version: !ruby/object:Gem::Requirement
87
- requirements:
88
- - - ">="
89
- - !ruby/object:Gem::Version
90
- segments:
91
- - 0
92
- version: "0"
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
93
77
  requirements: []
94
-
95
78
  rubyforge_project: poirot
96
- rubygems_version: 1.3.6
79
+ rubygems_version: 1.8.11
97
80
  signing_key:
98
81
  specification_version: 3
99
82
  summary: mustaches
100
83
  test_files: []
101
-