Module: Deface::Applicator::ClassMethods

Included in:
Override
Defined in:
lib/deface/applicator.rb

Instance Method Summary collapse

Instance Method Details

#apply(source, details, log = true, haml = false) ⇒ Object

applies all applicable overrides to given source



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/deface/applicator.rb', line 6

def apply(source, details, log=true, haml=false)
  overrides = find(details)

  if log && overrides.size > 0
    Rails.logger.info "\e[1;32mDeface:\e[0m #{overrides.size} overrides found for '#{details[:virtual_path]}'"
  end

  unless overrides.empty?
    if haml
      #convert haml to erb before parsing before
      source = Deface::HamlConverter.new(source).result
    end

    doc = Deface::Parser.convert(source)

    overrides.each do |override|
      if override.disabled?
        Rails.logger.info("\e[1;32mDeface:\e[0m '#{override.name}' is disabled") if log
        next
      end

      override.parsed_document = doc

      if override.end_selector.blank?
        # single css selector

        matches = doc.css(override.selector)

        if log
          Rails.logger.send(matches.size == 0 ? :error : :info, "\e[1;32mDeface:\e[0m '#{override.name}' matched #{matches.size} times with '#{override.selector}'")
        end

        matches.each do |match|
          override.validate_original(match)

          case override.action
            when :remove
              match.replace ""
            when :replace
              match.replace override.source_element
            when :replace_contents
              match.children.remove
              match.add_child(override.source_element)
            when :surround, :surround_contents

              new_source = override.source_element.clone(1)

              if original = new_source.css("code:contains('render_original')").first
                if override.action == :surround
                  original.replace match.clone(1)
                  match.replace new_source
                elsif override.action == :surround_contents
                  original.replace match.children
                  match.children.remove
                  match.add_child new_source
                end
              else
                #maybe we should log that the original wasn't found.
              end

            when :insert_before
              match.before override.source_element
            when :insert_after
              match.after override.source_element
            when :insert_top
              if match.children.size == 0
                match.children = override.source_element
              else
                match.children.before(override.source_element)
              end
            when :insert_bottom
              if match.children.size == 0
                match.children = override.source_element
              else
                match.children.after(override.source_element)
              end
            when :set_attributes
              override.attributes.each do |name, value|
                name = normalize_attribute_name(name)

                match.remove_attribute(name)
                match.remove_attribute("data-erb-#{name}")

                if match.attributes.key? name
                  match.set_attribute name, value.to_s
                else
                  match.set_attribute "data-erb-#{name}", value.to_s
                end
              end
            when :add_to_attributes
              override.attributes.each do |name, value|
                name = normalize_attribute_name(name)

                if match.attributes.key? name
                  match.set_attribute name, match.attributes[name].value << " #{value}"
                elsif match.attributes.key? "data-erb-#{name}"
                  match.set_attribute "data-erb-#{name}", match.attributes["data-erb-#{name}"].value << " #{value}"
                else
                  match.set_attribute "data-erb-#{name}", value.to_s
                end

              end
            when :remove_from_attributes
              override.attributes.each do |name, value|
                name = normalize_attribute_name(name)

                if match.attributes.key? name
                  match.set_attribute name, match.attributes[name].value.gsub(value.to_s, '').strip
                elsif match.attributes.key? "data-erb-#{name}"
                  match.set_attribute "data-erb-#{name}", match.attributes["data-erb-#{name}"].value.gsub(value.to_s, '').strip
                end
              end

          end

        end
      else
        unless [:remove, :replace, :replace_contents, :surround, :surround_contents].include? override.action
          raise Deface::NotSupportedError, ":#{override.action} action does not support :closing_selector"
        end
        # targeting range of elements as end_selector is present
        starting, ending = select_endpoints(doc, override.selector, override.end_selector)

        if starting && ending
          if log
            Rails.logger.info("\e[1;32mDeface:\e[0m '#{override.name}' matched starting with '#{override.selector}' and ending with '#{override.end_selector}'")
          end

          elements = select_range(starting, ending)

          case override.action
            when :remove
              elements.map &:remove
            when :replace
              starting.before(override.source_element)
              elements.map &:remove
            when :replace_contents
              elements[1..-2].map &:remove
              starting.after(override.source_element)
            when :surround, :surround_contents

              new_source = override.source_element.clone(1)

              if original = new_source.css("code:contains('render_original')").first

                if override.action == :surround
                  start = elements[0].clone(1)
                  original.replace start

                  elements[1..-1].each do |element|
                    element = element.clone(1)
                    start.after element
                    start = element
                  end

                  starting.before(new_source)
                  elements.map &:remove


                elsif override.action == :surround_contents

                  start = elements[1].clone(1)
                  original.replace start

                  elements[2...-1].each do |element|
                    element = element.clone(1)
                    start.after element
                    start = element
                  end

                  starting.after(new_source)
                  elements[1...-1].map &:remove
                end
              else
                #maybe we should log that the original wasn't found.
              end
          end
        else
          if starting.nil?
            Rails.logger.info("\e[1;32mDeface:\e[0m '#{override.name}' failed to match with starting selector '#{override.selector}'")
          else
            Rails.logger.info("\e[1;32mDeface:\e[0m '#{override.name}' failed to match with end selector '#{override.end_selector}'")
          end

        end
      end

    end

    #prevents any caching by rails in development mode
    details[:updated_at] = Time.now

    source = doc.to_s

    Deface::Parser.undo_erb_markup!(source)
  end

  source
end

#select_endpoints(doc, start, finish) ⇒ Object



207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/deface/applicator.rb', line 207

def select_endpoints(doc, start, finish)
  # targeting range of elements as end_selector is present
  #
  finish = "#{start} ~ #{finish}"
  starting    = doc.css(start).first

  ending = if starting && starting.parent
    starting.parent.css(finish).first
  else
    doc.css(finish).first
  end

  return starting, ending

end

#select_range(first, last) ⇒ Object

finds all elements upto closing sibling in nokgiri document



225
226
227
# File 'lib/deface/applicator.rb', line 225

def select_range(first, last)
  first == last ? [first] : [first, *select_range(first.next, last)]
end