Browse files

* Removed StateRegistry and corresponding test cases. We now use a mu…

…ch simpler

  method to take snapshots.

* Removed calls to undefine_finalizer in snapshot_ext.rb, as we are no longer
  using finalizers in snapshots at all. Also moved all classes except Snapshot
  from snapshot.rb to snapshot_ext.rb.

* Removed class Wee::StateHolder as it's no longer usable due to our new method
  of taking snapshots.

* Renamed Context#callback_registry to simply #callbacks.

* Lots of documentation added.

* No lazy init of @children and @decoration in Wee::Component.

* Moved core classes into core/ subdirectory

* Added method Presenter#with_renderer_for

* Added method Component#render_content_on for documentation purposes.
  • Loading branch information...
1 parent 4a510b2 commit 7f86c880a7a9518546e3a5cae6755b54165fc918 mneumann committed Dec 12, 2004
View
4 Makefile
@@ -1,5 +1,5 @@
rdoc: clean
- rdoc --main README --all --inline-source --op doc/rdoc lib README
-
+ rdoc --template html --main README --all --inline-source --op doc/rdoc lib README
+
clean:
rm -rf rdoc
View
69 README
@@ -8,17 +8,40 @@ The request/response cycle in Wee is actually split into two separate phases
or steps. Depending on the point-of-view (given that a page is rendered and
the user clicks on a link or button), the first phase is to invoke an action
(a "callback"). Then in the second phase, a new page is rendered and sent
-back to the user:
+back to the user. So the two steps are:
-1. invoke callback(s)
+1. invoke callbacks (action phase)
-2. render new page and display
+2. render new page and display (render phase)
-These two phases repeat permanently.
+These two phases repeat permanently. Which tasks are performed in each of them, is briefly listed below:
-=== Invoking Callbacks
+<b>Action:</b>
-Possible sources for callbacks are links (anchors) and all kind of
+1. restore snapshot (if not up-to-date)
+
+2. invoke actions
+
+3. backtrack state
+
+4. update url -> redirect to render phase (not yet)
+
+<b>Render:</b>
+
+1. restore snapshot (if not up-to-date)
+
+2. render
+
+For each session there is at most one request handled at the same time. That
+means, that there is either one action request or one render request handled.
+Why? Because we have only one components tree, which we update on action
+requests. As Wee allows to go back in time, we have to restore this components
+tree to a certain point in time before we can handle an action or render
+request. This disallows to handle e.g. two render requests simultaneous.
+
+=== Action Phase (Invoking Callbacks)
+
+Possible sources for callbacks are links (anchors) and all kinds of
form-elements like submit buttons, input-fields etc. There are two different
kinds of callbacks:
@@ -33,12 +56,12 @@ callbacks before any action callback is triggered.
There are two methods related to callback processing:
-* <i>Component#process_callback_chain</i>
+* Wee::Component#process_callback_chain
-* <i>Presenter#process_callbacks</i>
+* Wee::Presenter#process_callbacks
-Note that each Component is also a Presenter, but a Decoration is not a
-Component!
+Note that each Wee::Component is also a Wee::Presenter, whereas a
+Wee::Decoration is not a Component (but a Presenter)!
Method <i>process_callback_chain</i> invokes <i>process_callbacks</i> for
it's first decoration, or if the component has no decorations, the method is
@@ -60,9 +83,9 @@ programmer should take care to meet this assumption.
Similar as in the callback-step, there are two methods related to rendering a
page:
-* <i>Component#render_chain</i>
+* Wee::Component#render_chain
-* <i>Presenter#render</i>
+* Wee::Presenter#render
Method <i>Component#render_chain</i> starts rendering the decoration chain by
calling <i>Presenter#render</i> for the first decoration of the component or
@@ -72,3 +95,25 @@ the user. Note that method <i>render</i> might call other components
<i>render_chain</i> methods to display those components "inside" itself
(usually a component does this for it's child components, but this has to be
implemented by the programmer).
+
+=== Further Reads
+
+In this order:
+
+* Wee::Presenter
+* Wee::Component
+* Wee::Decoration
+* Wee::Delegate
+* Wee::AnswerDecoration
+
+== Decorations
+
+Decorations are used to modify the look and behaviour of a component, without
+modifying the components tree. A component can have more than one decoration.
+This is implemented as a linked list of decorations (Wee::Decoration#owner
+points to the next decoration), where Wee::Component#decoration points to the
+first decoration in the chain or to the component itself, if no decorations
+were specified. We actually use a Wee::ValueHolder for the <tt>@decoration</tt>
+instance variable of class Component to be able to easily backtrack it
+(required if you want to "undo" component calls).
+
View
108 TODO
@@ -1,29 +1,99 @@
-* Decoration#owner: use a ValueHolder, too. and register it for being backtracked.
+application.template_manager = {
+ My => {'html' => RDocTemplate.new('view/html/file.tmpl'), 'wap' => RDocTemplate.new('view/wap/file.tmpl')}
+}
-* mark_objects_for_backtracking() instead of state-registry (snapshot_objects)
+either this way, or reversed: flavour/style => template-manager
-* callback_registry -> callbacks
+class My < TemplatedComponent
+ def action_blah
+ end
+ def action_blub
+ end
-* apply_snapshot -> restore_snapshot?
+ attr_accessor :blah
-* improve error messages
+ # run-time
+ def template_flavour
+ session.property['style'] || 'html'
+ end
+end
+
+view/html/filt.tmpl:
+
+ <form action="__action_blah">
+ <input name="__input__blah">
+ </form>
+
+__action and __input get replaced
+
+
+* merge with trunk, but make a working-tag of trunk before
+
+* unify RequestHandler/Session
+
+* Wee = Web Engineering made Easy
+
+callback.rb -> core
+context -> core?
+
+Wee::Callback: abstract
+* values -> default_values
+Wee::BlockCallback
+* invoke -> call/[]
+
+overthink CallbackStream
+CallbackStream abstracts from the Request.
+use only blocks as callbacks.
+
+* different models:
+
+ M = Model, V = View, C = Controller
-* don't use Snapshot#apply directly. Everything should be done through the
- StateRegistry API, as snapshot is a internal data-structure.
+ M: e.g. use ActiveRecord as model
+ V: use external templates
+ C: Wee::Component
-* decoration: LiteralMethod. Move somewhere else.
+ MC: Wee::Component
+ V: external template
-== Decorations
+ MVC: Wee::Component
-Decorations are used to modify the look and behaviour of a component. A
-component can have more than one decoration, this is implemented by a
-linked list with the _next_ method of decorations. The @decoration instance
-variable points to the first decoration in the chain or to the component
-itself, if no decorations were specified. We use a ValueHolder for
-@decoration to be able to backtrack it. TODO
+* callbacks.create_callback_stream -> create_stream
-== Children
+* better webrick
-A component can have child components. These must be returned by method
-_children_, otherwise they will not take part in the process_request step
-and as such cannot perform actions.
+* AbstractSession:
+
+ last_access: time of last access
+
+ expires_after: expires after n seconds of inactivity (Time.now - last_access > expires_after) if expires_after.not_nil?
+ expires_after = n_seconds
+
+ max_lifetime: how long may this session run in total? nil == infinity
+ max_lifetime =
+
+ Session#terminate: quits a session
+
+ Application.remove_session(self.session_id)
+
+ Session#session_id
+ Session#application
+
+Application also stores other RequestHandlers. Session is one RequestHandler.
+
+ class RequestHandler
+ attr_reader :application
+
+ attr_reader :last_access
+ attr_accessor :expires_after, :max_lifetime
+
+ def alive?
+ returns true if it should not be terminated
+ end
+ end
+
+* Decoration#owner: use a ValueHolder, too. and register it for being backtracked.
+
+* mark_objects_for_backtracking() instead of state-registry (snapshot_objects)
+
+* improve error messages
View
9 lib/wee.rb
@@ -1,12 +1,11 @@
module Wee; end
-require 'wee/application'
-require 'wee/presenter'
-require 'wee/component'
-require 'wee/decoration'
require 'wee/context'
+require 'wee/application'
require 'wee/session'
-require 'wee/holder'
+
+require 'wee/core'
+
require 'wee/html_writer'
require 'wee/html_canvas'
require 'wee/stuff'
View
2 lib/wee/context.rb
@@ -2,7 +2,7 @@ class Wee::Context
attr_accessor :application
attr_accessor :request, :response, :session, :session_id
attr_accessor :page_id, :handler_id
- attr_accessor :callback_registry
+ attr_accessor :callbacks
attr_accessor :redirect
View
8 lib/wee/core.rb
@@ -0,0 +1,8 @@
+# independent files
+require 'wee/core/valueholder'
+require 'wee/core/snapshot'
+
+# dependent files
+require 'wee/core/presenter'
+require 'wee/core/decoration'
+require 'wee/core/component'
View
272 lib/wee/core/component.rb
@@ -4,26 +4,49 @@
class Wee::Component < Wee::Presenter
- # Starts rendering the decoration chain by calling method <i>render</i> for
- # the first decoration of the component, or calling <i>render</i> for the
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ # :section: Render
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+ public
+
+ # Starts rendering the decoration chain by calling method Presenter#render
+ # for the first decoration of the component, or calling <i>render</i> for the
# component itself if no decorations were specified.
#
# [+rendering_context+]
# An object of class RenderingContext
def render_chain(rendering_context)
- decoration().render(rendering_context)
+ decoration.render(rendering_context)
+ end
+
+ # This method renders the content of this component with the given renderer.
+ #
+ # *OVERWRITE* this method in your own component class to implement the
+ # view. By default this method does nothing!
+ #
+ # [+renderer+]
+ # A renderer object.
+
+ def render_content_on(renderer)
end
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ # :section: Callback
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+ public
+
# Starts processing the callbacks for the decoration chain by invoking method
- # <i>Presenter#process_callbacks</i> of the first decoration or the component
- # itself if no decorations were specified.
+ # #process_callbacks of the first decoration or the component itself if no
+ # decorations were specified.
#
# [+callback_stream+]
# An object of class CallbackStream
def process_callback_chain(callback_stream)
- decoration().process_callbacks(callback_stream)
+ decoration.process_callbacks(callback_stream)
end
# Process and invoke all callbacks specified for this component and all of
@@ -44,60 +67,176 @@ def process_callbacks(callback_stream)
end
end
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ # :section: Init
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
protected
# Initializes a newly created component.
#
# Call this method from your own components' <i>initialize</i> method using
# +super+, before setting up anything else!
- #
- # By default, only <tt>@decoration</tt> is registered for being backtracked,
- # but not <tt>@children</tt>. If you want to register your own objects for
- # being backtracked, i.e. being able to use the browsers back-button
- # correctly, then your <i>initialize</i> method should look like this one:
- #
- # def initialize
- # super() # calls Component#initialize
- # session.register_object_for_backtracking(your_object)
- # ...
- # end
def initialize() # :notnew:
- @decoration = Wee::StateHolder.new(self)
+ @decoration = Wee::ValueHolder.new(self)
@children = []
end
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ # :section: Children
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+ protected
+
# Returns all direct child components collected in an array.
+
+ def children
+ @children
+ end
+
+ # Add a child to the component. Example:
#
- # Either overwrite this method to return the child components of your
- # component, or just append the child components to the returned array
- # (prefered way):
- #
# class YourComponent < Wee::Component
# def initialize
- # super
- # children << ChildComponent.new
+ # super()
+ # add_child ChildComponent.new
# end
# end
#
- # If you dynamically append child components to this array at run-time (not
- # in initialize), then you should register <tt>@children</tt> for being
- # backtracked (of course only if you want backtracking at all):
+ # If you dynamically add child components to a component at run-time (not in
+ # initialize), then you should consider to backtrack the children array (of
+ # course only if you want backtracking at all):
#
- # def initialize
- # super()
- # session.register_object_for_backtracking(@children)
+ # def backtrack_state(snapshot)
+ # super
+ # snapshot.add(self.children)
# end
-
- def children
- @children
+ #
+
+ def add_child(child)
+ self.children << child
+ end
+
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ # :section: Decoration
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+ public
+
+ # Returns the first decoration from the component's decoration chain, or
+ # +self+ if no decorations were specified for the component.
+
+ def decoration
+ @decoration.value
+ end
+
+ # Set the pointer to the first decoration to +d+.
+
+ def decoration=(d)
+ @decoration.value = d
+ end
+
+ # Adds decoration +d+ in front of the decoration chain.
+
+ def add_decoration(d)
+ d.owner = self.decoration
+ self.decoration = d
+ end
+
+ # Remove decoration +d+ from the decoration chain.
+ #
+ # Returns the removed decoration or +nil+ if it did not exist in the
+ # decoration chain.
+
+ def remove_decoration(d)
+ if d == self.decoration # 'd' is in front
+ self.decoration = d.owner
+ else
+ last_decoration = self.decoration
+ next_decoration = nil
+ loop do
+ return nil if last_decoration == self or last_decoration.nil?
+ next_decoration = last_decoration.owner
+ break if d == next_decoration
+ last_decoration = next_decoration
+ end
+ last_decoration.owner = d.owner
+ end
+ d.owner = nil # decoration 'd' no longer is an owner of anything!
+ return d
end
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ # :section: Backtrack
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+ public
+
+ # Starts the backtrack-state phase for the decoration chain, by invoking
+ # method #backtrack_state of the first decoration or the component itself if
+ # no decorations were specified.
+ #
+ # See #backtrack_state for details.
+ #
+ # [+snapshot+]
+ # An object of class Snapshot
+
+ def backtrack_state_chain(snapshot)
+ decoration.backtrack_state(snapshot)
+ end
+
+ # Take snapshots of objects that should correctly be backtracked.
+ #
+ # Backtracking means that you can go back in time of the components' state.
+ # Therefore it is neccessary to take snapshots of those objects that want to
+ # participate in backtracking. Taking snapshots of the whole component tree
+ # would be too expensive and unflexible. Note that methods
+ # <i>take_snapshot</i> and <i>restore_snapshot</i> are called for those
+ # objects to take the snapshot (they behave like <i>marshal_dump</i> and
+ # <i>marshal_load</i>). Overwrite them if you want to define special
+ # behaviour.
+ #
+ # By default only <tt>@decoration</tt> is backtracked (which actually is a
+ # ValueHolder, as only the pointer changes not the decoration-object
+ # itself!).
+ #
+ # For example if you dynamically add children to your component, you might
+ # want to backtrack the children array. Therefore you simply pass it to the
+ # Snapshot#add method:
+ #
+ # def backtrack_state(snapshot)
+ # super
+ # snapshot.add(self.children)
+ # end
+ #
+ # This will call Array#take_snapshot to take the snapshot for the children
+ # array. If at a later point in time a snapshot is restored,
+ # Array#restore_snapshot will be called with the return value of
+ # Array#take_snapshot as argument.
+ #
+ # [+snapshot+]
+ # An object of class Snapshot
+
+ def backtrack_state(snapshot)
+ snapshot.add(@decoration)
+ children.each do |child| child.backtrack_state_chain(snapshot) end
+ end
+
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ # :section: Call/Answer
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+ protected
+
# Call another component. The calling component is neither rendered nor are
# it's callbacks processed until the called component answers using method
# #answer.
#
- # === How it works
+ # [+component+]
+ # The component to be called.
+ #
+ # <b>How it works</b>
#
# At first a continuation is created. The component to be called is then
# wrapped with an AnswerDecoration and the continuation is assigned to it's
@@ -123,16 +262,10 @@ def children
#
# This complicated procedure allows multiple action callbacks to be followed
# in the same request and even multiple answer's.
- #
- # [+component+]
- # The component to be called.
- #
def call(component)
- delegate = Wee::Delegate.new(component)
- answer = Wee::AnswerDecoration.new
- component.add_decoration(answer)
- add_decoration(delegate)
+ add_decoration(delegate = Wee::Delegate.new(component))
+ component.add_decoration(answer = Wee::AnswerDecoration.new)
result = callcc {|cc|
answer.on_answer = cc
@@ -141,14 +274,14 @@ def call(component)
remove_decoration(delegate)
component.remove_decoration(answer)
- answer.on_answer = nil
+ answer.on_answer = nil # TODO
return result
end
# Return from a called component.
#
- # After answering, the component that calls _answer_ should no further be
+ # After answering, the component that calls #answer should no further be
# used or reused.
#
# See #call for a detailed description of the call/answer mechanism.
@@ -161,55 +294,4 @@ def answer(*args)
throw :wee_back_to_process_callbacks
end
- # :section: Decoration-related methods
-
- public
-
- # Returns the first decoration from the component's decoration chain, or
- # +self+ if no decorations were specified for the component.
- #
- # DO NOT use <tt>@decoration</tt> directly, as it's a StateHolder!
-
- def decoration
- @decoration.value
- end
-
- # Set the pointer to the first decoration to +d+.
- #
- # DO NOT use <tt>@decoration</tt> directly, as it's a StateHolder!
-
- def decoration=(d)
- @decoration.value = d
- end
-
- # Adds decoration +d+ in front of the decoration chain.
-
- def add_decoration(d)
- d.owner = self.decoration
- self.decoration = d
- end
-
- # Remove decoration +d+ from the decoration chain.
- #
- # Returns the removed decoration or +nil+ if it did not exist in the
- # decoration chain.
-
- def remove_decoration(d)
- if d == self.decoration # 'd' is in front
- self.decoration = d.owner
- else
- last_decoration = self.decoration
- next_decoration = nil
- loop do
- return nil if last_decoration == self or last_decoration.nil?
- next_decoration = last_decoration.owner
- break if d == next_decoration
- last_decoration = next_decoration
- end
- last_decoration.owner = d.owner
- end
- d.owner = nil # decoration 'd' no longer is an owner of anything!
- return d
- end
-
end
View
68 lib/wee/core/decoration.rb
@@ -1,3 +1,29 @@
+# Abstract base class of all decorations. Forwards the methods
+# #process_callbacks, #render and #backtrack_state to the next decoration in
+# the chain. Subclasses should provide special behaviour in these methods,
+# otherwise the decoration does not make sense.
+#
+# For example, a HeaderFooterDecoration class could draw a header and footer
+# around the decorations or components below itself:
+#
+# class HeaderFooterDecoration < Wee::Decoration
+# def render(rendering_context)
+# with_renderer_for(rendering_context) do |r|
+# render_header_on(r)
+# super(rendering_context)
+# render_footer_on(r)
+# end
+# end
+#
+# def render_header_on(r)
+# r.text "header
+# end
+#
+# def render_footer_on(r)
+# ...
+# end
+# end
+
class Wee::Decoration < Wee::Presenter
# Points to the next decoration in the chain. A decoration is responsible for
@@ -7,57 +33,71 @@ class Wee::Decoration < Wee::Presenter
attr_accessor :owner
- # Go on with the next decoration in the chain.
+ # Forwards method call to the next decoration in the chain.
def process_callbacks(callback_stream)
@owner.process_callbacks(callback_stream)
end
- # Go on with the next decoration in the chain.
+ # Forwards method call to the next decoration in the chain.
def render(rendering_context)
@owner.render(rendering_context)
end
+ # Forwards method call to the next decoration in the chain.
+
+ def backtrack_state(snapshot)
+ @owner.backtrack_state(snapshot)
+ end
end
+# A Wee::Delegate breaks the decoration chain and forwards the methods
+# #process_callbacks, #render and #backtrack_state to the corresponding *chain*
+# method of it's _delegate_ component (a Wee::Component).
+
class Wee::Delegate < Wee::Decoration
def initialize(delegate)
@delegate = delegate
end
+ # Forwards method to the corresponding *chain* method of the _delegate_
+ # component.
+
def process_callbacks(callback_stream)
@delegate.process_callback_chain(callback_stream)
end
+ # Forwards method to the corresponding *chain* method of the _delegate_
+ # component.
+
def render(rendering_context)
@delegate.render_chain(rendering_context)
end
-end
-# A serializable Method class, which stores the literal method name instead of
-# an internal tree-node method id.
+ # Forwards method to the corresponding *chain* method of the _delegate_
+ # component.
-class LiteralMethod
- def initialize(object, method_name)
- @object, @method_name = object, method_name
+ def backtrack_state(snapshot)
+ @delegate.backtrack_state_chain(snapshot)
end
- def call(*args)
- @object.send(@method_name, *args)
- end
- alias [] call
end
+# A Wee::AnswerDecoration is wrapped around a component that will call
+# Component#answer. This makes it possible to use such components without the
+# need to call them (Component#call), e.g. as child components of other
+# components.
+
class Wee::AnswerDecoration < Wee::Decoration
# When a component answers, <tt>on_answer.call(args)</tt> will be executed
- # (unless nil).
+ # (unless nil), where +args+ are the arguments passed to Component#answer.
attr_accessor :on_answer
def process_callbacks(callback_stream)
args = catch(:wee_answer) { super; nil }
- unless args.nil?
+ if args != nil
# return to the calling component
@on_answer.call(*args) if @on_answer
end
View
22 lib/wee/core/holder.rb
@@ -1,22 +0,0 @@
-class Wee::ValueHolder
- attr_accessor :value
-
- def initialize(value=nil)
- @value = value
- end
-
- def take_snapshot
- [@value]
- end
-
- def apply_snapshot(snap)
- @value = snap[0]
- end
-end
-
-class Wee::StateHolder < Wee::ValueHolder
- def initialize(value)
- super
- Wee::Session.current.register_object_for_backtracking(self)
- end
-end
View
73 lib/wee/core/presenter.rb
@@ -4,34 +4,66 @@
class Wee::Presenter
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ # :section: Render
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+ public
+
# This method renders the content of the presenter with the given renderer.
#
- # OVERWRITE this method in your own presenter classes to implement the view.
- # By default this method does nothing!
+ # *OVERWRITE* this method in your own presenter classes to implement the
+ # view. By default this method does nothing!
#
# [+renderer+]
# A renderer object.
def render_content_on(renderer)
end
- # Render the presenter in the given rendering context. DO NOT overwrite this
- # method, unless you know exactly what you're doing!
+ # Render the presenter in the given rendering context. <b>DO NOT</b>
+ # overwrite this method, unless you know exactly what you're doing!
#
# Creates a new renderer object of the class returned by method
- # <i>renderer_class</i>, then invokes render_content_on with the new
+ # #renderer_class, then invokes #render_content_on with the new
# renderer.
#
# [+rendering_context+]
# An object of class RenderingContext
def render(rendering_context)
- r = renderer_class.new(rendering_context)
- r.current_component = self
- render_content_on(r)
- r.close # write outstanding brushes to the document
+ with_renderer_for(rendering_context) do |r| render_content_on(r) end
+ end
+
+ protected
+
+ # Creates a new renderer object of the class returned by method
+ # #renderer_class, then yields it to the block and finally closes the
+ # renderer.
+
+ def with_renderer_for(rendering_context)
+ renderer = renderer_class.new(rendering_context)
+ renderer.current_component = self
+ begin
+ yield renderer
+ ensure
+ renderer.close # write outstanding brushes to the document
+ end
+ end
+
+ # Returns the class used as renderer for this presenter. Overwrite this
+ # method if you want to use a different renderer.
+
+ def renderer_class
+ Wee::DefaultRenderer
end
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ # :section: Callback
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+ public
+
# Process all callbacks specified for this presenter.
#
# At first, this method invokes all input callbacks of this presenter, then
@@ -59,22 +91,33 @@ def process_callbacks(callback_stream) # :yields:
if cont = session.continuation_stack.pop
cont.call
- raise "FATAL"
+ raise "FATAL! Please inform the developer!"
end
end
end
- protected
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ # :section: Backtrack
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- # Returns the class used as renderer for this presenter. Overwrite this
- # method if you want to use a different renderer.
+ public
- def renderer_class
- Wee::HtmlCanvas
+ # Dummy implementation. See Component#backtrack_state for more information.
+ #
+ # [+snapshot+]
+ # An object of class Snapshot
+
+ def backtrack_state(snapshot)
end
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ # :section: Session
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+ public
+
# Returns the current session. A presenter (or component) has always an
# associated session. The returned object is of class Wee::Session or a
# subclass thereof.
View
9 lib/wee/core/snapshot.rb
@@ -1,4 +1,11 @@
-class Snapshot
+# This class is for backtracking the state of components (or
+# decorations/presenters). Components that want an undo-facility to be
+# implemented (triggered for example by a browsers back-button), have to
+# overwrite the Component#backtrack_state method. Class Wee::Snapshot simply
+# represents a collection of objects from which snapshots were taken via
+# methods take_snapshot.
+
+class Wee::Snapshot
def initialize
@objects = Hash.new
end
View
19 lib/wee/core/valueholder.rb
@@ -0,0 +1,19 @@
+# Implements a value holder. Useful for backtracking the reference assigned to
+# an instance variable (not the object itself!). An example where this is used
+# is the <tt>@decoration</tt> attribute of class Wee::Component.
+
+class Wee::ValueHolder
+ attr_accessor :value
+
+ def initialize(value=nil)
+ @value = value
+ end
+
+ def take_snapshot
+ @value
+ end
+
+ def restore_snapshot(value)
+ @value = value
+ end
+end
View
6 lib/wee/html_canvas.rb
@@ -277,7 +277,7 @@ def assign(act, obj=nil)
ctx = @canvas.context.context
obj ||= @canvas.current_component
- name(ctx.callback_registry.register(Wee::MethodCallback[obj, act], :input))
+ name(ctx.callbacks.register(Wee::MethodCallback[obj, act], :input))
end
end
@@ -381,7 +381,7 @@ def action(act, *args)
ctx = @canvas.context.context
obj = @canvas.current_component
- name(ctx.callback_registry.register(Wee::MethodCallback[obj, act, *args], :action))
+ name(ctx.callbacks.register(Wee::MethodCallback[obj, act, *args], :action))
end
end
@@ -408,7 +408,7 @@ def action(act, *args)
ctx = @canvas.context.context
obj = @canvas.current_component
href = ctx.application.gen_handler_url(ctx.session_id, ctx.page_id,
- act ? ctx.callback_registry.register(Wee::MethodCallback[obj, act, *args], :action) : '')
+ act ? ctx.callbacks.register(Wee::MethodCallback[obj, act, *args], :action) : '')
__action(href)
end
end
View
2 lib/wee/page.rb
@@ -1 +1 @@
-class Wee::Page < Struct.new(:snapshot, :callback_registry); end
+class Wee::Page < Struct.new(:snapshot, :callbacks); end
View
4 lib/wee/session.rb
@@ -93,7 +93,7 @@ def process_request
# 3. Store the page back into the store
page = Wee::Page.new(page.snapshot, Wee::CallbackRegistry.new) # remove all action/input handlers
- @context.callback_registry = page.callback_registry
+ @context.callbacks = page.callbacks
respond(@context) # render
@page_store[@context.page_id] = page # store
@@ -105,7 +105,7 @@ def process_request
# new page view.
s = {@context.handler_id => nil}.update(@context.request.query)
- callback_stream = page.callback_registry.create_callback_stream(s)
+ callback_stream = page.callbacks.create_callback_stream(s)
catch(:wee_back_to_session) {
@root_component.process_callback_chain(callback_stream)
View
4 lib/wee/snapshot.rb → lib/wee/snapshot_ext.rb
@@ -16,7 +16,7 @@ def restore_snapshot(snap)
class Array
def take_snapshot
- ObjectSpace.undefine_finalizer(dup)
+ dup
end
def restore_snapshot(snap)
@@ -26,7 +26,7 @@ def restore_snapshot(snap)
class String
def take_snapshot
- ObjectSpace.undefine_finalizer(dup)
+ dup
end
def restore_snapshot(snap)
View
5 lib/wee/stuff.rb
@@ -1,3 +1,8 @@
+module Wee
+ DefaultRenderer = Wee::HtmlCanvas
+ Version = "0.2-dev"
+end
+
class Wee::ErrorPage < Wee::Component
def initialize(msg)
@msg = msg
View
61 test/TC_state_registry.rb
@@ -1,61 +0,0 @@
-require 'test/unit'
-$LOAD_PATH.unshift "../lib"
-module Wee; end
-require 'wee/state_registry'
-
-class TC_StateRegistry < Test::Unit::TestCase
- class MyStateRegistry < Wee::StateRegistry
- def each_obj(&block) each_object(@registered_objects, &block) end
- end
-
- def test_finalizer
- s = MyStateRegistry.new
- 10_000.times do
- s.register("abc")
- end
- s.each_obj {|o, _| assert_equal("abc", o)}
- ObjectSpace.garbage_collect
- s.each_obj {|o, _| assert_equal("abc", o)}
- end
-
- def test_snapshot_marshal
- s = Wee::StateRegistry.new
- snaps = []
-
- obj = [1,2,3]
- s.register(obj)
-
- assert_equal [1,2,3], obj
- snaps << s.snapshot
-
- obj.push(10)
- assert_equal [1,2,3,10], obj
- snaps << s.snapshot
-
- snaps[0].apply
- assert_equal [1,2,3], obj
-
- snaps[1].apply
- assert_equal [1,2,3,10], obj
-
- snaps[0].apply
- assert_equal [1,2,3], obj
-
- # marshal ----------------------------
-
- str = Marshal.dump([s, obj, snaps])
- s, obj, snaps = nil, nil, nil
- s, obj, snaps = Marshal.load(str)
-
- assert_equal [1,2,3], obj
-
- snaps[0].apply
- assert_equal [1,2,3], obj
-
- snaps[1].apply
- assert_equal [1,2,3,10], obj
-
- snaps[0].apply
- assert_equal [1,2,3], obj
- end
-end
View
65 test/TC_state_registry_stress.rb
@@ -1,65 +0,0 @@
-$LOAD_PATH.unshift "../lib"
-module Wee; end
-require 'wee/state_registry'
-require 'wee/holder'
-require 'test/unit'
-
-def measure_memory(pid=$$)
- mem, res = `ps -p #{ pid } -l`.split("\n").last.strip.split(/\s+/)[6..7]
- return mem.to_i
-end
-
-class TC_StateRegistryStress < Test::Unit::TestCase
-
- def test_stress
- n = 100
- s = Wee::StateRegistry.new
- classes = [Object, String, Hash, Set, Array]
-
- n.times do
- classes.each do |klass|
- 1000.times do
- s << klass.new
-
- # a cyclic reference
- cyc = Wee::ValueHolder.new
- cyc.value = cyc
- s << cyc
- end
- end
-
- s.snapshot
- end
-
- GC.start
- mem = measure_memory
- stat = s.statistics
- objs, snaps = stat[:registered_objects], stat[:snapshots]
-
- assert(objs == 0, "all objects should have been garbage collected! (#{ objs })")
- assert(snaps == 0, "all snapshots should have been garbage collected! (#{ snaps })")
- assert(mem < 22_000, "memory consumption (#{ mem }) is higher than 22_000")
- end
-
- def test_stress_fail
- n = 10
- s = Wee::StateRegistry.new
- a = []
- classes = [Object, String, Hash, Set, Array]
-
- n.times do
- classes.each do |klass|
- 1000.times do
- obj = klass.new
- s << obj
- a << obj
- end
- end
- s.snapshot
- end
-
- GC.start
- mem = measure_memory
- assert(mem > 40_000)
- end
-end
View
43 test/test.rb
@@ -1,43 +0,0 @@
-$LOAD_PATH.unshift "../lib"
-module Wee; end
-require 'wee/state_registry'
-require 'wee/holder'
-
-def measure_memory(pid=$$)
- mem, res = `ps -p #{ pid } -l`.split("\n").last.strip.split(/\s+/)[6..7]
- return mem.to_i
-end
-
-class ValueHolder
- def initialize(value)
- @value = value
- end
-end
-class Component
- def initialize(state)
- @decoration = ValueHolder.new(self)
- state.register @decoration
- end
-end
-
-s = Wee::StateRegistry.new
-loop do
- # once a snapshot exists, old registered objects are contained in it, and will never
- # get recycled unless all "later" snapshots are recycled.
-
- # the size of each snapshot should increase!
-
- Component.new(s)
-
- # a snapshot links back to the object
- # one a snapshot has been taken, the registered_objects cannot be gc'ed, unless
- # it's former snapshot goes out of scope (gets gc'ed)!
- # One solution: GC before doing a snapshot!
-
- GC.start
- s.snapshot
-
- p s.statistics
-end
-
-# TODO: use will_call? to optimize
View
0 test/TC_component.rb → test/test_component.rb
File renamed without changes.

0 comments on commit 7f86c88

Please sign in to comment.