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/lib/deface/override.rb
CHANGED
@@ -1,13 +1,16 @@
|
|
1
1
|
module Deface
|
2
2
|
class Override
|
3
|
-
include
|
3
|
+
include TemplateHelper
|
4
|
+
include OriginalValidator
|
5
|
+
extend Applicator::ClassMethods
|
6
|
+
extend Search::ClassMethods
|
4
7
|
|
5
8
|
cattr_accessor :actions, :_early
|
6
9
|
attr_accessor :args
|
7
10
|
|
8
11
|
@@_early = []
|
9
|
-
@@actions = [:remove, :replace, :replace_contents, :surround, :surround_contents, :insert_after, :insert_before, :insert_top, :insert_bottom, :set_attributes]
|
10
|
-
@@sources = [:text, :partial, :template]
|
12
|
+
@@actions = [:remove, :replace, :replace_contents, :surround, :surround_contents, :insert_after, :insert_before, :insert_top, :insert_bottom, :set_attributes, :add_to_attributes, :remove_from_attributes]
|
13
|
+
@@sources = [:text, :erb, :haml, :partial, :template]
|
11
14
|
|
12
15
|
# Initializes new override, you must supply only one Target, Action & Source
|
13
16
|
# parameter for each override (and any number of Optional parameters).
|
@@ -29,7 +32,9 @@ module Deface
|
|
29
32
|
# * <tt>:insert_before</tt> - Inserts before all elements that match the supplied selector
|
30
33
|
# * <tt>:insert_top</tt> - Inserts inside all elements that match the supplied selector, before all existing child
|
31
34
|
# * <tt>:insert_bottom</tt> - Inserts inside all elements that match the supplied selector, after all existing child
|
32
|
-
# * <tt>:set_attributes</tt> - Sets
|
35
|
+
# * <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.
|
36
|
+
# * <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.
|
37
|
+
# * <tt>:remove_from_attributes</tt> - Removes value from attributes on all elements that match the supplied selector. Expects :attributes option to be passed.
|
33
38
|
#
|
34
39
|
# ==== Source
|
35
40
|
#
|
@@ -43,27 +48,32 @@ module Deface
|
|
43
48
|
# This needs to be unique within the same :virtual_path
|
44
49
|
# * <tt>:disabled</tt> - When set to true the override will not be applied.
|
45
50
|
# * <tt>:original</tt> - String containing original markup that is being overridden.
|
46
|
-
# If supplied Deface will log when the original markup changes, which helps highlight overrides that need
|
51
|
+
# If supplied Deface will log when the original markup changes, which helps highlight overrides that need
|
47
52
|
# attention when upgrading versions of the source application. Only really warranted for :replace overrides.
|
48
53
|
# NB: All whitespace is stripped before comparsion.
|
49
|
-
# * <tt>:closing_selector</tt> - A second css selector targeting an end element, allowing you to select a range
|
50
|
-
# of elements to apply an action against. The :closing_selector only supports the :replace, :remove and
|
54
|
+
# * <tt>:closing_selector</tt> - A second css selector targeting an end element, allowing you to select a range
|
55
|
+
# of elements to apply an action against. The :closing_selector only supports the :replace, :remove and
|
51
56
|
# :replace_contents actions, and the end element must be a sibling of the first/starting element. Note the CSS
|
52
57
|
# general sibling selector (~) is used to match the first element after the opening selector.
|
53
58
|
# * <tt>:sequence</tt> - Used to order the application of an override for a specific virtual path, helpful when
|
54
59
|
# an override depends on another override being applied first.
|
55
60
|
# Supports:
|
56
61
|
# :sequence => n - where n is a positive or negative integer (lower numbers get applied first, default 100).
|
57
|
-
# :sequence => {:before => "override_name"} - where "override_name" is the name of an override defined for the
|
58
|
-
# same virutal_path, the current override will be appplied before
|
62
|
+
# :sequence => {:before => "override_name"} - where "override_name" is the name of an override defined for the
|
63
|
+
# same virutal_path, the current override will be appplied before
|
59
64
|
# the named override passed.
|
60
65
|
# :sequence => {:after => "override_name") - the current override will be applied after the named override passed.
|
61
66
|
# * <tt>:attributes</tt> - A hash containing all the attributes to be set on the matched elements, eg: :attributes => {:class => "green", :title => "some string"}
|
62
67
|
#
|
63
68
|
def initialize(args, &content)
|
64
|
-
|
65
|
-
|
66
|
-
|
69
|
+
if Rails.application.try(:config).try(:deface).try(:enabled)
|
70
|
+
unless Rails.application.config.deface.try(:overrides)
|
71
|
+
@@_early << args
|
72
|
+
warn "[WARNING] You no longer need to manually require overrides, remove require for '#{args[:name]}'."
|
73
|
+
return
|
74
|
+
end
|
75
|
+
else
|
76
|
+
warn "[WARNING] You no longer need to manually require overrides, remove require for '#{args[:name]}'."
|
67
77
|
return
|
68
78
|
end
|
69
79
|
|
@@ -73,7 +83,7 @@ module Deface
|
|
73
83
|
args[:text] = content.call if block_given?
|
74
84
|
|
75
85
|
virtual_key = args[:virtual_path].to_sym
|
76
|
-
name_key = args[:name].to_s.parameterize
|
86
|
+
name_key = args[:name].to_s.parameterize
|
77
87
|
|
78
88
|
self.class.all[virtual_key] ||= {}
|
79
89
|
|
@@ -101,6 +111,10 @@ module Deface
|
|
101
111
|
end
|
102
112
|
|
103
113
|
self.class.all[virtual_key][name_key] = self
|
114
|
+
|
115
|
+
expire_compiled_template
|
116
|
+
|
117
|
+
self
|
104
118
|
end
|
105
119
|
|
106
120
|
def selector
|
@@ -138,10 +152,10 @@ module Deface
|
|
138
152
|
end
|
139
153
|
|
140
154
|
else
|
141
|
-
return @args[:sequence].to_i
|
155
|
+
return @args[:sequence].to_i
|
142
156
|
end
|
143
157
|
rescue SystemStackError
|
144
|
-
if defined?(Rails)
|
158
|
+
if defined?(Rails)
|
145
159
|
Rails.logger.error "\e[1;32mDeface: [WARNING]\e[0m Circular sequence dependency includes override named: '#{self.name}' on '#{@args[:virtual_path]}'."
|
146
160
|
end
|
147
161
|
|
@@ -159,32 +173,20 @@ module Deface
|
|
159
173
|
load_template_source(@args[:template], false)
|
160
174
|
elsif @args.key? :text
|
161
175
|
@args[:text]
|
176
|
+
elsif @args.key? :erb
|
177
|
+
@args[:erb]
|
178
|
+
elsif @args.key?(:haml) && Rails.application.config.deface.haml_support
|
179
|
+
haml_engine = Deface::HamlConverter.new(@args[:haml])
|
180
|
+
haml_engine.render
|
162
181
|
end
|
182
|
+
|
183
|
+
erb
|
163
184
|
end
|
164
185
|
|
165
186
|
def source_element
|
166
187
|
Deface::Parser.convert(source.clone)
|
167
188
|
end
|
168
189
|
|
169
|
-
def original_source
|
170
|
-
return nil unless @args[:original].present?
|
171
|
-
|
172
|
-
Deface::Parser.convert(@args[:original].clone)
|
173
|
-
end
|
174
|
-
|
175
|
-
# logs if original source has changed
|
176
|
-
def validate_original(match)
|
177
|
-
return true if self.original_source.nil?
|
178
|
-
|
179
|
-
valid = self.original_source.to_s.gsub(/\s/, '') == match.to_s.gsub(/\s/, '')
|
180
|
-
|
181
|
-
if !valid && defined?(Rails.logger) == "constant"
|
182
|
-
Rails.logger.error "\e[1;32mDeface: [WARNING]\e[0m The original source for '#{self.name}' has changed, this override should be reviewed to ensure it's still valid."
|
183
|
-
end
|
184
|
-
|
185
|
-
valid
|
186
|
-
end
|
187
|
-
|
188
190
|
def disabled?
|
189
191
|
@args.key?(:disabled) ? @args[:disabled] : false
|
190
192
|
end
|
@@ -198,164 +200,35 @@ module Deface
|
|
198
200
|
@args[:attributes] || []
|
199
201
|
end
|
200
202
|
|
201
|
-
|
202
|
-
|
203
|
-
def self.apply(source, details, log=true)
|
204
|
-
overrides = find(details)
|
205
|
-
|
206
|
-
if log
|
207
|
-
log = defined?(Rails.logger)
|
208
|
-
end
|
209
|
-
|
210
|
-
if log && overrides.size > 0
|
211
|
-
Rails.logger.info "\e[1;32mDeface:\e[0m #{overrides.size} overrides found for '#{details[:virtual_path]}'"
|
212
|
-
end
|
213
|
-
|
214
|
-
unless overrides.empty?
|
215
|
-
doc = Deface::Parser.convert(source)
|
216
|
-
|
217
|
-
overrides.each do |override|
|
218
|
-
if override.disabled?
|
219
|
-
Rails.logger.info("\e[1;32mDeface:\e[0m '#{override.name}' is disabled") if log
|
220
|
-
next
|
221
|
-
end
|
222
|
-
|
223
|
-
if override.end_selector.blank?
|
224
|
-
# single css selector
|
225
|
-
|
226
|
-
matches = doc.css(override.selector)
|
227
|
-
|
228
|
-
if log
|
229
|
-
Rails.logger.send(matches.size == 0 ? :error : :info, "\e[1;32mDeface:\e[0m '#{override.name}' matched #{matches.size} times with '#{override.selector}'")
|
230
|
-
end
|
231
|
-
|
232
|
-
matches.each do |match|
|
233
|
-
override.validate_original(match)
|
234
|
-
|
235
|
-
case override.action
|
236
|
-
when :remove
|
237
|
-
match.replace ""
|
238
|
-
when :replace
|
239
|
-
match.replace override.source_element
|
240
|
-
when :replace_contents
|
241
|
-
match.children.remove
|
242
|
-
match.add_child(override.source_element)
|
243
|
-
when :surround, :surround_contents
|
244
|
-
|
245
|
-
new_source = override.source_element.clone(1)
|
246
|
-
|
247
|
-
if original = new_source.css("code:contains('render_original')").first
|
248
|
-
if override.action == :surround
|
249
|
-
original.replace match.clone(1)
|
250
|
-
match.replace new_source
|
251
|
-
elsif override.action == :surround_contents
|
252
|
-
original.replace match.children
|
253
|
-
match.children.remove
|
254
|
-
match.add_child new_source
|
255
|
-
end
|
256
|
-
else
|
257
|
-
#maybe we should log that the original wasn't found.
|
258
|
-
end
|
259
|
-
|
260
|
-
when :insert_before
|
261
|
-
match.before override.source_element
|
262
|
-
when :insert_after
|
263
|
-
match.after override.source_element
|
264
|
-
when :insert_top
|
265
|
-
if match.children.size == 0
|
266
|
-
match.children = override.source_element
|
267
|
-
else
|
268
|
-
match.children.before(override.source_element)
|
269
|
-
end
|
270
|
-
when :insert_bottom
|
271
|
-
if match.children.size == 0
|
272
|
-
match.children = override.source_element
|
273
|
-
else
|
274
|
-
match.children.after(override.source_element)
|
275
|
-
end
|
276
|
-
when :set_attributes
|
277
|
-
override.attributes.each do |name, value|
|
278
|
-
match.set_attribute(name.to_s, value.to_s)
|
279
|
-
end
|
280
|
-
end
|
281
|
-
|
282
|
-
end
|
283
|
-
else
|
284
|
-
# targeting range of elements as end_selector is present
|
285
|
-
starting = doc.css(override.selector).first
|
286
|
-
|
287
|
-
if starting && starting.parent
|
288
|
-
ending = starting.parent.css(override.end_selector).first
|
289
|
-
else
|
290
|
-
ending = doc.css(override.end_selector).first
|
291
|
-
end
|
292
|
-
|
293
|
-
if starting && ending
|
294
|
-
if log
|
295
|
-
Rails.logger.info("\e[1;32mDeface:\e[0m '#{override.name}' matched starting with '#{override.selector}' and ending with '#{override.end_selector}'")
|
296
|
-
end
|
297
|
-
|
298
|
-
elements = select_range(starting, ending)
|
299
|
-
|
300
|
-
case override.action
|
301
|
-
when :remove
|
302
|
-
elements.map &:remove
|
303
|
-
when :replace
|
304
|
-
starting.before(override.source_element)
|
305
|
-
elements.map &:remove
|
306
|
-
when :replace_contents
|
307
|
-
elements[1..-2].map &:remove
|
308
|
-
starting.after(override.source_element)
|
309
|
-
end
|
310
|
-
else
|
311
|
-
if starting.nil?
|
312
|
-
Rails.logger.info("\e[1;32mDeface:\e[0m '#{override.name}' failed to match with starting selector '#{override.selector}'")
|
313
|
-
else
|
314
|
-
Rails.logger.info("\e[1;32mDeface:\e[0m '#{override.name}' failed to match with end selector '#{override.end_selector}'")
|
315
|
-
end
|
316
|
-
|
317
|
-
end
|
318
|
-
end
|
319
|
-
|
320
|
-
end
|
321
|
-
|
322
|
-
#prevents any caching by rails in development mode
|
323
|
-
details[:updated_at] = Time.now
|
324
|
-
|
325
|
-
source = doc.to_s
|
326
|
-
|
327
|
-
Deface::Parser.undo_erb_markup!(source)
|
328
|
-
end
|
329
|
-
|
330
|
-
source
|
203
|
+
def digest
|
204
|
+
Digest::MD5.new.update(@args.to_s).hexdigest
|
331
205
|
end
|
332
206
|
|
333
|
-
|
334
|
-
|
335
|
-
def self.find(details)
|
336
|
-
return [] if self.all.empty? || details.empty?
|
337
|
-
|
338
|
-
virtual_path = details[:virtual_path]
|
339
|
-
return [] if virtual_path.nil?
|
340
|
-
|
341
|
-
virtual_path = virtual_path.gsub(/^\//, '')
|
342
|
-
|
343
|
-
result = []
|
344
|
-
result << self.all[virtual_path.to_sym].try(:values)
|
345
|
-
|
346
|
-
result.flatten.compact.sort_by &:sequence
|
207
|
+
def self.all
|
208
|
+
Rails.application.config.deface.overrides.all
|
347
209
|
end
|
348
210
|
|
211
|
+
def self.digest(details)
|
212
|
+
overrides = self.find(details)
|
349
213
|
|
350
|
-
|
351
|
-
Rails.application.config.deface.overrides.all
|
214
|
+
Digest::MD5.new.update(overrides.inject('') { |digest, override| digest << override.digest }).hexdigest
|
352
215
|
end
|
353
216
|
|
354
217
|
private
|
355
|
-
|
218
|
+
|
219
|
+
# check if method is compiled for the current virtual path
|
356
220
|
#
|
357
|
-
def
|
358
|
-
|
221
|
+
def expire_compiled_template
|
222
|
+
if compiled_method_name = ActionView::CompiledTemplates.instance_methods.detect { |name| name =~ /#{args[:virtual_path].gsub(/[^a-z_]/, '_')}/ }
|
223
|
+
#if the compiled method does not contain the current deface digest
|
224
|
+
#then remove the old method - this will allow the template to be
|
225
|
+
#recompiled the next time it is rendered (showing the latest changes)
|
226
|
+
|
227
|
+
unless compiled_method_name =~ /\A_#{self.class.digest(:virtual_path => @args[:virtual_path])}_/
|
228
|
+
ActionView::CompiledTemplates.send :remove_method, compiled_method_name
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
359
232
|
end
|
360
233
|
|
361
234
|
end
|
data/lib/deface/railtie.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Deface
|
2
2
|
class Railtie < Rails::Railtie
|
3
3
|
# include rake tasks.
|
4
|
-
#
|
4
|
+
#
|
5
5
|
rake_tasks do
|
6
6
|
%w{utils precompile}.each { |r| load File.join([File.dirname(__FILE__) , "../../tasks/#{r}.rake"]) }
|
7
7
|
end
|
@@ -50,16 +50,26 @@ module Deface
|
|
50
50
|
# overrides if deface is enabled.
|
51
51
|
#
|
52
52
|
initializer "deface.environment", :after => :load_environment_config do |app|
|
53
|
-
|
53
|
+
if app.config.deface.enabled
|
54
|
+
# only decorate ActionView if deface is enabled
|
55
|
+
require "deface/action_view_extensions"
|
54
56
|
|
55
|
-
|
56
|
-
|
57
|
+
# setup real env object
|
58
|
+
app.config.deface = Deface::Environment.new
|
57
59
|
|
58
|
-
|
59
|
-
|
60
|
+
# checks if haml is loaded and enables support
|
61
|
+
if defined?(Haml)
|
62
|
+
app.config.deface.haml_support = true
|
63
|
+
require 'deface/haml_converter'
|
64
|
+
end
|
60
65
|
|
61
|
-
|
62
|
-
|
66
|
+
# catchs any overrides that we required manually
|
67
|
+
app.config.deface.overrides.early_check
|
68
|
+
else
|
69
|
+
# deface is disabled so clear any overrides
|
70
|
+
# that might have been manually required
|
71
|
+
# won't get loaded but just in case
|
72
|
+
Deface::Override._early.clear
|
63
73
|
end
|
64
74
|
end
|
65
75
|
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Deface
|
2
|
+
module Search
|
3
|
+
module ClassMethods
|
4
|
+
|
5
|
+
# finds all applicable overrides for supplied template
|
6
|
+
#
|
7
|
+
def find(details)
|
8
|
+
return [] if self.all.empty? || details.empty?
|
9
|
+
|
10
|
+
virtual_path = details[:virtual_path]
|
11
|
+
return [] if virtual_path.nil?
|
12
|
+
|
13
|
+
[/^\//, /\.\w+\z/].each { |regex| virtual_path.gsub!(regex, '') }
|
14
|
+
|
15
|
+
result = []
|
16
|
+
result << self.all[virtual_path.to_sym].try(:values)
|
17
|
+
|
18
|
+
result.flatten.compact.sort_by &:sequence
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -13,9 +13,18 @@ module Deface
|
|
13
13
|
name = parts.join("/")
|
14
14
|
end
|
15
15
|
|
16
|
+
#this needs to be reviewed for production mode, overrides not present
|
16
17
|
Rails.application.config.deface.enabled = apply_overrides
|
17
18
|
@lookup_context ||= ActionView::LookupContext.new(ActionController::Base.view_paths, {:formats => [:html]})
|
18
|
-
@lookup_context.
|
19
|
+
view = @lookup_context.disable_cache do
|
20
|
+
@lookup_context.find(name, prefix, partial)
|
21
|
+
end
|
22
|
+
|
23
|
+
if view.handler.to_s == "Haml::Plugin"
|
24
|
+
Deface::HamlConverter.new(view.source).result
|
25
|
+
else
|
26
|
+
view.source
|
27
|
+
end
|
19
28
|
end
|
20
29
|
|
21
30
|
#gets source erb for an element
|
@@ -0,0 +1 @@
|
|
1
|
+
%div{:class => @some, :id => "message"}= 'Hello, World!'
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module ActionView
|
4
|
+
describe Template do
|
5
|
+
include_context "mock Rails.application"
|
6
|
+
|
7
|
+
describe "with no overrides defined" do
|
8
|
+
before(:each) do
|
9
|
+
@updated_at = Time.now - 600
|
10
|
+
@template = ActionView::Template.new("<p>test</p>", "/some/path/to/file.erb", ActionView::Template::Handlers::ERB, {:virtual_path=>"posts/index", :format=>:html, :updated_at => @updated_at})
|
11
|
+
#stub for Rails < 3.1
|
12
|
+
unless defined?(@template.updated_at)
|
13
|
+
@template.stub(:updated_at).and_return(@updated_at)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should initialize new template object" do
|
18
|
+
@template.is_a?(ActionView::Template).should == true
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should return unmodified source" do
|
22
|
+
@template.source.should == "<p>test</p>"
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should not change updated_at" do
|
26
|
+
@template.updated_at.should == @updated_at
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "with a single remove override defined" do
|
32
|
+
before(:each) do
|
33
|
+
@updated_at = Time.now - 300
|
34
|
+
Deface::Override.new(:virtual_path => "posts/index", :name => "Posts#index", :remove => "p", :text => "<h1>Argh!</h1>")
|
35
|
+
@template = ActionView::Template.new("<p>test</p><%= raw(text) %>", "/some/path/to/file.erb", ActionView::Template::Handlers::ERB, {:virtual_path=>"posts/index", :format=>:html, :updated_at => @updated_at})
|
36
|
+
#stub for Rails < 3.1
|
37
|
+
unless defined?(@template.updated_at)
|
38
|
+
@template.stub(:updated_at).and_return(@updated_at + 500)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should return modified source" do
|
43
|
+
@template.source.should == "<%= raw(text) %>"
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should change updated_at" do
|
47
|
+
@template.updated_at.should > @updated_at
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|