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,216 @@
1
+ require 'cgi'
2
+ require 'cgi/session'
3
+ require 'yaml'
4
+ require 'thread'
5
+
6
+ class Dispatcher
7
+ # @@guard = Mutex.new
8
+
9
+
10
+ HTTPHEADER = { 'status'=> 'OK', 'Content-Type' => 'text/html; charset=utf-8'}
11
+ def Dispatcher.dispatch(cgi, application)
12
+ # @@guard.synchronize do
13
+ request_method = cgi.env_table['REQUEST_METHOD']
14
+ case request_method
15
+ when "GET"
16
+ Dispatcher.get(cgi, application)
17
+ when "POST"
18
+ Dispatcher.post(cgi, application)
19
+ else
20
+ raise "unexpected request method: "+request_method
21
+ end
22
+ # end
23
+ end
24
+
25
+ #TODO: remove duplication between get and post
26
+ #TODO: set request cycle to nil when done
27
+ #TODO: remove duplication between new page and respond to event
28
+ def Dispatcher.get(cgi, application)
29
+ # log = WEBrick::Log.new
30
+ # log.info("URI path= #{cgi.env_table['PATH_INFO']}")
31
+
32
+ # puts;puts cgi.env_table.each {|k,v| puts k.to_s+":"+v.to_s};puts
33
+
34
+ session = Dispatcher.get_session(cgi)
35
+
36
+ request_cycle = RequestCycle.new(session)
37
+ RequestCycle.set(request_cycle)
38
+ path_info = cgi.env_table['PATH_INFO']
39
+
40
+ if cgi.has_key?'listener'
41
+ puts "AJAXREQUEST! "+cgi['listener'].to_s
42
+
43
+ pathparts=cgi.env_table['REQUEST_URI'].split('/')
44
+ if pathparts.length==2
45
+ page_identifier = application.homepage.to_s
46
+ else
47
+ pagepath = pathparts[2]
48
+ page_identifier = application.bookmarkable_pages[pagepath].to_s
49
+ end
50
+
51
+ page = restore_page_from_session(page_identifier, path_info, session)
52
+ path_to_listener = cgi['listener']
53
+ listener = page[path_to_listener]
54
+ #TODO: this should be moved to the listener
55
+ #TODO: we should always store the session whether we
56
+ #rerender something or not
57
+ puts "!!"+listener.class.to_s
58
+ path_to_component = cgi['component']
59
+ #NOTICE: non existing cgi variables are return as empty string
60
+ if cgi.has_key?'menuitem'
61
+ listener.on_menu(cgi['menuitem'])
62
+ store_page_in_session(page,path_info,session)
63
+ path_to_redirect_to = cgi.env_table['REQUEST_URI']
64
+ path_to_redirect_to = path_to_redirect_to.split("?")[0]
65
+ puts "!!"+path_to_redirect_to
66
+ cgi.out({"status" => "302", "Location" => path_to_redirect_to}) do
67
+ ""
68
+ end
69
+ # cgi.header("Location" => path_to_redirect_to)
70
+ # cgi.out(HTTPHEADER) { page.render }
71
+ return
72
+
73
+ else
74
+ if path_to_component!=""
75
+ component = page[path_to_component]
76
+ listener.on_drop(component)
77
+ else
78
+ listener.on_click
79
+ end
80
+ store_page_in_session(page,path_info,session)
81
+
82
+ cgi.out(HTTPHEADER) { listener.render_component }
83
+ return
84
+ end
85
+ end
86
+
87
+ pathparts=path_info.split('/')
88
+ if pathparts.length<=2
89
+ page_identifier = application.homepage.to_s
90
+ else
91
+ pagepath = pathparts[2]
92
+ page_identifier = application.bookmarkable_pages[pagepath].to_s
93
+ end
94
+
95
+ # attribute = "pagemap_#{page_identifier}"
96
+ # page = session[attribute][1] if !session[attribute].nil?
97
+ page = session[path_info]
98
+ if !page.nil? #&& path_info == session[attribute][0]
99
+ render_page_and_store_in_session(request_cycle, page, session, cgi, path_info)
100
+ return
101
+ end
102
+
103
+ pathparts=path_info.split('/')
104
+ # puts "pathparts.length #{pathparts.length}"
105
+ # puts pathparts.join("::")
106
+ if (pathparts.length<2)
107
+ page = application.homepage.new
108
+ else
109
+ page_params = Hash.new
110
+ pagepath = pathparts[1]
111
+ (2..pathparts.length-1).step(2){|i| page_params[pathparts[i]]=pathparts[i+1]}
112
+ # puts pagepath
113
+ page = application.bookmarkable_pages[pagepath].new(page_params)
114
+ end
115
+
116
+ render_page_and_store_in_session(request_cycle, page, session, cgi, path_info)
117
+ end
118
+
119
+ def Dispatcher.post(cgi, application)
120
+ session = Dispatcher.get_session(cgi)
121
+
122
+ request_cycle = RequestCycle.new(session)
123
+ RequestCycle.set=request_cycle
124
+ # puts "cgi_page=#{cgi['page'].string}"
125
+ page = restore_page_from_session(cgi['page'].string, cgi.env_table['PATH_INFO'], session)
126
+
127
+ values = Hash.new()
128
+ cgi.keys.each { |key| values[key] = cgi[key]}
129
+ page.post(values)
130
+ path_info = cgi.env_table['PATH_INFO']
131
+ render_page_and_store_in_session(request_cycle, page, session, cgi, path_info)
132
+ end
133
+
134
+ private
135
+ def Dispatcher.get_session(cgi)
136
+ session = CGI::Session.new(cgi, 'database_manager' => CGI::Session::MemoryStore)
137
+ # puts "session id= #{session.session_id}"
138
+ return session
139
+ end
140
+
141
+ def Dispatcher.render_page_and_store_in_session(request_cycle, page, session, cgi, path_info)
142
+ # puts "Dispatcher.render_page_and_store_in_session(#{request_cycle}, #{page}, #{session}, #{cgi}, #{path_info})"
143
+ if (request_cycle.response_page_set?)
144
+ page = request_cycle.response_page
145
+ end
146
+
147
+ store_page_in_session(page, path_info, session)
148
+
149
+ cgi.out(HTTPHEADER) { page.render }
150
+ end
151
+
152
+ #TODO: restrict the number of pages remembered
153
+ def Dispatcher.store_page_in_session(page, path_info, session)
154
+ # pagename = page.class.to_s #key
155
+ # page_info = [path_info, page] #value
156
+ # puts "Storing: "+pagename
157
+ # puts "STORING path_info: "+path_info
158
+ session[path_info] = page
159
+ # NOTE: We need to close the session before we can start streaming the output
160
+ session.close
161
+ end
162
+
163
+ #TODO: restrict the number of pages remembered
164
+ def Dispatcher.restore_page_from_session(page_identifier, path_info, session)
165
+ #puts "Restoring: "+page_identifier
166
+ # attribute = "pagemap_#{page_identifier}"
167
+ # puts "RESTORING path_info: "+path_info
168
+ # page_info = session[attribute]
169
+ # page = page_info[1]
170
+ # puts "session=#{session.to_yaml}"
171
+ # puts "path_info=[#{path_info}]"
172
+ page = session[path_info]
173
+ raise "page expired!" if (page==nil)
174
+ return page #if path_info == page_info[0]
175
+ # return nil
176
+ end
177
+ end
178
+
179
+
180
+ class RequestCycle
181
+ @response_page
182
+ @session
183
+
184
+ def initialize(session)
185
+ super()
186
+ @session = session
187
+ end
188
+
189
+ #TODO: attr_accessor
190
+ def response_page= page
191
+ @response_page = page
192
+ end
193
+
194
+ #TODO: attr_accessor
195
+ def response_page
196
+ @response_page
197
+ end
198
+
199
+ def response_page_set?
200
+ return response_page!=nil
201
+ end
202
+
203
+ #TODO: attr_reader
204
+ def session
205
+ @session
206
+ end
207
+
208
+ def RequestCycle.get
209
+ current_request_cycle = Thread.current["current_request_cycle"]
210
+ return current_request_cycle
211
+ end
212
+
213
+ def RequestCycle.set(request_cycle)
214
+ Thread.current["current_request_cycle"]= request_cycle
215
+ end
216
+ end
@@ -0,0 +1,88 @@
1
+ require 'lapillus/base'
2
+
3
+ module Lapillus
4
+ class FormComponent < Component
5
+ def post(values)
6
+ self.value=values[path] if values.has_key?(path)
7
+ end
8
+
9
+ def render_to_element(element)
10
+ element.attributes['name'] = path
11
+ end
12
+ def value
13
+ return super if has_model?
14
+ return parent.model.send(identifier) if parent.has_model?
15
+ raise "model not set!"
16
+ end
17
+
18
+ def value=new_value
19
+ if property.nil?
20
+ @model=new_value
21
+ else
22
+ model.send(property.to_s+"=", new_value)
23
+ end
24
+ end
25
+
26
+ end
27
+
28
+ class FormTextComponent < FormComponent
29
+ def render_to_element(element)
30
+ super
31
+ text = value
32
+ text = '' if text.nil? # TODO: should this be done here, or is this the responsibility of the model ?
33
+ render_text(element, text)
34
+ end
35
+
36
+ #TODO: current form test are not real life enough
37
+ #TODO: use stringio in test instead of string
38
+ def value=text
39
+ text = text.string if (text.kind_of?(StringIO))
40
+ super(text)
41
+ end
42
+ end
43
+
44
+ class TextField < FormTextComponent
45
+ def render_text(element, text)
46
+ element.add_attribute('value', text)
47
+ end
48
+ end
49
+
50
+ def Container.textfield(id, options={})
51
+ options.keys.each {|key|
52
+ raise "Unknown key: #{key}" if key!=:model and key!=:property
53
+ }
54
+ internal_add_component(id) { TextField.new(id, options[:model]||"", options[:property]) }
55
+ end
56
+
57
+ class TextArea < FormTextComponent
58
+ def render_text(element, text)
59
+ element.text = text
60
+ end
61
+ end
62
+
63
+ def Container.textarea(id, options={})
64
+ options.keys.each {|key|
65
+ raise "Unknown key: #{key}" if key!=:model and key!=:property
66
+ }
67
+ internal_add_component(id) { TextArea.new(id, options[:model]||"", options[:property]) }
68
+ end
69
+
70
+ class FileUploadField < FormComponent
71
+ end
72
+
73
+ def Container.fileuploadfield(id, options={})
74
+ options.keys.each {|key|
75
+ raise "Unknown key: #{key}" if key!=:model and key!=:property
76
+ }
77
+ internal_add_component(id) { FileUploadField.new(id, options[:model]||"", options[:property]) }
78
+ end
79
+
80
+ class PasswordTextField < TextField
81
+ def reset_password(bool)
82
+ end
83
+ end
84
+
85
+ def Container.password_textfield(id, model="", options={})
86
+ internal_add_component(id) { PasswordTextField.new(id, model, options[:property]) }
87
+ end
88
+ end
@@ -0,0 +1,21 @@
1
+ require 'lapillus'
2
+ require 'lapillus/webrick_server'
3
+
4
+ class LapillusServer
5
+ attr_reader :server
6
+ def initialize(params={})
7
+ root_url = params[:root_url]
8
+ homepage = params[:homepage]
9
+ port = params[:port]
10
+ raise "root_url not set!" if root_url.nil?
11
+ raise "homepage not set!" if homepage.nil?
12
+ raise "port not set!" if port.nil?
13
+ @server = WebrickServer.new(port)
14
+ web_application = WebApplication.new
15
+ web_application.homepage = homepage
16
+ server.mount(root_url, web_application)
17
+ end
18
+ def start
19
+ server.start
20
+ end
21
+ end
@@ -0,0 +1,167 @@
1
+ require 'test/unit'
2
+ require 'lapillus'
3
+ require 'yaml'
4
+
5
+ class LapillusTester
6
+ include Test::Unit::Assertions
7
+ attr_reader :html
8
+
9
+ @page_class
10
+ @page_instance
11
+
12
+ def initialize
13
+ super()
14
+ RequestCycle.set(RequestCycle.new(Hash.new))
15
+ end
16
+
17
+ def start_page(page)
18
+ @page_class = page
19
+ @page_instance = @page_class.new()
20
+ render
21
+ end
22
+
23
+ def start_page_instance(page_instance)
24
+ @page_class = page_instance.class
25
+ @page_instance = page_instance
26
+ render
27
+ end
28
+
29
+ #TODO: should this method be public?
30
+ def render
31
+ @html = @page_instance.render
32
+ end
33
+
34
+ def assert_component(path, expected_component_class)
35
+ component = get_component_from_last_rendered_page(path)
36
+ assert_kind_of(expected_component_class, component,"Component #{component} on path #{path} is not a(n) #{expected_component_class}")
37
+ end
38
+
39
+ def assert_contains(expected_content, actual_content=@html)
40
+ full_message = build_message(nil, "content ? not found, but was ? ", expected_content, actual_content)
41
+ assert_block full_message do
42
+ !@html[expected_content].nil?
43
+ end
44
+ end
45
+
46
+ def assert_error_messages(expected_error_messages)
47
+ # TODO: implement assert_error_messages(expected_error_messages)
48
+ flunk 'You need to implement assert_error_messages(expected_error_messages) first!'
49
+ end
50
+
51
+ def assert_expire_previous_page
52
+ # TODO: implement assert_expire_previous_page
53
+ flunk 'You need to implement assert_expire_previous_page first!'
54
+ end
55
+
56
+ def assert_info_messages(expected_info_messages)
57
+ # TODO: implement assert_info_messages(expected_info_messages)
58
+ flunk 'You need to implement assert_info_messages(expected_info_messages) first!'
59
+ end
60
+
61
+ def assert_invisible(path)
62
+ # TODO: implement assert_invisible(path)
63
+ flunk 'You need to implement assert_invisible(path) first!'
64
+ end
65
+
66
+ def assert_label(path, expected_label_text)
67
+ assert_component(path, Lapillus::Label)
68
+ assert_equal(expected_label_text, get_component_from_last_rendered_page(path).value)
69
+ end
70
+
71
+ def assert_list_view(path, expected_list)
72
+ # TODO: implement assert_list_view(path, expected_list)
73
+ flunk 'You need to implement assert_list_view(path, expected_list) first!'
74
+ end
75
+
76
+ def assert_no_error_message
77
+ # TODO: implement assert_no_error_message
78
+ flunk 'You need to implement assert_no_error_message first!'
79
+ end
80
+
81
+ def assert_no_info_message
82
+ # TODO: implement assert_no_info_message
83
+ flunk 'You need to implement assert_no_info_message first!'
84
+ end
85
+
86
+ def assert_page_link(path, expected_page_class)
87
+ click_link(path)
88
+ assert_rendered_page(expected_page_class)
89
+ end
90
+
91
+ def assert_rendered_page(expected_rendered_page_class)
92
+ assert_kind_of(expected_rendered_page_class, @page_instance)
93
+ end
94
+
95
+ def assert_visible(path)
96
+ # TODO: implement assert_visible(path)
97
+ flunk 'You need to implement assert_visible(path) first!'
98
+ end
99
+
100
+ def click_link(path)
101
+ link = get_component_from_last_rendered_page(path)
102
+ assert_not_nil(link,"Link <#{path}> not found!")
103
+ page_parameters = link.page_parameters
104
+ @page_instance = @page_class.new(page_parameters) # TODO: page can be found in the link!
105
+ @html = @page_instance.render
106
+ end
107
+
108
+ def debug_component_trees
109
+ # TODO: implement debug_component_trees
110
+ end
111
+
112
+ def dump_page
113
+ puts @html
114
+ end
115
+
116
+ def get_component_from_last_rendered_page(path)
117
+ path_parts=path.split(":")
118
+ component=@page_instance[path_parts[0]]
119
+ assert_not_nil(component, "No component found on path '#{path_parts[0]}'")
120
+ (1..path_parts.size-1).each { |i|
121
+ component=component[path_parts[i]]
122
+ assert_not_nil(component, "No component found on path '#{path_parts.slice(0..i).join(':')}'")
123
+ }
124
+ component
125
+ end
126
+
127
+ def get_messages(level)
128
+ # TODO: implement get_messages(level)
129
+ end
130
+
131
+ def new_form_tester(path)
132
+ form = get_component_from_last_rendered_page(path)
133
+ assert_not_nil(form, "Form <#{path}> not found!")
134
+ return FormTester.new(form, self)
135
+ end
136
+ end
137
+
138
+ class FormTester
139
+ include Test::Unit::Assertions
140
+
141
+ @values
142
+
143
+ def initialize(form, tester)
144
+ @form = form
145
+ @values = Hash[]
146
+ @tester = tester
147
+ end
148
+
149
+ def set_value(key, value)
150
+ field = @form[key]
151
+ assert_not_nil(field, "TextField <#{key}> in Form not found!")
152
+ @values["#{@form.identifier}.#{key}"] = value
153
+ end
154
+
155
+ def submit(button=nil)
156
+ request_cycle = RequestCycle.new(Hash.new())
157
+ RequestCycle.set(request_cycle)
158
+ @values['submit']= StringIO.new(button) if !button.nil?
159
+ @form.post(@values)
160
+ @tester.render
161
+ end
162
+
163
+ def model
164
+ @form.model
165
+ end
166
+
167
+ end
@@ -0,0 +1,69 @@
1
+ require 'rubygems'
2
+ require 'mongrel'
3
+ require 'cgi'
4
+ require 'active_record'
5
+
6
+ class MongrelServer
7
+
8
+ def initialize(port)
9
+ $stdout.sync=true
10
+ $stderr.sync=true
11
+ @port = port
12
+ @server = Mongrel::HttpServer.new("0.0.0.0", @port)
13
+ end
14
+
15
+ def start
16
+ trap("INT"){ @server.stop }
17
+ trap('TERM'){ @server.stop }
18
+ @server.run.join
19
+ end
20
+
21
+ def mount(name, application)
22
+ @server.register(name, MongrelCGIWrapper.new(application))
23
+ end
24
+
25
+ def mount_context(url, directory)
26
+ @server.register(url, Mongrel::DirHandler.new(directory))
27
+ end
28
+
29
+ def allow_termination_for_test
30
+ Mongrel::Terminate.set_server=@server
31
+ @server.register("/terminate", Mongrel::Terminate.new)
32
+ end
33
+ end
34
+
35
+ class MongrelCGIWrapper < Mongrel::HttpHandler
36
+ @application
37
+
38
+ def initialize(application)
39
+ super()
40
+ @application = application
41
+ ActiveRecord::Base.allow_concurrency=true
42
+ end
43
+
44
+ def process(request, response)
45
+ cgi = Mongrel::CGIWrapper.new(request, response)
46
+ begin
47
+ Dispatcher.dispatch(cgi,@application)
48
+ rescue => e
49
+ STDERR.puts e
50
+ STDERR.puts e.backtrace.join("\n")
51
+ ensure
52
+ ActiveRecord::Base.clear_active_connections!
53
+ end
54
+ end
55
+ end
56
+
57
+ module Mongrel
58
+ class Terminate < Mongrel::HttpHandler
59
+ def Terminate.set_server=server
60
+ @@server = server
61
+ end
62
+ def process(request, response)
63
+ puts "trying to shutdown server"
64
+ @@server.stop
65
+ end
66
+ end
67
+ end
68
+
69
+
@@ -0,0 +1,45 @@
1
+ require 'lapillus/containers'
2
+
3
+ class SingleView < Lapillus::Fragment
4
+ def initialize(id)
5
+ super(id, id)
6
+ end
7
+ end
8
+
9
+ class MultiView < Lapillus::Component
10
+ attr_accessor :mode
11
+ def initialize(id, views)
12
+ super(id)
13
+ @views = views
14
+ views.each {|view| view.parent = self } #TODO: add test!
15
+ @mode=views[0].identifier
16
+ end
17
+
18
+ def current_view(id=mode)
19
+ @views.each do |v|
20
+ return v if v.identifier == id
21
+ end
22
+ raise "view with identifier #{id} not found!"
23
+ nil
24
+ end
25
+
26
+ def render_container(html)
27
+ singleview = current_view
28
+ result = singleview.render_container(html)
29
+ render_behaviours(result)
30
+ return result
31
+ end
32
+
33
+ # TODO: add test
34
+ def post(values)
35
+ current_view.post(values)
36
+ end
37
+ # TODO: add test
38
+ def component(identifier)
39
+ return current_view.component(identifier)
40
+ end
41
+ # TODO: add test
42
+ def [](path)
43
+ current_view[path]
44
+ end
45
+ end
@@ -0,0 +1,104 @@
1
+ require 'lapillus'
2
+
3
+ module Lapillus
4
+
5
+ class Pager < Container
6
+ attr_reader :current, :page_container
7
+ attr_accessor :pages, :navigation, :first_button, :previous_button
8
+ attr_accessor :next_button, :last_button, :position
9
+ def initialize(id, pages, &block)
10
+ super(id)
11
+ raise "no block given!" if !block_given?
12
+ @pages = pages
13
+ @current = 1
14
+ @block = block
15
+ @navigation = Lapillus::Container.new("navigation")
16
+ @first_button = FirstButton.new("first", self)
17
+ @previous_button = PreviousButton.new("previous", self)
18
+ @next_button = NextButton.new("next", self)
19
+ @last_button = LastButton.new("last", self)
20
+ @position = Label.new("position")
21
+ navigation.add(first_button)
22
+ navigation.add(previous_button)
23
+ navigation.add(next_button)
24
+ navigation.add(last_button)
25
+ navigation.add(position)
26
+ build_hierarchy
27
+ end
28
+
29
+ #TODO: this is not so handy
30
+ #components are recreated and stuff; split in constructor/on_render
31
+ #also parent and path do not work
32
+ def build_hierarchy
33
+ @page_container = ListItem.new("page")
34
+ @page_container.parent = self #NOTE: I could do this in the constructor!
35
+ if !@pages.empty?
36
+ @page_container.instance_exec(page_content, &@block)
37
+ # @components << @page_container ???
38
+ first_button.visible= previous_button.visible = current > 1
39
+ last_button.visible= next_button.visible = current < pages.size
40
+ position.model = "#{@current} van #{@pages.size}"
41
+ page_container.visible = true
42
+ navigation.visible = true
43
+ else
44
+ page_container.visible = false
45
+ navigation.visible = false
46
+ end
47
+ @components = []
48
+ add(page_container)
49
+ add(navigation)
50
+ end
51
+
52
+ #TODO: rewrite using closures
53
+ class PagerButton < Lapillus::AjaxLink
54
+ def initialize(id, pager)
55
+ super(id, pager) #pager is the container to be refreshed!
56
+ @pager = pager
57
+ end
58
+ end
59
+
60
+ class FirstButton < PagerButton
61
+ def on_click
62
+ @pager.current = 1
63
+ end
64
+ end
65
+
66
+ class PreviousButton < PagerButton
67
+ def on_click
68
+ @pager.current -= 1
69
+ end
70
+ end
71
+
72
+ class NextButton < PagerButton
73
+ def on_click
74
+ @pager.current += 1
75
+ end
76
+ end
77
+
78
+ class LastButton < PagerButton
79
+ def on_click
80
+ @pager.current = @pager.pages.size
81
+ end
82
+ end
83
+
84
+ def page_content
85
+ @pages[@current-1]
86
+ end
87
+
88
+ def current=(current)
89
+ @current = current
90
+ build_hierarchy
91
+ end
92
+
93
+ # #NOTE: only for test.. see NOTE build.hierarchy
94
+ # def get_component(path)
95
+ #
96
+ # super
97
+ # end
98
+
99
+ # #TODO: only tested indirectly
100
+ # def render_container(element)
101
+ # super
102
+ # end
103
+ end
104
+ end