lapillus 0.0.2 → 0.0.3

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