lapillus 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/examples/guest_book.rb +50 -0
  2. data/examples/hello_world.rb +12 -0
  3. data/examples/persons.rb +30 -0
  4. data/examples/tc_guest_book.rb +34 -0
  5. data/examples/tc_hello_world.rb +24 -0
  6. data/examples/tc_persons.rb +13 -0
  7. data/html/GuestBook.html +24 -0
  8. data/html/HelloWorld.html +5 -0
  9. data/html/LapillusTesterTestPage.html +12 -0
  10. data/html/Persons.html +7 -0
  11. data/html/TestPage.html +8 -0
  12. data/html/TestRemotePanel.html +8 -0
  13. data/lib/changelog.txt +6 -0
  14. data/lib/lapillus/base.rb +276 -0
  15. data/lib/lapillus/behaviours.rb +208 -0
  16. data/lib/lapillus/components.rb +116 -0
  17. data/lib/lapillus/containers.rb +234 -0
  18. data/lib/lapillus/dispatcher.rb +216 -0
  19. data/lib/lapillus/form_components.rb +88 -0
  20. data/lib/lapillus/lapillus_server.rb +21 -0
  21. data/lib/lapillus/lapillus_testers.rb +167 -0
  22. data/lib/lapillus/mongrel_server.rb +69 -0
  23. data/lib/lapillus/multiview.rb +45 -0
  24. data/lib/lapillus/pager.rb +104 -0
  25. data/lib/lapillus/process_upload.rb +11 -0
  26. data/lib/lapillus/web_application.rb +12 -0
  27. data/lib/lapillus/webrick_server.rb +90 -0
  28. data/lib/lapillus.rb +33 -0
  29. data/lib/license.txt +24 -0
  30. data/test/tc_base.rb +63 -0
  31. data/test/tc_behaviours.rb +106 -0
  32. data/test/tc_bookmarkablepagelink.rb +58 -0
  33. data/test/tc_components.rb +285 -0
  34. data/test/tc_containers.rb +173 -0
  35. data/test/tc_dispatcher.rb +106 -0
  36. data/test/tc_examples.rb +193 -0
  37. data/test/tc_form.rb +231 -0
  38. data/test/tc_fragments.rb +114 -0
  39. data/test/tc_hierarchy.rb +34 -0
  40. data/test/tc_lapillus.rb +30 -0
  41. data/test/tc_lapillus_testers.rb +108 -0
  42. data/test/tc_multiview.rb +91 -0
  43. data/test/tc_pager.rb +94 -0
  44. data/test/tc_rendering.rb +74 -0
  45. data/test/ts_all_test.rb +20 -0
  46. metadata +94 -0
