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
@@ -0,0 +1,13 @@
1
+ module Deface
2
+ module Actions
3
+ class ElementAction < Action
4
+ attr_reader :source_element
5
+
6
+ def initialize(options = {})
7
+ super(options)
8
+ @source_element = options[:source_element]
9
+ raise(DefaceError, "No source_element option specified") unless @source_element
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ module Deface
2
+ module Actions
3
+ class InsertAfter < ElementAction
4
+ def execute(target_element)
5
+ target_element.first.after(source_element)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Deface
2
+ module Actions
3
+ class InsertBefore < ElementAction
4
+ def execute(target_element)
5
+ target_element.first.before(source_element)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,14 @@
1
+ module Deface
2
+ module Actions
3
+ class InsertBottom < ElementAction
4
+ def execute(target_element)
5
+ target_element = target_element.first
6
+ if target_element.children.size == 0
7
+ target_element.children = source_element
8
+ else
9
+ target_element.children.after(source_element)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module Deface
2
+ module Actions
3
+ class InsertTop < ElementAction
4
+ def execute(target_element)
5
+ target_element = target_element.first
6
+ if target_element.children.size == 0
7
+ target_element.children = source_element
8
+ else
9
+ target_element.children.before(source_element)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ module Deface
2
+ module Actions
3
+ class Remove < Action
4
+ def execute(target_range)
5
+ target_range.map(&:remove)
6
+ end
7
+
8
+ def range_compatible?
9
+ true
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module Deface
2
+ module Actions
3
+ class RemoveFromAttributes < 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.gsub(value.to_s, '').strip)
7
+ elsif target_element.attributes.key?("data-erb-#{name}")
8
+ target_element.set_attribute("data-erb-#{name}", target_element.attributes["data-erb-#{name}"].value.gsub(value.to_s, '').strip)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ module Deface
2
+ module Actions
3
+ class Replace < ElementAction
4
+ def execute(target_range)
5
+ target_range.first.before(source_element)
6
+ target_range.map(&:remove)
7
+ end
8
+
9
+ def range_compatible?
10
+ true
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,19 @@
1
+ module Deface
2
+ module Actions
3
+ class ReplaceContents < ElementAction
4
+ def execute(target_range)
5
+ if target_range.length == 1
6
+ target_range.first.children.remove
7
+ target_range.first.add_child(source_element)
8
+ else
9
+ target_range[1..-2].map(&:remove)
10
+ target_range.first.after(source_element)
11
+ end
12
+ end
13
+
14
+ def range_compatible?
15
+ true
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,12 @@
1
+ module Deface
2
+ module Actions
3
+ class SetAttributes < AttributeAction
4
+ def execute_for_attribute(target_element, name, value)
5
+ target_element.remove_attribute(name)
6
+ target_element.remove_attribute("data-erb-#{name}")
7
+
8
+ target_element.set_attribute("data-erb-#{name}", value.to_s)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,24 @@
1
+ module Deface
2
+ module Actions
3
+ class Surround < SurroundAction
4
+ def execute(target_range)
5
+ original_placeholders.each do |placeholder|
6
+ start = target_range[0].clone(1)
7
+ placeholder.replace start
8
+
9
+ target_range[1..-1].each do |element|
10
+ element = element.clone(1)
11
+ start.after element
12
+ start = element
13
+ end
14
+ end
15
+ target_range.first.before(source_element)
16
+ target_range.map(&:remove)
17
+ end
18
+
19
+ def range_compatible?
20
+ true
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,15 @@
1
+ module Deface
2
+ module Actions
3
+ class SurroundAction < ElementAction
4
+ def source_element
5
+ @cloned_source_element ||= super.clone(1)
6
+ end
7
+
8
+ def original_placeholders
9
+ @original_placeholders ||= source_element.css("code:contains('render_original')")
10
+ raise(DefaceError, "The surround action couldn't find <%= render_original %> in your template") unless @original_placeholders.first
11
+ @original_placeholders
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,33 @@
1
+ module Deface
2
+ module Actions
3
+ class SurroundContents < SurroundAction
4
+ def execute(target_range)
5
+ if target_range.length == 1
6
+ target_element = target_range.first
7
+ original_placeholders.each do |placeholder|
8
+ placeholder.replace target_element.clone(1).children
9
+ end
10
+ target_element.children.remove
11
+ target_element.add_child(source_element)
12
+ else
13
+ original_placeholders.each do |placeholder|
14
+ start = target_range[1].clone(1)
15
+ placeholder.replace start
16
+
17
+ target_range[2...-1].each do |element|
18
+ element = element.clone(1)
19
+ start.after(element)
20
+ start = element
21
+ end
22
+ end
23
+ target_range.first.after(source_element)
24
+ target_range[1...-1].map(&:remove)
25
+ end
26
+ end
27
+
28
+ def range_compatible?
29
+ true
30
+ end
31
+ end
32
+ end
33
+ end
@@ -25,171 +25,18 @@ module Deface
25
25
  end
