poirot 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Gemfile.lock ADDED
@@ -0,0 +1,83 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ poirot (0.0.2)
5
+ mustache
6
+ rails (> 3)
7
+
8
+ GEM
9
+ remote: http://rubygems.org/
10
+ specs:
11
+ abstract (1.0.0)
12
+ actionmailer (3.0.9)
13
+ actionpack (= 3.0.9)
14
+ mail (~> 2.2.19)
15
+ actionpack (3.0.9)
16
+ activemodel (= 3.0.9)
17
+ activesupport (= 3.0.9)
18
+ builder (~> 2.1.2)
19
+ erubis (~> 2.6.6)
20
+ i18n (~> 0.5.0)
21
+ rack (~> 1.2.1)
22
+ rack-mount (~> 0.6.14)
23
+ rack-test (~> 0.5.7)
24
+ tzinfo (~> 0.3.23)
25
+ activemodel (3.0.9)
26
+ activesupport (= 3.0.9)
27
+ builder (~> 2.1.2)
28
+ i18n (~> 0.5.0)
29
+ activerecord (3.0.9)
30
+ activemodel (= 3.0.9)
31
+ activesupport (= 3.0.9)
32
+ arel (~> 2.0.10)
33
+ tzinfo (~> 0.3.23)
34
+ activeresource (3.0.9)
35
+ activemodel (= 3.0.9)
36
+ activesupport (= 3.0.9)
37
+ activesupport (3.0.9)
38
+ arel (2.0.10)
39
+ builder (2.1.2)
40
+ erubis (2.6.6)
41
+ abstract (>= 1.0.0)
42
+ i18n (0.5.0)
43
+ mail (2.2.19)
44
+ activesupport (>= 2.3.6)
45
+ i18n (>= 0.4.0)
46
+ mime-types (~> 1.16)
47
+ treetop (~> 1.4.8)
48
+ mime-types (1.16)
49
+ mustache (0.99.4)
50
+ polyglot (0.3.1)
51
+ rack (1.2.3)
52
+ rack-mount (0.6.14)
53
+ rack (>= 1.0.0)
54
+ rack-test (0.5.7)
55
+ rack (>= 1.0)
56
+ rails (3.0.9)
57
+ actionmailer (= 3.0.9)
58
+ actionpack (= 3.0.9)
59
+ activerecord (= 3.0.9)
60
+ activeresource (= 3.0.9)
61
+ activesupport (= 3.0.9)
62
+ bundler (~> 1.0)
63
+ railties (= 3.0.9)
64
+ railties (3.0.9)
65
+ actionpack (= 3.0.9)
66
+ activesupport (= 3.0.9)
67
+ rake (>= 0.8.7)
68
+ rdoc (~> 3.4)
69
+ thor (~> 0.14.4)
70
+ rake (0.9.2)
71
+ rdoc (3.8)
72
+ thor (0.14.6)
73
+ treetop (1.4.9)
74
+ polyglot (>= 0.3.1)
75
+ tzinfo (0.3.29)
76
+
77
+ PLATFORMS
78
+ ruby
79
+
80
+ DEPENDENCIES
81
+ mustache
82
+ poirot!
83
+ rails (>= 3.0.0)
data/Readme.markdown CHANGED
@@ -44,6 +44,24 @@ type will be set as `text/mustache` and the id will be `post-list-template`.
44
44
  <!-- template will be here! -->
45
45
  </script>
46
46
 
47
+ ### Javascript Helper
48
+
49
+ Poirot also adds a javascript helper for using mustache templates from the browser. In Rails 3.1 this will be automatically added to the asset pipeline.
50
+
51
+ If you are using rails 3 then you can run the `rails g poirot:install` to install the javascripts in your application.
52
+
53
+ Using the poirot javascript helper is simple, given a template added to the page using `template_include_tag`
54
+
55
+ <%= template_include_tag 'post_list' %>
56
+
57
+ You can render this template from javascript by doing the following
58
+
59
+ poirot.postList()
60
+
61
+ This will return the contents of the `post_list` template wrapped in a jQuery object, ready for inserting into the dom. If you have data to pass to the template then you can pass it as the argument to the function, e.g.
62
+
63
+ poirot.postList({foo: "bar"})
64
+
47
65
  ## Dependencies
48
66
 
49
67
  * Rails >3.0.0
