lapillus 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -16,7 +16,7 @@ end
16
16
 
17
17
  class CommentForm < Form
18
18
  attr_reader :comment
19
- textarea "text", :model => :comment, :property => :text
19
+ textarea :text, :model => :comment, :property => :text
20
20
  def initialize(id)
21
21
  super(id)
22
22
  @comment = Comment.new
@@ -30,14 +30,14 @@ end
30
30
 
31
31
  class GuestBook < Webpage
32
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))
33
+ listview :commentslist, :model => :comments do |comment|
34
+ add(Label.new(:date, comment.date.to_s))
35
+ add(MultiLineLabel.new(:text, comment.text))
36
36
  end
37
37
  def initialize
38
38
  super()
39
39
  @comments = []
40
- add(CommentForm.new("commentform"))
40
+ add(CommentForm.new(:commentform))
41
41
  end
42
42
  end
43
43
 
@@ -3,7 +3,7 @@ require 'lapillus'
3
3
  include Lapillus
4
4
 
5
5
  class HelloWorld < Webpage
6
- label "message", :model => "Hello world!"
6
+ label :message, :model => "Hello world!"
7
7
  end
8
8
 
9
9
  if __FILE__ == $0
@@ -10,10 +10,10 @@ class Person
10
10
  end
11
11
  end
12
12
 
13
- class Persons < Webpage
13
+ class Personspage < Webpage
14
14
  attr_reader :persons
15
- listview "personlist", :model => :persons do |person|
16
- label "name", :model => person, :property => :name
15
+ listview :personlist, :model => :persons do |person|
16
+ label :name, :model => person, :property => :name
17
17
  end
18
18
 
19
19
  def initialize
@@ -25,6 +25,6 @@ class Persons < Webpage
25
25
  end
26
26
 
27
27
  if __FILE__ == $0
28
- server = LapillusServer.new(:root_url => "/", :port => 2000, :homepage => Persons)
28
+ server = LapillusServer.new(:root_url => "/", :port => 2000, :homepage => Personspage)
29
29
  server.start
30
30
  end
@@ -10,15 +10,15 @@ class TC_HelloWorld < Test::Unit::TestCase
10
10
  assert_equal("Hello world!", page.message.value)
11
11
  end
12
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
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
24
  end
@@ -1,9 +1,9 @@
1
1
  require 'test/unit'
2
- require 'examples/persons'
2
+ require 'examples/personspage'
3
3
 
4
4
  class TC_Persons < Test::Unit::TestCase
5
5
  def test_persons
6
- page = Persons.new
6
+ page = Personspage.new
7
7
  assert_equal(1, page.components.size)
8
8
  assert_equal(2, page.personlist.size)
9
9
  assert_equal("jantje", page.personlist[0].name.value)
File without changes
data/lib/changelog.txt CHANGED
@@ -1,3 +1,13 @@
1
+ Changes Lapillus 0.0.2 --> 0.0.3
2
+ - Refactoring: Component identifiers have become Symbols instead of Strings
3
+ - Refactoring: use named parameters in Component constructor to set options (like :visible=false)
4
+ - Refactoring: Persons example renamed to PersonsPage example
5
+ - Refactoring: Lapillus now works with ruby 1.8.6 (version contains some changes in the REXML API)
6
+ - Refactoring: HTML rendering is now done using the visitor pattern instead of recursion
7
+ - First version of the Lapillus website (Thanks to Joris van Zundert)
8
+ URL: http://hi3.huygensinstituut.nl:2000/
9
+
10
+
1
11
  Changes Lapillus 0.0.1 --> 0.0.2
2
12
  - Applied mini-patch from Willem van den Ende to the examples
3
13
  - Added processUpload helper module
data/lib/lapillus/base.rb CHANGED
@@ -1,14 +1,76 @@
1
1
  module Lapillus
2
2
 
