erector 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/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
|
|