26
26
 
27
27
  override.parsed_document = doc
28
+ matches = override.matcher.matches(doc, log)
28
29
 
29
- if override.end_selector.blank?
30
- # single css selector
31
-
32
- matches = doc.css(override.selector)
33
-
34
- if log
35
- Rails.logger.send(matches.size == 0 ? :error : :info, "\e[1;32mDeface:\e[0m '#{override.name}' matched #{matches.size} times with '#{override.selector}'")
36
- end
37
-
38
- matches.each do |match|
39
- override.validate_original(match)
40
-
41
- case override.action
42
- when :remove
43
- match.replace ""
44
- when :replace
45
- match.replace override.source_element
46
- when :replace_contents
47
- match.children.remove
48
- match.add_child(override.source_element)
49
- when :surround, :surround_contents
50
-
51
- new_source = override.source_element.clone(1)
52
-
53
- if original = new_source.css("code:contains('render_original')").first
54
- if override.action == :surround
55
- original.replace match.clone(1)
56
- match.replace new_source
57
- elsif override.action == :surround_contents
58
- original.replace match.children
59
- match.children.remove
60
- match.add_child new_source
61
- end
62
- else
63
- #maybe we should log that the original wasn't found.
64
- end
65
-
66
- when :insert_before
67
- match.before override.source_element
68
- when :insert_after
69
- match.after override.source_element
70
- when :insert_top
71
- if match.children.size == 0
72
- match.children = override.source_element
73
- else
74
- match.children.before(override.source_element)
75
- end
76
- when :insert_bottom
77
- if match.children.size == 0
78
- match.children = override.source_element
79
- else
80
- match.children.after(override.source_element)
81
- end
82
- when :set_attributes
83
- override.attributes.each do |name, value|
84
- name = normalize_attribute_name(name)
85
-
86
- match.remove_attribute(name)
87
- match.remove_attribute("data-erb-#{name}")
88
-
89
- if match.attributes.key? name
90
- match.set_attribute name, value.to_s
91
- else
92
- match.set_attribute "data-erb-#{name}", value.to_s
93
- end
94
- end
95
- when :add_to_attributes
96
- override.attributes.each do |name, value|
97
- name = normalize_attribute_name(name)
98
-
99
- if match.attributes.key? name
100
- match.set_attribute name, match.attributes[name].value << " #{value}"
101
- elsif match.attributes.key? "data-erb-#{name}"
102
- match.set_attribute "data-erb-#{name}", match.attributes["data-erb-#{name}"].value << " #{value}"
103
- else
104
- match.set_attribute "data-erb-#{name}", value.to_s
105
- end
106
-
107
- end
108
- when :remove_from_attributes
109
- override.attributes.each do |name, value|
110
- name = normalize_attribute_name(name)
111
-
112
- if match.attributes.key? name
113
- match.set_attribute name, match.attributes[name].value.gsub(value.to_s, '').strip
114
- elsif match.attributes.key? "data-erb-#{name}"
115
- match.set_attribute "data-erb-#{name}", match.attributes["data-erb-#{name}"].value.gsub(value.to_s, '').strip
116
- end
117
- end
118
-
119
- end
120
-
121
- end
122
- else
123
- unless [:remove, :replace, :replace_contents, :surround, :surround_contents].include? override.action
124
- raise Deface::NotSupportedError, ":#{override.action} action does not support :closing_selector"
125
- end
126
- # targeting range of elements as end_selector is present
127
- starting, ending = select_endpoints(doc, override.selector, override.end_selector)
128
-
129
- if starting && ending
130
- if log
131
- Rails.logger.info("\e[1;32mDeface:\e[0m '#{override.name}' matched starting with '#{override.selector}' and ending with '#{override.end_selector}'")
132
- end
133
-
134
- elements = select_range(starting, ending)
135
-
136
- case override.action
137
- when :remove
138
- elements.map &:remove
139
- when :replace
140
- starting.before(override.source_element)
141
- elements.map &:remove
142
- when :replace_contents
143
- elements[1..-2].map &:remove
144
- starting.after(override.source_element)
145
- when :surround, :surround_contents
146
-
147
- new_source = override.source_element.clone(1)
148
-
149
- if original = new_source.css("code:contains('render_original')").first
150
-
151
- if override.action == :surround
152
- start = elements[0].clone(1)
153
- original.replace start
154
-
155
- elements[1..-1].each do |element|
156
- element = element.clone(1)
157
- start.after element
158
- start = element
159
- end
160
-
161
- starting.before(new_source)
162
- elements.map &:remove
163
-
164
-
165
- elsif override.action == :surround_contents
166
-
167
- start = elements[1].clone(1)
168
- original.replace start
169
-
170
- elements[2...-1].each do |element|
171
- element = element.clone(1)
172
- start.after element
173
- start = element
174
- end
175
-
176
- starting.after(new_source)
177
- elements[1...-1].map &:remove
178
- end
179
- else
180
- #maybe we should log that the original wasn't found.
181
- end
182
- end
183
- else
184
- if starting.nil?
185
- Rails.logger.info("\e[1;32mDeface:\e[0m '#{override.name}' failed to match with starting selector '#{override.selector}'")
186
- else
187
- Rails.logger.info("\e[1;32mDeface:\e[0m '#{override.name}' failed to match with end selector '#{override.end_selector}'")
188
- end
189
-
190
- end
30
+ if log
31
+ Rails.logger.send(matches.size == 0 ? :error : :info, "\e[1;32mDeface:\e[0m '#{override.name}' matched #{matches.size} times with '#{override.selector}'")
191
32
  end
