synvert-core 0.3.0 → 0.4.0

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