sbsm 1.0.0

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 (50) hide show
  1. data/COPYING +515 -0
  2. data/History.txt +5 -0
  3. data/Manifest.txt +49 -0
  4. data/README.txt +41 -0
  5. data/Rakefile +28 -0
  6. data/data/_flavored_uri.grammar +24 -0
  7. data/data/_uri.grammar +22 -0
  8. data/data/_zone_uri.grammar +24 -0
  9. data/data/flavored_uri.grammar +24 -0
  10. data/data/uri.grammar +22 -0
  11. data/data/zone_uri.grammar +24 -0
  12. data/install.rb +1098 -0
  13. data/lib/cgi/drbsession.rb +37 -0
  14. data/lib/sbsm/cgi.rb +79 -0
  15. data/lib/sbsm/drb.rb +19 -0
  16. data/lib/sbsm/drbserver.rb +162 -0
  17. data/lib/sbsm/exception.rb +28 -0
  18. data/lib/sbsm/flavored_uri_parser.rb +47 -0
  19. data/lib/sbsm/index.rb +66 -0
  20. data/lib/sbsm/lookandfeel.rb +176 -0
  21. data/lib/sbsm/lookandfeelfactory.rb +50 -0
  22. data/lib/sbsm/lookandfeelwrapper.rb +109 -0
  23. data/lib/sbsm/redefine_19_cookie.rb +4 -0
  24. data/lib/sbsm/redirector.rb +37 -0
  25. data/lib/sbsm/request.rb +162 -0
  26. data/lib/sbsm/session.rb +542 -0
  27. data/lib/sbsm/state.rb +301 -0
  28. data/lib/sbsm/time.rb +29 -0
  29. data/lib/sbsm/trans_handler.rb +119 -0
  30. data/lib/sbsm/turing.rb +25 -0
  31. data/lib/sbsm/uri_parser.rb +45 -0
  32. data/lib/sbsm/user.rb +47 -0
  33. data/lib/sbsm/validator.rb +256 -0
  34. data/lib/sbsm/viralstate.rb +47 -0
  35. data/lib/sbsm/zone_uri_parser.rb +48 -0
  36. data/test/data/dos_file.txt +2 -0
  37. data/test/data/lnf_file.txt +2 -0
  38. data/test/data/mac_file.txt +1 -0
  39. data/test/stub/cgi.rb +35 -0
  40. data/test/suite.rb +29 -0
  41. data/test/test_drbserver.rb +83 -0
  42. data/test/test_index.rb +90 -0
  43. data/test/test_lookandfeel.rb +230 -0
  44. data/test/test_session.rb +372 -0
  45. data/test/test_state.rb +176 -0
  46. data/test/test_trans_handler.rb +447 -0
  47. data/test/test_user.rb +44 -0
  48. data/test/test_validator.rb +126 -0
  49. data/usage-en.txt +112 -0
  50. metadata +142 -0
