erector 0.7.2 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.txt +17 -3
- data/VERSION.yml +2 -2
- data/bin/erector +1 -1
- data/lib/erector.rb +22 -2
- data/lib/erector/after_initialize.rb +34 -0
- data/lib/erector/caching.rb +93 -0
- data/lib/erector/convenience.rb +58 -0
- data/lib/erector/dependencies.rb +24 -0
- data/lib/erector/dependency.rb +21 -0
- data/lib/erector/{erect.rb → erect/erect.rb} +14 -4
- data/lib/erector/{erected.rb → erect/erected.rb} +6 -4
- data/lib/erector/{indenting.rb → erect/indenting.rb} +0 -0
- data/lib/erector/{rhtml.treetop → erect/rhtml.treetop} +51 -11
- data/lib/erector/errors.rb +12 -0
- data/lib/erector/extensions/hash.rb +21 -0
- data/lib/erector/externals.rb +88 -24
- data/lib/erector/html.rb +352 -0
- data/lib/erector/inline.rb +5 -5
- data/lib/erector/jquery.rb +36 -0
- data/lib/erector/mixin.rb +3 -5
- data/lib/erector/needs.rb +94 -0
- data/lib/erector/output.rb +117 -0
- data/lib/erector/rails.rb +2 -2
- data/lib/erector/rails/extensions/action_controller.rb +5 -3
- data/lib/erector/rails/extensions/rails_helpers.rb +159 -0
- data/lib/erector/rails/extensions/rails_widget.rb +98 -56
- data/lib/erector/rails/rails_form_builder.rb +8 -4
- data/lib/erector/rails/rails_version.rb +2 -2
- data/lib/erector/rails/template_handlers/ert_handler.rb +1 -1
- data/lib/erector/rails/template_handlers/rb_handler.rb +42 -1
- data/lib/erector/raw_string.rb +2 -2
- data/lib/erector/sass.rb +22 -0
- data/lib/erector/widget.rb +100 -653
- data/lib/erector/widgets.rb +1 -0
- data/lib/erector/widgets/external_renderer.rb +51 -0
- data/lib/erector/widgets/page.rb +45 -63
- data/lib/erector/widgets/table.rb +9 -1
- data/spec/erect/erect_rails_spec.rb +19 -17
- data/spec/erect/erect_spec.rb +11 -1
- data/spec/erect/erected_spec.rb +76 -5
- data/spec/erect/rhtml_parser_spec.rb +11 -1
- data/spec/erector/caching_spec.rb +267 -0
- data/spec/erector/convenience_spec.rb +258 -0
- data/spec/erector/dependency_spec.rb +46 -0
- data/spec/erector/externals_spec.rb +233 -0
- data/spec/erector/html_spec.rb +508 -0
- data/spec/erector/indentation_spec.rb +84 -24
- data/spec/erector/inline_spec.rb +19 -8
- data/spec/erector/jquery_spec.rb +35 -0
- data/spec/erector/mixin_spec.rb +1 -1
- data/spec/erector/needs_spec.rb +120 -0
- data/spec/erector/output_spec.rb +199 -0
- data/spec/erector/sample-file.txt +1 -0
- data/spec/erector/sass_spec.rb +33 -0
- data/spec/erector/widget_spec.rb +113 -932
- data/spec/erector/widgets/field_table_spec.rb +6 -6
- data/spec/erector/widgets/form_spec.rb +3 -3
- data/spec/erector/widgets/page_spec.rb +52 -6
- data/spec/erector/widgets/table_spec.rb +4 -4
- data/spec/spec_helper.rb +70 -29
- metadata +56 -19
- data/lib/erector/rails/extensions/rails_widget/rails_helpers.rb +0 -137
- data/spec/core_spec_suite.rb +0 -3
- data/spec/erector/external_spec.rb +0 -110
- data/spec/rails_spec_suite.rb +0 -3
- data/spec/spec.opts +0 -1
- data/spec/spec_suite.rb +0 -40
data/README.txt
CHANGED
@@ -13,7 +13,8 @@ modular decomposition, encapsulation) in views. See the rdoc for the
|
|
13
13
|
Erector::Widget class to learn how to make your own widgets, and visit the
|
14
14
|
project site at http://erector.rubyforge.org for more documentation.
|
15
15
|
|
16
|
-
No, seriously, we've got hella docs at http://erector.rubyforge.org
|
16
|
+
No, seriously, we've got hella docs at http://erector.rubyforge.org -- go
|
17
|
+
check it out.
|
17
18
|
|
18
19
|
== SYNOPSIS
|
19
20
|
|
@@ -34,7 +35,7 @@ No, seriously, we've got hella docs at http://erector.rubyforge.org
|
|
34
35
|
end
|
35
36
|
end
|
36
37
|
|
37
|
-
Hello.new(:target => 'world').
|
38
|
+
Hello.new(:target => 'world').to_html
|
38
39
|
=> "<html><head><title>Hello</title></head><body>Hello, <b class=\"big\">world</b>!</body></html>"
|
39
40
|
|
40
41
|
include Erector::Mixin
|
@@ -62,6 +63,17 @@ To install as a Rails plugin:
|
|
62
63
|
When installing this way, erector is automatically available to your Rails code
|
63
64
|
(no require directive is needed).
|
64
65
|
|
66
|
+
== TESTS
|
67
|
+
|
68
|
+
Three spec rake tasks are provided: spec:core (core functionality),
|
69
|
+
spec:erect (the erector command line tool), and spec:rails (rails integration).
|
70
|
+
You do not need to have Rails installed to run the latter two; they will clone
|
71
|
+
the rails git repository and set it up for testing automatically. You can test
|
72
|
+
against a different version of Rails by changing the constants in
|
73
|
+
lib/erector/rails/rails_version.rb
|
74
|
+
|
75
|
+
'rake spec' will run the complete set of specs.
|
76
|
+
|
65
77
|
== CREDITS:
|
66
78
|
|
67
79
|
Core Team:
|
@@ -75,12 +87,14 @@ Special Thanks To:
|
|
75
87
|
* John Firebaugh
|
76
88
|
* Nathan Sobo
|
77
89
|
* Nick Kallen
|
90
|
+
* Alon Salant
|
91
|
+
* Andy Peterson
|
78
92
|
|
79
93
|
== LICENSE:
|
80
94
|
|
81
95
|
(The MIT License)
|
82
96
|
|
83
|
-
Copyright (c) 2007-
|
97
|
+
Copyright (c) 2007-2010 Pivotal Labs and the Erector Project
|
84
98
|
|
85
99
|
Permission is hereby granted, free of charge, to any person obtaining
|
86
100
|
a copy of this software and associated documentation files (the
|
data/VERSION.yml
CHANGED
data/bin/erector
CHANGED
data/lib/erector.rb
CHANGED
@@ -1,11 +1,31 @@
|
|
1
|
+
module Erector
|
2
|
+
end
|
3
|
+
|
1
4
|
require "cgi"
|
2
5
|
require "yaml"
|
3
|
-
|
4
|
-
require "
|
6
|
+
begin
|
7
|
+
require "sass"
|
8
|
+
rescue LoadError => e
|
9
|
+
# oh well, no Sass
|
10
|
+
end
|
11
|
+
|
12
|
+
require "erector/errors"
|
5
13
|
require "erector/extensions/object"
|
14
|
+
require "erector/extensions/hash"
|
6
15
|
require "erector/raw_string"
|
16
|
+
require "erector/dependencies"
|
17
|
+
require "erector/dependency"
|
7
18
|
require "erector/externals"
|
19
|
+
require "erector/output"
|
20
|
+
require "erector/caching"
|
21
|
+
require "erector/after_initialize"
|
22
|
+
require "erector/needs"
|
23
|
+
require "erector/html"
|
24
|
+
require "erector/convenience"
|
25
|
+
require "erector/jquery"
|
26
|
+
require "erector/sass"
|
8
27
|
require "erector/widget"
|
28
|
+
|
9
29
|
require "erector/inline"
|
10
30
|
require "erector/unicode"
|
11
31
|
require "erector/widgets"
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Erector
|
2
|
+
module AfterInitialize
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def after_initialize(instance=nil, &blk)
|
9
|
+
if blk
|
10
|
+
after_initialize_parts << blk
|
11
|
+
elsif instance
|
12
|
+
if superclass.respond_to?(:after_initialize)
|
13
|
+
superclass.after_initialize instance
|
14
|
+
end
|
15
|
+
after_initialize_parts.each do |part|
|
16
|
+
instance.instance_eval &part
|
17
|
+
end
|
18
|
+
else
|
19
|
+
raise ArgumentError, "You must provide either an instance or a block"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
def after_initialize_parts
|
25
|
+
@after_initialize_parts ||= []
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize(*args, &blk)
|
30
|
+
super
|
31
|
+
self.class.after_initialize self
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Erector
|
2
|
+
class Cache
|
3
|
+
def initialize
|
4
|
+
@stores = {}
|
5
|
+
end
|
6
|
+
|
7
|
+
def store_for(klass)
|
8
|
+
@stores[klass] ||= Hash.new {|h,k| h[k] = {}}
|
9
|
+
end
|
10
|
+
|
11
|
+
def []=(*args)
|
12
|
+
value = args.pop
|
13
|
+
klass = args.shift
|
14
|
+
params = args.first.is_a?(Hash) ? args.first : {}
|
15
|
+
content_method = args.last.is_a?(Symbol) ? args.last : nil
|
16
|
+
store_for(klass)[key(params)][content_method] = value
|
17
|
+
end
|
18
|
+
|
19
|
+
def [](klass, params = {}, content_method = nil)
|
20
|
+
store_for(klass)[key(params)][content_method]
|
21
|
+
end
|
22
|
+
|
23
|
+
def delete(klass, params = {})
|
24
|
+
store_for(klass).delete(key(params))
|
25
|
+
end
|
26
|
+
|
27
|
+
def delete_all(klass)
|
28
|
+
@stores.delete(klass)
|
29
|
+
end
|
30
|
+
|
31
|
+
# convert hash-key to array-key for compatibility with 1.8.6
|
32
|
+
def key(params)
|
33
|
+
params.to_a
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
module Caching
|
38
|
+
def self.included(base)
|
39
|
+
base.extend ClassMethods
|
40
|
+
end
|
41
|
+
|
42
|
+
module ClassMethods
|
43
|
+
def cacheable(value = true)
|
44
|
+
@cachable = value
|
45
|
+
end
|
46
|
+
|
47
|
+
def cachable(value = true)
|
48
|
+
@cachable = value
|
49
|
+
end
|
50
|
+
|
51
|
+
def cachable?
|
52
|
+
if @cachable.nil?
|
53
|
+
superclass.respond_to?(:cachable?) && superclass.cachable?
|
54
|
+
else
|
55
|
+
@cachable
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def cache
|
60
|
+
@@cache ||= nil
|
61
|
+
end
|
62
|
+
|
63
|
+
def cache=(c)
|
64
|
+
@@cache = c
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def cache
|
69
|
+
self.class.cache
|
70
|
+
end
|
71
|
+
|
72
|
+
def should_cache?
|
73
|
+
cache && block.nil? && self.class.cachable?
|
74
|
+
end
|
75
|
+
|
76
|
+
def _render(options = {})
|
77
|
+
if should_cache?
|
78
|
+
cache[self.class, assigns, options[:content_method_name]] ||= super
|
79
|
+
else
|
80
|
+
super
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def _render_via(parent, options = {})
|
85
|
+
if should_cache?
|
86
|
+
parent.output << cache[self.class, assigns, options[:content_method_name]] ||= parent.capture { super }
|
87
|
+
parent.output.widgets << self.class # todo: test!!!
|
88
|
+
else
|
89
|
+
super
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Erector
|
2
|
+
module Convenience
|
3
|
+
# Render (like to_html) but adding newlines and indentation.
|
4
|
+
# You may just want to call to_html(:prettyprint => true)
|
5
|
+
# so you can pass in other rendering options as well.
|
6
|
+
def to_pretty(options = {})
|
7
|
+
to_html(options.merge(:prettyprint => true))
|
8
|
+
end
|
9
|
+
|
10
|
+
# Render (like to_html) but stripping all tags and inserting some
|
11
|
+
# appropriate formatting. Currently we format p, br, ol, ul, and li
|
12
|
+
# tags.
|
13
|
+
def to_text(options = {})
|
14
|
+
# TODO: make text output a first class rendering strategy, like HTML is now,
|
15
|
+
# so we can do things like nested lists and numbered lists
|
16
|
+
html = to_html(options.merge(:prettyprint => false))
|
17
|
+
html.gsub!(/^<p[^>]*>/m, '')
|
18
|
+
html.gsub!(/(<(ul|ol)>)?<li>/, "\n* ")
|
19
|
+
html.gsub!(/<(\/?(ul|ol|p|br))[^>]*( \/)?>/, "\n")
|
20
|
+
CGI.unescapeHTML(html.gsub(/<[^>]*>/, ''))
|
21
|
+
end
|
22
|
+
|
23
|
+
# Emits the result of joining the elements in array with the separator.
|
24
|
+
# The array elements and separator can be Erector::Widget objects,
|
25
|
+
# which are rendered, or strings, which are html-escaped and output.
|
26
|
+
def join(array, separator)
|
27
|
+
first = true
|
28
|
+
array.each do |widget_or_text|
|
29
|
+
if !first
|
30
|
+
text separator
|
31
|
+
end
|
32
|
+
first = false
|
33
|
+
text widget_or_text
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Convenience method to emit a css file link, which looks like this:
|
38
|
+
# <link href="erector.css" rel="stylesheet" type="text/css" />
|
39
|
+
# The parameter is the full contents of the href attribute, including any ".css" extension.
|
40
|
+
#
|
41
|
+
# If you want to emit raw CSS inline, use the #style method instead.
|
42
|
+
def css(href, options = {})
|
43
|
+
link({:rel => 'stylesheet', :type => 'text/css', :href => href}.merge(options))
|
44
|
+
end
|
45
|
+
|
46
|
+
# Convenience method to emit an anchor tag whose href and text are the same,
|
47
|
+
# e.g. <a href="http://example.com">http://example.com</a>
|
48
|
+
def url(href, options = {})
|
49
|
+
a href, ({:href => href}.merge(options))
|
50
|
+
end
|
51
|
+
|
52
|
+
# makes a unique id based on the widget's class name and object id
|
53
|
+
# that you can use as the HTML id of an emitted element
|
54
|
+
def dom_id
|
55
|
+
"#{self.class.name.gsub(/:+/,"_")}_#{self.object_id}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Erector
|
2
|
+
class Dependencies < Array
|
3
|
+
def push(*new_dependencies_args)
|
4
|
+
new_dependencies = new_dependencies_args.select do |new_dependency|
|
5
|
+
!include?(new_dependency)
|
6
|
+
end
|
7
|
+
new_dependencies.each do |dep|
|
8
|
+
unless dep.is_a? Erector::Dependency
|
9
|
+
raise "expected Dependency, got #{dep.class}: #{dep.inspect}"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
super(*new_dependencies)
|
13
|
+
end
|
14
|
+
|
15
|
+
alias_method :<<, :push
|
16
|
+
|
17
|
+
def uniq
|
18
|
+
inject(self.class.new) do |memo, item|
|
19
|
+
memo << item unless memo.any? {|memo_item| memo_item == item}
|
20
|
+
memo
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Erector
|
2
|
+
class Dependency
|
3
|
+
attr_reader :type, :text, :options
|
4
|
+
|
5
|
+
def initialize(type, text, options = {})
|
6
|
+
text = text.read if text.is_a? IO
|
7
|
+
text = self.class.interpolate(text) if options[:interpolate] # todo: test
|
8
|
+
@type, @text, @options = type, text, options
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.interpolate(s)
|
12
|
+
eval("<<INTERPOLATE\n" + s + "\nINTERPOLATE").chomp
|
13
|
+
end
|
14
|
+
|
15
|
+
def ==(other)
|
16
|
+
(self.type == other.type and
|
17
|
+
self.text == other.text and
|
18
|
+
self.options == other.options) ? true : false
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -1,14 +1,16 @@
|
|
1
1
|
require "optparse"
|
2
2
|
require "rake"
|
3
|
-
require "erector/erected" # pull this out so we don't recreate the grammar every time
|
3
|
+
require "erector/erect/erected" # pull this out so we don't recreate the grammar every time
|
4
4
|
|
5
5
|
module Erector
|
6
6
|
class Erect
|
7
|
-
attr_reader :files, :verbose, :mode, :output_dir
|
7
|
+
attr_reader :files, :verbose, :mode, :output_dir, :superklass, :method_name
|
8
8
|
def initialize(args)
|
9
9
|
@verbose = true
|
10
10
|
@mode = :to_erector
|
11
11
|
@output_dir = nil
|
12
|
+
@superklass = 'Erector::Widget'
|
13
|
+
@method_name = 'content'
|
12
14
|
|
13
15
|
opts = OptionParser.new do |opts|
|
14
16
|
opts.banner = "Usage: erector [options] [file|dir]*"
|
@@ -30,6 +32,14 @@ module Erector
|
|
30
32
|
@mode = :to_html
|
31
33
|
end
|
32
34
|
|
35
|
+
opts.on("--superclass SUPERCLASS", "Superclass for new widget (default Erector::Widget)") do |superklass|
|
36
|
+
@superklass = superklass
|
37
|
+
end
|
38
|
+
|
39
|
+
opts.on("--method METHOD", "Method containing content for widget (default 'content')") do |method_name|
|
40
|
+
@method_name = method_name
|
41
|
+
end
|
42
|
+
|
33
43
|
opts.on("-o", "--output-dir DIRECTORY", "Output files to DIRECTORY (default: output files go next to input files)") do |dir|
|
34
44
|
@output_dir = dir
|
35
45
|
end
|
@@ -87,7 +97,7 @@ module Erector
|
|
87
97
|
files.each do |file|
|
88
98
|
say "Erecting #{file}... "
|
89
99
|
begin
|
90
|
-
e = Erector::Erected.new(file)
|
100
|
+
e = Erector::Erected.new(file, @superklass, @method_name)
|
91
101
|
e.convert
|
92
102
|
say " --> #{e.filename}\n"
|
93
103
|
rescue => e
|
@@ -116,7 +126,7 @@ module Erector
|
|
116
126
|
FileUtils.mkdir_p(dir)
|
117
127
|
output_file = "#{dir}/#{filename}.html"
|
118
128
|
File.open(output_file, "w") do |f|
|
119
|
-
f.puts widget.
|
129
|
+
f.puts widget.to_html
|
120
130
|
end
|
121
131
|
say " --> #{output_file}\n"
|
122
132
|
else
|
@@ -7,8 +7,10 @@ Treetop.load("#{dir}/rhtml.treetop")
|
|
7
7
|
module Erector
|
8
8
|
class Erected
|
9
9
|
|
10
|
-
def initialize(in_file)
|
10
|
+
def initialize(in_file, superklass = 'Erector::Widget', method_name = 'content')
|
11
11
|
@in_file = in_file
|
12
|
+
@superklass = superklass
|
13
|
+
@method_name = method_name
|
12
14
|
end
|
13
15
|
|
14
16
|
def filename
|
@@ -20,9 +22,9 @@ module Erector
|
|
20
22
|
parent = File.dirname(@in_file)
|
21
23
|
grandparent = File.dirname(parent)
|
22
24
|
if File.basename(grandparent) == "views"
|
23
|
-
["Views::" + classize(File.basename(parent)) + "::" + base,
|
25
|
+
["Views::" + classize(File.basename(parent)) + "::" + base, @superklass]
|
24
26
|
else
|
25
|
-
[base,
|
27
|
+
[base, @superklass]
|
26
28
|
end
|
27
29
|
end
|
28
30
|
|
@@ -47,7 +49,7 @@ module Erector
|
|
47
49
|
else
|
48
50
|
File.open(filename, "w") do |f|
|
49
51
|
f.puts("class #{classname} < #{parent_class}")
|
50
|
-
f.puts(" def
|
52
|
+
f.puts(" def #{@method_name}")
|
51
53
|
f.puts(parsed.set_indent(2).convert)
|
52
54
|
f.puts(" end")
|
53
55
|
f.puts("end")
|
File without changes
|
@@ -47,7 +47,7 @@ grammar Rhtml
|
|
47
47
|
rule scriptlet
|
48
48
|
'<%' space code space '%>' <Erector::Indenting> {
|
49
49
|
def convert
|
50
|
-
text = code.
|
50
|
+
text = code.text_value_removing_trims.strip
|
51
51
|
if text =~ /\bdo( |.*|)?$/
|
52
52
|
line_in text
|
53
53
|
elsif text == "end"
|
@@ -62,7 +62,7 @@ grammar Rhtml
|
|
62
62
|
rule printlet
|
63
63
|
'<%=' space code space '%>' <Erector::Indenting> {
|
64
64
|
def convert
|
65
|
-
line "rawtext #{code.
|
65
|
+
line "rawtext #{code.convert_removing_trims}"
|
66
66
|
end
|
67
67
|
}
|
68
68
|
end
|
@@ -70,13 +70,21 @@ grammar Rhtml
|
|
70
70
|
rule hprintlet
|
71
71
|
'<%=' space 'h' ' '+ code space '%>' <Erector::Indenting> {
|
72
72
|
def convert
|
73
|
-
line "text #{code.
|
73
|
+
line "text #{code.convert_removing_trims}"
|
74
74
|
end
|
75
75
|
}
|
76
76
|
end
|
77
77
|
|
78
78
|
rule code
|
79
79
|
(('%' !'>') / [^%])* <Erector::Indenting> {
|
80
|
+
def convert_removing_trims
|
81
|
+
convert.gsub(/\s*\-\s*$/, '')
|
82
|
+
end
|
83
|
+
|
84
|
+
def text_value_removing_trims
|
85
|
+
text_value.gsub(/\s*\-\s*$/, '')
|
86
|
+
end
|
87
|
+
|
80
88
|
def convert
|
81
89
|
code = text_value.strip
|
82
90
|
# matches a word, followed by either a word, a string, or a symbol
|
@@ -158,13 +166,19 @@ grammar Rhtml
|
|
158
166
|
|
159
167
|
rule attributes
|
160
168
|
first:attribute rest:attributes* {
|
161
|
-
def convert
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
169
|
+
def convert(internal = false)
|
170
|
+
out = " " + first.convert +
|
171
|
+
if rest.empty?
|
172
|
+
""
|
173
|
+
else
|
174
|
+
",#{rest.elements.first.convert(true)}" # this is hacky -- is there a better way?
|
175
|
+
end
|
176
|
+
|
177
|
+
if (! internal) && out =~ /[\(\)]/ && out =~ /^(\s*)(.*?)(\s*)$/
|
178
|
+
out = "(#{$2})#{$3}"
|
179
|
+
end
|
180
|
+
|
181
|
+
out
|
168
182
|
end
|
169
183
|
}
|
170
184
|
end
|
@@ -173,7 +187,7 @@ grammar Rhtml
|
|
173
187
|
space n:(tagname) space '=' space v:quoted space {
|
174
188
|
def convert
|
175
189
|
attr_name = (n.text_value =~ /[-:]/) ? "'#{n.text_value}'" : ":#{n.text_value}"
|
176
|
-
"#{attr_name} =>
|
190
|
+
"#{attr_name} => #{v.convert}"
|
177
191
|
end
|
178
192
|
}
|
179
193
|
end
|
@@ -183,6 +197,32 @@ grammar Rhtml
|
|
183
197
|
def value
|
184
198
|
val.text_value
|
185
199
|
end
|
200
|
+
|
201
|
+
def convert
|
202
|
+
extract_erb(val.text_value)
|
203
|
+
end
|
204
|
+
|
205
|
+
def parenthesize_if_necessary(s)
|
206
|
+
return s if s.strip =~ /^\(.*\)$/ || s =~ /^[A-Z0-9_]*$/i
|
207
|
+
"(" + s + ")"
|
208
|
+
end
|
209
|
+
|
210
|
+
def extract_erb(s, parenthesize = true)
|
211
|
+
if s =~ /^(.*?)<%=(.*?)%>(.*?)$/
|
212
|
+
pre, code, post = $1.html_unescape.escape_single_quotes, $2, $3.html_unescape.escape_single_quotes
|
213
|
+
out = ""
|
214
|
+
out = "'#{pre}' + " unless pre.length == 0
|
215
|
+
out += parenthesize_if_necessary(code.strip)
|
216
|
+
unless post.length == 0
|
217
|
+
post = extract_erb(post, false)
|
218
|
+
out += " + #{post}"
|
219
|
+
end
|
220
|
+
out = parenthesize_if_necessary(out) if parenthesize
|
221
|
+
out
|
222
|
+
else
|
223
|
+
"'" + s.html_unescape.escape_single_quotes + "'"
|
224
|
+
end
|
225
|
+
end
|
186
226
|
}
|
187
227
|
end
|
188
228
|
|