3
- class Component
3
+
4
+ class RenderableComponent
5
+ def initialize
6
+ @behaviours = {}
7
+ @visible = true
8
+ end
9
+ def render_container(visitor, element)
10
+ on_render
11
+ return if !visible?
12
+ new_element = REXML::Element.new(element)
13
+ visitor.container_output.add(new_element)
14
+ visitor.container_output = new_element
15
+ render_children(visitor, element)
16
+ visitor.container_output = new_element.parent
17
+ render_to_element(new_element)
18
+ render_behaviours(new_element)
19
+ end
20
+
21
+ def visible?
22
+ @visible
23
+ end
24
+
25
+ def render_behaviours(element)
26
+ @behaviours.each_value {|behaviour| behaviour.render_to_element(element)}
27
+ end
28
+
29
+ #NOTE: override in subclasses
30
+ def render_children(visitor, element)
31
+ end
32
+
33
+ #NOTE: override in subclasses
34
+ def render_to_element(new_element)
35
+ end
36
+
37
+ #NOTE: override in subclasses
38
+ def on_render
39
+ end
40
+
41
+ # this method is meant for AJAX
42
+ def render_component
43
+ doc = REXML::Document.new webpage.default_htmlfile
44
+ visitor = HtmlVisitor.new(webpage)
45
+ doc.root.accept(visitor)
46
+ result_dom = visitor.container_output.root
47
+ element = REXML::XPath.first( result_dom, "//*[@id=\"#{path}\"]" )
48
+ if element == nil
49
+ message = "Identifier "+path+" not found! in "+doc.to_s
50
+ raise message
51
+ end
52
+ formatter = REXML::Formatters::Default.new
53
+ to_send = element.children.inject("") {|result, child|
54
+ formatter.write(child, result)
55
+ }
56
+ #puts "DEBUG: "+to_send
57
+ return to_send
58
+ end
59
+
60
+
61
+ end
62
+
63
+ class Component < RenderableComponent
4
64
  attr_reader :identifier, :property, :behaviours
5
65
  attr_writer :visible, :model
6
- def initialize(id, model=nil, property=nil)
66
+ def initialize(id, options={})
67
+ super()
68
+ raise "FATAL: use a symbol as identifier! #{id.class}: #{id}" if id!=""&&!id.kind_of?(Symbol)
69
+ raise "FATAL: use named parameters! #{options.class}" if !options.kind_of?(Hash)
7
70
  @identifier = id
8
- @model = model
9
- @property = property
10
- @behaviours = {}
11
- @visible = true
71
+ @model = options[:model]
72
+ @property = options[:property]
73
+ @visible = options[:visible]||true
12
74
  end
13
75
  #TODO: make model defensive!
14
76
  def model
@@ -30,13 +92,6 @@ module Lapillus
30
92
  end
31
93
  end
32
94
 
33
- def on_render
34
- end
35
-
36
- def visible?
37
- @visible
38
- end
39
-
40
95
  def has_model?
41
96
  !@model.nil?
42
97
  end
@@ -87,21 +142,6 @@ module Lapillus
87
142
  return @behaviours[behaviour_class]
88
143
  end
89
144
 
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
145
 
106
146
  protected
107
147
  def hierarchy
@@ -109,17 +149,6 @@ module Lapillus
109
149
  [self]
110
150
  end
111
151
 
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
152
  #TODO: not here!
124
153
  def post(values)
125
154
  end
@@ -128,7 +157,7 @@ module Lapillus
128
157
  @parent = parent
129
158
  end
130
159
 
131
- private
160
+ # private
132
161
  def remove_behaviour(behaviour_class)
133
162
  @behaviours.delete(behaviour_class)
134
163
  end
@@ -158,8 +187,8 @@ module Lapillus
158
187
  end
159
188
 
160
189
  public
161
- def initialize(id, model=nil, property=nil)
162
- super(id, model, property)
190
+ def initialize(id, options={})
191
+ super(id, options)
163
192
  @components = []