data/lib/sbsm/state.rb ADDED
@@ -0,0 +1,301 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # State Based Session Management
4
+ # Copyright (C) 2004 Hannes Wyss
5
+ #
6
+ # This library is free software; you can redistribute it and/or
7
+ # modify it under the terms of the GNU Lesser General Public
8
+ # License as published by the Free Software Foundation; either
9
+ # version 2.1 of the License, or (at your option) any later version.
10
+ #
11
+ # This library is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
+ # Lesser General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Lesser General Public
17
+ # License along with this library; if not, write to the Free Software
18
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
+ #
20
+ # ywesee - intellectual capital connected, Winterthurerstrasse 52, CH-8006 Z�rich, Switzerland
21
+ # hwyss@ywesee.com
22
+ #
23
+ # State -- sbsm -- 22.10.2002 -- hwyss@ywesee.com
24
+
25
+ module SBSM
26
+ class ProcessingError < RuntimeError
27
+ attr_reader :key, :value
28
+ def initialize(message, key, value)
29
+ super(message)
30
+ @key = key
31
+ @value = value
32
+ end
33
+ end
34
+ class Warning
35
+ attr_reader :key, :value, :message
36
+ def initialize(message, key, value)
37
+ @message = message
38
+ @key = key
39
+ @value = value
40
+ end
41
+ end
42
+ class State
43
+ attr_reader :errors, :infos, :events, :previous, :warnings, :model
44
+ attr_accessor :next, :request_path, :redirected
45
+ DIRECT_EVENT = nil
46
+ ZONE = nil
47
+ ZONES = []
48
+ ZONE_EVENT = nil
49
+ EVENT_MAP = {}
50
+ GLOBAL_MAP = {}
51
+ REVERSE_MAP = {}
52
+ VIEW = nil
53
+ VOLATILE = false
54
+ def State::direct_event
55
+ self::DIRECT_EVENT
56
+ end
57
+ def State::zone
58
+ self::ZONE
59
+ end
60
+ def State::zones
61
+ self::ZONES
62
+ end
63
+ def initialize(session, model)
64
+ @session = session
65
+ @model = model
66
+ @events = self::class::GLOBAL_MAP.dup.update(self::class::EVENT_MAP.dup)
67
+ @default_view = self::class::VIEW
68
+ @errors = {}
69
+ @infos = []
70
+ @warnings = []
71
+ @viral_modules = []
72
+ touch()
73
+ end
74
+ def init
75
+ end
76
+ def add_warning(message, key, value)
77
+ if(key.is_a? String)
78
+ key = key.intern
79
+ end
80
+ warning = Warning.new(message, key, value)
81
+ @warnings.push(warning)
82
+ end
83
+ def back
84
+ @previous
85
+ end
86
+ def __checkout
87
+ return if(@checked_out)
88
+ @checked_out = true
89
+ @model = nil
90
+ if(@next.respond_to?(:unset_previous))
91
+ @next.unset_previous
92
+ end
93
+ @next = nil
94
+ if(@previous.respond_to?(:__checkout))
95
+ @previous.__checkout
96
+ end
97
+ @previous = nil
98
+ end
99
+ def create_error(msg, key, val)
100
+ ProcessingError.new(msg.to_s, key, val)
101
+ end
102
+ def default
103
+ self
104
+ end
105
+ def direct_event
106
+ self::class::DIRECT_EVENT
107
+ end
108
+ def error?
109
+ !@errors.empty?
110
+ end
111
+ def error(key)
112
+ @errors[key]
113
+ end
114
+ def error_check_and_store(key, value, mandatory=[])
115
+ if(value.is_a? RuntimeError)
116
+ @errors.store(key, value)
117
+ elsif(mandatory.include?(key) && mandatory_violation(value))
118
+ error = create_error('e_missing_' << key.to_s, key, value)
119
+ @errors.store(key, error)
120
+ end
121
+ end
122
+ if RUBY_VERSION >= '1.9'
123
+ def extend(mod)
124
+ if(mod.constants.include?(:VIRAL))
125
+ @viral_modules.push(mod)
126
+ end
127
+ if(mod.constants.include?(:EVENT_MAP))
128
+ @events.update(mod::EVENT_MAP)
129
+ end
130
+ super
131
+ end
132
+ else
133
+ def extend(mod)
134
+ if(mod.constants.include?('VIRAL'))
135
+ @viral_modules.push(mod)
136
+ end
137
+ if(mod.constants.include?('EVENT_MAP'))
138
+ @events.update(mod::EVENT_MAP)
139
+ end
140
+ super
141
+ end
142
+ end
143
+ def http_headers
144
+ @http_headers || view.http_headers
145
+ end
146
+ def info?
147
+ !@infos.empty?
148
+ end
149
+ def info(key)
150
+ @infos[key]
151
+ end
152
+ def mandatory_violation(value)
153
+ value.nil? || (value.respond_to?(:empty?) && value.empty?)
154
+ end
155
+ def previous=(state)
156
+ if(@previous.nil? && state.respond_to?(:next=))
157
+ state.next = self
158
+ @previous = state
159
+ while state
160
+ if state.previous == self
161
+ state.unset_previous
162
+ end
163
+ state = state.previous
164
+ end
165
+ @previous
166
+ end
167
+ end
168
+ def sort
169
+ return self unless @model.respond_to?(:sort!)
170
+ get_sortby!
171
+ @model.sort! { |a, b| compare_entries(a, b) }
172
+ @model.reverse! if(@sort_reverse)
173
+ self
174
+ end
175
+ def touch
176
+ @mtime = Time.now
177
+ end
178
+ def to_html(context)
179
+ view.to_html(context)
180
+ end
181
+ def trigger(event)
182
+ if(@redirected)
183
+ @redirected = false
184
+ else
185
+ @errors = {}
186
+ @infos = []
187
+ @warnings = []
188
+ end
189
+ state = if(event && !event.to_s.empty? && self.respond_to?(event))
190
+ _trigger(event)
191
+ elsif(klass = @events[event])
192
+ klass.new(@session, @model)
193
+ end
194
+ state ||= self.default
195
+ if(state.respond_to?(:previous=))
196
+ state.previous = self
197
+ end
198
+ state
199
+ end
200
+ def _trigger(event)
201
+ self.send(event)
202
+ end
203
+ def unset_previous
204
+ if @previous.respond_to?(:next=)
205
+ @previous.next = nil
206
+ end
207
+ @previous = nil
208
+ end
209
+ def warning(key)
210
+ @warnings.select { |warning| warning.key == key }.first
211
+ end
212
+ def warning?
213
+ !@warnings.empty?
214
+ end
215
+ def user_input(keys=[], mandatory=[])
216
+ keys = [keys] unless keys.is_a?(Array)
217
+ mandatory = [mandatory] unless mandatory.is_a?(Array)
218
+ if(hash = @session.user_input(*keys))
219
+ if keys.size == 1
220
+ unless(error_check_and_store(keys.first, hash, mandatory))
221
+ hash
222
+ end
223
+ else
224
+ hash.each { |key, value|
225
+ if(error_check_and_store(key, value, mandatory))
226
+ hash.delete(key)
227
+ end
228
+ }
229
+ hash
230
+ end
231
+ else
232
+ {}
233
+ end
234
+ end
235
+ def view
236
+ klass = @default_view
237
+ if(klass.is_a?(Hash))
238
+ klass = klass.fetch(@session.user.class) {
239
+ klass[:default]
240
+ }
241
+ end
242
+ model = @filter ? @filter.call(@model) : @model
243
+ view = klass.new(model, @session)
244
+ @http_headers = view.http_headers
245
+ view
246
+ end
247
+ def volatile?
248
+ self::class::VOLATILE
249
+ end
250
+ def zone
251
+ self::class::ZONE
252
+ end
253
+ def zones
254
+ self::class::ZONES
255
+ end
256
+ def zone_navigation
257
+ []
258
+ end
259
+ def <=>(other)
260
+ @mtime <=> other.mtime
261
+ end
262
+ protected
263
+ def compare_entries(a, b)
264
+ @sortby.each { |sortby|
265
+ aval, bval = nil
266
+ begin
267
+ aval = a.send(sortby)
268
+ bval = b.send(sortby)
269
+ rescue
270
+ warn "could not sort by #{sortby}"
271
+ next
272
+ end
273
+ res = if (aval.nil? && bval.nil?)
274
+ 0
275
+ elsif (aval.nil?)
276
+ 1
277
+ elsif (bval.nil?)
278
+ -1
279
+ else
280
+ aval <=> bval
281
+ end
282
+ return res if(res.nonzero?)
283
+ }
284
+ 0
285
+ end
286
+ def get_sortby!
287
+ @sortby ||= []
288
+ if(sortvalue = @session.user_input(:sortvalue))
289
+ sortvalue = sortvalue.to_sym
290
+ if(@sortby.first == sortvalue)
291
+ @sort_reverse = !@sort_reverse
292
+ else
293
+ @sort_reverse = self.class::REVERSE_MAP[sortvalue]
294
+ end
295
+ @sortby.delete(sortvalue)
296
+ @sortby.unshift(sortvalue)
297
+ end
298
+ end
299
+ attr_reader :mtime
300
+ end
301
+ end
data/lib/sbsm/time.rb ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # State Based Session Management
4
+ # Copyright (C) 2004 Hannes Wyss
5
+ #
6
+ # This library is free software; you can redistribute it and/or
7
+ # modify it under the terms of the GNU Lesser General Public
8
+ # License as published by the Free Software Foundation; either
9
+ # version 2.1 of the License, or (at your option) any later version.
10
+ #
11
+ # This library is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
+ # Lesser General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Lesser General Public
17
+ # License along with this library; if not, write to the Free Software
18
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
+ #
20
+ # ywesee - intellectual capital connected, Winterthurerstrasse 52, CH-8006 Z�rich, Switzerland
21
+ # hwyss@ywesee.com
22
+ #
23
+ # Time -- sbsm -- 20.11.2002 -- hwyss@ywesee.com
24
+
25
+ class Time
26
+ def rfc1123
27
+ gmtime.strftime('%a, %d %b %Y %H:%M:%S %Z')
28
+ end
29
+ end
@@ -0,0 +1,119 @@
1
+ #!/usr/bin/env ruby
2
+ # TransHandler -- sbsm -- 23.09.2004 -- hwyss@ywesee.com
3
+
4
+ $USING_STRSCAN = true
5
+ require 'rockit/rockit'
6
+ require 'cgi'
7
+ require 'singleton'
8
+ require 'yaml'
9
+
10
+ module SBSM
11
+ class AbstractTransHandler
12
+ attr_reader :parser_name
13
+ CONFIG_PATH = '../etc/trans_handler.yml'
14
+ @@empty_check ||= nil
15
+ @@lang_check ||= nil
16
+ @@uri_parser ||= nil
17
+ HANDLER_URI = '/index.rbx'
18
+ def initialize(name)
19
+ @parser_name = name
20
+ @parser_method = "_#{name}_parser"
21
+ @grammar_path = File.expand_path("../../data/#{name}.grammar",
22
+ File.dirname(__FILE__))
23
+ @parser_path = File.expand_path("#{name}_parser.rb",
24
+ File.dirname(__FILE__))
25
+ end
26
+ def config(request)
27
+ config = Hash.new { {} }
28
+ begin
29
+ path = File.expand_path(CONFIG_PATH, request.server.document_root)
30
+ path.untaint
31
+ config.update(YAML.load(File.read(path)))
32
+ config
33
+ rescue StandardError => err
34
+ fmt = 'Unable to load url configuration: %s'
35
+ request.server.log_warn(fmt, err.message)
36
+ fmt = 'Hint: store configuration in a YAML-File at DOCUMENT_ROOT/%s'
37
+ request.server.log_notice(fmt, CONFIG_PATH)
38
+ config
39
+ end
40
+ end
41
+ def handle_shortcut(request, config)
42
+ if(notes = config['shortcut'][request.uri])
43
+ notes.each { |key, val|
44
+ request.notes.add(key, val)
45
+ }
46
+ request.uri = HANDLER_URI
47
+ end
48
+ end
49
+ def parse_uri(request)
50
+ @uri_parser ||= self.uri_parser
51
+ ast = @uri_parser.parse(request.uri)
52
+ values = request.notes
53
+ ast.children_names.each { |name|
54
+ case name
55
+ when'language', 'flavor', 'event', 'zone'
56
+ values.add(name, ast.send(name).value)
57
+ when 'variables'
58
+ ast.variables.each { |pair|
59
+ key = pair.key.value
60
+ val = if(pair.children_names.include?('value'))
61
+ CGI.unescape(pair.value.value.to_s)
62
+ else
63
+ ''
64
+ end
65
+ values.add(key, val)
66
+ }
67
+ end
68
+ }
69
+ end
70
+ def translate_uri(request)
71
+ @@empty_check ||= Regexp.new('^/?$')
72
+ @@lang_check ||= Regexp.new('^/[a-z]{2}(/|$)')
73
+ config = config(request)
74
+ handle_shortcut(request, config)
75
+ uri = request.uri
76
+ case uri
77
+ when @@empty_check
78
+ request.uri = config['redirect']['/'] || HANDLER_URI
79
+ when @@lang_check
80
+ self.parse_uri(request)
81
+ request.uri = HANDLER_URI
82
+ end
83
+ Apache::DECLINED
84
+ end
85
+ def uri_parser(grammar_path=@grammar_path, parser_path=@parser_path)
86
+ if(File.exist?(grammar_path))
87
+ oldpath = File.expand_path("_" << File.basename(grammar_path),
88
+ File.dirname(grammar_path))
89
+ src = File.read(grammar_path)
90
+ unless(File.exists?(oldpath) && File.read(oldpath)==src)
91
+ File.delete(oldpath) if File.exists?(oldpath)
92
+ Parse.generate_parser_from_file_to_file(grammar_path,
93
+ parser_path, @parser_method, 'SBSM')
94
+ File.open(oldpath, 'w') { |f| f << src }
95
+ end
96
+ end
97
+ require parser_path
98
+ SBSM.send(@parser_method)
99
+ end
100
+ end
101
+ class TransHandler < AbstractTransHandler
102
+ include Singleton
103
+ def initialize
104
+ super('uri')
105
+ end
106
+ end
107
+ class FlavoredTransHandler < AbstractTransHandler
108
+ include Singleton
109
+ def initialize
110
+ super('flavored_uri')
111
+ end
112
+ end
113
+ class ZoneTransHandler < AbstractTransHandler
114
+ include Singleton
115
+ def initialize
116
+ super('zone_uri')
117
+ end
118
+ end
119
+ end