honkster-erector 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. data/README.txt +116 -0
  2. data/VERSION.yml +4 -0
  3. data/bin/erector +14 -0
  4. data/lib/erector.rb +34 -0
  5. data/lib/erector/abstract_widget.rb +172 -0
  6. data/lib/erector/after_initialize.rb +34 -0
  7. data/lib/erector/caching.rb +93 -0
  8. data/lib/erector/convenience.rb +58 -0
  9. data/lib/erector/dependencies.rb +24 -0
  10. data/lib/erector/dependency.rb +30 -0
  11. data/lib/erector/erect/erect.rb +160 -0
  12. data/lib/erector/erect/erected.rb +75 -0
  13. data/lib/erector/erect/indenting.rb +36 -0
  14. data/lib/erector/erect/rhtml.treetop +233 -0
  15. data/lib/erector/errors.rb +12 -0
  16. data/lib/erector/extensions/hash.rb +21 -0
  17. data/lib/erector/extensions/object.rb +18 -0
  18. data/lib/erector/externals.rb +97 -0
  19. data/lib/erector/html.rb +352 -0
  20. data/lib/erector/inline.rb +37 -0
  21. data/lib/erector/jquery.rb +36 -0
  22. data/lib/erector/mixin.rb +12 -0
  23. data/lib/erector/needs.rb +94 -0
  24. data/lib/erector/output.rb +117 -0
  25. data/lib/erector/rails.rb +27 -0
  26. data/lib/erector/rails/extensions/action_controller.rb +16 -0
  27. data/lib/erector/rails/extensions/rails_helpers.rb +159 -0
  28. data/lib/erector/rails/extensions/rails_widget.rb +126 -0
  29. data/lib/erector/rails/rails_form_builder.rb +24 -0
  30. data/lib/erector/rails/rails_version.rb +6 -0
  31. data/lib/erector/rails/template_handlers/ert_handler.rb +32 -0
  32. data/lib/erector/rails/template_handlers/rb_handler.rb +52 -0
  33. data/lib/erector/raw_string.rb +8 -0
  34. data/lib/erector/sass.rb +22 -0
  35. data/lib/erector/unicode.rb +18185 -0
  36. data/lib/erector/unicode_builder.rb +67 -0
  37. data/lib/erector/version.rb +12 -0
  38. data/lib/erector/widget.rb +54 -0
  39. data/lib/erector/widgets.rb +6 -0
  40. data/lib/erector/widgets/environment_badge.rb +29 -0
  41. data/lib/erector/widgets/external_renderer.rb +51 -0
  42. data/lib/erector/widgets/field_table.rb +110 -0
  43. data/lib/erector/widgets/form.rb +30 -0
  44. data/lib/erector/widgets/page.rb +165 -0
  45. data/lib/erector/widgets/table.rb +104 -0
  46. data/rails/init.rb +4 -0
  47. data/spec/erect/erect_rails_spec.rb +114 -0
  48. data/spec/erect/erect_spec.rb +175 -0
  49. data/spec/erect/erected_spec.rb +164 -0
  50. data/spec/erect/rhtml_parser_spec.rb +361 -0
  51. data/spec/erector/caching_spec.rb +269 -0
  52. data/spec/erector/convenience_spec.rb +259 -0
  53. data/spec/erector/dependency_spec.rb +67 -0
  54. data/spec/erector/externals_spec.rb +236 -0
  55. data/spec/erector/html_spec.rb +509 -0
  56. data/spec/erector/indentation_spec.rb +211 -0
  57. data/spec/erector/inline_spec.rb +94 -0
  58. data/spec/erector/jquery_spec.rb +35 -0
  59. data/spec/erector/mixin_spec.rb +65 -0
  60. data/spec/erector/needs_spec.rb +120 -0
  61. data/spec/erector/output_spec.rb +199 -0
  62. data/spec/erector/sample-file.txt +1 -0
  63. data/spec/erector/sass_spec.rb +33 -0
  64. data/spec/erector/unicode_builder_spec.rb +75 -0
  65. data/spec/erector/widget_spec.rb +250 -0
  66. data/spec/erector/widgets/field_table_spec.rb +133 -0
  67. data/spec/erector/widgets/form_spec.rb +31 -0
  68. data/spec/erector/widgets/page_spec.rb +85 -0
  69. data/spec/erector/widgets/table_spec.rb +99 -0
  70. data/spec/spec_helper.rb +95 -0
  71. metadata +191 -0
