deface 0.7.2 → 0.8.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/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
|