164
193
  classes_to_process.each {|clazz|
165
194
  clazz.stored_components.each {|stored_component|
@@ -191,6 +220,7 @@ module Lapillus
191
220
 
192
221
  #deprecated
193
222
  def component(identifier)
223
+ identifier = identifier.intern if identifier.kind_of?(String)
194
224
  result = components.find{|component|
195
225
  component.identifier.eql?(identifier)
196
226
  }
@@ -201,49 +231,19 @@ module Lapillus
201
231
  #NOTE: is this really the responsibility of container?
202
232
  def post(values)
203
233
  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
234
  end
213
235
 
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
236
 
244
- def render_to_element(element)
237
+ #TODO: duplicate with NullComponent.render_children!
238
+ def render_children(visitor, element)
239
+ visitor.current_container = self #NOTE: Added!
240
+ element.children.each do |child|
241
+ # puts child.class
242
+ child.accept(visitor)
243
+ end
244
+ visitor.current_container = self.parent #NOTE: Added!
245
245
  end
246
-
246
+
247
247
  private
248
248
  def classes_to_process
249
249
  classes = []
@@ -266,7 +266,7 @@ module Lapillus
266
266
  #Note: Internal
267
267
  class StoredComponent
268
268
  attr_reader :identifier, :block
269
-
269
+
270
270
  def initialize(id, block)
271
271
  @identifier = id
272
272
  @block = block
@@ -1,11 +1,26 @@
1
1
  require 'rexml/attribute'
2
2
  require 'yaml'
3
3
 
4
+ module REXML
5
+ class Element
6
+ def add_javascript(javascript)
7
+ script = REXML::Element.new('script')
8
+ script.add_attributes({'type'=>'text/javascript', 'language'=>'javascript'})
9
+ text=REXML::Text.new("\n// <![CDATA[\n#{javascript}\n// ]]>\n")
10
+ def text.to_s
11
+ @normalized=@string
12
+ end
13
+ script.add_text(text)
14
+ self.add_element(script)
15
+ end
16
+ end
17
+ end
18
+
4
19
  module Lapillus
5
20
 
6
21
  class Behaviour < Component
7
- def initialize(id="", value=nil)
8
- super(id, value)
22
+ def initialize(id="", options={})
23
+ super(id, options)
9
24
  end
10
25
 
11
26
  # def add_javascript_to_header root, src
@@ -27,10 +42,10 @@ end
27
42
 
28
43
  class AttributeModifier < Behaviour
29
44
  def render_to_element(element)
30
- attribute = REXML::Attribute.new(identifier, value)
31
- def attribute.to_string
32
- return "#@expanded_name=\"#@value\""
33
- end
45
+ attribute = REXML::Attribute.new(identifier.to_s, value)
46
+ # def attribute.to_string
47
+ # return "#@expanded_name=\"#@value\""
48
+ # end
34
49
  element.add_attribute(attribute)
35
50
  end
36
51
  end
@@ -38,7 +53,7 @@ end
38
53
  #TODO test
39
54
  class UniqueIdentifier < AttributeModifier
40
55
  def initialize
41
- super("id")
56
+ super(:id)
42
57
  end
43
58
  def value
44
59
  parent.path
@@ -47,10 +62,10 @@ end
47
62
 
48
63
  class FadeIn < Behaviour
49
64
  def render_to_element(element)
50
- attribute = REXML::Attribute.new(identifier, "new Effect.Appear('#{value}');")
51
- def attribute.to_string
52
- return "#@expanded_name=\"#@value\""
53
- end
65
+ attribute = REXML::Attribute.new(identifier.to_s, "new Effect.Appear('#{value}');")
66
+ # def attribute.to_string
67
+ # return "#@expanded_name=\"#@value\""
68
+ # end
54
69
  element.add_attribute(attribute)
55
70
  end
56
71
  end
@@ -108,15 +123,8 @@ class Draggable < UniqueIdentifier
108
123
  #add_javascript_to_header(element.root_node,"/editiemachine/context/javascript/prototype.js")
109
124
  #add_javascript_to_header(element.root_node,"/editiemachine/context/javascript/scriptaculous.js")
110
125
 
111
- script = REXML::Element.new('script')
112
- script.add_attributes({'type'=>'text/javascript', 'language'=>'javascript'})
113
126
  options=@option.to_a.collect{|o| "#{o[0]}:#{o[1]}" }.sort.concat(@extra_options).join(",")
114
- text=REXML::Text.new("\n// <![CDATA[\nnew Draggable('#{parent.path}', {#{options}});\n// ]]>\n")
115
- def text.to_s
116
- @normalized=@string
117
- end
118
- script.add_text(text)
119
- element.add_element(script)
127
+ element.add_javascript("new Draggable('#{parent.path}', {#{options}});")
120
128
  end
121
129
  end
122
130
 
@@ -124,7 +132,7 @@ end
124
132
  # todo: add test
125
133
  class OnClick < Behaviour
126
134
  def initialize(component_to_refresh=nil)
127
- super(nil, component_to_refresh)
135
+ super("", :model => component_to_refresh)
128
136
  component_to_refresh.add_behaviour(UniqueIdentifier.new) if has_model?
129
137
  end
130
138
 
@@ -135,20 +143,34 @@ class OnClick < Behaviour
135
143
  javascript = "new Ajax.Request('', { method: 'get', parameters: { listener:'#{parent.path}' }}); return false;"
136
144
  end
137
145
  attribute = REXML::Attribute.new("onclick", javascript)
138
- # TODO: this should be the default for attributes in
139
- # Lapillus somewhere!
140
- def attribute.to_string
141
- return "#@expanded_name=\"#@value\""
142
- end
146
+ # # TODO: this should be the default for attributes in
147
+ # # Lapillus somewhere!
148
+ # def attribute.to_string
149
+ # return "#@expanded_name=\"#@value\""
150
+ # end
143
151
  element.add_attribute(attribute)
144
152
  end
145
153
  end
146
154
 
155
+ class PostWithAjax < AttributeModifier
156
+ attr_accessor :component_to_refresh
157
+ def initialize(component_to_refresh=nil)
158
+ super(:onsubmit)
159
+ @component_to_refresh = component_to_refresh
160
+ component_to_refresh.add_behaviour(UniqueIdentifier.new) if !component_to_refresh.nil?
161
+ end
162
+
163
+ def value
164
+ updateScript = "new Ajax.Updater('#{component_to_refresh.path}', '', { evalScripts: true, method: 'post', parameters:Form.serialize(this)}); return false;"
165
+ return updateScript
166
+ end
167
+ end
168
+
147
169
  # value is the component to rerender when a link is clicked
148
170
  # the component should have an id attribute which is set to its path
149
171
  class AjaxLink < Container
150
- def initialize(id, value=nil)
151
- super(id, value)
172
+ def initialize(id, options={})
173
+ super(id, options)
152
174
  value.add_behaviour(UniqueIdentifier.new) if has_model?
153
175
  end
154
176
 
@@ -159,9 +181,9 @@ class AjaxLink < Container
159
181
  javascript = "new Ajax.Request('', { method: 'get', parameters: { listener:'#{path}' }}); return false;"
160
182
  end
161
183
  attribute = REXML::Attribute.new("onclick", javascript)
162
- def attribute.to_string
163
- return "#@expanded_name=\"#@value\""
164
- end
184
+ # def attribute.to_string
185
+ # return "#@expanded_name=\"#@value\""
186
+ # end
165
187
  element.add_attribute(attribute)
166
188
  element.add_attribute("href","#")
167
189
  end
@@ -194,14 +216,7 @@ class Droppable < UniqueIdentifier
194
216
  });
