deface 0.7.2 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -1
- data/README.markdown +6 -2
- data/deface.gemspec +6 -5
- data/lib/deface.rb +3 -1
- data/lib/deface/action_view_extensions.rb +35 -3
- data/lib/deface/applicator.rb +192 -0
- data/lib/deface/environment.rb +8 -7
- data/lib/deface/haml_converter.rb +65 -0
- data/lib/deface/original_validator.rb +34 -0
- data/lib/deface/override.rb +56 -183
- data/lib/deface/railtie.rb +18 -8
- data/lib/deface/search.rb +22 -0
- data/lib/deface/template_helper.rb +10 -1
- data/spec/assets/shared/_hello.html.haml +1 -0
- data/spec/assets/shared/pirate.html.haml +5 -0
- data/spec/deface/action_view_template_spec.rb +52 -0
- data/spec/deface/applicator_spec.rb +367 -0
- data/spec/deface/environment_spec.rb +8 -3
- data/spec/deface/haml_converter_spec.rb +62 -0
- data/spec/deface/override_spec.rb +126 -5
- data/spec/deface/template_helper_spec.rb +21 -3
- data/spec/spec_helper.rb +17 -0
- metadata +42 -14
- data/spec/deface/template_spec.rb +0 -286
data/Gemfile
CHANGED
data/README.markdown
CHANGED
@@ -59,7 +59,11 @@ Action
|
|
59
59
|
|
60
60
|
* <tt>:insert_bottom</tt> - Inserts inside all elements that match the supplied selector, as the last child.
|
61
61
|
|
62
|
-
* <tt>:set_attributes</tt> - Sets
|
62
|
+
* <tt>:set_attributes</tt> - Sets attributes on all elements that match the supplied selector, replacing existing attribute value if present or adding if not. Expects :attributes option to be passed.
|
63
|
+
|
64
|
+
* <tt>:add_to_attributes</tt> - Appends value to attributes on all elements that match the supplied selector, adds attribute if not present. Expects :attributes option to be passed.
|
65
|
+
|
66
|
+
* <tt>:remove_from_attributes</tt> - Removes value from attributes on all elements that match the supplied selector. Expects :attributes option to be passed.
|
63
67
|
|
64
68
|
Source
|
65
69
|
------
|
@@ -85,7 +89,7 @@ Optional
|
|
85
89
|
* <tt>:sequence => {:before => "*override_name*"}</tt> - where "*override_name*" is the name of an override defined for the
|
86
90
|
same virutal_path, the current override will be appplied before
|
87
91
|
the named override passed.
|
88
|
-
* <tt>:sequence => {:after => "*override_name*"
|
92
|
+
* <tt>:sequence => {:after => "*override_name*"}</tt> - the current override will be applied after the named override passed.
|
89
93
|
|
90
94
|
* <tt>:attributes</tt> - A hash containing all the attributes to be set on the matched elements, eg: :attributes => {:class => "green", :title => "some string"}
|
91
95
|
|
data/deface.gemspec
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "deface"
|
3
|
-
s.version = "0.
|
3
|
+
s.version = "0.8.0"
|
4
4
|
|
5
5
|
s.authors = ["Brian D Quinn"]
|
6
|
-
s.description = "Deface is a library that allows you to customize ERB views in a Rails application without editing the underlying view."
|
7
|
-
s.email = "brian@
|
6
|
+
s.description = "Deface is a library that allows you to customize ERB & HAML views in a Rails application without editing the underlying view."
|
7
|
+
s.email = "brian@spreecommerce.com"
|
8
8
|
s.extra_rdoc_files = [
|
9
9
|
"README.markdown"
|
10
10
|
]
|
@@ -13,10 +13,11 @@ Gem::Specification.new do |s|
|
|
13
13
|
s.homepage = "http://github.com/railsdog/deface"
|
14
14
|
s.rdoc_options = ["--charset=UTF-8"]
|
15
15
|
s.require_paths = ["lib"]
|
16
|
-
s.summary = "Deface is a library that allows you to customize ERB views in Rails"
|
16
|
+
s.summary = "Deface is a library that allows you to customize ERB & HAML views in Rails"
|
17
17
|
|
18
18
|
s.add_dependency('nokogiri', '~> 1.5.0')
|
19
19
|
s.add_dependency('rails', '>= 3.0.9')
|
20
20
|
|
21
|
-
s.add_development_dependency('rspec', '>= 2.
|
21
|
+
s.add_development_dependency('rspec', '>= 2.8.0')
|
22
|
+
s.add_development_dependency('haml', '>= 3.1.4')
|
22
23
|
end
|
data/lib/deface.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
require "action_view"
|
2
2
|
require "action_controller"
|
3
|
-
require "deface/action_view_extensions"
|
4
3
|
require "deface/template_helper"
|
4
|
+
require "deface/original_validator"
|
5
|
+
require "deface/applicator"
|
6
|
+
require "deface/search"
|
5
7
|
require "deface/override"
|
6
8
|
require "deface/parser"
|
7
9
|
require "deface/environment"
|
@@ -1,12 +1,44 @@
|
|
1
1
|
ActionView::Template.class_eval do
|
2
|
-
alias_method :
|
2
|
+
alias_method :initialize_without_deface, :initialize
|
3
3
|
|
4
4
|
def initialize(source, identifier, handler, details)
|
5
5
|
if Rails.application.config.deface.enabled
|
6
|
-
|
6
|
+
haml = handler.to_s == "Haml::Plugin"
|
7
|
+
|
8
|
+
processed_source = Deface::Override.apply(source, details, true, haml )
|
9
|
+
|
10
|
+
if haml && processed_source != source
|
11
|
+
handler = ActionView::Template::Handlers::ERB
|
12
|
+
end
|
13
|
+
else
|
14
|
+
processed_source = source
|
15
|
+
end
|
16
|
+
|
17
|
+
initialize_without_deface(processed_source, identifier, handler, details)
|
18
|
+
end
|
19
|
+
|
20
|
+
alias_method :render_without_deface, :render
|
21
|
+
|
22
|
+
# refresh view to get source again if
|
23
|
+
# view needs to be recompiled
|
24
|
+
#
|
25
|
+
def render(view, locals, buffer=nil, &block)
|
26
|
+
if @compiled && !view.respond_to?(method_name)
|
27
|
+
@compiled = false
|
28
|
+
@source = refresh(view).source
|
7
29
|
end
|
30
|
+
render_without_deface(view, locals, buffer, &block)
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
alias_method :method_name_without_deface, :method_name
|
8
35
|
|
9
|
-
|
36
|
+
# inject deface hash into compiled view method name
|
37
|
+
# used to determine if recompilation is needed
|
38
|
+
#
|
39
|
+
def method_name
|
40
|
+
deface_hash = Deface::Override.digest(:virtual_path => @virtual_path)
|
41
|
+
"_#{deface_hash}_#{method_name_without_deface}"
|
10
42
|
end
|
11
43
|
end
|
12
44
|
|
@@ -0,0 +1,192 @@
|
|
1
|
+
module Deface
|
2
|
+
module Applicator
|
3
|
+
module ClassMethods
|
4
|
+
# applies all applicable overrides to given source
|
5
|
+
#
|
6
|
+
def apply(source, details, log=true, haml=false)
|
7
|
+
overrides = find(details)
|
8
|
+
|
9
|
+
if log && overrides.size > 0
|
10
|
+
Rails.logger.info "\e[1;32mDeface:\e[0m #{overrides.size} overrides found for '#{details[:virtual_path]}'"
|
11
|
+
end
|
12
|
+
|
13
|
+
unless overrides.empty?
|
14
|
+
if haml
|
15
|
+
#convert haml to erb before parsing before
|
16
|
+
source = Deface::HamlConverter.new(source).result
|
17
|
+
end
|
18
|
+
|
19
|
+
doc = Deface::Parser.convert(source)
|
20
|
+
|
21
|
+
overrides.each do |override|
|
22
|
+
if override.disabled?
|
23
|
+
Rails.logger.info("\e[1;32mDeface:\e[0m '#{override.name}' is disabled") if log
|
24
|
+
next
|
25
|
+
end
|
26
|
+
|
27
|
+
if override.end_selector.blank?
|
28
|
+
# single css selector
|
29
|
+
|
30
|
+
matches = doc.css(override.selector)
|
31
|
+
|
32
|
+
if log
|
33
|
+
Rails.logger.send(matches.size == 0 ? :error : :info, "\e[1;32mDeface:\e[0m '#{override.name}' matched #{matches.size} times with '#{override.selector}'")
|
34
|
+
end
|
35
|
+
|
36
|
+
matches.each do |match|
|
37
|
+
override.validate_original(match)
|
38
|
+
|
39
|
+
case override.action
|
40
|
+
when :remove
|
41
|
+
match.replace ""
|
42
|
+
when :replace
|
43
|
+
match.replace override.source_element
|
44
|
+
when :replace_contents
|
45
|
+
match.children.remove
|
46
|
+
match.add_child(override.source_element)
|
47
|
+
when :surround, :surround_contents
|
48
|
+
|
49
|
+
new_source = override.source_element.clone(1)
|
50
|
+
|
51
|
+
if original = new_source.css("code:contains('render_original')").first
|
52
|
+
if override.action == :surround
|
53
|
+
original.replace match.clone(1)
|
54
|
+
match.replace new_source
|
55
|
+
elsif override.action == :surround_contents
|
56
|
+
original.replace match.children
|
57
|
+
match.children.remove
|
58
|
+
match.add_child new_source
|
59
|
+
end
|
60
|
+
else
|
61
|
+
#maybe we should log that the original wasn't found.
|
62
|
+
end
|
63
|
+
|
64
|
+
when :insert_before
|
65
|
+
match.before override.source_element
|
66
|
+
when :insert_after
|
67
|
+
match.after override.source_element
|
68
|
+
when :insert_top
|
69
|
+
if match.children.size == 0
|
70
|
+
match.children = override.source_element
|
71
|
+
else
|
72
|
+
match.children.before(override.source_element)
|
73
|
+
end
|
74
|
+
when :insert_bottom
|
75
|
+
if match.children.size == 0
|
76
|
+
match.children = override.source_element
|
77
|
+
else
|
78
|
+
match.children.after(override.source_element)
|
79
|
+
end
|
80
|
+
when :set_attributes
|
81
|
+
override.attributes.each do |name, value|
|
82
|
+
name = normalize_attribute_name(name)
|
83
|
+
|
84
|
+
match.remove_attribute(name)
|
85
|
+
match.remove_attribute("data-erb-#{name}")
|
86
|
+
|
87
|
+
if match.attributes.key? name
|
88
|
+
match.set_attribute name, value.to_s
|
89
|
+
else
|
90
|
+
match.set_attribute "data-erb-#{name}", value.to_s
|
91
|
+
end
|
92
|
+
end
|
93
|
+
when :add_to_attributes
|
94
|
+
override.attributes.each do |name, value|
|
95
|
+
name = normalize_attribute_name(name)
|
96
|
+
|
97
|
+
if match.attributes.key? name
|
98
|
+
match.set_attribute name, match.attributes[name].value << " #{value}"
|
99
|
+
elsif match.attributes.key? "data-erb-#{name}"
|
100
|
+
match.set_attribute "data-erb-#{name}", match.attributes["data-erb-#{name}"].value << " #{value}"
|
101
|
+
else
|
102
|
+
match.set_attribute "data-erb-#{name}", value.to_s
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
when :remove_from_attributes
|
107
|
+
override.attributes.each do |name, value|
|
108
|
+
name = normalize_attribute_name(name)
|
109
|
+
|
110
|
+
if match.attributes.key? name
|
111
|
+
match.set_attribute name, match.attributes[name].value.gsub(value.to_s, '').strip
|
112
|
+
elsif match.attributes.key? "data-erb-#{name}"
|
113
|
+
match.set_attribute "data-erb-#{name}", match.attributes["data-erb-#{name}"].value.gsub(value.to_s, '').strip
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
else
|
121
|
+
# targeting range of elements as end_selector is present
|
122
|
+
starting = doc.css(override.selector).first
|
123
|
+
|
124
|
+
if starting && starting.parent
|
125
|
+
ending = starting.parent.css(override.end_selector).first
|
126
|
+
else
|
127
|
+
ending = doc.css(override.end_selector).first
|
128
|
+
end
|
129
|
+
|
130
|
+
if starting && ending
|
131
|
+
if log
|
132
|
+
Rails.logger.info("\e[1;32mDeface:\e[0m '#{override.name}' matched starting with '#{override.selector}' and ending with '#{override.end_selector}'")
|
133
|
+
end
|
134
|
+
|
135
|
+
elements = select_range(starting, ending)
|
136
|
+
|
137
|
+
case override.action
|
138
|
+
when :remove
|
139
|
+
elements.map &:remove
|
140
|
+
when :replace
|
141
|
+
starting.before(override.source_element)
|
142
|
+
elements.map &:remove
|
143
|
+
when :replace_contents
|
144
|
+
elements[1..-2].map &:remove
|
145
|
+
starting.after(override.source_element)
|
146
|
+
end
|
147
|
+
else
|
148
|
+
if starting.nil?
|
149
|
+
Rails.logger.info("\e[1;32mDeface:\e[0m '#{override.name}' failed to match with starting selector '#{override.selector}'")
|
150
|
+
else
|
151
|
+
Rails.logger.info("\e[1;32mDeface:\e[0m '#{override.name}' failed to match with end selector '#{override.end_selector}'")
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
|
+
|
159
|
+
#prevents any caching by rails in development mode
|
160
|
+
details[:updated_at] = Time.now
|
161
|
+
|
162
|
+
source = doc.to_s
|
163
|
+
|
164
|
+
Deface::Parser.undo_erb_markup!(source)
|
165
|
+
end
|
166
|
+
|
167
|
+
source
|
168
|
+
end
|
169
|
+
|
170
|
+
private
|
171
|
+
# finds all elements upto closing sibling in nokgiri document
|
172
|
+
#
|
173
|
+
def select_range(first, last)
|
174
|
+
first == last ? [first] : [first, *select_range(first.next, last)]
|
175
|
+
end
|
176
|
+
|
177
|
+
def normalize_attribute_name(name)
|
178
|
+
name = name.to_s.gsub /"|'/, ''
|
179
|
+
|
180
|
+
if /\Adata-erb-/ =~ name
|
181
|
+
name.gsub! /\Adata-erb-/, ''
|
182
|
+
end
|
183
|
+
|
184
|
+
name
|
185
|
+
end
|
186
|
+
|
187
|
+
def set_attributes(match, name, value)
|
188
|
+
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
data/lib/deface/environment.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
module Deface
|
2
2
|
|
3
3
|
class Environment
|
4
|
-
attr_accessor :overrides, :enabled
|
4
|
+
attr_accessor :overrides, :enabled, :haml_support
|
5
5
|
def initialize
|
6
|
-
@overrides
|
7
|
-
@enabled
|
6
|
+
@overrides = Overrides.new
|
7
|
+
@enabled = true
|
8
|
+
@haml_support = false
|
8
9
|
end
|
9
10
|
end
|
10
11
|
|
@@ -20,9 +21,10 @@ module Deface
|
|
20
21
|
end
|
21
22
|
|
22
23
|
def load_all(app)
|
24
|
+
#clear overrides before reloading them
|
25
|
+
app.config.deface.overrides.all.clear
|
26
|
+
|
23
27
|
# check application for specified overrides paths
|
24
|
-
#
|
25
|
-
#
|
26
28
|
override_paths = app.paths["app/overrides"]
|
27
29
|
enumerate_and_load(override_paths, app.root)
|
28
30
|
|
@@ -43,7 +45,7 @@ module Deface
|
|
43
45
|
Deface::Override._early.clear
|
44
46
|
end
|
45
47
|
|
46
|
-
private
|
48
|
+
private
|
47
49
|
def enumerate_and_load(paths, root)
|
48
50
|
paths ||= ["app/overrides"]
|
49
51
|
|
@@ -56,4 +58,3 @@ module Deface
|
|
56
58
|
end
|
57
59
|
end
|
58
60
|
end
|
59
|
-
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Deface
|
2
|
+
class HamlConverter < Haml::Engine
|
3
|
+
def result
|
4
|
+
Deface::Parser.undo_erb_markup! String.new(render)
|
5
|
+
end
|
6
|
+
|
7
|
+
def push_script(text, preserve_script, in_tag = false, preserve_tag = false,
|
8
|
+
escape_html = false, nuke_inner_whitespace = false)
|
9
|
+
push_text "<%= #{text.strip} %>"
|
10
|
+
|
11
|
+
if block_given?
|
12
|
+
yield
|
13
|
+
push_silent('end')
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def push_silent(text, can_suppress = false)
|
18
|
+
push_text "<% #{text.strip} %>"
|
19
|
+
end
|
20
|
+
|
21
|
+
def parse_old_attributes(line)
|
22
|
+
attributes_hash, rest, last_line = super(line)
|
23
|
+
|
24
|
+
attributes_hash = deface_attributes(attributes_hash)
|
25
|
+
|
26
|
+
return attributes_hash, rest, last_line
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
def parse_new_attributes(line)
|
31
|
+
attributes, rest, last_line = super(line)
|
32
|
+
|
33
|
+
attributes[1] = deface_attributes(attributes[1])
|
34
|
+
|
35
|
+
return attributes, rest, last_line
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
# coverts { attributes into deface compatibily attributes
|
41
|
+
def deface_attributes(attrs)
|
42
|
+
return if attrs.nil?
|
43
|
+
attrs.gsub! /\{|\}/, ''
|
44
|
+
attrs = attrs.split(',')
|
45
|
+
|
46
|
+
if attrs.join.include? "=>"
|
47
|
+
attrs.map!{|a| a.split("=>").map(&:strip) }
|
48
|
+
else
|
49
|
+
attrs.map!{|a| a.split(": ").map(&:strip) }
|
50
|
+
end
|
51
|
+
|
52
|
+
attrs.map! do |a|
|
53
|
+
if a[1][0] != ?' && a[1][0] != ?"
|
54
|
+
a[0] = %Q{"data-erb-#{a[0].gsub(/:|'|"/,'')}"}
|
55
|
+
a[1] = %Q{"<%= #{a[1]} %>"}
|
56
|
+
end
|
57
|
+
|
58
|
+
a
|
59
|
+
end
|
60
|
+
|
61
|
+
attrs.map{ |a| a.join " => " }.join(', ')
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Deface
|
2
|
+
module OriginalValidator
|
3
|
+
|
4
|
+
def original_source
|
5
|
+
return nil unless @args[:original].present?
|
6
|
+
|
7
|
+
Deface::Parser.convert(@args[:original].clone)
|
8
|
+
end
|
9
|
+
|
10
|
+
# logs if original source has changed
|
11
|
+
def validate_original(match)
|
12
|
+
hashed_original = Digest::SHA1.hexdigest(match.to_s.gsub(/\s/, ''))
|
13
|
+
|
14
|
+
if @args[:original].present?
|
15
|
+
valid = @args[:original] == hashed_original
|
16
|
+
|
17
|
+
unless valid
|
18
|
+
valid = self.original_source.to_s.gsub(/\s/, '') == match.to_s.gsub(/\s/, '')
|
19
|
+
end
|
20
|
+
|
21
|
+
if !valid && defined?(Rails.logger)
|
22
|
+
Rails.logger.error "\e[1;32mDeface: [ERROR]\e[0m The original source for '#{self.name}' has changed, this override should be reviewed to ensure it's still valid."
|
23
|
+
end
|
24
|
+
|
25
|
+
return valid
|
26
|
+
else
|
27
|
+
Rails.logger.info "\e[1;32mDeface: [WARNING]\e[0m No :original defined for '#{self.name}', you should change its definition to include:\n :original => '#{hashed_original}' "
|
28
|
+
|
29
|
+
return nil
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|