adamh-html_namespacing 0.0.5 → 0.1.0

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/Manifest CHANGED
@@ -8,7 +8,9 @@ ext/html_namespacing/html_namespacing.c
8
8
  ext/html_namespacing/html_namespacing_ext.c
9
9
  lib/html_namespacing.rb
10
10
  lib/html_namespacing/plugin.rb
11
+ lib/html_namespacing/plugin/dom_scan_jquery.compressed.js
11
12
  lib/html_namespacing/plugin/rails.rb
13
+ lib/html_namespacing/plugin/dom_scan_jquery.js
12
14
  lib/html_namespacing/plugin/sass.rb
13
15
  Manifest
14
16
  html_namespacing.gemspec
data/README.rdoc CHANGED
@@ -150,11 +150,11 @@ which is standard Sass and will equate to ".NAMESPACE" in this context.)
150
150
 
151
151
  Available options to <tt>HtmlNamespacing::Plugin::Rails.install</tt> are:
152
152
 
153
- <tt>:template_to_namespace_callback</tt>: Ruby lambda function which accepts an
154
- <tt>ActionView::Template</tt> and returns an HTML namespacing string. The
155
- default is:
153
+ <tt>:path_to_namespace_callback</tt>: Ruby lambda function which accepts a
154
+ relative path (e.g., "foo/bar") and returns an HTML namespacing string
155
+ (e.g., "foo-bar"). The default is:
156
156
 
157
- lambda { |template| template.path =~ /^([^\.]+)/ && $1.gsub('/', '-') || nil }
157
+ lambda { |path| path.gsub('/', '-') }
158
158
 
159
159
  If the callback returns <tt>nil</tt>, HTML namespacing will not be applied.
160
160
 
@@ -167,6 +167,23 @@ an <tt>Exception</tt>, an <tt>ActionView::Template</tt>, and an
167
167
  If your <tt>:handle_exception_callback</tt> does not raise an exception,
168
168
  the template will be rendered as if HtmlNamespacing were not installed.
169
169
 
170
+ <tt>:javascript</tt>: If set, enable
171
+ <tt>html_namespacing_javascript_tag()</tt> (see below). You will need to
172
+ <tt>include HtmlNamespacing::Plugin::Rails::Helpers</tt> in your helpers to
173
+ gain access to this method.
174
+
175
+ <tt>:javascript_root</tt>: Root of namespaced JavaScript files, if you are
176
+ using <tt>html_namespacing_javascript_tag()</tt> (see below).
177
+
178
+ <tt>:javascript_optional_suffix</tt>: optional suffix for your JavaScript
179
+ files, if you are using <tt>html_namespacing_javascript_tag()</tt>. For
180
+ instance, if <tt>:javascript_optional_suffix</tt> is <tt>'compressed'</tt> and
181
+ you rendered the partial <tt>app/views/foos/_foo.html.haml</tt>, then the
182
+ (presumably minified) file
183
+ <tt>app/javascripts/views/foos/_foo.compressed.js</tt> will be included if it
184
+ exists (otherwise the standard <tt>app/javascripts/views/foos/_foo.js</tt>,
185
+ otherwise nothing).
186
+
170
187
  Available options to <tt>HtmlNamespacing::Plugin::Sass.install</tt> are:
171
188
 
172
189
  <tt>:prefix</tt> (default <tt>views</tt>): subdirectory of Sass directory for
@@ -229,66 +246,80 @@ reader.
229
246
 
230
247
  === jQuery Namespacing
231
248
 
232
- Insert the following before all other JavaScript:
249
+ Use the following Rails helper, possibly in your default layout:
250
+
251
+ <tt>html_namespacing_javascript_tag(:jquery)</tt>
252
+
253
+ Afterwards, similar to our namespaced CSS framework above, you can easily
254
+ namespace JavaScript behavior:
255
+
256
+ <tt>app/javascripts/views/foos/_description.js</tt>:
257
+
258
+ $NS().find('p').click(function() { alert("Yup, you clicked!"); });
233
259
 