@@ -0,0 +1,21 @@
1
+ require 'rails'
2
+
3
+ module Poirot
4
+ module Generators
5
+ class InstallGenerator < ::Rails::Generators::Base
6
+
7
+ desc "This generator installs Poirot JavaScript helper"
8
+ source_root File.expand_path('../../../../../vendor/assets/javascripts/poirot', __FILE__)
9
+
10
+ def copy_mustache
11
+ say_status("copying", "Mustache.js", :green)
12
+ copy_file "mustache.js", "public/javascripts/mustache.js"
13
+ end
14
+
15
+ def copy_poirot
16
+ say_status("copying", "poirot.js", :green)
17
+ copy_file "poirot.js", "public/javascripts/poirot.js"
18
+ end
19
+ end
20
+ end
21
+ end if ::Rails.version < "3.1"
@@ -2,10 +2,21 @@ module Poirot
2
2
  module AssetHelper
3
3
  def template_include_tag(*sources)
4
4
  sources.collect do |source|
5
- template_path = Rails.root.join('app/views', controller_name, "_#{source}.html.mustache")
6
- template = File.open(template_path, "rb")
7
- content_tag :script, template.read.html_safe, :type => "text/mustache", :id => "#{source.dasherize}-template"
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"
8
7
  end.join("\n").html_safe
9
8
  end
9
+
10
+ def _poirot_resolve_partial_path(source)
11
+ if source.to_s =~ /^\// # absolute path to a different view folder
12
+ segments = source.to_s.split('/')
13
+ partial_name = "_#{segments.pop}.html.mustache"
14
+ segments << partial_name
15
+ Rails.root.join('app/views', *segments)
16
+ else
17
+ Rails.root.join('app/views', controller_name, "_#{source}.html.mustache")
18
+ end
19
+ end
20
+
10
21
  end
11
22
  end
@@ -1,10 +1,6 @@
1
1
  module Poirot
2
- class Handler < ActionView::Template::Handler
3
- include ActionView::Template::Handlers::Compilable
4
-
5
- self.default_format = :mustache
6
-
7
- def compile(template)
2
+ class Handler
3
+ def self.call(template)
8
4
  view_path = "#{template.virtual_path}_view"
9
5
  abs_view_path = Rails.root.join('app/views', view_path)
10
6
  view_class = begin
@@ -15,4 +11,4 @@ module Poirot
15
11
  "#{view_class}.new(self, '#{template.source.gsub(/'/, "\\\\'")}').render.html_safe"
16
12
  end
17
13
  end
18
- end
14
+ end
@@ -1,3 +1,3 @@
1
1
  module Poirot
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
data/lib/poirot/view.rb CHANGED
@@ -36,11 +36,13 @@ module Poirot
36
36
  self[name.tr('@','').to_sym] = instance_var
37
37
  end
38
38
 
39
- @renderer.instance_variable_get("@locals").each do |name, val|
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") || {}
41
+
42
+ locals.each do |name, val|
40
43
  instance_variable_set("@#{name}", val)
41
44
  self[name] = val
42
45
  end
43
-
44
46
  end
45
47
  end
46
48
  end
data/lib/poirot.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'mustache'
1
2
  require 'poirot/handler'
2
3
  require 'poirot/view'
3
4
  require 'poirot/asset_helper'
@@ -6,11 +7,10 @@ ActionView::Template.register_template_handler(:mustache, Poirot::Handler)
6
7
  ActionView::Base.send :include, Poirot::AssetHelper
7
8
 
8
9
  module Poirot
10
+ class Engine < Rails::Engine ; end if Rails.version >= "3.1"
9
11
  class Railtie < Rails::Railtie
10
-
11
12
  config.before_configuration do |app|