@@ -0,0 +1,50 @@
1
+ require 'rubygems'
2
+ require 'lapillus'
3
+ require 'date'
4
+
5
+ include Lapillus
6
+
7
+ class Comment
8
+ attr_reader :date
9
+ attr_accessor :text
10
+
11
+ def initialize
12
+ @date = Date.today
13
+ @text = ''
14
+ end
15
+ end
16
+
17
+ class CommentForm < Form
18
+ attr_reader :comment
19
+ textarea "text", :model => :comment, :property => :text
20
+ def initialize(id)
21
+ super(id)
22
+ @comment = Comment.new
23
+ end
24
+ def on_submit(button)
25
+ parent.comments << comment
26
+ parent.commentslist.refresh
27
+ @comment = Comment.new
28
+ end
29
+ end
30
+
31
+ class GuestBook < Webpage
32
+ attr_reader :comments
33
+ listview "commentslist", :model => :comments do |comment|
34
+ add(Label.new("date", comment.date.to_s))
35
+ add(MultiLineLabel.new("text", comment.text))
36
+ end
37
+ def initialize
38
+ super()
39
+ @comments = []
40
+ add(CommentForm.new("commentform"))
41
+ end
42
+ end
43
+
44
+
45
+
46
+
47
+ if __FILE__ == $0
48
+ server = LapillusServer.new(:root_url => "/", :port => 2000, :homepage => GuestBook)
49
+ server.start
50
+ end
@@ -0,0 +1,12 @@
1
+ require 'rubygems'
2
+ require 'lapillus'
3
+ include Lapillus
4
+
5
+ class HelloWorld < Webpage
6
+ label "message", :model => "Hello world!"
7
+ end
8
+
9
+ if __FILE__ == $0
10
+ server = LapillusServer.new(:root_url => "/", :port => 2000, :homepage => HelloWorld)
11
+ server.start
12
+ end
@@ -0,0 +1,30 @@
1
+ require 'rubygems'
2
+ require 'lapillus'
3
+ include Lapillus
4
+
5
+ class Person
6
+ attr_reader :name
7
+
8
+ def initialize(name)
9
+ @name = name
10
+ end
11
+ end
12
+
13
+ class Persons < Webpage
14
+ attr_reader :persons
15
+ listview "personlist", :model => :persons do |person|
16
+ label "name", :model => person, :property => :name
17
+ end
18
+
19
+ def initialize
20
+ super()
21
+ person1 = Person.new("jantje")
22
+ person2 = Person.new("pietje")
23
+ @persons = [person1, person2]
24
+ end
25
+ end
26
+
27
+ if __FILE__ == $0
28
+ server = LapillusServer.new(:root_url => "/", :port => 2000, :homepage => Persons)
29
+ server.start
30
+ end
@@ -0,0 +1,34 @@
1
+ require "test/unit"
2
+ require 'examples/guest_book'
3
+
4
+ class TC_GuestBook < Test::Unit::TestCase
5
+ def test_guestbook_render
6
+ expected = <<EOF
7
+ <html>
8
+ <body>
9
+ <form enctype="multipart/form-data" method="post" lapillus:id="commentform">
10
+ Add your comment here:
11
+ <p/>
12
+ <textarea name="commentform.text" lapillus:id="text"></textarea>
13
+ <p/>
14
+ <input type="submit" value="Submit"/>
15
+ <input name="page" type="hidden" value="GuestBook"/></form>
16
+ <p/>
17
+ <span lapillus:id="commentslist"/>
18
+ <!--<lapillus:remove>\n <p>\n\t 1/2/2004<br/>\n\t More comment text here.\n </p>\n </lapillus:remove> -->
19
+ </body>
20
+ </html>
21
+ EOF
22
+
23
+ guestbook = GuestBook.new
24
+ assert_equal(expected.strip, guestbook.render)
25
+ end
26
+
27
+ # def test_guestbook_post
28
+ # #xxxform = Form.new("form")
29
+ # guestbook = GuestBook.new
30
+ # values = {'commentform.text'=> "a nice story!"}
31
+ # guestbook.post(values)
32
+ # end
33
+
34
+ end
@@ -0,0 +1,24 @@
1
+ require 'test/unit'
2
+ require 'examples/hello_world'
3
+
4
+ class TC_HelloWorld < Test::Unit::TestCase
5
+ def test_hello_world
6
+ page = HelloWorld.new
7
+ assert_equal(1, page.components.size)
8
+ assert page.message.has_model?
9
+ assert_equal("Hello world!", page.message.model)
10
+ assert_equal("Hello world!", page.message.value)
11
+ end
12
+
13
+ # def test_html
14
+ # hello_world = HelloWorld.new
15
+ # result = <<EOF
16
+ #<html>
17
+ #<body>
18
+ # <span lapillus:id="message">Hello World!</span>
19
+ #</body>
20
+ #</html>
21
+ #EOF
22
+ # assert_equal( result.strip, hello_world.render )
23
+ # end
24
+ end
@@ -0,0 +1,13 @@
1
+ require 'test/unit'
2
+ require 'examples/persons'
3
+
4
+ class TC_Persons < Test::Unit::TestCase
5
+ def test_persons
6
+ page = Persons.new
7
+ assert_equal(1, page.components.size)
8
+ assert_equal(2, page.personlist.size)
9
+ assert_equal("jantje", page.personlist[0].name.value)
10
+ assert_equal("pietje", page.personlist[1].name.value)
11
+ # puts page.render
12
+ end
13
+ end
@@ -0,0 +1,24 @@
1
+ <html>
2
+ <body>
3
+ <form lapillus:id = "commentform">
4
+ Add your comment here:
5
+ <p/>
6
+ <textarea lapillus:id = "text">This is a comment</textarea>
7
+ <p/>
8
+ <input type = "submit" value = "Submit"/>
9
+ </form>
10
+ <p/>
11
+ <span lapillus:id = "commentslist">
12
+ <p>
13
+ <span lapillus:id = "date">1/1/2004</span><br/>
14
+ <span lapillus:id = "text">Comment text goes here.</span>
15
+ </p>
16
+ </span>
17
+ <!--<lapillus:remove>
18
+ <p>
19
+ 1/2/2004<br/>
20
+ More comment text here.
21
+ </p>
22
+ </lapillus:remove> -->
23
+ </body>
24
+ </html>
@@ -0,0 +1,5 @@
1
+ <html>
2
+ <body>
3
+ <span lapillus:id="message">Message goes here</span>
4
+ </body>
5
+ </html>
@@ -0,0 +1,12 @@
1
+ <html>
2
+ <body>
3
+ <span lapillus:id="label">label</span>
4
+ <span lapillus:id="link">link</span>
5
+ <form lapillus:id="form">
6
+ <input id="name" lapillus:id="name"/>
7
+ <input id="title" lapillus:id="title"/>
8
+ <input type="submit" value="button 1"/>
9
+ <input type="submit" value="button 2"/>
10
+ </form>
11
+ </body>
12
+ </html>
data/html/Persons.html ADDED
@@ -0,0 +1,7 @@
1
+ <html>
2
+ <body>
3
+ <span lapillus:id = "personlist">
4
+ <span lapillus:id = "name">Name goes here</span><br/>
5
+ </span>
6
+ </body>
7
+ </html>
@@ -0,0 +1,8 @@
1
+ <html>
2
+ <body>
3
+ This should be visible
4
+ <span lapillus:id="remote_panel">
5
+ NOT visible
6
+ </span>
7
+ </body>
8
+ </html>
@@ -0,0 +1,8 @@
1
+ <html>
2
+ <title>
3
+ not visible
4
+ </title>
5
+ <body>
6
+ contents of remote panel
7
+ </body>
8
+ </html>
data/lib/changelog.txt ADDED
@@ -0,0 +1,6 @@
1
+ Changes Lapillus 0.0.1 --> 0.0.2
2
+ - Applied mini-patch from Willem van den Ende to the examples
3
+ - Added processUpload helper module
4
+ - Added Image class
5
+ - Fixed render bugs in RemotePanel class
6
+ - Applied MIT license to source code
@@ -0,0 +1,276 @@
1
+ module Lapillus
2
+
3
+ class Component
4
+ attr_reader :identifier, :property, :behaviours
5
+ attr_writer :visible, :model
6
+ def initialize(id, model=nil, property=nil)
7
+ @identifier = id
8
+ @model = model
9
+ @property = property
10
+ @behaviours = {}
11
+ @visible = true
12
+ end
13
+ #TODO: make model defensive!
14
+ def model
15
+ if @model.kind_of?(Symbol)
16
+ parent.send(@model)
17
+ else
18
+ @model
19
+ end
20
+ end
21
+ def parent
22
+ raise "parent not set!" if @parent.nil?
23
+ @parent
24
+ end
25
+ def value
26
+ if property.nil?
27
+ model
28
+ else
29
+ model.send(property)
30
+ end
31
+ end
32
+
33
+ def on_render
34
+ end
35
+
36
+ def visible?
37
+ @visible
38
+ end
39
+
40
+ def has_model?
41
+ !@model.nil?
42
+ end
43
+
44
+ #TODO: test parent, hierarchy, path, page
45
+ def has_parent?
46
+ !@parent.nil?
47
+ end
48
+
49
+ #TODO: add test for path that starts with a panel instead of an Webpage or Form
50
+ #NOTE: its not only panel... only webpage and form we do not want to see...
51
+ def path
52
+ hier = hierarchy
53
+ if hier[0].kind_of?(Panel)
54
+ start = 0
55
+ else
56
+ start = 1
57
+ end
58
+ hier[start..-1].collect {|component| component.identifier}.join(".")
59
+ end
60
+
61
+ def webpage
62
+ hierarchy.first
63
+ end
64
+
65
+ #TODO: this method is only tested indirectly
66
+ def session
67
+ request_cycle = RequestCycle.get()
68
+ return request_cycle.session
69
+ end
70
+
71
+ #TODO: this method is only tested indirectly
72
+ def response_page= page
73
+ request_cycle = RequestCycle.get()
74
+ request_cycle.response_page = page
75
+ end
76
+
77
+ def add_behaviour(behaviour)
78
+ behaviour.parent=self
79
+ @behaviours[behaviour.class] = behaviour
80
+ end
81
+
82
+ def has_behaviour?(behaviour_class)
83
+ return behaviour(behaviour_class)
84
+ end
85
+
86
+ def behaviour(behaviour_class)
87
+ return @behaviours[behaviour_class]
88
+ end
89
+
90
+ # this method is meant for AJAX
91
+ def render_component
92
+ doc = REXML::Document.new webpage.default_htmlfile
93
+ result_dom = webpage.render_container(doc.root)
94
+ element = REXML::XPath.first( result_dom, "//*[@id=\"#{path}\"]" )
95
+ if element == nil
96
+ message = "Identifier "+identifier+" not found! in "+doc.to_s
97
+ raise message
98
+ end
99
+ to_send = element.children.inject("") {|result, child|
100
+ child.write(result)
101
+ }
102
+ # puts "DEBUG: "+to_send
103
+ return to_send
104
+ end
105
+
106
+ protected
107
+ def hierarchy
108
+ return parent.hierarchy + [self] if has_parent?
109
+ [self]
110
+ end
111
+
112
+ def render_container(container_element)
113
+ new_element = REXML::Element.new(container_element)
114
+ render_to_element(new_element)
115
+ render_behaviours(new_element)
116
+ return new_element
117
+ end
118
+
119
+ def render_behaviours(element)
120
+ @behaviours.each_value {|behaviour| behaviour.render_to_element(element)}
121
+ end
122
+
123
+ #TODO: not here!
124
+ def post(values)
125
+ end
126
+
127
+ def parent=parent
128
+ @parent = parent
129
+ end
130
+
131
+ private
132
+ def remove_behaviour(behaviour_class)
133
+ @behaviours.delete(behaviour_class)
134
+ end
135
+ end
136
+
137
+ class Container < Component
138
+ attr_reader :components
139
+ def self.add_component(id, clazz, model=nil)
140
+ if model.nil?
141
+ internal_add_component(id) { clazz.new(id) }
142
+ else
143
+ internal_add_component(id) { clazz.new(id, model) }
144
+ end
145
+ end
146
+ private
147
+ def self.internal_add_component(id, &block)
148
+ stored_components << StoredComponent.new(id, block)
149
+ define_method id do
150
+ component(id)
151
+ end
152
+ end
153
+ def self.internal_add_block(id, block)
154
+ stored_blocks[id]=block
155
+ end
156
+ def self.block(id)
157
+ stored_blocks[id]
158
+ end
159
+
160
+ public
161
+ def initialize(id, model=nil, property=nil)
162
+ super(id, model, property)
163
+ @components = []
164
+ classes_to_process.each {|clazz|
165
+ clazz.stored_components.each {|stored_component|
166
+ #TODO model symbol stuff
167
+ new_component = stored_component.block.call
168
+ new_component.parent = self #NOTE: I could do this in the constructor!
169
+ components << new_component
170
+ }
171
+ }
172
+ end
173
+
174
+ #deprecated
175
+ def add(component)
176
+ @components.push(component)
177
+ component.parent = self
178
+ end
179
+
180
+ #deprecated
181
+ def [](path)
182
+ pathparts = path.split('.')
183
+ result = nil
184
+ container = self
185
+ pathparts.each {|pathpart|
186
+ result = container.component(pathpart)
187
+ container = result
188
+ }
189
+ return result
190
+ end
191
+
192
+ #deprecated
193
+ def component(identifier)
194
+ result = components.find{|component|
195
+ component.identifier.eql?(identifier)
196
+ }
197
+ raise "Component #{identifier} does not exist in container #{self.path}!\n" if result.nil?
198
+ return result
199
+ end
200
+
201
+ #NOTE: is this really the responsibility of container?
202
+ def post(values)
203
+ components.each {|component| component.post(values)}
204
+ end
205
+
206
+ def render_container(container)
207
+ new_element = REXML::Element.new(container)
208
+ render_children(container, new_element)
209
+ render_to_element(new_element)
210
+ render_behaviours(new_element)
211
+ return new_element
212
+ end
213
+
214
+ protected
215
+ def render_children(container_input,container_output)
216
+ container_input.children.each do |child|
217
+ case child.node_type
218
+ when :text
219
+ container_output.add_text(child.value)
220
+ when :element
221
+ #puts "processing element: "+child.name
222
+ if child.name!="fragment"
223
+ component_id = child.attributes['lapillus:id']
224
+ if !component_id.nil?
225
+ child_component = self[component_id]
226
+ child_component.on_render
227
+ container_output.add(child_component.render_container(child)) if child_component.visible?
228
+ else
229
+ new_element = REXML::Element.new(child)
230
+ container_output.add(new_element)
231
+ render_children(child, new_element)
232
+ end
233
+ end
234
+ when :comment
235
+ comment = REXML::Comment.new(child)
236
+ container_output.add(comment)
237
+ else
238
+ puts "unknown node type: "+child.node_type.to_s
239
+ end
240
+ end
241
+ return container_output
242
+ end
243
+
244
+ def render_to_element(element)
245
+ end
246
+
247
+ private
248
+ def classes_to_process
249
+ classes = []
250
+ class_to_start_with = self.class
251
+ while (class_to_start_with.superclass!=Component)
252
+ classes << class_to_start_with
253
+ class_to_start_with = class_to_start_with.superclass
254
+ end
255
+ return classes
256
+ end
257
+
258
+ def self.stored_components
259
+ @stored_components ||= []
260
+ end
261
+ def self.stored_blocks
262
+ @stored_blocks ||= {}
263
+ end
264
+ end
265
+
266
+ #Note: Internal
267
+ class StoredComponent
268
+ attr_reader :identifier, :block
269
+
270
+ def initialize(id, block)
271
+ @identifier = id
272
+ @block = block
273
+ end
274
+ end
275
+
276
+ end