195
217
  EOF
196
218
  super
197
- script= REXML::Element.new('script')
198
- script.add_attributes({'type'=>'text/javascript', 'language'=>'javascript'})
199
- text=REXML::Text.new("\n// <![CDATA[\n#{javascript}\n// ]]>\n")
200
- def text.to_s
201
- @normalized=@string
202
- end
203
- script.add_text(text)
204
- element.add_element(script)
219
+ element.add_javascript(javascript)
205
220
  end
206
221
  end
207
222
 
@@ -1,4 +1,5 @@
1
1
  require 'lapillus/base'
2
+ require 'uri'
2
3
 
3
4
  module Lapillus
4
5
 
@@ -23,7 +24,7 @@ module Lapillus
23
24
  options.keys.each {|key|
24
25
  raise "Unknown key: #{key}" if key!=:model and key!=:property
25
26
  }
26
- internal_add_component(id) { Label.new(id, options[:model], options[:property]) }
27
+ internal_add_component(id) { Label.new(id, options) }
27
28
  end
28
29
 
29
30
  #TODO: Add test!
@@ -32,7 +33,7 @@ module Lapillus
32
33
  options.keys.each {|key|
33
34
  raise "Unknown key: #{key}" if key!=:model and key!=:property
34
35
  }
35
- add(Label.new(id, options[:model], options[:property]))
36
+ add(Label.new(id, options))
36
37
  end
37
38
  end
38
39
 
@@ -84,14 +85,15 @@ module Lapillus
84
85
  class Image < Component
85
86
  attr_reader :root_url
86
87
 
87
- def initialize(id, model, root_url)
88
- super(id, model)
88
+ def initialize(id, options={})
89
+ super(id, options)
90
+ root_url = options[:root_url]
89
91
  raise "root_url is not set!" if root_url.nil?
90
92
  @root_url = root_url
91
93
  end
92
94
 
93
95
  def render_to_element(element)
94
- url = "/images/#{value}"
96
+ url = "/images/#{URI::escape(value)}"
95
97
  url = root_url + url if root_url != "/"
96
98
  element.add_attribute("src", url)
97
99
  end
@@ -101,7 +103,7 @@ module Lapillus
101
103
  options.keys.each {|key|
102
104
  raise "Unknown key: #{key}" if key!=:model and key!=:root_url
103
105
  }
104
- internal_add_component(id) { Image.new(id, options[:model], options[:root_url]) }
106
+ internal_add_component(id) { Image.new(id, options) }
105
107
  end
106
108
 
107
109
  class ListItem < Container
@@ -109,7 +111,7 @@ module Lapillus
109
111
  options.keys.each {|key|
110
112
  raise "Unknown key: #{key}" if key!=:model and key!=:root_url
111
113
  }
112
- add(Image.new(id, options[:model], options[:root_url]))
114
+ add(Image.new(id, options))
113
115
  end
114
116
  end
115
117