patch-html_namespacing 0.2.0.1
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/README.rdoc +406 -0
- data/ext/html_namespacing/extconf.rb +6 -0
- data/ext/html_namespacing/html_namespacing.c +481 -0
- data/ext/html_namespacing/html_namespacing.h +35 -0
- data/ext/html_namespacing/html_namespacing_ext.c +97 -0
- data/lib/html_namespacing.rb +9 -0
- data/lib/html_namespacing/plugin.rb +10 -0
- data/lib/html_namespacing/plugin/dom_scan_jquery.js +44 -0
- data/lib/html_namespacing/plugin/dom_scan_jquery.js.compressed +1 -0
- data/lib/html_namespacing/plugin/rails.rb +206 -0
- data/lib/html_namespacing/plugin/sass.rb +93 -0
- data/spec/c_extension_spec.rb +58 -0
- data/spec/spec_helper.rb +4 -0
- metadata +87 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
#ifndef HTML_NAMESPACING_H
|
2
|
+
#define HTML_NAMESPACING_H
|
3
|
+
|
4
|
+
#ifdef __cplusplus
|
5
|
+
extern "C" {
|
6
|
+
#endif
|
7
|
+
|
8
|
+
typedef struct _HtmlNamespacingAllocationStrategy {
|
9
|
+
void *(*malloc)(size_t size);
|
10
|
+
void (*free)(void *ptr);
|
11
|
+
void *(*realloc)(void *ptr, size_t size);
|
12
|
+
} HtmlNamespacingAllocationStrategy;
|
13
|
+
|
14
|
+
int
|
15
|
+
add_namespace_to_html_with_length(
|
16
|
+
const char *html,
|
17
|
+
size_t html_len,
|
18
|
+
const char *ns,
|
19
|
+
char **ret,
|
20
|
+
size_t *ret_len);
|
21
|
+
|
22
|
+
int
|
23
|
+
add_namespace_to_html_with_length_and_allocation_strategy(
|
24
|
+
const char *html,
|
25
|
+
size_t html_len,
|
26
|
+
const char *ns,
|
27
|
+
char **ret,
|
28
|
+
size_t *ret_len,
|
29
|
+
HtmlNamespacingAllocationStrategy allocation_strategy);
|
30
|
+
|
31
|
+
#ifdef __cplusplus
|
32
|
+
}
|
33
|
+
#endif
|
34
|
+
|
35
|
+
#endif /* HTML_NAMESPACING_H */
|
@@ -0,0 +1,97 @@
|
|
1
|
+
#include <errno.h>
|
2
|
+
|
3
|
+
#include "ruby.h"
|
4
|
+
|
5
|
+
#include "html_namespacing.h"
|
6
|
+
|
7
|
+
#include <stdio.h>
|
8
|
+
|
9
|
+
VALUE rb_mHtmlNamespacing = Qnil;
|
10
|
+
|
11
|
+
static const HtmlNamespacingAllocationStrategy ALLOCATION_STRATEGY = {
|
12
|
+
(void *(*)(size_t)) ruby_xmalloc,
|
13
|
+
ruby_xfree,
|
14
|
+
(void *(*)(void *, size_t)) ruby_xrealloc
|
15
|
+
};
|
16
|
+
|
17
|
+
/*
|
18
|
+
* call-seq:
|
19
|
+
* HtmlNamespacing.add_namespace_to_html(html, ns) => String
|
20
|
+
*
|
21
|
+
* Returns new HTML string based on +html+ with the HTML class +ns+ to root
|
22
|
+
* elements.
|
23
|
+
*
|
24
|
+
* If +html+ is nil, returns +nil+.
|
25
|
+
*
|
26
|
+
* If +ns+ is nil, returns +html+.
|
27
|
+
*/
|
28
|
+
|
29
|
+
VALUE
|
30
|
+
html_namespacing_add_namespace_to_html(
|
31
|
+
VALUE obj,
|
32
|
+
VALUE html,
|
33
|
+
VALUE ns)
|
34
|
+
{
|
35
|
+
/*
|
36
|
+
* It's almost tempting to manually allocate the RString object to save
|
37
|
+
* ourselves the stupid extra copy. (add_namespace_to_html_with_length()
|
38
|
+
* implicitly copies the string, and here we are copying it again because
|
39
|
+
* Ruby can't convert from a char* to an RString? How lame....)
|
40
|
+
*
|
41
|
+
* But for now, let's just do the extra copy (implicit in rb_str_new2) and
|
42
|
+
* be done with it.
|
43
|
+
*/
|
44
|
+
|
45
|
+
const char *html_ptr;
|
46
|
+
size_t html_len;
|
47
|
+
const char *ns_ptr;
|
48
|
+
char *ret_ptr;
|
49
|
+
size_t ret_len;
|
50
|
+
int rv;
|
51
|
+
VALUE ret;
|
52
|
+
|
53
|
+
if (TYPE(html) == T_NIL) {
|
54
|
+
return Qnil;
|
55
|
+
}
|
56
|
+
Check_Type(html, T_STRING);
|
57
|
+
|
58
|
+
if (TYPE(ns) == T_NIL) {
|
59
|
+
return html;
|
60
|
+
}
|
61
|
+
|
62
|
+
Check_Type(ns, T_STRING);
|
63
|
+
|
64
|
+
html_ptr = RSTRING_PTR(html);
|
65
|
+
html_len = RSTRING_LEN(html);
|
66
|
+
|
67
|
+
ns_ptr = RSTRING_PTR(ns);
|
68
|
+
|
69
|
+
rv = add_namespace_to_html_with_length_and_allocation_strategy(
|
70
|
+
html_ptr,
|
71
|
+
html_len,
|
72
|
+
ns_ptr,
|
73
|
+
&ret_ptr,
|
74
|
+
&ret_len,
|
75
|
+
ALLOCATION_STRATEGY);
|
76
|
+
if (rv == EINVAL) {
|
77
|
+
rb_raise(rb_eArgError, "Badly-formed HTML: %s", html_ptr);
|
78
|
+
}
|
79
|
+
if (rv != 0) {
|
80
|
+
rb_raise(rb_eRuntimeError, "Unknown error in add_namespace_to_html");
|
81
|
+
}
|
82
|
+
|
83
|
+
ret = rb_str_new(ret_ptr, ret_len);
|
84
|
+
ruby_xfree(ret_ptr);
|
85
|
+
|
86
|
+
return ret;
|
87
|
+
}
|
88
|
+
|
89
|
+
/*
|
90
|
+
* Holds functions related to HTML namespacing.
|
91
|
+
*/
|
92
|
+
void
|
93
|
+
Init_html_namespacing_ext()
|
94
|
+
{
|
95
|
+
rb_mHtmlNamespacing = rb_define_module("HtmlNamespacing");
|
96
|
+
rb_define_module_function(rb_mHtmlNamespacing, "add_namespace_to_html", html_namespacing_add_namespace_to_html, 2);
|
97
|
+
}
|
@@ -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
|
+
});
|
@@ -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,206 @@
|
|
1
|
+
begin
|
2
|
+
require 'glob_fu'
|
3
|
+
rescue LoadError
|
4
|
+
require 'rubygems'
|
5
|
+
require 'glob_fu'
|
6
|
+
end
|
7
|
+
|
8
|
+
module HtmlNamespacing
|
9
|
+
module Plugin
|
10
|
+
module Rails
|
11
|
+
def self.install(options = {})
|
12
|
+
@options = options
|
13
|
+
install_rails_2_3(options)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Called by ActionView
|
17
|
+
def self.path_to_namespace(relative_path)
|
18
|
+
if func = @options[:path_to_namespace_callback]
|
19
|
+
func.call(relative_path)
|
20
|
+
else
|
21
|
+
HtmlNamespacing::Plugin.default_relative_path_to_namespace(relative_path)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.handle_exception(e, template, view)
|
26
|
+
if func = @options[:handle_exception_callback]
|
27
|
+
func.call(e, template, view)
|
28
|
+
else
|
29
|
+
raise(e)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.javascript_root
|
34
|
+
@options[:javascript_root] || RAILS_ROOT + '/app/javascripts/views'
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.stylesheet_root
|
38
|
+
@options[:stylesheet_root] || RAILS_ROOT + '/app/stylesheets/views'
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.javascript_optional_suffix
|
42
|
+
@options[:javascript_optional_suffix]
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.stylesheet_optional_suffix
|
46
|
+
@options[:stylesheet_optional_suffix]
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.template_formats
|
50
|
+
@formats ||= Set.new(@options[:template_formats] || ['html'])
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.skip_template?(path)
|
54
|
+
specifier = @options[:skip_templates]
|
55
|
+
if specifier.respond_to?(:call)
|
56
|
+
specifier.call(path)
|
57
|
+
elsif specifier.respond_to?(:include?)
|
58
|
+
specifier.include?(path)
|
59
|
+
else
|
60
|
+
specifier === path
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
module Helpers
|
65
|
+
#
|
66
|
+
# Return the javascript for the rendered partials, wrapped in
|
67
|
+
# a script tag.
|
68
|
+
#
|
69
|
+
def html_namespacing_javascript_tag(framework, options = {})
|
70
|
+
js = html_namespacing_javascript(framework, options)
|
71
|
+
unless js.blank?
|
72
|
+
"<script type=\"text/javascript\"><!--//--><![CDATA[//><!--\n#{js}//--><!]]></script>"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
#
|
77
|
+
# Return the javascript for the rendered partials.
|
78
|
+
#
|
79
|
+
def html_namespacing_javascript(framework, options = {})
|
80
|
+
root = Pathname.new(Rails.javascript_root)
|
81
|
+
optional_suffix = Rails.javascript_optional_suffix
|
82
|
+
files = html_namespacing_files('js',
|
83
|
+
:format => options[:format],
|
84
|
+
:optional_suffix => optional_suffix,
|
85
|
+
:root => root.to_s)
|
86
|
+
unless files.empty?
|
87
|
+
r = [HtmlNamespaceJs[framework][:top]] << "\n"
|
88
|
+
files.each do |path|
|
89
|
+
relative_path = Pathname.new(path).relative_path_from(root)
|
90
|
+
r << html_namespacing_inline_js(framework, relative_path.to_s, path) << "\n"
|
91
|
+
end
|
92
|
+
r.join
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
#
|
97
|
+
# Return the CSS for the rendered partials, wrapped in a style
|
98
|
+
# tag.
|
99
|
+
#
|
100
|
+
def html_namespacing_style_tag(options = {}, style_tag_attributes = {})
|
101
|
+
css = html_namespacing_styles(options)
|
102
|
+
unless css.blank?
|
103
|
+
attribute_string = style_tag_attributes.map{|k,v| " #{k}=\"#{v}\""}.join
|
104
|
+
"<style type=\"text/css\"#{attribute_string}>#{css}</style>"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
#
|
109
|
+
# Return the CSS for the rendered partials.
|
110
|
+
#
|
111
|
+
def html_namespacing_styles(options = {})
|
112
|
+
root = Pathname.new(Rails.stylesheet_root)
|
113
|
+
optional_suffix = Rails.stylesheet_optional_suffix
|
114
|
+
files = html_namespacing_files('sass',
|
115
|
+
:format => options[:format],
|
116
|
+
:optional_suffix => optional_suffix,
|
117
|
+
:root => root.to_s)
|
118
|
+
unless files.empty?
|
119
|
+
r = []
|
120
|
+
files.each do |path|
|
121
|
+
relative_path = Pathname.new(path).relative_path_from(root)
|
122
|
+
r << html_namespacing_inline_css(relative_path.to_s, path)
|
123
|
+
end
|
124
|
+
r.join
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
private
|
129
|
+
|
130
|
+
HtmlNamespaceJs = {
|
131
|
+
:jquery => {
|
132
|
+
:top => File.open(File.join(File.dirname(__FILE__), 'dom_scan_jquery.js.compressed')) { |f| f.read },
|
133
|
+
:each => 'jQuery(function($){var NS=%s,$NS=function(){return $($.NS[NS]||[])};%s});'
|
134
|
+
}
|
135
|
+
}
|
136
|
+
|
137
|
+
def html_namespacing_files(suffix, options={})
|
138
|
+
format = options[:format]
|
139
|
+
(Array === format ? format : [format]).inject([]) do |r, f|
|
140
|
+
r.concat(GlobFu.find(
|
141
|
+
html_namespacing_rendered_paths,
|
142
|
+
:suffix => suffix,
|
143
|
+
:extra_suffix => f && f.to_s,
|
144
|
+
:optional_suffix => options[:optional_suffix],
|
145
|
+
:root => options[:root]
|
146
|
+
))
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def html_namespacing_inline_js(framework, relative_path, absolute_path)
|
151
|
+
namespace = ::HtmlNamespacing::Plugin::Rails.path_to_namespace(relative_path.sub(/\..*$/, ''))
|
152
|
+
content = File.open(absolute_path) { |f| f.read }
|
153
|
+
|
154
|
+
HtmlNamespaceJs[framework][:each] % [namespace.to_json, content]
|
155
|
+
end
|
156
|
+
|
157
|
+
def html_namespacing_inline_css(relative_path, absolute_path)
|
158
|
+
HtmlNamespacing.options[:styles_for].call(relative_path, absolute_path)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
private
|
163
|
+
|
164
|
+
def self.install_rails_2_3(options = {})
|
165
|
+
if options[:javascript]
|
166
|
+
::ActionView::Base.class_eval do
|
167
|
+
attr_writer(:html_namespacing_rendered_paths)
|
168
|
+
def html_namespacing_rendered_paths
|
169
|
+
@html_namespacing_rendered_paths ||= []
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
::ActionView::Template.class_eval do
|
175
|
+
def render_with_html_namespacing(view, local_assigns = {})
|
176
|
+
html = render_without_html_namespacing(view, local_assigns)
|
177
|
+
|
178
|
+
return html if HtmlNamespacing::Plugin::Rails.skip_template?(path)
|
179
|
+
|
180
|
+
view.html_namespacing_rendered_paths << path_without_format_and_extension if view.respond_to?(:html_namespacing_rendered_paths)
|
181
|
+
|
182
|
+
if HtmlNamespacing::Plugin::Rails.template_formats.include?(format)
|
183
|
+
add_namespace_to_html(html, view)
|
184
|
+
else
|
185
|
+
html
|
186
|
+
end
|
187
|
+
end
|
188
|
+
alias_method_chain :render, :html_namespacing
|
189
|
+
|
190
|
+
private
|
191
|
+
|
192
|
+
def add_namespace_to_html(html, view)
|
193
|
+
HtmlNamespacing::add_namespace_to_html(html, html_namespace)
|
194
|
+
rescue ArgumentError => e
|
195
|
+
HtmlNamespacing::Plugin::Rails.handle_exception(e, self, view)
|
196
|
+
html # unless handle_exception() raised something
|
197
|
+
end
|
198
|
+
|
199
|
+
def html_namespace
|
200
|
+
HtmlNamespacing::Plugin::Rails.path_to_namespace(path_without_format_and_extension)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module HtmlNamespacing
|
2
|
+
module Plugin
|
3
|
+
module Sass
|
4
|
+
class << self
|
5
|
+
def install(options = {})
|
6
|
+
options[:prefix] ||= 'views'
|
7
|
+
options[:callback] ||= lambda { |p| HtmlNamespacing::Plugin.default_relative_path_to_namespace(p) }
|
8
|
+
options[:compress] ||= false
|
9
|
+
@options = options
|
10
|
+
Sass_2_2.install(options)
|
11
|
+
HtmlNamespacing.options[:styles_for] = method(:styles_for)
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :options
|
15
|
+
|
16
|
+
def sass_options
|
17
|
+
::Sass::Plugin.options
|
18
|
+
end
|
19
|
+
|
20
|
+
def styles_for(relative_path, absolute_path)
|
21
|
+
# Assume the file exists. (If it was found by GlobFu, it should.)
|
22
|
+
css_path = File.join(sass_options[:css_location], self.options[:prefix], relative_path.sub(/\.[^\.\/]*\z/, '.css'))
|
23
|
+
File.read(css_path)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module Sass_2_2
|
28
|
+
def self.install(options = {})
|
29
|
+
self.allow_updating_partials
|
30
|
+
self.add_super_rule_to_tree_node(options)
|
31
|
+
self.try_to_enable_memoization
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def self.allow_updating_partials
|
37
|
+
::Sass::Plugin.module_eval do
|
38
|
+
private
|
39
|
+
|
40
|
+
# Though a bit over-zealous, who *cares* if we render partial
|
41
|
+
# Sass files?
|
42
|
+
def forbid_update?(*args)
|
43
|
+
false
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.add_super_rule_to_tree_node(namespacing_options)
|
49
|
+
::Sass::Tree::RuleNode.class_eval do
|
50
|
+
def to_s_with_namespacing(tabs, super_rules = nil)
|
51
|
+
super_rules ||= namespacing_rules
|
52
|
+
to_s_without_namespacing(tabs, super_rules)
|
53
|
+
end
|
54
|
+
alias_method_chain(:to_s, :namespacing)
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
define_method(:namespacing_prefix) do
|
59
|
+
namespacing_options[:prefix]
|
60
|
+
end
|
61
|
+
define_method(:namespacing_callback) do
|
62
|
+
namespacing_options[:callback]
|
63
|
+
end
|
64
|
+
|
65
|
+
def namespacing_regex
|
66
|
+
/^#{Regexp.quote(Sass.sass_options[:css_location])}\/#{Regexp.quote(namespacing_prefix)}\/(.*)\.css$/
|
67
|
+
end
|
68
|
+
|
69
|
+
def namespace
|
70
|
+
@namespace ||= if options[:css_filename] =~ namespacing_regex
|
71
|
+
namespacing_callback.call($1.split('.')[0])
|
72
|
+
end
|
73
|
+
@namespace
|
74
|
+
end
|
75
|
+
|
76
|
+
def namespacing_rules
|
77
|
+
namespace && [ ".#{namespace}" ]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.try_to_enable_memoization
|
83
|
+
::Sass::Tree::RuleNode.class_eval do
|
84
|
+
extend ActiveSupport::Memoizable
|
85
|
+
memoize :namespacing_rules
|
86
|
+
end
|
87
|
+
rescue NameError
|
88
|
+
# ActiveSupport isn't loaded
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|