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 CHANGED
@@ -1,3 +1,3 @@
1
1
  source :rubygems
2
-
2
+
3
3
  gemspec
@@ -59,7 +59,11 @@ Action
59
59
 
60
60
  * <tt>:insert_bottom</tt> - Inserts inside all elements that match the supplied selector, as the last child.
61
61
 
62
- * <tt>:set_attributes</tt> - Sets (or adds) attributes to all elements that match the supplied selector, expects :attributes option to be passed.
62
+ * <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.
63
+
64
+ * <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.
65
+
66
+ * <tt>:remove_from_attributes</tt> - Removes value from attributes on all elements that match the supplied selector. Expects :attributes option to be passed.
63
67
 
64
68
  Source
65
69
  ------
@@ -85,7 +89,7 @@ Optional
85
89
  * <tt>:sequence => {:before => "*override_name*"}</tt> - where "*override_name*" is the name of an override defined for the
86
90
  same virutal_path, the current override will be appplied before
87
91
  the named override passed.
88
- * <tt>:sequence => {:after => "*override_name*")</tt> - the current override will be applied after the named override passed.
92
+ * <tt>:sequence => {:after => "*override_name*"}</tt> - the current override will be applied after the named override passed.
89
93
 
90
94
  * <tt>:attributes</tt> - A hash containing all the attributes to be set on the matched elements, eg: :attributes => {:class => "green", :title => "some string"}
91
95
 
@@ -1,10 +1,10 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "deface"
3
- s.version = "0.7.2"
3
+ s.version = "0.8.0"
4
4
 
5
5
  s.authors = ["Brian D Quinn"]
6
- s.description = "Deface is a library that allows you to customize ERB views in a Rails application without editing the underlying view."
7
- s.email = "brian@railsdog.com"
6
+ s.description = "Deface is a library that allows you to customize ERB & HAML views in a Rails application without editing the underlying view."
7
+ s.email = "brian@spreecommerce.com"
8
8
  s.extra_rdoc_files = [
9
9
  "README.markdown"
10
10
  ]
@@ -13,10 +13,11 @@ Gem::Specification.new do |s|
13
13
  s.homepage = "http://github.com/railsdog/deface"
14
14
  s.rdoc_options = ["--charset=UTF-8"]
15
15
  s.require_paths = ["lib"]
16
- s.summary = "Deface is a library that allows you to customize ERB views in Rails"
16
+ s.summary = "Deface is a library that allows you to customize ERB & HAML views in Rails"
17
17
 
18
18
  s.add_dependency('nokogiri', '~> 1.5.0')
19
19
  s.add_dependency('rails', '>= 3.0.9')
20
20
 
21
- s.add_development_dependency('rspec', '>= 2.7.0')
21
+ s.add_development_dependency('rspec', '>= 2.8.0')
22
+ s.add_development_dependency('haml', '>= 3.1.4')
22
23
  end
@@ -1,7 +1,9 @@
1
1
  require "action_view"
2
2
  require "action_controller"
3
- require "deface/action_view_extensions"
4
3
  require "deface/template_helper"
4
+ require "deface/original_validator"
5
+ require "deface/applicator"
6
+ require "deface/search"
5
7
  require "deface/override"
6
8
  require "deface/parser"
7
9
  require "deface/environment"
@@ -1,12 +1,44 @@
1
1
  ActionView::Template.class_eval do
2
- alias_method :rails_initialize, :initialize
2
+ alias_method :initialize_without_deface, :initialize
3
3
 
4
4
  def initialize(source, identifier, handler, details)
5
5
  if Rails.application.config.deface.enabled
6
- source = Deface::Override.apply(source, details)
6
+ haml = handler.to_s == "Haml::Plugin"
7
+
8
+ processed_source = Deface::Override.apply(source, details, true, haml )
9
+
10
+ if haml && processed_source != source
11
+ handler = ActionView::Template::Handlers::ERB
12
+ end
13
+ else
14
+ processed_source = source
15
+ end
16
+
17
+ initialize_without_deface(processed_source, identifier, handler, details)
18
+ end
19
+
20
+ alias_method :render_without_deface, :render
21
+
22
+ # refresh view to get source again if
23
+ # view needs to be recompiled
24
+ #
25
+ def render(view, locals, buffer=nil, &block)
26
+ if @compiled && !view.respond_to?(method_name)
27
+ @compiled = false
28
+ @source = refresh(view).source
7
29
  end
