synvert-core 0.3.0 → 0.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8ee7de00fd1f401d3d4ea506c941871da5714229
4
- data.tar.gz: 406b2f2ecdfdb8b837ad0e8839c5e37b1c9d821f
3
+ metadata.gz: e006c23fc6ca2d1398bfaf32db73c4ced664a94e
4
+ data.tar.gz: d7dc0a2a30fb4e544f4bd25307b4177ed6914f38
5
5
  SHA512:
6
- metadata.gz: d1db8ef41e1b179c38df52c22091418183e40b879921b462bc47d9f9821a47cb4d4823983bb54024624717cbcf39bfa5b4a86575c4cd69a6ae9b179c7eb1bab6
7
- data.tar.gz: f394394861d482865963fccd5024a340ddabf9e43077a0467a79ef9de77285348905e09d70f5e334d1e1ae759737dfd709bb5c49a955ff7b331eaacb13082482
6
+ metadata.gz: c05837b831a3c9aa02c1c0bd5a88adeeddafd98114a562462749d3f92a4728b0fd1e38415cd26d7816a46705c5528285a33705c480018abdb59fb9a9c82d01bd
7
+ data.tar.gz: 857e3cbd4cfc5ca0c77dc452e615a23006436418c600b09c6738dbee267abbb71042ddbc1a2b7a0b65f531cef0b24811ee19b8fa6edfa97ef9043a32cd77a025
data/CHANGELOG.md CHANGED
@@ -1,17 +1,23 @@
1
1
  # CHANGELOG
2
2
 
3
- ## 0.3.0
3
+ ## 0.4.0 (2014-07-26)
4
+
5
+ * Add erb support
6
+ * Add replace_erb_stmt_with_expr dsl
7
+ * Improve Parser::AST::Node#to_value
8
+
9
+ ## 0.3.0 (2014-07-12)
4
10
 
5
11
  * Rename node.source(instance) to node.to_source
6
12
  * Add has_key? and hash_value helper methods for hash node
7
13
  * Fix Instance#check_conflict_actions
8
14
 
9
- ## 0.2.0
15
+ ## 0.2.0 (2014-05-16)
10
16
 
11
17
  * Add remove_file dsl
12
18
  * Add warn dsl
13
19
  * Return empty array if no available rewriters
14
20
 
15
- ## 0.1.0
21
+ ## 0.1.0 (2014-05-04)
16
22
 
17
23
  * Abstract from synvert
data/lib/synvert/core.rb CHANGED
@@ -7,12 +7,14 @@ require 'parser'
7
7
  require 'parser/current'
8
8
  require 'ast'
9
9
  require 'active_support/inflector'
10
+ require 'erubis'
10
11
  require 'synvert/core/node_ext'
11
12
 
12
13
  module Synvert
13
14
  module Core
14
15
  autoload :Configuration, 'synvert/core/configuration'
15
16
  autoload :Rewriter, 'synvert/core/rewriter'
17
+ autoload :Engine, 'synvert/core/engine'
16
18
  autoload :RewriterNotFound, 'synvert/core/exceptions'
17
19
  autoload :GemfileLockNotFound, 'synvert/core/exceptions'
18
20
  autoload :MethodNotSupported, 'synvert/core/exceptions'