@@ -0,0 +1,259 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../spec_helper")
2
+
3
+ describe Erector::Convenience do
4
+ include Erector::Mixin
5
+
6
+ describe "#to_pretty" do
7
+ it "calls to_html with :prettyprint => true" do
8
+ widget = Erector.inline do
9
+ div "foo"
10
+ end
11
+ mock(widget).to_html({:prettyprint => true})
12
+ widget.to_pretty
13
+ end
14
+
15
+ it "passes extra options through to to_html" do
16
+ pending "RR problem with Ruby 1.9" if RUBY_VERSION >= "1.9.0"
17
+ widget = Erector.inline do
18
+ div "foo"
19
+ end
20
+ mock(widget).to_html({:prettyprint => true, :extra => "yay"})
21
+ widget.to_pretty(:extra => "yay")
22
+ end
23
+ end
24
+
25
+ describe "#to_s" do
26
+ it "returns html" do
27
+ capturing_stderr do
28
+ Erector.inline do
29
+ div "foo"
30
+ end.to_s.should == "<div>foo</div>"
31
+ end
32
+ end
33
+ end
34
+
35
+ describe "#to_html" do
36
+ it "returns html" do
37
+ Erector.inline do
38
+ div "foo"
39
+ end.to_html.should == "<div>foo</div>"
40
+ end
41
+ end
42
+
43
+ describe "#to_text" do
44
+ it "strips tags" do
45
+ Erector.inline do
46
+ div "foo"
47
+ end.to_text.should == "foo"
48
+ end
49
+
50
+ it "unescapes named entities" do
51
+ s = "my \"dog\" has fleas & <ticks>"
52
+ Erector.inline do
53
+ text s
54
+ end.to_text.should == s
55
+ end
56
+
57
+ it "ignores >s inside attribute strings" do
58
+ Erector.inline do
59
+ a "foo", :href => "http://example.com/x>y"
60
+ end.to_text.should == "foo"
61
+ end
62
+
63
+ def with_prettyprint_default(value = true)
64
+ old_default = Erector::Widget.new.prettyprint_default
65
+ begin
66
+ Erector::Widget.prettyprint_default = value
67
+ yield
68
+ ensure
69
+ Erector::Widget.prettyprint_default = old_default
70
+ end
71
+ end
72
+
73
+ it "doesn't inherit unwanted pretty-printed whitespace (i.e. it turns off prettyprinting)" do
74
+ with_prettyprint_default(true) do
75
+ Erector.inline do
76
+ div { div { div "foo" } }
77
+ end.to_text.should == "foo"
78
+ end
79
+ end
80
+
81
+ it "passes extra attributes through to to_s" do
82
+ class Funny < Erector::Widget
83
+ def content
84
+ div "foo"
85
+ end
86
+
87
+ def funny
88
+ div "haha"
89
+ end
90
+ end
91
+ Funny.new.to_text(:content_method_name => :funny).should == "haha"
92
+ end
93
+
94
+ it "doesn't turn a p into a newline if it's at the beginning of the string" do
95
+ Erector.inline do
96
+ p "hi"
97
+ end.to_text.should == "hi\n"
98
+ end
99
+
100
+ it "puts a blank line (two newlines) after a /p tag" do
101
+ Erector.inline do
102
+ p "first paragraph"
103
+ p "second paragraph"
104
+ end.to_text.should == "first paragraph\n\nsecond paragraph\n"
105
+ end
106
+
107
+ it "separates p tags with attributes" do
108
+ Erector.inline do
109
+ p "first paragraph", :class => "first"
110
+ p "second paragraph", :class => "second"
111
+ end.to_text.should == "first paragraph\n\nsecond paragraph\n"
112
+ end
113
+
114
+ it "puts a newline after a br tag" do
115
+ Erector.inline do
116
+ text "first line"
117
+ br
118
+ text "second line"
119
+ end.to_text.should == "first line\nsecond line"
120
+ end
121
+
122
+ it "formats a UL (unordered list) using asterisks for bullets" do
123
+ Erector.inline do
124
+ ul do
125
+ li "vanilla"
126
+ li "chocolate"
127
+ li "strawberry"
128
+ end
129
+ end.to_text.should == "\n* vanilla\n* chocolate\n* strawberry\n"
130
+ end
131
+
132
+ # it's too hard to keep track of numbers with a regexp munger, so just use asterisks for bullets
133
+ # todo: integrate text output into core rendering code
134
+ it "formats an OL (ordered list)" do
135
+ Erector.inline do
136
+ ol do
137
+ li "vanilla"
138
+ li "chocolate"
139
+ li "strawberry"
140
+ end
141
+ end.to_text.should == "\n* vanilla\n* chocolate\n* strawberry\n"
142
+ end
143
+ end
144
+
145
+ describe "#join" do
146
+ it "empty array means nothing to join" do
147
+ erector do
148
+ join [], Erector::Widget.new { text "x" }
149
+ end.should == ""
150
+ end
151
+
152
+ it "larger example with two tabs" do
153
+ erector do
154
+ tab1 =
155
+ Erector.inline do
156
+ a "Upload document", :href => "/upload"
157
+ end
158
+ tab2 =
159
+ Erector.inline do
160
+ a "Logout", :href => "/logout"
161
+ end
162
+ join [tab1, tab2],
163
+ Erector::Widget.new { text nbsp(" |"); text " " }
164
+ end.should ==
165
+ '<a href="/upload">Upload document</a>&#160;| <a href="/logout">Logout</a>'
166
+ end
167
+
168
+ it "plain string as join separator means pass it to text" do
169
+ erector do
170
+ join [
171
+ Erector::Widget.new { text "x" },
172
+ Erector::Widget.new { text "y" }
173
+ ], "<>"
174
+ end.should == "x&lt;&gt;y"
175
+ end
176
+
177
+ it "plain string as item to join means pass it to text" do
178
+ erector do
179
+ join [
180
+ "<",
181
+ "&"
182
+ ], Erector::Widget.new { text " + " }
183
+ end.should == "&lt; + &amp;"
184
+ end
185
+ end
186
+
187
+ describe "#css" do
188
+ it "makes a link when passed a string" do
189
+ erector do
190
+ css "erector.css"
191
+ end.should == "<link href=\"erector.css\" rel=\"stylesheet\" type=\"text/css\" />"
192
+ end
193
+
194
+ it "accepts a media attribute" do
195
+ erector do
196
+ css "print.css", :media => "print"
197
+ end.should == "<link href=\"print.css\" media=\"print\" rel=\"stylesheet\" type=\"text/css\" />"
198
+ end
199
+
200
+ it "passes extra attributes through" do
201
+ erector { css "foo.css", :title => 'Foo' }.should == "<link href=\"foo.css\" rel=\"stylesheet\" title=\"Foo\" type=\"text/css\" />"
202
+ end
203
+ end
204
+
205
+ describe "#url" do
206
+ it "renders an anchor tag with the same href and text" do
207
+ erector do
208
+ url "http://example.com"
209
+ end.should == "<a href=\"http://example.com\">http://example.com</a>"
210
+ end
211
+
212
+ it "accepts extra attributes" do
213
+ erector do
214
+ url "http://example.com", :onclick=>"alert('foo')"
215
+ end.should == "<a href=\"http://example.com\" onclick=\"alert('foo')\">http://example.com</a>"
216
+ end
217
+
218
+ end
219
+
220
+ describe "#dom_id" do
221
+ class DOMIDWidget < Erector::Widget
222
+ def content
223
+ div :id => dom_id
224
+ end
225
+ end
226
+
227
+ it "makes a unique id based on the widget's class name and object id" do
228
+ widget = DOMIDWidget.new
229
+ widget.dom_id.should include("#{widget.object_id}")
230
+ widget.dom_id.should include("DOMIDWidget")
231
+ end
232
+
233
+ it "can be used as an HTML id" do
234
+ widget = DOMIDWidget.new
235
+ widget.to_html.should == "<div id=\"#{widget.dom_id}\"></div>"
236
+ end
237
+
238
+ describe 'for a namespaced widget class' do
239
+
240
+ module ::ErectorConvenienceSpec
241
+ class NestedWidget < Erector::Widget
242
+ end
243
+ end
244
+
245
+ it 'is colon escaped' do
246
+ g = ErectorConvenienceSpec::NestedWidget.new
247
+ g.dom_id.should_not =~ /:/
248
+ end
249
+
250
+ it 'combines all parent namespaces' do
251
+ g = ErectorConvenienceSpec::NestedWidget.new
252
+ g.dom_id.should == "ErectorConvenienceSpec_NestedWidget_#{g.object_id}"
253
+ end
254
+
255
+ end
256
+
257
+ end
258
+ end
259
+
@@ -0,0 +1,67 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../spec_helper")
2
+ require 'benchmark'
3
+ require 'active_support' # for Symbol#to_proc
4
+
5
+ module DependencySpec
6
+ describe Erector::Dependency do
7
+ it "can be constructed with type and text" do
8
+ x = Erector::Dependency.new(:foo, "abc")
9
+ x.type.should == :foo
10
+ x.text.should == "abc"
11
+ x.options.should == {}
12
+ end
13
+
14
+ it "can be constructed with type, text, and options" do
15
+ x = Erector::Dependency.new(:foo, "abc", {:bar => 7})
16
+ x.options.should == {:bar => 7}
17
+ end
18
+
19
+ it "can be constructed with a file" do
20
+ file = File.new("#{File.dirname(__FILE__)}/sample-file.txt")
21
+ x = Erector::Dependency.new(:foo, file)
22
+ x.text.should == "sample file contents, 2 + 2 = \#{2 + 2}\n"
23
+ end
24
+
25
+ it "can be constructed with a file and interpolate the text" do
26
+ file = File.new("#{File.dirname(__FILE__)}/sample-file.txt")
27
+ x = Erector::Dependency.new(:foo, file, :interpolate => true)
28
+ x.text.should == "sample file contents, 2 + 2 = 4\n"
29
+ end
30
+
31
+ describe "comparison methods" do
32
+ before do
33
+ @castor = Erector::Dependency.new(:foo, "abc", {:bar => 7})
34
+ @pollux = Erector::Dependency.new(:foo, "abc", {:bar => 7})
35
+ @leo = Erector::Dependency.new(:foo, "abc")
36
+ @pisces = Erector::Dependency.new(:foo, "xyz", {:bar => 7})
37
+ end
38
+
39
+ it "is equal to an identical external" do
40
+ @castor.should == @pollux
41
+ [@castor].should include(@pollux)
42
+ @castor.eql?(@pollux).should be_true
43
+ @castor.hash.should == @pollux.hash
44
+ end
45
+
46
+ it "is not equal to an otherwise identical external with different options" do
47
+ @castor.should_not == @leo
48
+ [@castor].should_not include(@leo)
49
+ @castor.eql?(@leo).should_not be_true
50
+ @castor.hash.should_not == @leo.hash
51
+ end
52
+
53
+ it "is not equal to a different external with the same options" do
54
+ @castor.should_not == @pisces
55
+ [@castor].should_not include(@pisces)
56
+ @castor.eql?(@pisces).should_not be_true
57
+ @castor.hash.should_not == @pisces.hash
58
+ end
59
+
60
+ # see http://blog.nathanielbibler.com/post/73525836/using-the-ruby-array-uniq-with-custom-classes
61
+ it "works with uniq" do
62
+ [@castor, @pollux, @leo].uniq.should == [@castor, @leo]
63
+ end
64
+
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,236 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../spec_helper")
2
+ require 'benchmark'
3
+ require 'active_support' # for Symbol#to_proc
4
+
5
+ module ExternalsSpec
6
+
7
+ describe "adding dependencies" do
8
+ before do
9
+ @args = [:what, :ever, :is, :passed]
10
+ @result = Erector::Dependency.new :js, '/foo.js'
11
+ @result2 = Erector::Dependency.new :css, '/foo.css'
12
+ end
13
+
14
+ after do
15
+ Erector::Widget.my_dependencies.clear
16
+ end
17
+
18
+ it "calls #interpret_args with given arguments and passes result to #push_dependency" do
19
+ pending "RR problem with Ruby 1.9" if RUBY_VERSION >= "1.9.0"
20
+ mock(Erector::Widget).interpret_args(*@args).returns(@result)
21
+ Erector::Widget.depends_on *@args
22
+ end
23
+
24
+ it "starts out with no items in @_dependencies" do
25
+ class Quesadilla < Erector::Widget
26
+ end
27
+ Quesadilla.my_dependencies.should == []
28
+ end
29
+
30
+
31
+ describe '#interpret_args' do
32
+
33
+ class Test
34
+ include Erector::Externals
35
+ end
36
+
37
+ it "will infer that a .js extension is javascript" do
38
+ x = Test.send :interpret_args, ('/path/to/a.js')
39
+ x.text.should == '/path/to/a.js'
40
+ x.type.should == :js
41
+ end
42
+
43
+ it "will infer that a .css extension is a stylesheet" do
44
+ x = Test.send :interpret_args, ('/path/to/a.css')
45
+ x.text.should == '/path/to/a.css'
46
+ x.type.should == :css
47
+ end
48
+
49
+ it "will capture render options when just a file is mentioned" do
50
+ x = Test.send(:interpret_args, '/path/to/a.css', :render=>:link)
51
+ x.text.should == '/path/to/a.css'
52
+ x.type.should == :css
53
+ x.options.should == {:render=>:link} # could also be "embed"
54
+ end
55
+
56
+ it "embeds javascript" do
57
+ x = Test.send :interpret_args, :js, "alert('foo')"
58
+ x.text.should == "alert('foo')"
59
+ x.type.should == :js
60
+ end
61
+
62
+ it "guesses Javascript type from .js" do
63
+ x = Test.send :interpret_args, "/script/foo.js"
64
+ x.text.should == "/script/foo.js"
65
+ x.type.should == :js
66
+ end
67
+
68
+ it "guesses CSS type from .css" do
69
+ x = Test.send :interpret_args, "/script/foo.css"
70
+ x.text.should == "/script/foo.css"
71
+ x.type.should == :css
72
+ end
73
+
74
+ it "add multiple files without an options hash" do
75
+ x = Test.send :interpret_args, :js, "/script/foo.js", "/script/bar.js"
76
+ x.size.should == 2
77
+ x[0].text.should == "/script/foo.js"
78
+ x[0].type.should == :js
79
+ x[1].text.should == "/script/bar.js"
80
+ x[1].type.should == :js
81
+ end
82
+
83
+ it "add multiple files with an options hash" do
84
+ x = Test.send :interpret_args, :js, "/script/foo.js", "/script/bar.js", :embed=>true
85
+ x.size.should == 2
86
+ x[0].text.should == "/script/foo.js"
87
+ x[0].type.should == :js
88
+ x[0].options[:embed].should == true
89
+ x[1].text.should == "/script/bar.js"
90
+ x[1].type.should == :js
91
+ x[1].options[:embed].should == true
92
+ end
93
+
94
+ it "adds multiple files from hash" do
95
+ x = Test.send :interpret_args, :js => ["foo.js", "bar.js"]
96
+ x.size.should == 2
97
+ x[0].text.should == "foo.js"
98
+ x[0].type.should == :js
99
+ x[1].text.should == "bar.js"
100
+ x[1].type.should == :js
101
+ end
102
+
103
+ it "adds multiple files from hash of different types" do
104
+ x = Test.send :interpret_args, :js => ["foo.js", "bar.js"], :css=>'file.css'
105
+ x.size.should == 3
106
+ x.map(&:text).include?('foo.js')
107
+ x.map(&:text).include?('bar.js')
108
+ x.map(&:text).include?('file.css')
109
+ end
110
+
111
+ it "adds multiple files from hash and preserves the options" do
112
+ x = Test.send :interpret_args, :js => ["foo.js", "bar.js"], :foo=>false
113
+ x.size.should == 2
114
+ x[0].text.should == "foo.js"
115
+ x[0].type.should == :js
116
+ x[0].options.should == {:foo=>false}
117
+ x[1].text.should == "bar.js"
118
+ x[1].type.should == :js
119
+ x[1].options.should == {:foo=>false}
120
+ end
121
+ end
122
+
123
+ end
124
+
125
+ describe 'extracting the dependencies (integration tests)' do
126
+ attr_reader :HotSauce, :SourCream, :Tabasco
127
+
128
+ before do
129
+ @HotSauce = Class.new(Erector::Widget) do
130
+ depends_on :css, "/css/tapatio.css", :media => "print"
131
+ depends_on :css, "/css/salsa_picante.css"
132
+ depends_on :js, "/lib/jquery.js"
133
+ depends_on :js, "/lib/picante.js"
134
+ end
135
+ @SourCream = Class.new(Erector::Widget) do
136
+ depends_on :css, "/css/sourcream.css"
137
+ depends_on :js, "/lib/jquery.js"
138
+ depends_on :js, "/lib/dairy.js"
139
+ end
140
+ @Tabasco = Class.new(self.HotSauce) do
141
+ depends_on :js, "tabasco.js"
142
+ depends_on :css, "/css/salsa_picante.css"
143
+ end
144
+ end
145
+
146
+ it "can be fetched via the type" do
147
+ self.HotSauce.dependencies(:css).map(&:text).should == [
148
+ "/css/tapatio.css",
149
+ "/css/salsa_picante.css",
150
+ ]
151
+ end
152
+
153
+ it "can be filtered via the class" do
154
+ self.SourCream.dependencies(:css).map(&:text).should == [
155
+ "/css/sourcream.css",
156
+ ]
157
+ end
158
+
159
+ it "grabs dependencies from superclasses too" do
160
+ self.Tabasco.dependencies(:js).map(&:text).should == ["/lib/jquery.js", "/lib/picante.js", "tabasco.js"]
161
+ end
162
+
163
+ it "retains the options" do
164
+ self.HotSauce.dependencies(:css).map(&:options).should == [
165
+ {:media => "print"},
166
+ {}
167
+ ]
168
+ end
169
+
170
+ it "removes duplicates" do
171
+ self.Tabasco.dependencies(:css).map(&:text).should == [
172
+ "/css/tapatio.css",
173
+ "/css/salsa_picante.css",
174
+ ]
175
+ end
176
+
177
+ it "works with strings or symbols" do
178
+ self.HotSauce.dependencies("css").map(&:text).should == [
179
+ "/css/tapatio.css",
180
+ "/css/salsa_picante.css",
181
+ ]
182
+ end
183
+
184
+ class Taco < Erector::Widget
185
+ depends_on :filling, "beef"
186
+ depends_on :filling, "beef", :media => "print"
187
+ end
188
+
189
+ it "considers options when removing duplicates" do
190
+ Taco.dependencies(:filling).map(&:text).should == ["beef", "beef"]
191
+ end
192
+
193
+
194
+ end
195
+
196
+ describe "rendering with externals" do
197
+ class Dinner < Erector::Widget
198
+ external :js, "/dinner.js"
199
+
200
+ def content
201
+ span "dinner"
202
+ widget Dessert
203
+ end
204
+ end
205
+
206
+ class Dessert < Erector::Widget
207
+ external :js, "/dessert.js"
208
+ external :css, "/dessert.css"
209
+
210
+ def content
211
+ span "dessert"
212
+ end
213
+ end
214
+
215
+ it "#render_with_externals sticks the externals for all its rendered sub-widgets at the end of the output buffer" do
216
+ s = Dinner.new.render_with_externals
217
+ s.join.should ==
218
+ "<span>dinner</span>" +
219
+ "<span>dessert</span>" +
220
+ "<link href=\"/dessert.css\" media=\"all\" rel=\"stylesheet\" type=\"text/css\" />" +
221
+ "<script src=\"/dinner.js\" type=\"text/javascript\"></script>" +
222
+ "<script src=\"/dessert.js\" type=\"text/javascript\"></script>"
223
+ end
224
+
225
+ it "#render_externals returns externals for all rendered sub-widgets to an output buffer" do
226
+ widget = Dinner.new
227
+ widget.to_html
228
+ widget.render_externals.join.should ==
229
+ "<link href=\"/dessert.css\" media=\"all\" rel=\"stylesheet\" type=\"text/css\" />" +
230
+ "<script src=\"/dinner.js\" type=\"text/javascript\"></script>" +
231
+ "<script src=\"/dessert.js\" type=\"text/javascript\"></script>"
232
+ end
233
+ end
234
+
235
+
236
+ end