tofu 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,206 @@
1
+ require 'tofu'
2
+ require 'rinda/tuplespace'
3
+ require 'singleton'
4
+ require 'digest/md5'
5
+ require 'enumerator'
6
+
7
+ class Njet
8
+ def initialize(value)
9
+ @value = value
10
+ end
11
+
12
+ def ===(other)
13
+ @value != other
14
+ end
15
+ end
16
+
17
+ class Store
18
+ include Singleton
19
+
20
+ def initialize
21
+ @tree = []
22
+ @ts = Rinda::TupleSpace.new(3)
23
+ @ts.write([:latest, 0])
24
+ end
25
+ attr_reader :tree
26
+
27
+ def each_slice(n, &blk)
28
+ _, key = @ts.take([:latest, nil])
29
+ @tree.each_slice(n, &blk)
30
+ nil
31
+ ensure
32
+ @ts.write([:latest, key])
33
+ end
34
+
35
+ def headline
36
+ _, value = @tree.first
37
+ value ? value[0] : nil
38
+ end
39
+
40
+ def via_drb
41
+ 'drb ' + Thread.current[:DRb]['client'].stream.peeraddr[2] rescue nil
42
+ end
43
+
44
+ def via_cgi(context)
45
+ context.req.peeraddr[2] rescue nil
46
+ end
47
+
48
+ def latest
49
+ @ts.read([:latest, nil])[1]
50
+ end
51
+
52
+ def wait(key)
53
+ @ts.read([:latest, Njet.new(key)], 10) rescue nil
54
+ end
55
+
56
+ def import_string(str)
57
+ str.dup.force_encoding('utf-8')
58
+ rescue
59
+ "(?)"
60
+ end
61
+
62
+ def add(str, context=nil)
63
+ str = import_string(str)
64
+ from = context ? via_cgi(context) : via_drb
65
+ from ||= 'local'
66
+ @ts.take([:latest, nil])
67
+ begin
68
+ key = -10 * Time.now.to_f
69
+ @tree.unshift([key, [str, from]])
70
+ str
71
+ ensure
72
+ @ts.write([:latest, key.to_i])
73
+ end
74
+ end
75
+ end
76
+
77
+ class KotoSession < Tofu::Session
78
+ def initialize(bartender, hint=nil)
79
+ super
80
+ @content = Store.instance
81
+ @base = BaseTofu.new(self)
82
+ @age = nil
83
+ @interval = 5000
84
+ end
85
+ attr_reader :interval
86
+ attr_reader :content
87
+
88
+ def expires
89
+ Time.now + 60
90
+ end
91
+
92
+ def do_GET(context)
93
+ context.res_header('pragma', 'no-cache')
94
+ context.res_header('cache-control', 'no-cache')
95
+ context.res_header('expires', 'Thu, 01 Dec 1994 16:00:00 GMT')
96
+ dispatch_event(context)
97
+ do_inner_html(context)
98
+ reset_age
99
+ context.res_header('content-type', 'text/html; charset=utf-8')
100
+ context.res_body(@base.to_html(context))
101
+ end
102
+
103
+ def wait
104
+ @content.wait(@age) if @age
105
+ @age = @content.latest
106
+ end
107
+
108
+ def lookup_view(context)
109
+ @base
110
+ end
111
+
112
+ def reset_age
113
+ @age = nil
114
+ end
115
+
116
+ def headline
117
+ @content.headline || 'Koya'
118
+ end
119
+ end
120
+
121
+ class BaseTofu < Tofu::Tofu
122
+ set_erb('base.erb')
123
+
124
+ def initialize(session)
125
+ super(session)
126
+ @enter = EnterTofu.new(session)
127
+ @list = ListTofu.new(session)
128
+ end
129
+ end
130
+
131
+ class EnterTofu < Tofu::Tofu
132
+ set_erb('enter.erb')
133
+
134
+ def do_enter(context, params)
135
+ str ,= params['str']
136
+ str = '(nil)' if (str.nil? || str.empty?)
137
+ @session.content.add(str, context)
138
+ @session.reset_age
139
+ end
140
+
141
+ def tofu_id
142
+ 'enter'
143
+ end
144
+ end
145
+
146
+ class ListTofu < Tofu::Tofu
147
+ set_erb('list.erb')
148
+
149
+ Color = Hash.new do |h, k|
150
+ md5 = Digest::MD5.new
151
+ md5 << k.to_s
152
+ r = 0b01111111 & md5.digest[0].unpack("c").first
153
+ g = 0b01111111 & md5.digest[1].unpack("c").first
154
+ b = 0b01111111 & md5.digest[2].unpack("c").first
155
+ h[k] = sprintf("#%02x%02x%02x", r, g, b)
156
+ end
157
+
158
+ def initialize(session)
159
+ super(session)
160
+ @content = session.content
161
+ @color = Color
162
+ end
163
+
164
+ def to_inner_html(context)
165
+ @session.wait
166
+ to_html(context)
167
+ end
168
+
169
+ def group_header(from, time)
170
+ from + ' @ ' + time.strftime("%H:%M")
171
+ end
172
+ end
173
+
174
+ class MyTofulet < Tofu::CGITofulet
175
+ def [](key)
176
+ Store.instance if key == 'store'
177
+ end
178
+ end
179
+
180
+ WEBrick::Daemon.start unless $DEBUG
181
+ tofu = Tofu::Bartender.new(KotoSession, 'koto_8082')
182
+ s = WEBrick::HTTPServer.new(:Port => 8082)
183
+ s.mount("/", Tofu::Tofulet, tofu)
184
+ s.start
185
+
186
+ =begin
187
+ # dRuby & CGI style
188
+ unless $DEBUG
189
+ exit!(0) if fork
190
+ Process.setsid
191
+ exit!(0) if fork
192
+ end
193
+
194
+ uri = ARGV.shift || 'druby://localhost:54322'
195
+ tofu = Tofu::Bartender.new(KotoSession, 'koto_' + uri.split(':').last)
196
+ DRb.start_service(uri, MyTofulet.new(tofu))
197
+
198
+ unless $DEBUG
199
+ STDIN.reopen('/dev/null')
200
+ STDOUT.reopen('/dev/null', 'w')
201
+ STDERR.reopen('/dev/null', 'w')
202
+ end
203
+
204
+ DRb.thread.join
205
+ =end
206
+
@@ -0,0 +1,20 @@
1
+ <%= update_after(@session.interval, context)%>
2
+ <%= on_update_script('document.title = "' + h(@session.headline) + '"') %>
3
+ <ul>
4
+ <%
5
+ last_group = nil
6
+ @content.each_slice(30) do |ary|
7
+ ary.each do |k, v|
8
+ time = Time.at(-0.1 * k)
9
+ str, from = v
10
+ group = group_header(from, time)
11
+ if group != last_group %>
12
+ <li class="group" style="color:<%= @color[from] %>"><%=h group %></li>
13
+ <% last_group = group
14
+ end
15
+ %> <li><%=h str %></li><%
16
+ end
17
+ break
18
+ end
19
+ %>
20
+ </ul>
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tofu
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Masatoshi SEKI
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-01-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.11'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.11'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ description: tiny web-ui framework for plain WEBrick lovers
42
+ email:
43
+ - seki@ruby-lang.org
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - doc/Div.rd
49
+ - doc/request.gif
50
+ - doc/request.obj
51
+ - doc/response.gif
52
+ - doc/response.obj
53
+ - doc/yfd01.gif
54
+ - doc/yfd01.obj
55
+ - doc/yfd02.gif
56
+ - doc/yfd02.obj
57
+ - doc/yfd03.gif
58
+ - doc/yfd03.obj
59
+ - lib/tofu.rb
60
+ - lib/tofu/version.rb
61
+ - sample/koto/base.erb
62
+ - sample/koto/enter.erb
63
+ - sample/koto/koto.rb
64
+ - sample/koto/list.erb
65
+ homepage: https://github.com/seki/Tofu
66
+ licenses:
67
+ - MIT
68
+ metadata: {}
69
+ post_install_message:
70
+ rdoc_options: []
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ requirements: []
84
+ rubyforge_project:
85
+ rubygems_version: 2.5.1
86
+ signing_key:
87
+ specification_version: 4
88
+ summary: tiny web-ui framework for me.
89
+ test_files: []