deface 0.9.1 → 1.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/.rspec +3 -0
- data/.travis.yml +1 -0
- data/README.markdown +139 -59
- data/deface.gemspec +4 -2
- data/gemfiles/haml3_2.gemfile +5 -0
- data/lib/deface.rb +28 -1
- data/lib/deface/action_view_extensions.rb +7 -1
- data/lib/deface/actions/action.rb +19 -0
- data/lib/deface/actions/add_to_attributes.rb +15 -0
- data/lib/deface/actions/attribute_action.rb +33 -0
- data/lib/deface/actions/element_action.rb +13 -0
- data/lib/deface/actions/insert_after.rb +9 -0
- data/lib/deface/actions/insert_before.rb +9 -0
- data/lib/deface/actions/insert_bottom.rb +14 -0
- data/lib/deface/actions/insert_top.rb +14 -0
- data/lib/deface/actions/remove.rb +13 -0
- data/lib/deface/actions/remove_from_attributes.rb +13 -0
- data/lib/deface/actions/replace.rb +14 -0
- data/lib/deface/actions/replace_contents.rb +19 -0
- data/lib/deface/actions/set_attributes.rb +12 -0
- data/lib/deface/actions/surround.rb +24 -0
- data/lib/deface/actions/surround_action.rb +15 -0
- data/lib/deface/actions/surround_contents.rb +33 -0
- data/lib/deface/applicator.rb +35 -191
- data/lib/deface/dsl/context.rb +7 -3
- data/lib/deface/dsl/loader.rb +5 -2
- data/lib/deface/environment.rb +31 -1
- data/lib/deface/haml_converter.rb +33 -5
- data/lib/deface/matchers/element.rb +13 -0
- data/lib/deface/matchers/range.rb +52 -0
- data/lib/deface/override.rb +28 -57
- data/lib/deface/sources/copy.rb +15 -0
- data/lib/deface/sources/cut.rb +25 -0
- data/lib/deface/sources/erb.rb +9 -0
- data/lib/deface/sources/haml.rb +14 -0
- data/lib/deface/sources/partial.rb +13 -0
- data/lib/deface/sources/source.rb +11 -0
- data/lib/deface/sources/template.rb +13 -0
- data/lib/deface/sources/text.rb +9 -0
- data/lib/deface/utils/failure_finder.rb +41 -0
- data/spec/deface/action_view_template_spec.rb +28 -2
- data/spec/deface/actions/add_to_attributes_spec.rb +62 -0
- data/spec/deface/actions/insert_after_spec.rb +19 -0
- data/spec/deface/actions/insert_before_spec.rb +19 -0
- data/spec/deface/actions/insert_bottom_spec.rb +28 -0
- data/spec/deface/actions/insert_top_spec.rb +28 -0
- data/spec/deface/actions/remove_from_attributes_spec.rb +81 -0
- data/spec/deface/actions/remove_spec.rb +30 -0
- data/spec/deface/actions/replace_contents_spec.rb +29 -0
- data/spec/deface/actions/replace_spec.rb +52 -0
- data/spec/deface/actions/set_attributes_spec.rb +62 -0
- data/spec/deface/actions/surround_contents_spec.rb +57 -0
- data/spec/deface/actions/surround_spec.rb +57 -0
- data/spec/deface/applicator_spec.rb +0 -354
- data/spec/deface/dsl/context_spec.rb +14 -7
- data/spec/deface/dsl/loader_spec.rb +12 -4
- data/spec/deface/override_spec.rb +26 -3
- data/spec/deface/template_helper_spec.rb +11 -0
- data/spec/deface/utils/failure_finder_spec.rb +76 -0
- data/spec/spec_helper.rb +21 -2
- data/tasks/utils.rake +24 -0
- metadata +91 -14
data/.gitignore
CHANGED
data/.rspec
ADDED
data/.travis.yml
CHANGED
data/README.markdown
CHANGED
@@ -104,60 +104,76 @@ You should save your overrides in the ````app/overrides````, normally one overri
|
|
104
104
|
|
105
105
|
Replaces all instances of `h1` in the `posts/_form.html.erb` partial with `<h1>New Post</h1>`
|
106
106
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
107
|
+
```ruby
|
108
|
+
Deface::Override.new(:virtual_path => "posts/_form",
|
109
|
+
:name => "example-1",
|
110
|
+
:replace => "h1",
|
111
|
+
:text => "<h1>New Post</h1>")
|
112
|
+
```
|
111
113
|
|
112
114
|
Alternatively pass it a block of code to run:
|
113
115
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
116
|
+
```ruby
|
117
|
+
Deface::Override.new(:virtual_path => "posts/_form",
|
118
|
+
:name => "example-1",
|
119
|
+
:replace => "h1") do
|
120
|
+
"<h1>New Post</h1>"
|
121
|
+
end
|
122
|
+
```
|
119
123
|
|
120
124
|
Inserts `<%= link_to "List Comments", comments_url(post) %>` before all instances of `p` with css class `comment` in `posts/index.html.erb`
|
121
125
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
+
```ruby
|
127
|
+
Deface::Override.new(:virtual_path => "posts/index",
|
128
|
+
:name => "example-2",
|
129
|
+
:insert_before => "p.comment",
|
130
|
+
:text => "<%= link_to 'List Comments', comments_url(post) %>")
|
131
|
+
```
|
126
132
|
|
127
133
|
Inserts the contents of `shared/_comment.html.erb` after all instances of `div` with an id of `comment_21` in `posts/show.html.erb`
|
128
134
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
135
|
+
```ruby
|
136
|
+
Deface::Override.new(:virtual_path => "posts/show",
|
137
|
+
:name => "example-3",
|
138
|
+
:insert_after => "div#comment_21",
|
139
|
+
:partial => "shared/comment")
|
140
|
+
```
|
133
141
|
|
134
142
|
Removes any ERB block containing the string `helper_method` in the `posts/new.html.erb` template, will also log if markup being removed does not exactly match `<%= helper_method %>`
|
135
143
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
144
|
+
```ruby
|
145
|
+
Deface::Override.new(:virtual_path => "posts/new",
|
146
|
+
:name => "example-4",
|
147
|
+
:remove => "code[erb-loud]:contains('helper_method')",
|
148
|
+
:original => "<%= helper_method %>")
|
149
|
+
```
|
140
150
|
|
141
151
|
Wraps the `div` with id of `products` in ruby if statement, the <%= render_original %> in the `text` indicates where the matching content should be re-included.
|
142
152
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
153
|
+
```ruby
|
154
|
+
Deface::Override.new(:virtual_path => "posts/new",
|
155
|
+
:name => "example-5",
|
156
|
+
:surround => "div#products",
|
157
|
+
:text => "<% if @product.present? %><%= render_original %><% end %>")
|
158
|
+
```
|
147
159
|
|
148
160
|
Sets (or adds if not present) the `class` and `title` attributes to all instances of `a` with an id of `link` in `posts/index.html.erb`
|
149
161
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
162
|
+
```ruby
|
163
|
+
Deface::Override.new(:virtual_path => 'posts/index',
|
164
|
+
:name => 'add_attrs_to_a_link',
|
165
|
+
:set_attributes => 'a#link',
|
166
|
+
:attributes => {:class => 'pretty', :title => 'This is a link'})
|
167
|
+
```
|
154
168
|
|
155
169
|
Remove an entire ERB if statement (and all it's contents) in the 'admin/products/index.html.erb' template, using the :closing_selector.
|
156
170
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
171
|
+
```ruby
|
172
|
+
Deface::Override.new(:virtual_path => 'admin/products/index',
|
173
|
+
:name => "remove_if_statement",
|
174
|
+
:remove => "code[erb-silent]:contains('if @product.sold?')",
|
175
|
+
:closing_selector => "code[erb-silent]:contains('end')"
|
176
|
+
```
|
161
177
|
|
162
178
|
### Scope
|
163
179
|
|
@@ -168,15 +184,34 @@ Deface scopes overrides by virtual_path (or partial / template file), that means
|
|
168
184
|
You can redefine an existing override by simply declaring a new override with the same <tt>:virtual_path</tt> and <tt>:name</tt> that was originally used.
|
169
185
|
You do not need to resupply all the values originally used, just the ones you want to change:
|
170
186
|
|
187
|
+
```ruby
|
171
188
|
Deface::Override.new(:virtual_path => 'posts/index',
|
172
189
|
:name => 'add_attrs_to_a_link',
|
173
190
|
:disabled => true)
|
191
|
+
```
|
192
|
+
|
193
|
+
### Namespacing
|
194
|
+
|
195
|
+
If you want to avoid inadvertently redefining overrides in other engines, you can use the `namespaced` option to have
|
196
|
+
an override automatically be namespaced to the engine in which it was defined:
|
174
197
|
|
198
|
+
```ruby
|
199
|
+
Deface::Override.new(:virtual_path => 'posts/index',
|
200
|
+
:name => 'add_link',
|
201
|
+
:namespaced => true)
|
202
|
+
```
|
203
|
+
|
204
|
+
So for example if the above override was defined in `MyEngine` it would be automatically named `my_engine_add_link`.
|
205
|
+
This can also be activated globally for all DSL overrides in your app's `application.rb` file:
|
206
|
+
|
207
|
+
```ruby
|
208
|
+
config.deface.namespaced = true # default is false
|
209
|
+
```
|
175
210
|
|
176
211
|
Using the Deface DSL (.deface files)
|
177
212
|
------------------------------------
|
178
213
|
|
179
|
-
Instead of defining Deface::Override instances directly, you can alternatively add `.deface` files to the `app/overrides` folder and Deface will automatically them
|
214
|
+
Instead of defining Deface::Override instances directly, you can alternatively add `.deface` files to the `app/overrides` folder and Deface will automatically pick them up.
|
180
215
|
The path of each override should match the path of the view template you want to modify, say for example if you have a template at:
|
181
216
|
|
182
217
|
app/views/posts/_post.html.erb
|
@@ -187,8 +222,10 @@ Then you can override it by adding a .deface file at:
|
|
187
222
|
|
188
223
|
The format of a .deface file is a comment showing the action to be performed, followed by any markup that would be normally passed to the :erb, :text, :haml arguments:
|
189
224
|
|
190
|
-
|
191
|
-
|
225
|
+
```html
|
226
|
+
<!-- insert_after 'h1' -->
|
227
|
+
<h2>These robots are awesome.</h2>
|
228
|
+
```
|
192
229
|
|
193
230
|
The same effect can also be achieved with haml, by changing the overrides filename to:
|
194
231
|
|
@@ -196,29 +233,53 @@ The same effect can also be achieved with haml, by changing the overrides filena
|
|
196
233
|
|
197
234
|
and including haml source:
|
198
235
|
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
236
|
+
```haml
|
237
|
+
/
|
238
|
+
insert_after 'h1'
|
239
|
+
%h2 These robots are awesome.
|
240
|
+
```
|
203
241
|
|
204
242
|
#### Additional Options
|
205
243
|
|
206
244
|
You can include all the additional options you can normally use when defining a Deface::Override manually, a more complex example:
|
207
245
|
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
246
|
+
```html
|
247
|
+
<!-- replace_contents 'h1' closing_selector 'div#intro' disabled -->
|
248
|
+
<p>This is a complicated example</p>
|
249
|
+
```
|
212
250
|
|
213
251
|
#### Disabled / Enabled
|
214
252
|
|
215
253
|
The DSL does not accept the instance style ````:disabled => boolean```` instead you can simply include either:
|
216
254
|
|
217
|
-
|
255
|
+
```html
|
256
|
+
<!-- enabled -->
|
257
|
+
```
|
218
258
|
|
219
259
|
or
|
220
260
|
|
221
|
-
|
261
|
+
```html
|
262
|
+
<!-- disabled -->
|
263
|
+
```
|
264
|
+
|
265
|
+
#### Namespacing
|
266
|
+
|
267
|
+
When using the DSL, overrides automatically take their name from the filename of the file in which they are defined
|
268
|
+
(ie `my_override.html.erb.deface` becomes `my_override`) so overrides with the same filename will replace each other,
|
269
|
+
even if they are defined in separate engines. If you want to avoid this, you can use the `namespaced` option :
|
270
|
+
|
271
|
+
```erb
|
272
|
+
<!-- insert_bottom 'head' namespaced-->
|
273
|
+
```
|
274
|
+
or activate it globally for all DSL overrides in your app's `application.rb` file:
|
275
|
+
|
276
|
+
```ruby
|
277
|
+
config.deface.namespaced = true # default is false
|
278
|
+
```
|
279
|
+
|
280
|
+
Each override will then have its name namespaced to the engine in which it was defined
|
281
|
+
(ie `my_override.html.erb.deface` defined in `MyEngine` becomes `my_engine_my_override`),
|
282
|
+
allowing overrides in different engines with identical filenames to co-exist.
|
222
283
|
|
223
284
|
### DSL usage for overrides that do not include markup
|
224
285
|
|
@@ -230,9 +291,10 @@ So the override filename becomes simply:
|
|
230
291
|
|
231
292
|
And the contents:
|
232
293
|
|
233
|
-
|
234
|
-
|
235
|
-
|
294
|
+
```ruby
|
295
|
+
add_to_attributes 'a#search'
|
296
|
+
attributes :alt => 'Click here to search'
|
297
|
+
```
|
236
298
|
|
237
299
|
|
238
300
|
Rake Tasks
|
@@ -280,27 +342,39 @@ Implementation
|
|
280
342
|
|
281
343
|
Deface temporarily converts ERB files into a pseudo HTML markup that can be parsed and queired by Nokogiri, using the following approach:
|
282
344
|
|
283
|
-
|
345
|
+
```erb
|
346
|
+
<%= some ruby code %>
|
347
|
+
```
|
284
348
|
|
285
|
-
|
349
|
+
becomes:
|
286
350
|
|
287
|
-
|
351
|
+
```html
|
352
|
+
<code erb-loud> some ruby code </code>
|
353
|
+
```
|
288
354
|
|
289
355
|
and
|
290
356
|
|
291
|
-
|
357
|
+
```erb
|
358
|
+
<% other ruby code %>
|
359
|
+
```
|
292
360
|
|
293
|
-
|
361
|
+
becomes:
|
294
362
|
|
295
|
-
|
363
|
+
```html
|
364
|
+
<code erb-silent> other ruby code </code>
|
365
|
+
```
|
296
366
|
|
297
367
|
ERB that is contained inside a HTML tag definition is converted slightly differently to ensure a valid HTML document that Nokogiri can parse:
|
298
368
|
|
299
|
-
|
369
|
+
```erb
|
370
|
+
<p id="<%= dom_id @product %>" <%= "style='display:block';" %>>
|
371
|
+
```
|
300
372
|
|
301
|
-
|
373
|
+
becomes:
|
302
374
|
|
303
|
-
|
375
|
+
```html
|
376
|
+
<p data-erb-id="<%= dom_id @product %>" data-erb-0="<%= "style='display:block';" %>">
|
377
|
+
```
|
304
378
|
|
305
379
|
Deface overrides have full access to all variables accessible to the view being customized.
|
306
380
|
|
@@ -313,4 +387,10 @@ Deface uses the amazing Nokogiri library (and in turn libxml) for parsing HTML /
|
|
313
387
|
|
314
388
|
2. Parsing will fail and result in invalid output if ERB blocks are responsible for closing an HTML tag that was opened normally, i.e. don't do this:
|
315
389
|
|
316
|
-
|
390
|
+
```html
|
391
|
+
<div <%= ">" %>
|
392
|
+
```
|
393
|
+
|
394
|
+
3. Gems or Spree Extensions that add overrides to your application will load them in the order they are added to your Gemfile.
|
395
|
+
|
396
|
+
4. Applying an override to a view that contains invalid markup (which, occasionally happens in Spree views) can break rendering that would normally pass a browser's own permissive rendering. This is because the nokogiri library takes it upon itself to correct the issue, which doesn't happen prior to applying deface. Sometimes that correction changes the rendering of the view in an unintended manner, appearing to break it. The easiest way to tell if this is the cause of an issue for you is to put your view into http://deface.heroku.com/ and diff the output with the html which rails renders without your override. If you see a difference in the structure of the html, you may have invalid markup in your view which nokogiri is correcting for you. See [Spree issue #1789](https://github.com/spree/spree/pull/1789) for an example of what may be wrong in a view.
|
data/deface.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "deface"
|
3
|
-
s.version = "0.
|
3
|
+
s.version = "1.0.0.rc1"
|
4
4
|
|
5
5
|
s.authors = ["Brian D Quinn"]
|
6
6
|
s.description = "Deface is a library that allows you to customize ERB & HAML views in a Rails application without editing the underlying view."
|
@@ -17,7 +17,9 @@ Gem::Specification.new do |s|
|
|
17
17
|
|
18
18
|
s.add_dependency('nokogiri', '~> 1.5.0')
|
19
19
|
s.add_dependency('rails', '~> 3.1')
|
20
|
+
s.add_dependency('colorize', '>= 0.5.8')
|
20
21
|
|
21
|
-
s.add_development_dependency('rspec', '>= 2.
|
22
|
+
s.add_development_dependency('rspec', '>= 2.11.0')
|
22
23
|
s.add_development_dependency('haml', '>= 3.1.4')
|
24
|
+
s.add_development_dependency('simplecov', '>= 0.6.4')
|
23
25
|
end
|
data/lib/deface.rb
CHANGED
@@ -6,8 +6,35 @@ require "deface/applicator"
|
|
6
6
|
require "deface/search"
|
7
7
|
require "deface/override"
|
8
8
|
require "deface/parser"
|
9
|
-
require "deface/environment"
|
10
9
|
require "deface/dsl/loader"
|
10
|
+
require "deface/sources/source"
|
11
|
+
require "deface/sources/text"
|
12
|
+
require "deface/sources/erb"
|
13
|
+
require "deface/sources/haml"
|
14
|
+
require "deface/sources/partial"
|
15
|
+
require "deface/sources/template"
|
16
|
+
require "deface/sources/copy"
|
17
|
+
require "deface/sources/cut"
|
18
|
+
require "deface/actions/action"
|
19
|
+
require "deface/actions/element_action"
|
20
|
+
require "deface/actions/replace"
|
21
|
+
require "deface/actions/remove"
|
22
|
+
require "deface/actions/replace_contents"
|
23
|
+
require "deface/actions/surround_action"
|
24
|
+
require "deface/actions/surround"
|
25
|
+
require "deface/actions/surround_contents"
|
26
|
+
require "deface/actions/insert_before"
|
27
|
+
require "deface/actions/insert_after"
|
28
|
+
require "deface/actions/insert_top"
|
29
|
+
require "deface/actions/insert_bottom"
|
30
|
+
require "deface/actions/attribute_action"
|
31
|
+
require "deface/actions/set_attributes"
|
32
|
+
require "deface/actions/add_to_attributes"
|
33
|
+
require "deface/actions/remove_from_attributes"
|
34
|
+
require "deface/matchers/element"
|
35
|
+
require "deface/matchers/range"
|
36
|
+
require "deface/environment"
|
37
|
+
require "colorize"
|
11
38
|
|
12
39
|
module Deface
|
13
40
|
if defined?(Rails)
|
@@ -2,7 +2,7 @@ ActionView::Template.class_eval do
|
|
2
2
|
alias_method :initialize_without_deface, :initialize
|
3
3
|
|
4
4
|
def initialize(source, identifier, handler, details)
|
5
|
-
if Rails.application.config.deface.enabled
|
5
|
+
if Rails.application.config.deface.enabled && should_be_defaced?(handler)
|
6
6
|
haml = handler.to_s == "Haml::Plugin"
|
7
7
|
|
8
8
|
processed_source = Deface::Override.apply(source, details, true, haml )
|
@@ -50,6 +50,12 @@ ActionView::Template.class_eval do
|
|
50
50
|
#we digest the whole method name as if it gets too long there's problems
|
51
51
|
"_#{Digest::MD5.new.update("#{deface_hash}_#{method_name_without_deface}").hexdigest}"
|
52
52
|
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def should_be_defaced?(handler)
|
57
|
+
handler.to_s.demodulize == "ERB" || handler.class.to_s.demodulize == "ERB" || handler.to_s == "Haml::Plugin"
|
58
|
+
end
|
53
59
|
end
|
54
60
|
|
55
61
|
#fix for Rails 3.1 not setting virutal_path anymore (BOO!)
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Deface
|
2
|
+
module Actions
|
3
|
+
class AddToAttributes < AttributeAction
|
4
|
+
def execute_for_attribute(target_element, name, value)
|
5
|
+
if target_element.attributes.key?(name)
|
6
|
+
target_element.set_attribute(name, target_element.attributes[name].value << " #{value}")
|
7
|
+
elsif target_element.attributes.key?("data-erb-#{name}")
|
8
|
+
target_element.set_attribute("data-erb-#{name}", target_element.attributes["data-erb-#{name}"].value << " #{value}")
|
9
|
+
else
|
10
|
+
target_element.set_attribute("data-erb-#{name}", value.to_s)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Deface
|
2
|
+
module Actions
|
3
|
+
class AttributeAction < Action
|
4
|
+
attr_reader :attributes
|
5
|
+
|
6
|
+
def initialize(options = {})
|
7
|
+
super options
|
8
|
+
@attributes = options[:attributes]
|
9
|
+
raise(DefaceError, "No attributes option specified") unless @attributes
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute(target_element)
|
13
|
+
target_element = target_element.first
|
14
|
+
attributes.each do |name, value|
|
15
|
+
execute_for_attribute(target_element, normalize_attribute_name(name), value)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
|
21
|
+
def normalize_attribute_name(name)
|
22
|
+
name = name.to_s.gsub(/"|'/, '')
|
23
|
+
|
24
|
+
if /\Adata-erb-/ =~ name
|
25
|
+
name.gsub!(/\Adata-erb-/, '')
|
26
|
+
end
|
27
|
+
|
28
|
+
name
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|