12
13
  app.config.autoload_paths += %W(#{app.config.root}/app/views)
13
14
  end
14
-
15
15
  end
16
16
  end
@@ -0,0 +1,2 @@
1
+ //= require ./mustache
2
+ //= require ./poirot
@@ -0,0 +1,325 @@
1
+ /*
2
+ mustache.js — Logic-less templates in JavaScript
3
+
4
+ See http://mustache.github.com/ for more info.
5
+ */
6
+
7
+ var Mustache = function() {
8
+ var Renderer = function() {};
9
+
10
+ Renderer.prototype = {
11
+ otag: "{{",
12
+ ctag: "}}",
13
+ pragmas: {},
14
+ buffer: [],
15
+ pragmas_implemented: {
16
+ "IMPLICIT-ITERATOR": true
17
+ },
18
+ context: {},
19
+
20
+ render: function(template, context, partials, in_recursion) {
21
+ // reset buffer & set context
22
+ if(!in_recursion) {
23
+ this.context = context;
24
+ this.buffer = []; // TODO: make this non-lazy
25
+ }
26
+
27
+ // fail fast
28
+ if(!this.includes("", template)) {
29
+ if(in_recursion) {
30
+ return template;
31
+ } else {
32
+ this.send(template);
33
+ return;
34
+ }
35
+ }
36
+
37
+ template = this.render_pragmas(template);
38
+ var html = this.render_section(template, context, partials);
39
+ if(in_recursion) {
40
+ return this.render_tags(html, context, partials, in_recursion);
41
+ }
42
+
43
+ this.render_tags(html, context, partials, in_recursion);
44
+ },
45
+
46
+ /*
47
+ Sends parsed lines
48
+ */
49
+ send: function(line) {
50
+ if(line !== "") {
51
+ this.buffer.push(line);
52
+ }
53
+ },
54
+
55
+ /*
56
+ Looks for %PRAGMAS
57
+ */
58
+ render_pragmas: function(template) {
59
+ // no pragmas
60
+ if(!this.includes("%", template)) {
61
+ return template;
62
+ }
63
+
64
+ var that = this;
65
+ var regex = new RegExp(this.otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" +
66
+ this.ctag, "g");
67
+ return template.replace(regex, function(match, pragma, options) {
68
+ if(!that.pragmas_implemented[pragma]) {
69
+ throw({message:
70
+ "This implementation of mustache doesn't understand the '" +
71
+ pragma + "' pragma"});
72
+ }
73
+ that.pragmas[pragma] = {};
74
+ if(options) {
75
+ var opts = options.split("=");
76
+ that.pragmas[pragma][opts[0]] = opts[1];
77
+ }
78
+ return "";
79
+ // ignore unknown pragmas silently
80
+ });
81
+ },
82
+
83
+ /*
84
+ Tries to find a partial in the curent scope and render it
85
+ */
86
+ render_partial: function(name, context, partials) {
87
+ name = this.trim(name);
88
+ if(!partials || partials[name] === undefined) {
89
+ throw({message: "unknown_partial '" + name + "'"});
90
+ }
91
+ if(typeof(context[name]) != "object") {
92
+ return this.render(partials[name], context, partials, true);
93
+ }
94
+ return this.render(partials[name], context[name], partials, true);
95
+ },
96
+
97
+ /*
98
+ Renders inverted (^) and normal (#) sections
99
+ */
100
+ render_section: function(template, context, partials) {
101
+ if(!this.includes("#", template) && !this.includes("^", template)) {
102
+ return template;
103
+ }
104
+
105
+ 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");
110
+
111
+ // 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) {
116
+ // false or empty list, render it
117
+ return that.render(content, context, partials, true);
118
+ } else {
119
+ return "";
120
+ }
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);
126
+ }).join("");
127
+ } else if(that.is_object(value)) { // Object, Use it as subcontext!
128
+ return that.render(content, that.create_context(value),
129
+ partials, true);
130
+ } else if(typeof value === "function") {
131
+ // higher order section
132
+ return value.call(context, content, function(text) {
133
+ return that.render(text, context, partials, true);
134
+ });
135
+ } else if(value) { // boolean section
136
+ return that.render(content, context, partials, true);
137
+ } else {
138
+ return "";
139
+ }
140
+ }
141
+ });
142
+ },
143
+
144
+ /*
145
+ Replace {{foo}} and friends with values from our view
146
+ */
147
+ render_tags: function(template, context, partials, in_recursion) {
148
+ // tit for tat
149
+ var that = this;
150
+
151
+ var new_regex = function() {
152
+ return new RegExp(that.otag + "(=|!|>|\\{|%)?([^\\/#\\^]+?)\\1?" +
153
+ that.ctag + "+", "g");
154
+ };
155
+
156
+ var regex = new_regex();
157
+ var tag_replace_callback = function(match, operator, name) {
158
+ switch(operator) {
159
+ case "!": // ignore comments
160
+ return "";
161
+ case "=": // set new delimiters, rebuild the replace regexp
162
+ that.set_delimiters(name);
163
+ regex = new_regex();
164
+ return "";
165
+ case ">": // render partial
166
+ return that.render_partial(name, context, partials);
167
+ case "{": // the triple mustache is unescaped
168
+ return that.find(name, context);
169
+ default: // escape the value
170
+ return that.escape(that.find(name, context));
171
+ }
172
+ };
173
+ var lines = template.split("\n");
174
+ for(var i = 0; i < lines.length; i++) {
175
+ lines[i] = lines[i].replace(regex, tag_replace_callback, this);
176
+ if(!in_recursion) {
177
+ this.send(lines[i]);
178
+ }
179
+ }
180
+
181
+ if(in_recursion) {
182
+ return lines.join("\n");
183
+ }
184
+ },
185
+
186
+ set_delimiters: function(delimiters) {
187
+ var dels = delimiters.split(" ");
188
+ this.otag = this.escape_regex(dels[0]);
189
+ this.ctag = this.escape_regex(dels[1]);
190
+ },
191
+
192
+ escape_regex: function(text) {
193
+ // thank you Simon Willison
194
+ if(!arguments.callee.sRE) {
195
+ var specials = [
196
+ '/', '.', '*', '+', '?', '|',
197
+ '(', ')', '[', ']', '{', '}', '\\'
198
+ ];
199
+ arguments.callee.sRE = new RegExp(
200
+ '(\\' + specials.join('|\\') + ')', 'g'
201
+ );
202
+ }
203
+ return text.replace(arguments.callee.sRE, '\\$1');
204
+ },
205
+
206
+ /*
207
+ find `name` in current `context`. That is find me a value
208
+ from the view object
209
+ */
210
+ find: function(name, context) {
211
+ name = this.trim(name);
212
+
213
+ // Checks whether a value is thruthy or false or 0
214
+ function is_kinda_truthy(bool) {
215
+ return bool === false || bool === 0 || bool;
216
+ }
217
+
218
+ 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];
223
+ }
224
+
225
+ if(typeof value === "function") {
226
+ return value.apply(context);
227
+ }
228
+ if(value !== undefined) {
229
+ return value;
230
+ }
231
+ // silently ignore unkown variables
232
+ return "";
233
+ },
234
+
235
+ // Utility methods
236
+
237
+ /* includes tag */
238
+ includes: function(needle, haystack) {
239
+ return haystack.indexOf(this.otag + needle) != -1;
240
+ },
241
+
242
+ /*
243
+ Does away with nasty characters
244
+ */
245
+ escape: function(s) {
246
+ s = String(s === null ? "" : s);
247
+ return s.replace(/&(?!\w+;)|["'<>\\]/g, function(s) {
248
+ switch(s) {
249
+ case "&": return "&amp;";
250
+ case "\\": return "\\\\";
251
+ case '"': return '&quot;';
252
+ case "'": return '&#39;';
253
+ case "<": return "&lt;";
254
+ case ">": return "&gt;";
255
+ default: return s;
256
+ }
257
+ });
258
+ },
259
+
260
+ // by @langalex, support for arrays of strings
261
+ create_context: function(_context) {
262
+ if(this.is_object(_context)) {
263
+ return _context;
264
+ } else {
265
+ var iterator = ".";
266
+ if(this.pragmas["IMPLICIT-ITERATOR"]) {
267
+ iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator;
268
+ }
269
+ var ctx = {};
270
+ ctx[iterator] = _context;
271
+ return ctx;
272
+ }
273
+ },
274
+
275
+ is_object: function(a) {
276
+ return a && typeof a == "object";
277
+ },
278
+
279
+ is_array: function(a) {
280
+ return Object.prototype.toString.call(a) === '[object Array]';
281
+ },
282
+
283
+ /*
284
+ Gets rid of leading and trailing whitespace
285
+ */
286
+ trim: function(s) {
287
+ return s.replace(/^\s*|\s*$/g, "");
288
+ },
289
+
290
+ /*
291
+ Why, why, why? Because IE. Cry, cry cry.
292
+ */
293
+ map: function(array, fn) {
294
+ if (typeof array.map == "function") {
295
+ return array.map(fn);
296
+ } else {
297
+ var r = [];
298
+ var l = array.length;
299
+ for(var i = 0; i < l; i++) {
300
+ r.push(fn(array[i]));
301
+ }
302
+ return r;
303
+ }
304
+ }
305
+ };
306
+
307
+ return({
308
+ name: "mustache.js",
309
+ version: "0.3.1-dev",
310
+
311
+ /*
312
+ Turns a template and view into HTML
313
+ */
314
+ to_html: function(template, view, partials, send_fun) {
315
+ var renderer = new Renderer();
316
+ if(send_fun) {
317
+ renderer.send = send_fun;
318
+ }
319
+ renderer.render(template, view, partials);
320
+ if(!send_fun) {
321
+ return renderer.buffer.join("\n");
322
+ }
323
+ }
324
+ });
325
+ }();
@@ -0,0 +1,23 @@
1
+ var poirot = (function ($) {
2
+
3
+ var poirot = {
4
+ partials: {}
5
+ }
6
+
7
+ $(document).ready(function () {
8
+ $('script[type="text/mustache"]').each(function () {
9
+ var template = $(this).text()
10
+ var methodName = this.id.replace(/-([a-z])/g, function (str) {
11
+ return str.replace("-", "").toUpperCase()
12
+ }).replace("Template", "")
13
+
14
+ poirot.partials[methodName] = template
15
+
16
+ poirot[methodName] = function (presenter) {
17
+ return $(Mustache.to_html(template, presenter, poirot.partials))
18
+ }
19
+ })
20
+ })
21
+
22
+ return poirot
23
+ })(jQuery)
metadata CHANGED
@@ -1,8 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: poirot
3
3
  version: !ruby/object:Gem::Version