30
+ render_without_deface(view, locals, buffer, &block)
31
+ end
32
+
33
+
34
+ alias_method :method_name_without_deface, :method_name
8
35
 
9
- rails_initialize(source, identifier, handler, details)
36
+ # inject deface hash into compiled view method name
37
+ # used to determine if recompilation is needed
38
+ #
39
+ def method_name
40
+ deface_hash = Deface::Override.digest(:virtual_path => @virtual_path)
41
+ "_#{deface_hash}_#{method_name_without_deface}"
10
42
  end
11
43
  end
12
44
 
@@ -0,0 +1,192 @@
1
+ module Deface
2
+ module Applicator
3
+ module ClassMethods
4
+ # applies all applicable overrides to given source
5
+ #
6
+ def apply(source, details, log=true, haml=false)
7
+ overrides = find(details)
8
+
9
+ if log && overrides.size > 0
10
+ Rails.logger.info "\e[1;32mDeface:\e[0m #{overrides.size} overrides found for '#{details[:virtual_path]}'"
11
+ end
12
+
13
+ unless overrides.empty?
14
+ if haml
15
+ #convert haml to erb before parsing before
16
+ source = Deface::HamlConverter.new(source).result
17
+ end
18
+
19
+ doc = Deface::Parser.convert(source)
20
+
21
+ overrides.each do |override|
22
+ if override.disabled?
23
+ Rails.logger.info("\e[1;32mDeface:\e[0m '#{override.name}' is disabled") if log
24
+ next
25
+ end
26
+
27
+ if override.end_selector.blank?
28
+ # single css selector
29
+
30
+ matches = doc.css(override.selector)
31
+
32
+ if log
33
+ Rails.logger.send(matches.size == 0 ? :error : :info, "\e[1;32mDeface:\e[0m '#{override.name}' matched #{matches.size} times with '#{override.selector}'")
34
+ end
35
+
36
+ matches.each do |match|
37
+ override.validate_original(match)
38
+
39
+ case override.action
40
+ when :remove
41
+ match.replace ""
42
+ when :replace
43
+ match.replace override.source_element
44
+ when :replace_contents
45
+ match.children.remove
46
+ match.add_child(override.source_element)
47
+ when :surround, :surround_contents
48
+
49
+ new_source = override.source_element.clone(1)
50
+
51
+ if original = new_source.css("code:contains('render_original')").first
52
+ if override.action == :surround
53
+ original.replace match.clone(1)
54
+ match.replace new_source
55
+ elsif override.action == :surround_contents
56
+ original.replace match.children
57
+ match.children.remove
58
+ match.add_child new_source
59
+ end
60
+ else
61
+ #maybe we should log that the original wasn't found.
62
+ end
63
+
64
+ when :insert_before
65
+ match.before override.source_element
66
+ when :insert_after
67
+ match.after override.source_element
68
+ when :insert_top
69
+ if match.children.size == 0
70
+ match.children = override.source_element
71
+ else
72
+ match.children.before(override.source_element)
73
+ end
74
+ when :insert_bottom
75
+ if match.children.size == 0
76
+ match.children = override.source_element
77
+ else
78
+ match.children.after(override.source_element)
79
+ end
80
+ when :set_attributes
81
+ override.attributes.each do |name, value|
82
+ name = normalize_attribute_name(name)
83
+
84
+ match.remove_attribute(name)
85
+ match.remove_attribute("data-erb-#{name}")
86
+
87
+ if match.attributes.key? name
88
+ match.set_attribute name, value.to_s
89
+ else
90
+ match.set_attribute "data-erb-#{name}", value.to_s
91
+ end
92
+ end
93
+ when :add_to_attributes
94
+ override.attributes.each do |name, value|
95
+ name = normalize_attribute_name(name)
96
+
97
+ if match.attributes.key? name
98
+ match.set_attribute name, match.attributes[name].value << " #{value}"
99
+ elsif match.attributes.key? "data-erb-#{name}"
100
+ match.set_attribute "data-erb-#{name}", match.attributes["data-erb-#{name}"].value << " #{value}"
101
+ else
102
+ match.set_attribute "data-erb-#{name}", value.to_s
103
+ end
104
+
105
+ end
106
+ when :remove_from_attributes
107
+ override.attributes.each do |name, value|
108
+ name = normalize_attribute_name(name)
109
+
110
+ if match.attributes.key? name
111
+ match.set_attribute name, match.attributes[name].value.gsub(value.to_s, '').strip
112
+ elsif match.attributes.key? "data-erb-#{name}"
113
+ match.set_attribute "data-erb-#{name}", match.attributes["data-erb-#{name}"].value.gsub(value.to_s, '').strip
114
+ end
115
+ end
116
+
117
+ end
118
+
119
+ end
120
+ else
121
+ # targeting range of elements as end_selector is present
122
+ starting = doc.css(override.selector).first
123
+
124
+ if starting && starting.parent
125
+ ending = starting.parent.css(override.end_selector).first
126
+ else
127
+ ending = doc.css(override.end_selector).first
128
+ end
129
+
130
+ if starting && ending
131
+ if log
132
+ Rails.logger.info("\e[1;32mDeface:\e[0m '#{override.name}' matched starting with '#{override.selector}' and ending with '#{override.end_selector}'")
133
+ end
134
+
135
+ elements = select_range(starting, ending)
136
+
137
+ case override.action
138
+ when :remove
139
+ elements.map &:remove
140
+ when :replace
141
+ starting.before(override.source_element)
142
+ elements.map &:remove
143
+ when :replace_contents
144
+ elements[1..-2].map &:remove
145
+ starting.after(override.source_element)
146
+ end
147
+ else
148
+ if starting.nil?
149
+ Rails.logger.info("\e[1;32mDeface:\e[0m '#{override.name}' failed to match with starting selector '#{override.selector}'")
150
+ else
151
+ Rails.logger.info("\e[1;32mDeface:\e[0m '#{override.name}' failed to match with end selector '#{override.end_selector}'")
152
+ end
153
+
154
+ end
155
+ end
156
+
157
+ end
158
+
159
+ #prevents any caching by rails in development mode
160
+ details[:updated_at] = Time.now
161
+
162
+ source = doc.to_s
163
+
164
+ Deface::Parser.undo_erb_markup!(source)
165
+ end
166
+
167
+ source
168
+ end
169
+
170
+ private
171
+ # finds all elements upto closing sibling in nokgiri document
172
+ #
173
+ def select_range(first, last)
174
+ first == last ? [first] : [first, *select_range(first.next, last)]
175
+ end
176
+
177
+ def normalize_attribute_name(name)
178
+ name = name.to_s.gsub /"|'/, ''
179
+
180
+ if /\Adata-erb-/ =~ name
181
+ name.gsub! /\Adata-erb-/, ''
182
+ end
183
+
184
+ name
185
+ end
186
+
187
+ def set_attributes(match, name, value)
188
+
189
+ end
190
+ end
191
+ end
192
+ end
@@ -1,10 +1,11 @@
1
1
  module Deface
