deface 0.7.2 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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