lapillus 0.0.2

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.
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