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.
Files changed (63) hide show
  1. data/.gitignore +1 -0
  2. data/.rspec +3 -0
  3. data/.travis.yml +1 -0
  4. data/README.markdown +139 -59
  5. data/deface.gemspec +4 -2
  6. data/gemfiles/haml3_2.gemfile +5 -0
  7. data/lib/deface.rb +28 -1
  8. data/lib/deface/action_view_extensions.rb +7 -1
  9. data/lib/deface/actions/action.rb +19 -0
  10. data/lib/deface/actions/add_to_attributes.rb +15 -0
  11. data/lib/deface/actions/attribute_action.rb +33 -0
  12. data/lib/deface/actions/element_action.rb +13 -0
  13. data/lib/deface/actions/insert_after.rb +9 -0
  14. data/lib/deface/actions/insert_before.rb +9 -0
  15. data/lib/deface/actions/insert_bottom.rb +14 -0
  16. data/lib/deface/actions/insert_top.rb +14 -0
  17. data/lib/deface/actions/remove.rb +13 -0
  18. data/lib/deface/actions/remove_from_attributes.rb +13 -0
  19. data/lib/deface/actions/replace.rb +14 -0
  20. data/lib/deface/actions/replace_contents.rb +19 -0
  21. data/lib/deface/actions/set_attributes.rb +12 -0
  22. data/lib/deface/actions/surround.rb +24 -0
  23. data/lib/deface/actions/surround_action.rb +15 -0
  24. data/lib/deface/actions/surround_contents.rb +33 -0
  25. data/lib/deface/applicator.rb +35 -191
  26. data/lib/deface/dsl/context.rb +7 -3
  27. data/lib/deface/dsl/loader.rb +5 -2
  28. data/lib/deface/environment.rb +31 -1
  29. data/lib/deface/haml_converter.rb +33 -5
  30. data/lib/deface/matchers/element.rb +13 -0
  31. data/lib/deface/matchers/range.rb +52 -0
  32. data/lib/deface/override.rb +28 -57
  33. data/lib/deface/sources/copy.rb +15 -0
  34. data/lib/deface/sources/cut.rb +25 -0
  35. data/lib/deface/sources/erb.rb +9 -0
  36. data/lib/deface/sources/haml.rb +14 -0
  37. data/lib/deface/sources/partial.rb +13 -0
  38. data/lib/deface/sources/source.rb +11 -0
  39. data/lib/deface/sources/template.rb +13 -0
  40. data/lib/deface/sources/text.rb +9 -0
  41. data/lib/deface/utils/failure_finder.rb +41 -0
  42. data/spec/deface/action_view_template_spec.rb +28 -2
  43. data/spec/deface/actions/add_to_attributes_spec.rb +62 -0
  44. data/spec/deface/actions/insert_after_spec.rb +19 -0
  45. data/spec/deface/actions/insert_before_spec.rb +19 -0
  46. data/spec/deface/actions/insert_bottom_spec.rb +28 -0
  47. data/spec/deface/actions/insert_top_spec.rb +28 -0
  48. data/spec/deface/actions/remove_from_attributes_spec.rb +81 -0
  49. data/spec/deface/actions/remove_spec.rb +30 -0
  50. data/spec/deface/actions/replace_contents_spec.rb +29 -0
  51. data/spec/deface/actions/replace_spec.rb +52 -0
  52. data/spec/deface/actions/set_attributes_spec.rb +62 -0
  53. data/spec/deface/actions/surround_contents_spec.rb +57 -0
  54. data/spec/deface/actions/surround_spec.rb +57 -0
  55. data/spec/deface/applicator_spec.rb +0 -354
  56. data/spec/deface/dsl/context_spec.rb +14 -7
  57. data/spec/deface/dsl/loader_spec.rb +12 -4
  58. data/spec/deface/override_spec.rb +26 -3
  59. data/spec/deface/template_helper_spec.rb +11 -0
  60. data/spec/deface/utils/failure_finder_spec.rb +76 -0
  61. data/spec/spec_helper.rb +21 -2
  62. data/tasks/utils.rake +24 -0
  63. metadata +91 -14
data/.gitignore CHANGED
@@ -2,3 +2,4 @@
2
2
  .DS_Store
3
3
  pkg
4
4
  Gemfile.lock
5
+ coverage/
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ -c
2
+ -f d
3
+ --order random
@@ -6,3 +6,4 @@ rvm:
6
6
  gemfile:
7
7
  - gemfiles/rails3_1.gemfile
8
8
  - gemfiles/rails3_2.gemfile
9
+ - gemfiles/haml3_2.gemfile
@@ -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
- Deface::Override.new(:virtual_path => "posts/_form",
108
- :name => "example-1",
109
- :replace => "h1",
110
- :text => "<h1>New Post</h1>")
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
- Deface::Override.new(:virtual_path => "posts/_form",
115
- :name => "example-1",
116
- :replace => "h1") do
117
- "<h1>New Post</h1>"
118
- end
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
- Deface::Override.new(:virtual_path => "posts/index",
123
- :name => "example-2",
124
- :insert_before => "p.comment",
125
- :text => "<%= link_to "List Comments", comments_url(post) %>")
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
- Deface::Override.new(:virtual_path => "posts/show",
130
- :name => "example-3",
131
- :insert_after => "div#comment_21",
132
- :partial => "shared/comment")
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
- Deface::Override.new(:virtual_path => "posts/new",
137
- :name => "example-4",
138
- :remove => "code[erb-loud]:contains('helper_method')",
139
- :original => "<%= helper_method %>")
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
- Deface::Override.new(:virtual_path => "posts/new",
144
- :name => "example-5",
145
- :surround => "div#products",
146
- :text => "<% if @product.present? %><%= render_original %><% end %>")
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
- Deface::Override.new(:virtual_path => 'posts/index',
151
- :name => 'add_attrs_to_a_link',
152
- :set_attributes => 'a#link',
153
- :attributes => {:class => 'pretty', :title => 'This is a link'})
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
- Deface::Override.new(:virtual_path => 'admin/products/index',
158
- :name => "remove_if_statement",
159
- :remove => "code[erb-silent]:contains('if @product.sold?')",
160
- :closing_selector => "code[erb-silent]:contains('end')"
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 pick up.
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
- <!-- insert_after 'h1' -->
191
- <h2>These robots are awesome.</h2>
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
- insert_after 'h1'
201
- %h2 These robots are awesome.
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
- <!-- replace_contents 'h1'
209
- closing_selector 'div#intro'
210
- disabled -->
211
- <p>This is a complicated example</p>
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
- <!-- enabled -->
255
+ ```html
256
+ <!-- enabled -->
257
+ ```
218
258
 
219
259
  or
220
260
 
221
- <!-- disabled -->
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
- add_to_attributes 'a#search'
234
- attributes :alt => 'Click here to search'
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
- <%= some ruby code %>
345
+ ```erb
346
+ <%= some ruby code %>
347
+ ```
284
348
 
285
- becomes
349
+ becomes:
286
350
 
287
- <code erb-loud> some ruby code </code>
351
+ ```html
352
+ <code erb-loud> some ruby code </code>
353
+ ```
288
354
 
289
355
  and
290
356
 
291
- <% other ruby code %>
357
+ ```erb
358
+ <% other ruby code %>
359
+ ```
292
360
 
293
- becomes
361
+ becomes:
294
362
 
295
- <code erb-silent> other ruby code </code>
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
- <p id="<%= dom_id @product %>" <%= "style='display:block';" %>>
369
+ ```erb
370
+ <p id="<%= dom_id @product %>" <%= "style='display:block';" %>>
371
+ ```
300
372
 
301
- becomes
373
+ becomes:
302
374
 
303
- <p data-erb-id="&lt;%= dom_id @product %&gt;" data-erb-0="&lt;%= &quot;style='display:block';&quot; %&gt;">
375
+ ```html
376
+ <p data-erb-id="&lt;%= dom_id @product %&gt;" data-erb-0="&lt;%= &quot;style='display:block';&quot; %&gt;">
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
- &lt;div <%= ">" %>
390
+ ```html
391
+ &lt;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.
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "deface"
3
- s.version = "0.9.1"
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.8.0')
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
@@ -0,0 +1,5 @@
1
+ source :rubygems
2
+
3
+ gem 'haml', '~> 3.2.0.beta.3'
4
+
5
+ gemspec :path=>"../"
@@ -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,19 @@
1
+ module Deface
2
+ module Actions
3
+ class Action
4
+ def initialize(options = {})
5
+ end
6
+
7
+ class << self
8
+ def to_sym
9
+ self.to_s.demodulize.underscore.to_sym
10
+ end
11
+ end
12
+
13
+ def range_compatible?
14
+ false
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -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