192
33
 
34
+ if matches.empty?
35
+ override.failure = "failed to match :#{override.action} selector '#{override.selector}'"
36
+ else
37
+ override.failure = nil
38
+ matches.each {|match| override.execute_action match }
39
+ end
193
40
  end
194
41
 
195
42
  #prevents any caching by rails in development mode
@@ -202,41 +49,38 @@ module Deface
202
49
 
203
50
  source
204
51
  end
52
+ end
205
53
 
54
+ def execute_action(target_element)
55
+ validate_original(target_element)
56
+ create_action_command.execute(target_element)
57
+ end
206
58
 
207
- def select_endpoints(doc, start, finish)
208
- # targeting range of elements as end_selector is present
209
- #
210
- finish = "#{start} ~ #{finish}"
211
- starting = doc.css(start).first
212
-
213
- ending = if starting && starting.parent
214
- starting.parent.css(finish).first
215
- else
216
- doc.css(finish).first
217
- end
218
-
219
- return starting, ending
220
-
221
- end
222
-
223
- # finds all elements upto closing sibling in nokgiri document
224
- #
225
- def select_range(first, last)
226
- first == last ? [first] : [first, *select_range(first.next, last)]
227
- end
228
-
229
- private
59
+ def execute_action_on_range(target_range)
60
+ create_action_command.execute_on_range(target_range)
61
+ end
230
62
 
231
- def normalize_attribute_name(name)
232
- name = name.to_s.gsub /"|'/, ''
63
+ def create_action_command
64
+ commands = Rails.application.config.deface.actions
65
+ command = commands.find { |command| command.to_sym == action }
66
+ raise(DefaceError, "Action #{action} not found") unless command
67
+ command.new(:source_element => safe_source_element, :attributes => attributes)
68
+ end
233
69
 
234
- if /\Adata-erb-/ =~ name
235
- name.gsub! /\Adata-erb-/, ''
236
- end
70
+ def compatible_with_end_selector?
71
+ create_action_command.range_compatible?
72
+ end
237
73
 
238
- name
74
+ def matcher
75
+ if end_selector.blank?
76
+ Deface::Matchers::Element.new(selector) # single css selector
77
+ else
78
+ unless compatible_with_end_selector?
79
+ raise Deface::NotSupportedError, ":#{action} action does not support :closing_selector"
239
80
  end
81
+ # targeting range of elements as end_selector is present
82
+ Deface::Matchers::Range.new(name, selector, end_selector)
83
+ end
240
84
  end
241
85
  end
242
86
  end