poirot 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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