2
2
 
3
3
  class Environment
4
- attr_accessor :overrides, :enabled
4
+ attr_accessor :overrides, :enabled, :haml_support
5
5
  def initialize
6
- @overrides = Overrides.new
7
- @enabled = true
6
+ @overrides = Overrides.new
7
+ @enabled = true
8
+ @haml_support = false
8
9
  end
9
10
  end
10
11
 
@@ -20,9 +21,10 @@ module Deface
20
21
  end
21
22
 
22
23
  def load_all(app)
24
+ #clear overrides before reloading them
25
+ app.config.deface.overrides.all.clear
26
+
23
27
  # check application for specified overrides paths
24
- #
25
- #
26
28
  override_paths = app.paths["app/overrides"]
27
29
  enumerate_and_load(override_paths, app.root)
28
30
 
@@ -43,7 +45,7 @@ module Deface
43
45
  Deface::Override._early.clear
44
46
  end
45
47
 
46
- private
48
+ private
47
49
  def enumerate_and_load(paths, root)
48
50
  paths ||= ["app/overrides"]
49
51
 
@@ -56,4 +58,3 @@ module Deface
56
58
  end
57
59
  end
58
60
  end
59
-
@@ -0,0 +1,65 @@
1
+ module Deface
2
+ class HamlConverter < Haml::Engine
3
+ def result
4
+ Deface::Parser.undo_erb_markup! String.new(render)
5
+ end
6
+
7
+ def push_script(text, preserve_script, in_tag = false, preserve_tag = false,
8
+ escape_html = false, nuke_inner_whitespace = false)
9
+ push_text "<%= #{text.strip} %>"
10
+
11
+ if block_given?
12
+ yield
13
+ push_silent('end')
14
+ end
15
+ end
16
+
17
+ def push_silent(text, can_suppress = false)
18
+ push_text "<% #{text.strip} %>"
19
+ end
20
+
21
+ def parse_old_attributes(line)
22
+ attributes_hash, rest, last_line = super(line)
23
+
24
+ attributes_hash = deface_attributes(attributes_hash)
25
+
26
+ return attributes_hash, rest, last_line
27
+ end
28
+
29
+
30
+ def parse_new_attributes(line)
31
+ attributes, rest, last_line = super(line)
32
+
33
+ attributes[1] = deface_attributes(attributes[1])
34
+
35
+ return attributes, rest, last_line
36
+ end
37
+
38
+ private
39
+
40
+ # coverts { attributes into deface compatibily attributes
41
+ def deface_attributes(attrs)
42
+ return if attrs.nil?
43
+ attrs.gsub! /\{|\}/, ''
44
+ attrs = attrs.split(',')
45
+
46
+ if attrs.join.include? "=>"
47
+ attrs.map!{|a| a.split("=>").map(&:strip) }
48
+ else
49
+ attrs.map!{|a| a.split(": ").map(&:strip) }
50
+ end
51
+
52
+ attrs.map! do |a|
53
+ if a[1][0] != ?' && a[1][0] != ?"
54
+ a[0] = %Q{"data-erb-#{a[0].gsub(/:|'|"/,'')}"}
55
+ a[1] = %Q{"<%= #{a[1]} %>"}
56
+ end
57
+
58
+ a
59
+ end
60
+
61
+ attrs.map{ |a| a.join " => " }.join(', ')
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,34 @@
1
+ module Deface
2
+ module OriginalValidator
3
+
4
+ def original_source
5
+ return nil unless @args[:original].present?
6
+
7
+ Deface::Parser.convert(@args[:original].clone)
8
+ end
9
+
10
+ # logs if original source has changed
11
+ def validate_original(match)
12
+ hashed_original = Digest::SHA1.hexdigest(match.to_s.gsub(/\s/, ''))
13
+
14
+ if @args[:original].present?
15
+ valid = @args[:original] == hashed_original
16
+
17
+ unless valid
18
+ valid = self.original_source.to_s.gsub(/\s/, '') == match.to_s.gsub(/\s/, '')
19
+ end
20
+
21
+ if !valid && defined?(Rails.logger)
22
+ Rails.logger.error "\e[1;32mDeface: [ERROR]\e[0m The original source for '#{self.name}' has changed, this override should be reviewed to ensure it's still valid."
23
+ end
24
+
25
+ return valid
26
+ else
27
+ Rails.logger.info "\e[1;32mDeface: [WARNING]\e[0m No :original defined for '#{self.name}', you should change its definition to include:\n :original => '#{hashed_original}' "
28
+
29
+ return nil
30
+ end
31
+ end
32
+
33
+ end
34
+ end