@@ -0,0 +1,8 @@
1
+ # encoding: utf-8
2
+
3
+ module Synvert::Core
4
+ # Engine defines how to encode / decode other files (like erb).
5
+ module Engine
6
+ autoload :ERB, 'synvert/core/engine/erb'
7
+ end
8
+ end
@@ -0,0 +1,139 @@
1
+ # encoding: utf-8
2
+ require 'erubis'
3
+
4
+ module Synvert::Core
5
+ module Engine
6
+ ERUBY_EXPR_SPLITTER = "; ;"
7
+ ERUBY_STMT_SPLITTER = "; ;"
8
+
9
+ class ERB
10
+ class <<self
11
+ # convert erb to ruby code.
12
+ #
13
+ # @param source [String] erb source code.
14
+ # @return [String] ruby source code.
15
+ def encode(source)
16
+ Erubis.new(source.gsub("-%>", "%>"), :escape => false, :trim => false).src
17
+ end
18
+
19
+ # convert ruby code to erb.
20
+ #
21
+ # @param source [String] ruby source code.
22
+ # @return [String] erb source code.
23
+ def decode(source)
24
+ source = decode_ruby_stmt(source)
25
+ source = decode_ruby_output(source)
26
+ source = decode_html_output(source)
27
+ source = remove_erubis_buf(source)
28
+ end
29
+
30
+ private
31
+
32
+ def decode_ruby_stmt(source)
33
+ source.gsub(/#{ERUBY_STMT_SPLITTER}(.+?)#{ERUBY_STMT_SPLITTER}/m) { "<%#{$1}%>" }
34
+ end
35
+
36
+ def decode_ruby_output(source)
37
+ source.gsub(/@output_buffer.append=\((.+?)\);#{ERUBY_EXPR_SPLITTER}/m) { "<%=#{$1}%>" }
38
+ .gsub(/@output_buffer.append= (.+?)\s+(do|\{)(\s*\|[^|]*\|)?\s*#{ERUBY_EXPR_SPLITTER}/m) { |m| "<%=#{m.sub("@output_buffer.append= ", "").sub(ERUBY_EXPR_SPLITTER, "")}%>" }
39
+ end
40
+
41
+ def decode_html_output(source)
42
+ source.gsub(/@output_buffer.safe_append='(.+?)'.freeze;/m) { reverse_escape_text($1) }
43
+ .gsub(/@output_buffer.safe_append=\((.+?)\);#{ERUBY_EXPR_SPLITTER}/m) { reverse_escape_text($1) }
44
+ .gsub(/@output_buffer.safe_append=(.+?)\s+(do|\{)(\s*\|[^|]*\|)?\s*#{ERUBY_EXPR_SPLITTER}/m) { reverse_escape_text($1) }
45
+ end
46
+
47
+ def remove_erubis_buf(source)
48
+ source.sub("@output_buffer = output_buffer || ActionView::OutputBuffer.new;", "").sub("@output_buffer.to_s", "")
49
+ end
50
+
51
+ def reverse_escape_text(source)
52
+ source.gsub("\\\\", "\\").gsub("\\'", "'")
53
+ end
54
+ end
55
+ end
56
+
57
+ # borrowed from rails
58
+ class Erubis < ::Erubis::Eruby
59
+ def add_preamble(src)
60
+ @newline_pending = 0
61
+ src << "@output_buffer = output_buffer || ActionView::OutputBuffer.new;"
62
+ end
63
+
64
+ def add_text(src, text)
65
+ return if text.empty?
66
+
67
+ if text == "\n"
68
+ @newline_pending += 1
69
+ else
70
+ src << "@output_buffer.safe_append='"
71
+ src << "\n" * @newline_pending if @newline_pending > 0
72
+ src << escape_text(text)
73
+ src << "'.freeze;"
74
+
75
+ @newline_pending = 0
76
+ end
77
+ end
78
+
79
+ # Erubis toggles <%= and <%== behavior when escaping is enabled.
80
+ # We override to always treat <%== as escaped.
81
+ def add_expr(src, code, indicator)
82
+ case indicator
83
+ when '=='
84
+ add_expr_escaped(src, code)
85
+ else
86
+ super
87
+ end
88
+ end
89
+
90
+ BLOCK_EXPR = /\s+(do|\{)(\s*\|[^|]*\|)?\s*\Z/
91
+
92
+ def add_expr_literal(src, code)
93
+ flush_newline_if_pending(src)
94
+ if code =~ BLOCK_EXPR
95
+ src << '@output_buffer.append= ' << code << ERUBY_EXPR_SPLITTER
96
+ else
97
+ src << '@output_buffer.append=(' << code << ');' << ERUBY_EXPR_SPLITTER
98
+ end
99
+ end
100
+
101
+ def add_expr_escaped(src, code)
102
+ flush_newline_if_pending(src)
103
+ if code =~ BLOCK_EXPR
104
+ src << "@output_buffer.safe_append= " << code << ERUBY_EXPR_SPLITTER
105
+ else
106
+ src << "@output_buffer.safe_append=(" << code << ");" << ERUBY_EXPR_SPLITTER
107
+ end
108
+ end
109
+
110
+ def add_stmt(src, code)
111
+ flush_newline_if_pending(src)
112
+ if code != "\n" && code != ""
113
+ index = if code =~ /\A(\s*)\r?\n/
114
+ $1.length
115
+ elsif code =~ /\A(\s+)/
116
+ $1.end_with?(' ') ? $1.length - 1 : $1.length
117
+ else
118
+ 0
119
+ end
120
+ code.insert(index, ERUBY_STMT_SPLITTER)
121
+ code.insert(-1, ERUBY_STMT_SPLITTER[0...-1])
122
+ end
123
+ super
124
+ end
125
+
126
+ def add_postamble(src)
127
+ flush_newline_if_pending(src)
128
+ src << '@output_buffer.to_s'
129
+ end
130
+
131
+ def flush_newline_if_pending(src)
132
+ if @newline_pending > 0
133
+ src << "@output_buffer.safe_append='#{"\n" * @newline_pending}'.freeze;"
134
+ @newline_pending = 0
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
@@ -176,10 +176,18 @@ class Parser::AST::Node
176
176
  # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
177
177
  def to_value
178
178
  case self.type
179
- when :str, :sym
179
+ when :int, :str, :sym
180
180
  self.children.last
181
+ when :true
182
+ true
183
+ when :false
184
+ false
181
185
  when :array
182
186
  self.children.map(&:to_value)
187
+ when :irange
188
+ (self.children.first.to_value..self.children.last.to_value)
189
+ when :begin
190
+ self.children.first.to_value
183
191
  else
184
192
  raise Synvert::Core::MethodNotSupported.new "to_value is not handled for #{self.inspect}"
185
193
  end
@@ -295,6 +303,7 @@ private
295
303
  actual.to_s =~ Regexp.new(expected.to_s, Regexp::MULTILINE)
296
304
  end
297
305
  when Array
306
+ return false unless expected.length == actual.length
298
307
  actual.zip(expected).all? { |a, e| match_value?(a, e) }
299
308
  when NilClass
300
309
  actual.nil?
@@ -21,6 +21,7 @@ module Synvert::Core
21
21
  autoload :InsertAction, 'synvert/core/rewriter/action'
22
22
  autoload :InsertAfterAction, 'synvert/core/rewriter/action'
23
23
  autoload :ReplaceWithAction, 'synvert/core/rewriter/action'
24
+ autoload :ReplaceErbStmtWithExprAction, 'synvert/core/rewriter/action'
24
25
  autoload :RemoveAction, 'synvert/core/rewriter/action'
25
26
 
26
27
  autoload :Warning, 'synvert/core/rewriter/warning'
@@ -221,4 +221,42 @@ module Synvert::Core
221
221
  ''
222
222
  end
223
223
  end
224
+
225
+ # ReplaceErbStmtWithExprAction to replace erb stmt code to expr,
226
+ # e.g. <% form_for ... %> => <%= form_for ... %>.
227
+ class Rewriter::ReplaceErbStmtWithExprAction < Rewriter::Action
228
+ def initialize(instance, code=nil)
229
+ super
230
+ end
231
+
232
+ # Begin position of code to replace.
233
+ #
234
+ # @return [Integer] begin position.
235
+ def begin_pos
236
+ node_begin_pos = @node.loc.expression.begin_pos
237
+ while @instance.current_source[node_begin_pos -= 1] == ' '
238
+ end
239
+ node_begin_pos - Engine::ERUBY_STMT_SPLITTER.length + 1
240
+ end
241
+
242
+ # End position of code to replace.
243
+ #
244
+ # @return [Integer] end position.
245
+ def end_pos
246
+ node_begin_pos = @node.loc.expression.begin_pos
247
+ node_end_pos = @node.loc.expression.end_pos
248
+ node_begin_pos += @instance.current_source[node_begin_pos..node_end_pos].index "do"
249
+ while @instance.current_source[node_begin_pos += 1] != '@'
250
+ end
251
+ node_begin_pos
252
+ end
253
+
254
+ # The rewritten erb expr code.
255
+ #
256
+ # @return [String] rewritten code.
257
+ def rewritten_code
258
+ @instance.current_source[begin_pos...end_pos].sub(Engine::ERUBY_STMT_SPLITTER, "@output_buffer.append= ")
259
+ .sub(Engine::ERUBY_STMT_SPLITTER, Engine::ERUBY_EXPR_SPLITTER)
260
+ end
261
+ end
224
262
  end
@@ -44,6 +44,7 @@ module Synvert::Core
44
44
  unless Configuration.instance.get(:skip_files).include? file_path
45
45
  begin
46
46
  source = File.read(file_path)
47
+ source = Engine::ERB.encode(source) if file_path =~ /\.erb$/
47
48
  buffer = Parser::Source::Buffer.new file_path
48
49
  buffer.source = source
49
50
 
@@ -64,6 +65,7 @@ module Synvert::Core
64
65
  end
65
66
  @actions = []
66
67
 
68
+ source = Engine::ERB.decode(source) if file_path =~ /\.erb/
67
69
  File.write file_path, source
68
70
  end while !@conflict_actions.empty?
69
71
  end
@@ -152,6 +154,12 @@ module Synvert::Core
152
154
  @actions << Rewriter::ReplaceWithAction.new(self, code)
153
155
  end
154
156
 
157
+ # Parse replace_erb_stmt_with_expr dsl, it creates a [Synvert::Core::Rewriter::ReplaceErbStmtWithExprAction] to
158
+ # replace erb stmt code to expr code.
159
+ def replace_erb_stmt_with_expr
160
+ @actions << Rewriter::ReplaceErbStmtWithExprAction.new(self)
161
+ end
162
+
155
163
  # Parse remove dsl, it creates a [Synvert::Core::Rewriter::RemoveAction] to current node.
156
164
  def remove
157
165
  @actions << Rewriter::RemoveAction.new(self)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Synvert
4
4
  module Core
5
- VERSION = "0.3.0"
5
+ VERSION = "0.4.0"
6
6
  end
7
7
  end
data/spec/spec_helper.rb CHANGED
@@ -12,7 +12,6 @@ end
12
12
  RSpec.configure do |config|
13
13
  config.include ParserHelper
14
14
 
15
- config.treat_symbols_as_metadata_keys_with_true_values = true
16
15
  config.run_all_when_everything_filtered = true
17
16
  config.filter_run :focus
18
17
 
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ module Synvert::Core
4
+ describe Engine::ERB do
5
+ it "encodes / decodes" do
6
+ source =<<-EOF
7
+ <%content_for :head do%>
8
+ <style>
9
+ body {
10
+ background-image: url(<%= asset_path('bg.png') %>);
11
+ }
12
+ </style>
13
+ <%end%>
14
+
15
+ <%
16
+ foo = 'bar'
17
+ post = Post.find(:first)
18
+ bar = 'foo'
19
+ %>
20
+
21
+ <% if User.current &&
22
+ User.current.admin %>
23
+ <%= rounded_content("page") do %>
24
+ <div class='test'>
25
+ <% if post %>
26
+ <div id="title"><%= foo %></div>
27
+ <% form_for post do |f| %>
28
+ <label><%= link_to_function 'test', "confirm('test');" %></label>
29
+ <%= f.text_field 'bar' %>
30
+ <% end %>
31
+ <% end %></div>
32
+ <% end %>
33
+ <% end %>
34
+ EOF
35
+ encoded_source = Engine::ERB.encode(source)
36
+ buffer = Parser::Source::Buffer.new "(test)"
37
+ buffer.source = encoded_source
38
+ parser = Parser::CurrentRuby.new
39
+ parser.reset
40
+ parser.parse buffer
41
+
42
+ expect(Engine::ERB.decode(encoded_source)).to eq source
43
+ end
44
+ end
45
+ end
@@ -149,6 +149,11 @@ describe Parser::AST::Node do
149
149
  end
150
150
 
151
151
  describe "#to_value" do
152
+ it 'gets for int' do
153
+ node = parse("1")
154
+ expect(node.to_value).to eq 1
155
+ end
156
+
152
157
  it 'gets for string' do
153
158
  node = parse("'str'")
154
159
  expect(node.to_value).to eq "str"
@@ -159,6 +164,18 @@ describe Parser::AST::Node do
159
164
  expect(node.to_value).to eq :str
160
165
  end
161
166
 
167
+ it 'get for boolean' do
168
+ node = parse("true")
169
+ expect(node.to_value).to be_truthy
170
+ node = parse("false")
171
+ expect(node.to_value).to be_falsey
172
+ end
173
+
174
+ it 'get for range' do
175
+ node = parse("(1..10)")
176
+ expect(node.to_value).to eq (1..10)
177
+ end
178
+
162
179
  it 'gets for array' do
163
180
  node = parse("['str', :str]")
164
181
  expect(node.to_value).to eq ['str', :str]
data/synvert-core.gemspec CHANGED
@@ -20,6 +20,7 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_runtime_dependency "parser"
22
22
  spec.add_runtime_dependency "activesupport"
23
+ spec.add_runtime_dependency "erubis"
23
24
 
24
25
  spec.add_development_dependency "bundler", "~> 1.6"
25
26
  spec.add_development_dependency "rake"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: synvert-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Huang
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-12 00:00:00.000000000 Z
11
+ date: 2014-07-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parser
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: erubis
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: bundler
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -97,6 +111,8 @@ files:
97
111
  - Rakefile
98
112
  - lib/synvert/core.rb
99
113
  - lib/synvert/core/configuration.rb
114
+ - lib/synvert/core/engine.rb
115
+ - lib/synvert/core/engine/erb.rb
100
116
  - lib/synvert/core/exceptions.rb
101
117
  - lib/synvert/core/node_ext.rb
102
118
  - lib/synvert/core/rewriter.rb
@@ -110,6 +126,7 @@ files:
110
126
  - spec/spec_helper.rb
111
127
  - spec/support/parser_helper.rb
112
128
  - spec/synvert/core/configuration_spec.rb
129
+ - spec/synvert/core/engine/erb_spec.rb
113
130
  - spec/synvert/core/node_ext_spec.rb
114
131
  - spec/synvert/core/rewriter/action_spec.rb
115
132
  - spec/synvert/core/rewriter/condition_spec.rb
@@ -147,6 +164,7 @@ test_files:
147
164
  - spec/spec_helper.rb
148
165
  - spec/support/parser_helper.rb
149
166
  - spec/synvert/core/configuration_spec.rb
167
+ - spec/synvert/core/engine/erb_spec.rb
150
168
  - spec/synvert/core/node_ext_spec.rb
151
169
  - spec/synvert/core/rewriter/action_spec.rb
152
170
  - spec/synvert/core/rewriter/condition_spec.rb