4
- prerelease:
5
- version: 0.0.2
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 3
9
+ version: 0.0.3
6
10
  platform: ruby
7
11
  authors:
8
12
  - Oliver Nightingale
@@ -11,17 +15,18 @@ autorequire:
11
15
  bindir: bin
12
16
  cert_chain: []
13
17
 
14
- date: 2011-07-06 00:00:00 +01:00
18
+ date: 2011-08-15 00:00:00 +01:00
15
19
  default_executable:
16
20
  dependencies:
17
21
  - !ruby/object:Gem::Dependency
18
22
  name: rails
19
23
  prerelease: false
20
24
  requirement: &id001 !ruby/object:Gem::Requirement
21
- none: false
22
25
  requirements:
23
26
  - - ">"
24
27
  - !ruby/object:Gem::Version
28
+ segments:
29
+ - 3
25
30
  version: "3"
26
31
  type: :runtime
27
32
  version_requirements: *id001
@@ -29,10 +34,11 @@ dependencies:
29
34
  name: mustache
30
35
  prerelease: false
31
36
  requirement: &id002 !ruby/object:Gem::Requirement
32
- none: false
33
37
  requirements:
34
38
  - - ">="
35
39
  - !ruby/object:Gem::Version
40
+ segments:
41
+ - 0
36
42
  version: "0"
