wee 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/wee/page.rb ADDED
@@ -0,0 +1 @@
1
+ class Wee::Page < Struct.new(:snapshot, :handler_registry); end
@@ -0,0 +1,144 @@
1
+ require 'wee/page'
2
+ require 'wee/state_registry'
3
+ require 'wee/handler_registry'
4
+ require 'thread'
5
+
6
+ class Wee::Session
7
+ attr_accessor :root_component, :page_store
8
+ attr_reader :state_registry
9
+
10
+ def self.current
11
+ sess = Thread.current['Wee::Session']
12
+ raise "not in session" if sess.nil?
13
+ return sess
14
+ end
15
+
16
+ def register_object_for_backtracking(obj)
17
+ @state_registry << obj
18
+ end
19
+
20
+ def initialize(&block)
21
+ Thread.current['Wee::Session'] = self
22
+
23
+ @next_page_id = 0
24
+ @mutex = Mutex.new
25
+ @state_registry = Wee::StateRegistry.new
26
+
27
+ block.call(self)
28
+
29
+ raise ArgumentError, "No root component specified" if @root_component.nil?
30
+ raise ArgumentError, "No page_store specified" if @page_store.nil?
31
+
32
+ @initial_snapshot = @state_registry.snapshot
33
+
34
+ ensure
35
+ Thread.current['Wee::Session'] = nil
36
+ end
37
+
38
+ def setup(context)
39
+ end
40
+
41
+ def handle_request(context)
42
+ Thread.current['Wee::Session'] = self
43
+
44
+ @mutex.synchronize do
45
+
46
+ setup(context)
47
+
48
+ if context.page_id.nil?
49
+
50
+ # No page_id was specified in the URL. This means that we start with a
51
+ # fresh component and a fresh page_id, then redirect to render itself.
52
+
53
+ handle_new_page_view(context, @initial_snapshot)
54
+
55
+ elsif page = @page_store.fetch(context.page_id, false)
56
+
57
+ # A valid page_id was specified and the corresponding page exists.
58
+
59
+ page.snapshot.apply unless context.resource_id
60
+
61
+ raise "invalid request URL! both request_id and handler_id given!" if context.resource_id and context.handler_id
62
+
63
+ if context.resource_id
64
+ # This is a resource request
65
+ res = page.handler_registry.get_resource(context.resource_id)
66
+
67
+ context.response.status = 200
68
+ context.response['Content-Type'] = res.content_type
69
+ context.response.body = res.content
70
+
71
+ elsif context.handler_id.nil?
72
+
73
+ # No action/inputs were specified -> render page
74
+ #
75
+ # 1. Reset the action/input fields (as they are regenerated in the
76
+ # rendering process).
77
+ # 2. Render the page (respond).
78
+ # 3. Store the page back into the store (only neccessary if page is not
79
+ # stored in memory).
80
+
81
+ page = Wee::Page.new(page.snapshot, Wee::HandlerRegistry.new) # remove all action/input handlers
82
+ context.handler_registry = page.handler_registry
83
+ respond(context.freeze) # render
84
+ @page_store[context.page_id] = page # store
85
+
86
+ else
87
+
88
+ # Actions/inputs were specified.
89
+ #
90
+ # We process the request and invoke actions/inputs. Then we generate a
91
+ # new page view.
92
+
93
+ context.handler_registry = page.handler_registry
94
+ @root_component.decoration.process_request(context.freeze)
95
+ handle_new_page_view(context)
96
+
97
+ end
98
+
99
+ else
100
+
101
+ # A page_id was specified in the URL, but there's no page for it in the
102
+ # page store. Either the page has timed out, or an invalid page_id was
103
+ # specified.
104
+ #
105
+ # TODO:: Display an "invalid page or page timed out" message, which
106
+ # forwards to /app/session-id
107
+
108
+ raise "Not yet implemented"
109
+
110
+ end
111
+
112
+ end # mutex
113
+
114
+ ensure
115
+ Thread.current['Wee::Session'] = nil
116
+ end
117
+
118
+ private
119
+
120
+ def handle_new_page_view(context, snapshot=nil)
121
+ new_page_id = create_new_page_id()
122
+ new_page = Wee::Page.new(snapshot || @state_registry.snapshot, Wee::HandlerRegistry.new)
123
+ @page_store[new_page_id] = new_page
124
+
125
+ redirect_url = "#{ context.application.path }/s:#{ context.session_id }/p:#{ new_page_id }"
126
+ context.response.set_redirect(WEBrick::HTTPStatus::MovedPermanently, redirect_url)
127
+ end
128
+
129
+ def respond(context)
130
+ context.response.status = 200
131
+ context.response['Content-Type'] = 'text/html'
132
+
133
+ rctx = Wee::RenderingContext.new(context, Wee::HtmlWriter.new(context.response.body))
134
+ renderer = Wee::HtmlCanvas.new(rctx)
135
+ @root_component.render_on(renderer)
136
+ end
137
+
138
+ def create_new_page_id
139
+ @next_page_id.to_s
140
+ ensure
141
+ @next_page_id += 1
142
+ end
143
+
144
+ end
@@ -0,0 +1,47 @@
1
+ class Object
2
+ def take_snapshot
3
+ snap = Hash.new
4
+ instance_variables.each do |iv|
5
+ snap[iv] = instance_variable_get(iv)
6
+ end
7
+ snap
8
+ end
9
+
10
+ def apply_snapshot(snap)
11
+ instance_variables.each do |iv|
12
+ instance_variable_set(iv, snap[iv])
13
+ end
14
+ end
15
+ end
16
+
17
+ class Array
18
+ def take_snapshot
19
+ dup
20
+ end
21
+
22
+ def apply_snapshot(snap)
23
+ replace(snap)
24
+ end
25
+ end
26
+
27
+ class String
28
+ def take_snapshot
29
+ dup
30
+ end
31
+
32
+ def apply_snapshot(snap)
33
+ replace(snap)
34
+ end
35
+ end
36
+
37
+ class Struct
38
+ def take_snapshot
39
+ snap = Hash.new
40
+ each_pair {|k,v| snap[k] = v}
41
+ snap
42
+ end
43
+
44
+ def apply_snapshot(snap)
45
+ snap.each_pair {|k,v| send(k.to_s + "=", v)}
46
+ end
47
+ end
@@ -0,0 +1,173 @@
1
+ require 'thread'
2
+ require 'set'
3
+ require 'wee/snapshot'
4
+
5
+ class Wee::StateRegistry
6
+ def initialize
7
+ @registered_objects = Hash.new # { oid => Set:{snap_oid1, snap_oid2}
8
+ @snap_to_oid_map = Hash.new # { snap_oid => Set:{oid1, oid2} }
9
+
10
+ @finalizer_snap = proc {|snap_oid|
11
+ Thread.exclusive do
12
+ @snap_to_oid_map[snap_oid].each do |oid|
13
+ if r = @registered_objects[oid]
14
+ r.delete(snap_oid)
15
+ end
16
+ end
17
+ @snap_to_oid_map.delete(snap_oid)
18
+ end
19
+ }
20
+
21
+ @finalizer_obj = proc {|oid|
22
+ Thread.exclusive do
23
+ (@registered_objects.delete(oid) || []).each do |snap_oid|
24
+ with_object(snap_oid) { |snap|
25
+ snap.delete(oid)
26
+ @snap_to_oid_map[snap_oid].delete(oid)
27
+ }
28
+ end
29
+ end
30
+ }
31
+ end
32
+
33
+ def marshal_load(dump)
34
+ initialize
35
+ objs, snaps = dump
36
+
37
+ objs.each do |obj|
38
+ register(obj)
39
+ end
40
+
41
+ snaps.each do |snap|
42
+ set = (@snap_to_oid_map[snap.object_id] ||= Set.new)
43
+
44
+ snap.each do |oid, hash|
45
+ set.add(oid)
46
+ @registered_objects[oid].add(snap.object_id)
47
+ end
48
+
49
+ ObjectSpace.define_finalizer(snap, @finalizer_snap)
50
+ end
51
+ end
52
+
53
+ # TODO: should do a GC before marshalling?!
54
+ # NOTE: we have to marshal the @registered_objects too, as we might have not
55
+ # yet taken any snapshot
56
+ def marshal_dump
57
+ objs = []
58
+ snaps = []
59
+
60
+ each_object {|obj| objs << obj}
61
+ each_snapshot { |snap| snaps << snap }
62
+
63
+ [objs, snaps]
64
+ end
65
+
66
+ def snapshot
67
+ snap = Snapshot.new
68
+ set = (@snap_to_oid_map[snap.object_id] ||= Set.new)
69
+
70
+ each_object do |obj|
71
+ snap.add_object(obj)
72
+ set.add(obj.object_id)
73
+ @registered_objects[obj.object_id].add(snap.object_id)
74
+ end
75
+
76
+ ObjectSpace.define_finalizer(snap, @finalizer_snap)
77
+
78
+ return snap
79
+ end
80
+
81
+ def register(obj)
82
+ @registered_objects[obj.object_id] ||= Set.new
83
+ ObjectSpace.define_finalizer(obj, @finalizer_obj)
84
+ end
85
+
86
+ alias << register
87
+
88
+ def each_object(&block)
89
+ Thread.exclusive do
90
+ @registered_objects.each_key do |oid|
91
+ with_object(oid, &block)
92
+ end
93
+ end
94
+ end
95
+
96
+ def each_snapshot(&block)
97
+ Thread.exclusive do
98
+ @snap_to_oid_map.each_key do |oid|
99
+ with_object(oid, &block)
100
+ end
101
+ end
102
+ end
103
+
104
+ private
105
+
106
+ def with_object(oid, &block)
107
+ begin
108
+ obj = ObjectSpace._id2ref(oid)
109
+ rescue RangeError
110
+ return
111
+ end
112
+ block.call(obj) if block
113
+ end
114
+
115
+ class Snapshot
116
+ def initialize
117
+ @data = Hash.new
118
+ end
119
+
120
+ def delete(key)
121
+ @data.delete(key)
122
+ end
123
+
124
+ def each(&block)
125
+ Thread.exclusive do
126
+ @data.each(&block)
127
+ end
128
+ end
129
+
130
+ def add_object(obj)
131
+ @data[obj.object_id] = obj.take_snapshot
132
+ end
133
+
134
+ def apply
135
+ each do |oid, snap|
136
+ with_object(oid) {|obj|
137
+ obj.apply_snapshot(snap)
138
+ }
139
+ end
140
+ end
141
+
142
+ def marshal_dump
143
+ # generates a { obj => {instance variables} } hash
144
+ dump = Hash.new
145
+
146
+ each do |oid, hash|
147
+ with_object(oid) {|obj| dump[obj] = hash }
148
+ end
149
+
150
+ dump
151
+ end
152
+
153
+ def marshal_load(dump)
154
+ initialize
155
+ dump.each do |obj, hash|
156
+ @data[obj.object_id] = hash
157
+ end
158
+ end
159
+
160
+ private
161
+
162
+ def with_object(oid, &block)
163
+ begin
164
+ obj = ObjectSpace._id2ref(oid)
165
+ rescue RangeError
166
+ return
167
+ end
168
+ block.call(obj) if block
169
+ end
170
+
171
+ end # class Snapshot
172
+
173
+ end
data/lib/wee/stuff.rb ADDED
@@ -0,0 +1,29 @@
1
+ class Wee::ErrorPage < Wee::Component
2
+ def initialize(msg)
3
+ @msg = msg
4
+ super()
5
+ end
6
+
7
+ def render_content_on(r)
8
+ r << "<html><head><title>Error: #{@msg}</title><head><body>Error: #{@msg}</body></html>"
9
+ end
10
+ end
11
+
12
+ def parse_url(request)
13
+ hash = {}
14
+ request.path_info.split('/').each do |part|
15
+ # we are only interested in "k:v" parts
16
+ next unless part.include?(':')
17
+
18
+ k, v = part.split(/:/, 2)
19
+ hash[k] = v
20
+ end
21
+ hash
22
+ end
23
+
24
+ # for Ruby 1.8
25
+ module Enumerable
26
+ def min_by(&block)
27
+ min {|i,j| block.call(i) <=> block.call(j) }
28
+ end
29
+ end
@@ -0,0 +1,9 @@
1
+ require 'cache/cache'
2
+
3
+ module Wee::Utils; end
4
+
5
+ class Wee::Utils::LRUCache < Cache::StorageCache
6
+ def initialize(capacity=20)
7
+ super(Cache::Strategy::LRU.new(capacity))
8
+ end
9
+ end
@@ -0,0 +1,15 @@
1
+ require 'webrick'
2
+ class Wee::Application
3
+ def start(hash=nil)
4
+ hash = {:Port => 2000}.update(hash||{})
5
+ server = WEBrick::HTTPServer.new(hash)
6
+ server.mount_proc(hash[:mount_path] || self.path) {|req, res| self.handle_request(req, res)}
7
+ trap("INT") {
8
+ trap("INT", "IGNORE")
9
+ self.shutdown
10
+ server.shutdown
11
+ exit
12
+ }
13
+ server.start
14
+ end
15
+ end
metadata ADDED
@@ -0,0 +1,55 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.8.1
3
+ specification_version: 1
4
+ name: wee
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.1.0
7
+ date: 2004-11-02
8
+ summary: Wee is a framework for building dynamic dynamic web applications.
9
+ require_paths:
10
+ - lib
11
+ author: Michael Neumann
12
+ email: mneumann@ntecs.de
13
+ homepage: http://wee.rubyforge.org
14
+ rubyforge_project: wee
15
+ description:
16
+ autorequire: wee
17
+ default_executable:
18
+ bindir: bin
19
+ has_rdoc: false
20
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
21
+ requirements:
22
+ -
23
+ - ">"
24
+ - !ruby/object:Gem::Version
25
+ version: 0.0.0
26
+ version:
27
+ platform: ruby
28
+ files:
29
+ - lib/wee
30
+ - lib/cache
31
+ - lib/wee.rb
32
+ - lib/wee/holder.rb
33
+ - lib/wee/utils
34
+ - lib/wee/snapshot.rb
35
+ - lib/wee/delegate_decoration.rb
36
+ - lib/wee/session.rb
37
+ - lib/wee/context.rb
38
+ - lib/wee/webrick.rb
39
+ - lib/wee/stuff.rb
40
+ - lib/wee/handler_registry.rb
41
+ - lib/wee/html_canvas.rb
42
+ - lib/wee/html_writer.rb
43
+ - lib/wee/component.rb
44
+ - lib/wee/application.rb
45
+ - lib/wee/state_registry.rb
46
+ - lib/wee/page.rb
47
+ - lib/wee/utils/cache.rb
48
+ - lib/cache/cache.rb
49
+ test_files: []
50
+ rdoc_options: []
51
+ extra_rdoc_files: []
52
+ executables: []
53
+ extensions: []
54
+ requirements: []
55
+ dependencies: []