234
- $(document).ready(function() {
235
- $.NS = {};
260
+ You just write code specific to one Rails partial without actually writing the
261
+ partial's name anywhere, and you will not need to rewrite any selectors if you
262
+ rename or move the partial (as long as you rename or move the corresponding
263
+ JavaScript file).
264
+
265
+ (All namespaced JavaScript partials will be included inline, all in separate
266
+ <tt>jQuery(document).ready()</tt> blocks. Two local variables will be
267
+ available: <tt>NS</tt>, the namespace string, and <tt>$NS()</tt>, a function
268
+ returning a jQuery object containing all elements at the root of the
269
+ namespace.)
270
+
271
+ === Caching and JavaScript Namespacing
272
+
273
+ Caching throws a wrench into operations.
274
+ <tt>html_namespacing_javascript_tag()</tt> works by watching which templates get
275
+ rendered; when reading from the cache, not all templates will have
276
+ <tt>render()</tt> called on them.
277
+
278
+ The general solution is to cache the list of rendered templates along with the
279
+ data being cached. A new view method, <tt>html_rendering_rendered_paths</tt>,
280
+ returns an Array to be used for this purpose. As a practical example, here is a
281
+ plugin which makes JavaScript namespacing work with
282
+ {cache_advance}[http://github.com/aub/cache_advance]:
283
+
284
+ class HtmlNamespacingCachePlugin
285
+ def self.key_suffix
286
+ 'HTML_NAMESPACING_DATA'
287
+ end
236
288
 
237
- var SPLIT_REGEX = /\s+/;
289
+ def cache_it_options(view)
290
+ { :view => view }
291
+ end
238
292
 
239
- function populate_NS_from_node(node) {
240
- var node_stack = [];
241
- node_stack.push(node);
293
+ def before_render(cache_name, cache_key, options)
294
+ options[:html_namespacing_rendered_paths_length] = paths(options).length
295
+ end
242
296
 
243
- while (node_stack.length) {
244
- var cur_node = node_stack.pop();
297
+ def after_render(cache_name, cache_key, data, options)
298
+ old_length = options[:html_namespacing_rendered_paths_length]
299
+ new_length = paths(options).length
245
300
 
246
- // Populate entry in $.NS
247
- var class_name_list = node.className;
248
- var class_names = class_name_list.split(SPLIT_REGEX);
249
- for (var i = 0; i < class_names.length; i++) {
250
- var class_name = class_names[i];
251
- if (!$.NS[class_name]) {
252
- $.NS[class_name] = [cur_node];
253
- } else {
254
- $.NS[class_name].push(cur_node);
255
- }
256
- }
301
+ delta = paths(options)[old_length..new_length]
302
+ unless delta.empty?
303
+ key = cache_key + self.class.key_suffix
304
+ Rails.cache.write(key, delta)
305
+ end
306
+ end
257
307
 
258
- // "recurse" to the children, so they are handled in document order
259
- var children = cur_node.childNodes;
260
- for (var j = children.length - 1; j >= 0; j--) {
261
- var subnode = children[j];
262
- node_stack.push(subnode);
263
- }
264
- }
265
- }
308
+ def after_read(cache_name, cache_key, data, options)
309
+ key = cache_key + self.class.key_suffix
310
+ if delta = Rails.cache.read(key)
311
+ paths(options).concat(delta)
312
+ end
313
+ end
266
314
 
267
- populate_NS_from_node(document.getElementsByTagName('body')[0]);
268
- });
269
-
270
- (The CPU cost of this code is small but nonzero, as it precomputes many
271
- selectors. On a 75kb page with a 2.5Ghz computer, it might take 75ms with
272
- Firebug profiling turned on. This may or may not be faster than writing each
273
- selector by hand.)
274
-
275
- Afterwards, if you set up a JavaScript framework similar to the CSS framework
276
- above, you can easily namespace JavaScript behavior:
277
-
278
- <tt>javascripts/onload/foos/_description.js</tt>:
279
-
280
- $(document).ready(function() {
281
- var $NS = $.NS['foos-_description']; // auto-generated line at top of file
282
- $NS.find('p').click(function() { alert("Yup, you clicked!"); });
283
- });
315
+ protected
316
+
317
+ def paths(options)
318
+ options[:view].html_namespacing_rendered_paths
319
+ end
320
+ end
284
321
 
285
- In the above file, if every line but the third is automatically generated based
286
- on the file's location (an exercise, for now, left to the reader), the third
287
- line can be written in full confidence that only paragraphs within the
288
- <tt>_description</tt> partial will have this jQuery code bound. The end result?
289
- You can write code specific to one Rails partial without actually writing the
290
- partial's name anywhere, and you will not need to rewrite any selectors if you
291
- rename or move the partial.
322
+ Other plugins are welcome; I will consider integrating them into this project.
292
323
 
293
324
  == Tips and Tricks
294
325
 
@@ -312,10 +343,10 @@ Or this Sass:
312
343
 
313
344
  Or the following JavaScript (following the <tt>$NS</tt> example above):
314
345
 
315
- var $div = $NS.find('div');
346
+ var $div = $NS().find('div');
316
347
 
317
- Nothing will be selected, because the <tt>div</tt> element is not a child. You
318
- would need the following CSS:
348
+ Nothing will be selected, because the <tt>div</tt> element is not a child.
349
+ Instead, you need the following CSS:
319
350
 
320
351
  div.foos-show { font-weight: bold; }
321
352
 
@@ -326,7 +357,7 @@ Or this sass:
326
357
 
327
358
  Or the following JavaScript:
328
359
 
329
- var $div = $NS.filter('div');
360
+ var $div = $NS().filter('div');
330
361
 
331
362
  Also, watch out for nesting: selectors with the <tt>foos-show</tt> namespace
332
363
  will match anything inside any partials rendered by
@@ -338,9 +369,12 @@ HTML namespacing produces plenty of tiny CSS and/or JavaScript files. Best
338
369
  practice is to bundle all namespaced files together: by virtue of being
339
370
  namespaced, it is safe to concatenate them. (Advanced CSS users should
340
371
  be thoughtful about
341
- "cascading order and specificity":http://www.w3.org/TR/CSS2/cascade.html#cascading-order;
372
+ {cascading order and specificity}[http://www.w3.org/TR/CSS2/cascade.html#cascading-order]
342
373
  and anybody bundling JavaScript files together should wrap each with
343
- <tt>(function() { ... })();</tt> to prevent variable leakage.)
374
+ <tt>(function() { ... })();</tt> to prevent variable leakage.) CSS files can be
375
+ concatenated using a tool such as
376
+ {asset_library}[http://github.com/adamh/asset_library]; JavaScript files are
377
+ concatenated automatically in <tt>html_namespacing_javascript_tag()</tt>.
344
378
 
345
379
  These and similar strategies should be considered when building an HTML
346
380
  namespacing framework.
data/Rakefile CHANGED
@@ -2,7 +2,7 @@ require 'rubygems'
2
2
  require 'rake'
3
3
  require 'echoe'
4
4
 
5
- Echoe.new('html_namespacing', '0.0.5') do |p|
5
+ Echoe.new('html_namespacing', '0.1.0') do |p|
6
6
  p.author = 'Adam Hooper'
7
7
  p.email = 'adam@adamhooper.com'
8
8
  p.summary = 'Automatic HTML namespacing'
@@ -61,10 +61,10 @@ html_namespacing_add_namespace_to_html(
61
61
 
62
62
  Check_Type(ns, T_STRING);
63
63
 
64
- html_ptr = RSTRING(html)->ptr;
65
- html_len = RSTRING(html)->len;
64
+ html_ptr = RSTRING_PTR(html);
65
+ html_len = RSTRING_LEN(html);
66
66
 
67
- ns_ptr = RSTRING(ns)->ptr;
67
+ ns_ptr = RSTRING_PTR(ns);
68
68
 
69
69
  rv = add_namespace_to_html_with_length_and_allocation_strategy(
70
70
  html_ptr,
@@ -2,16 +2,16 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{html_namespacing}
5
- s.version = "0.0.5"
5
+ s.version = "0.1.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Adam Hooper"]
9
- s.date = %q{2009-07-10}
9
+ s.date = %q{2009-07-28}
10
10
  s.description = %q{Inserts "class=" attributes within snippets of HTML so CSS and JavaScript can use automatic scopes}
11
11
  s.email = %q{adam@adamhooper.com}
12
12
  s.extensions = ["ext/html_namespacing/extconf.rb"]
13
13
  s.extra_rdoc_files = ["README.rdoc"]
14
- s.files = ["README.rdoc", "Rakefile", "test/c_extension_test.rb", "test/test_helper.rb", "ext/html_namespacing/extconf.rb", "ext/html_namespacing/html_namespacing.h", "ext/html_namespacing/html_namespacing.c", "ext/html_namespacing/html_namespacing_ext.c", "lib/html_namespacing.rb", "lib/html_namespacing/plugin.rb", "lib/html_namespacing/plugin/rails.rb", "lib/html_namespacing/plugin/sass.rb", "Manifest", "html_namespacing.gemspec"]
14
+ s.files = ["README.rdoc", "Rakefile", "test/c_extension_test.rb", "test/test_helper.rb", "ext/html_namespacing/extconf.rb", "ext/html_namespacing/html_namespacing.h", "ext/html_namespacing/html_namespacing.c", "ext/html_namespacing/html_namespacing_ext.c", "lib/html_namespacing.rb", "lib/html_namespacing/plugin.rb", "lib/html_namespacing/plugin/dom_scan_jquery.compressed.js", "lib/html_namespacing/plugin/rails.rb", "lib/html_namespacing/plugin/dom_scan_jquery.js", "lib/html_namespacing/plugin/sass.rb", "Manifest", "html_namespacing.gemspec"]
15
15
  s.has_rdoc = true
16
16
  s.homepage = %q{http://github.com/adamh/html_namespacing}
17
17
  s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Html_namespacing", "--main", "README.rdoc"]
@@ -0,0 +1 @@
1
+ jQuery(function(b){function a(f){var c=/[ \t\n]+/,m={},o=[f],n,l,p,h,k,g,e,d;while(o.length){n=o.pop();l=n.className;if(l){p=l.split(c);for(k=0;k<p.length;k++){h=p[k];if(!m[h]){m[h]=[n]}else{m[h].push(n)}}}e=n.childNodes;for(var g=e.length-1;g>=0;g--){d=e[g];o.push(d)}}return m}b.NS=a(document.getElementsByTagName("body")[0])});
@@ -0,0 +1,44 @@
1
+ jQuery(function($) {
2
+ function NS_from_node(root_node) {
3
+ var SPLIT_REGEX = /[ \t\n]+/,
4
+ ret = {},
5
+ node_stack = [root_node],
6
+ cur_node,
7
+ class_name_list,
8
+ class_names,
9
+ class_name,
10
+ i,
11
+ j,
12
+ children,
13
+ subnode;
14
+
15
+ while (node_stack.length) {
16
+ cur_node = node_stack.pop();
17
+
18
+ // Populate entry in $.NS
19
+ class_name_list = cur_node.className;
20
+ if (class_name_list) {
21
+ class_names = class_name_list.split(SPLIT_REGEX);
22
+ for (i = 0; i < class_names.length; i++) {
23
+ class_name = class_names[i];
24
+ if (!ret[class_name]) {
25
+ ret[class_name] = [cur_node];
26
+ } else {
27
+ ret[class_name].push(cur_node);
28
+ }
29
+ }
30
+ }
31
+
32
+ // "recurse" to the children, so they are handled in document order
33
+ children = cur_node.childNodes;
34
+ for (var j = children.length - 1; j >= 0; j--) {
35
+ subnode = children[j];
36
+ node_stack.push(subnode);
37
+ }
38
+ }
39
+
40
+ return ret;
41
+ }
42
+
43
+ $.NS = NS_from_node(document.getElementsByTagName('body')[0]);
44
+ });
@@ -3,15 +3,15 @@ module HtmlNamespacing
3
3
  module Rails
4
4
  def self.install(options = {})
5
5
  @options = options
6
- install_rails_2_3
6
+ install_rails_2_3(options)
7
7
  end
8
8
 
9
9
  # Called by ActionView
10
- def self.path_to_namespace(template)
11
- if func = @options[:template_to_namespace_callback]
12
- func.call(template)
10
+ def self.path_to_namespace(relative_path)
11
+ if func = @options[:path_to_namespace_callback]
12
+ func.call(relative_path)
13
13
  else
14
- HtmlNamespacing::Plugin.default_relative_path_to_namespace(template.path)
14
+ HtmlNamespacing::Plugin.default_relative_path_to_namespace(relative_path)
15
15
  end
16
16
  end
17
17
 
@@ -23,31 +23,111 @@ module HtmlNamespacing
23
23
  end
24
24
  end
25
25
 
26
+ def self.javascript_root
27
+ @options[:javascript_root] || RAILS_ROOT + '/app/javascripts/views'
28
+ end
29
+
30
+ def self.javascript_optional_suffix
31
+ @options[:javascript_optional_suffix]
32
+ end
33
+
34
+ module Helpers
35
+ def html_namespacing_javascript_tag(framework)
36
+ files = html_namespacing_files
37
+ unless files.empty?
38
+ r = "<script type=\"text/javascript\"><!--//--><![CDATA[//><!--\n"
39
+ r << HtmlNamespaceJs[framework][:top]
40
+ r << "\n"
41
+ files.collect do |relative_path, absolute_path|
42
+ r << html_namespacing_inline_js(framework, relative_path, absolute_path)
43
+ r << "\n"
44
+ end
45
+ r << "//--><!]]></script>"
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ HtmlNamespaceJs = {
52
+ :jquery => {
53
+ :top => File.open(File.join(File.dirname(__FILE__), 'dom_scan_jquery.compressed.js')) { |f| f.read },
54
+ :each => 'jQuery(function($){var NS=%s,$NS=function(){return $($.NS[NS])};%s});'
55
+ }
56
+ }
57
+
58
+ def html_namespacing_files
59
+ suffix = ::HtmlNamespacing::Plugin::Rails.javascript_optional_suffix
60
+ root = ::HtmlNamespacing::Plugin::Rails.javascript_root
61
+
62
+ r = html_namespacing_rendered_paths
63
+
64
+ r.uniq!
65
+
66
+ r.collect! do |relative_path|
67
+ prefix = File.join(root, relative_path)
68
+ absolute_path = "#{prefix}.js"
69
+ absolute_path_with_suffix = "#{prefix}.#{suffix}.js" if suffix
70
+
71
+ if suffix && File.exist?(absolute_path_with_suffix)
72
+ [relative_path, absolute_path_with_suffix]
73
+ elsif File.exist?(absolute_path)
74
+ [relative_path, absolute_path]
75
+ else
76
+ nil
77
+ end
78
+ end
79
+
80
+ r.compact!
81
+
82
+ r
83
+ end
84
+
85
+ def html_namespacing_inline_js(framework, relative_path, absolute_path)
86
+ namespace = ::HtmlNamespacing::Plugin::Rails.path_to_namespace(relative_path)
87
+ content = File.open(absolute_path) { |f| f.read }
88
+
89
+ HtmlNamespaceJs[framework][:each] % [namespace.to_json, content]
90
+ end
91
+ end
92
+
26
93
  private
27
94
 
28
95
  def self.install_rails_2_3(options = {})
96
+ if options[:javascript]
97
+ ::ActionView::Base.class_eval do
98
+ attr_writer(:html_namespacing_rendered_paths)
99
+ def html_namespacing_rendered_paths
100
+ @html_namespacing_rendered_paths ||= []
101
+ end
102
+ end
103
+ end
104
+
29
105
  ::ActionView::Template.class_eval do
30
- def render_template_with_html_namespacing(*args)
31
- s = render_template_without_html_namespacing(*args)
106
+ def render_with_html_namespacing(view, local_assigns = {})
107
+ html = render_without_html_namespacing(view, local_assigns)
108
+
109
+ view.html_namespacing_rendered_paths << path_without_format_and_extension if view.respond_to?(:html_namespacing_rendered_paths)
32
110
 
33
111
  if format == 'html'
34
- begin
35
- HtmlNamespacing::add_namespace_to_html(s, html_namespace)
36
- rescue ArgumentError => e
37
- view = args.first
38
- HtmlNamespacing::Plugin::Rails.handle_exception(e, self, view)
39
- s # unless handle_exception() raised something
40
- end
112
+ add_namespace_to_html(html, view)
41
113
  else
42
- s
114
+ html
43
115
  end
44
116
  end
45
- alias_method_chain :render_template, :html_namespacing
117
+ alias_method_chain :render, :html_namespacing
118
+
119
+ private
120
+
121
+ def add_namespace_to_html(html, view)
122
+ HtmlNamespacing::add_namespace_to_html(html, html_namespace)
123
+ rescue ArgumentError => e
124
+ HtmlNamespacing::Plugin::Rails.handle_exception(e, self, view)
125
+ html # unless handle_exception() raised something
126
+ end
46
127
 
47
128
  def html_namespace
48
- HtmlNamespacing::Plugin::Rails.path_to_namespace(self)
129
+ HtmlNamespacing::Plugin::Rails.path_to_namespace(path_without_format_and_extension)
49
130
  end
50
- memoize :html_namespace
51
131
  end
52
132
  end
53
133
  end
@@ -4,9 +4,7 @@ module HtmlNamespacing
4
4
  autoload(:Sass, File.dirname(__FILE__) + '/plugin/sass')
5
5
 
6
6
  def self.default_relative_path_to_namespace(path)
7
- if path =~ /^([^\.]+)/
8
- $1.gsub(/\//, '-')
9
- end
7
+ path.gsub(/\//, '-')
10
8
  end
11
9
  end
12
10
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: adamh-html_namespacing
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adam Hooper
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-07-10 00:00:00 -07:00
12
+ date: 2009-07-28 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -32,12 +32,15 @@ files:
32
32
  - ext/html_namespacing/html_namespacing_ext.c
33
33
  - lib/html_namespacing.rb
34
34
  - lib/html_namespacing/plugin.rb
35
+ - lib/html_namespacing/plugin/dom_scan_jquery.compressed.js
35
36
  - lib/html_namespacing/plugin/rails.rb
37
+ - lib/html_namespacing/plugin/dom_scan_jquery.js
36
38
  - lib/html_namespacing/plugin/sass.rb
37
39
  - Manifest
38
40
  - html_namespacing.gemspec
39
41
  has_rdoc: true
40
42
  homepage: http://github.com/adamh/html_namespacing
43
+ licenses:
41
44
  post_install_message:
42
45
  rdoc_options:
43
46
  - --line-numbers
@@ -64,7 +67,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
64
67
  requirements: []
65
68
 
66
69
  rubyforge_project: html_namespacing
67
- rubygems_version: 1.2.0
70
+ rubygems_version: 1.3.5
68
71
  signing_key:
69
72
  specification_version: 2
70
73
  summary: Automatic HTML namespacing