37
43
  type: :runtime
38
44
  version_requirements: *id002
@@ -48,14 +54,19 @@ extra_rdoc_files: []
48
54
  files:
49
55
  - .gitignore
50
56
  - Gemfile
57
+ - Gemfile.lock
51
58
  - Rakefile
52
59
  - Readme.markdown
60
+ - lib/generators/poirot/install/install_generator.rb
53
61
  - lib/poirot.rb
54
62
  - lib/poirot/asset_helper.rb
55
63
  - lib/poirot/handler.rb
56
64
  - lib/poirot/version.rb
57
65
  - lib/poirot/view.rb
58
66
  - poirot.gemspec
67
+ - vendor/assets/javascripts/poirot/index.js
68
+ - vendor/assets/javascripts/poirot/mustache.js
69
+ - vendor/assets/javascripts/poirot/poirot.js
59
70
  has_rdoc: true
60
71
  homepage: http://rubygems.org/gems/poirot
61
72
  licenses: []
@@ -66,21 +77,23 @@ rdoc_options: []
66
77
  require_paths:
67
78
  - lib
68
79
  required_ruby_version: !ruby/object:Gem::Requirement
69
- none: false
70
80
  requirements:
71
81
  - - ">="
72
82
  - !ruby/object:Gem::Version
83
+ segments:
84
+ - 0
73
85
  version: "0"
74
86
  required_rubygems_version: !ruby/object:Gem::Requirement
75
- none: false
76
87
  requirements:
77
88
  - - ">="
78
89
  - !ruby/object:Gem::Version
90
+ segments:
91
+ - 0
79
92
  version: "0"
80
93
  requirements: []
81
94
 
82
95
  rubyforge_project: poirot
83
- rubygems_version: 1.6.2
96
+ rubygems_version: 1.3.6
84
97
  signing_key:
85
98
  specification_version: 3
86
99
  summary: mustaches