plumo 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d9a765709e5fe89c6026a19a017c94aac11c5f990e086002a5d83d856a19524a
4
+ data.tar.gz: f209c5807a9bacb2e0a4292f40f8be7aa46471fb998b5463b2a0f9f363574857
5
+ SHA512:
6
+ metadata.gz: 8d5a123b92fc2c152c015343247a078b602af9b61cb8add276425f728bc7f1ad8e122b25eeb0ee82f15687daaa2adcbee835ca67068509f9e70dbfb3fd10b885
7
+ data.tar.gz: 8afa3b523d58b2aa3707530f4f721bba34050e36a8144ade70c6e80fb5815a9ebafde4b0af269e55599358435f87ee45b0f84a3c8c4488b00b85a7a549c0a46c
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ /pkg/
2
+ /.bundle/
3
+ /vendor/bundle
4
+
5
+ .ruby-version
6
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in mrtable.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,25 @@
1
+ BSD 2-Clause License
2
+
3
+ Copyright (c) 2019, sonota88
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ 1. Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ 2. Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1 @@
1
+ TODO
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ # require "rake/testtask"
3
+
4
+ # Rake::TestTask.new(:test) do |t|
5
+ # t.libs << "test"
6
+ # t.libs << "lib"
7
+ # t.test_files = FileList['test/**/*_test.rb']
8
+ # end
9
+
10
+ # task :default => :test
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $script_path = ARGV[0]
4
+ $updated_at = Time.now
5
+ $status = :running
6
+ $child_pid = nil
7
+
8
+ def kill_child
9
+ puts "kill"
10
+ Process.kill :INT, $child_pid
11
+ Process.waitpid $child_pid
12
+ end
13
+
14
+ def main
15
+ puts "spawn"
16
+ $child_pid = spawn "ruby #{$script_path}"
17
+
18
+ while $status == :running
19
+ t = File.stat($script_path).mtime
20
+
21
+ if $updated_at < t
22
+ puts "changed"
23
+ $updated_at = t
24
+ kill_child
25
+ break
26
+ end
27
+
28
+ sleep 1
29
+ end
30
+ end
31
+
32
+ Signal.trap(:INT) do
33
+ $status = :stop
34
+ kill_child
35
+ end
36
+
37
+ begin
38
+ while $status == :running
39
+ main
40
+ end
41
+ rescue => e
42
+ p e
43
+ end
@@ -0,0 +1,196 @@
1
+ # coding: utf-8
2
+
3
+ require 'webrick'
4
+ require 'cgi'
5
+ require 'json'
6
+ require 'pp'
7
+ require 'timeout'
8
+
9
+ class Plumo
10
+
11
+ class NullLogger
12
+ def <<(arg)
13
+ ;
14
+ end
15
+ end
16
+
17
+ def initialize(w, h, opts={})
18
+ @w = w
19
+ @h = h
20
+ @session_id = nil
21
+
22
+ default_opts = {
23
+ port: 9080,
24
+ num_deq_max: 100
25
+ }
26
+
27
+ @opts = default_opts.merge(opts)
28
+
29
+ @q = Thread::SizedQueue.new(100)
30
+
31
+ @close_q = Thread::Queue.new
32
+
33
+ @status = :starting
34
+ @server = nil
35
+ @server_thread = nil
36
+ end
37
+
38
+ def enq_reset
39
+ @q.clear
40
+ @q.enq({
41
+ type: :reset,
42
+ payload: {
43
+ w: "#{@w}px",
44
+ h: "#{@h}px"
45
+ }
46
+ })
47
+ end
48
+
49
+ def handle_comet(req)
50
+ qsize = @q.size
51
+
52
+ params = CGI.parse(req.body)
53
+ session_id = params["sessionid"][0]
54
+
55
+ if session_id != @session_id
56
+ @session_id = session_id
57
+ enq_reset
58
+ end
59
+
60
+ events = []
61
+ if @q.empty?
62
+ events << @q.deq
63
+ end
64
+
65
+ loop do
66
+ break if @q.empty?
67
+ break if @opts[:num_deq_max] <= events.size
68
+
69
+ events << @q.deq
70
+ end
71
+
72
+ {
73
+ events: events,
74
+ qsize: qsize,
75
+ }
76
+ end
77
+
78
+ def start
79
+ @status = :running
80
+
81
+ logger_access = NullLogger.new
82
+
83
+ @server = WEBrick::HTTPServer.new(
84
+ DocumentRoot: File.join(__dir__, "public"),
85
+ BindAddress: '127.0.0.1',
86
+ Port: @opts[:port],
87
+ AccessLog: [
88
+ [logger_access, WEBrick::AccessLog::COMMON_LOG_FORMAT],
89
+ [logger_access, WEBrick::AccessLog::REFERER_LOG_FORMAT]
90
+ ]
91
+ )
92
+
93
+ @server.mount_proc("/ping") do |req, res|
94
+ res.content_type = "application/json"
95
+ res.body = JSON.generate({ status: @status })
96
+ end
97
+
98
+ @server.mount_proc("/close") do |req, res|
99
+ @close_q.enq 1
100
+
101
+ res.content_type = "application/json"
102
+ res.body = "{}"
103
+ end
104
+
105
+ @server.mount_proc("/comet") do |req, res|
106
+ res_data = handle_comet(req)
107
+
108
+ res.content_type = "application/json"
109
+ res.body = JSON.generate(res_data)
110
+ end
111
+
112
+ Signal.trap(:INT) do
113
+ @status = :stop
114
+
115
+ @q.clear
116
+ @q.enq({ type: :close })
117
+
118
+ begin
119
+ Timeout.timeout(1) do
120
+ @close_q.deq
121
+ end
122
+ rescue Timeout::Error
123
+ $stderr.puts "timed out"
124
+ end
125
+
126
+ exit
127
+ end
128
+
129
+ @server_thread = Thread.new do
130
+ @server.start
131
+ end
132
+
133
+ sleep 0.1
134
+ end
135
+
136
+ def draw(*cmds)
137
+ if @status == :running
138
+ @q.enq({
139
+ type: :cmds,
140
+ cmds: cmds
141
+ })
142
+ end
143
+ end
144
+
145
+ def line(x0, y0, x1, y1, style={})
146
+ cmds = []
147
+
148
+ if style.key?(:color)
149
+ cmds << [:strokeStyle, style[:color]]
150
+ end
151
+
152
+ cmds += [
153
+ [:beginPath],
154
+ [:moveTo, x0, y0],
155
+ [:lineTo, x1, y1],
156
+ [:stroke]
157
+ ]
158
+
159
+ draw(*cmds)
160
+ end
161
+
162
+ def fill_rect(x, y, w, h, style={})
163
+ cmds = []
164
+
165
+ if style.key?(:color)
166
+ cmds << [:fillStyle, style[:color]]
167
+ end
168
+
169
+ cmds += [
170
+ [:beginPath],
171
+ [:fillRect, x, y, w, h],
172
+ ]
173
+
174
+ draw(*cmds)
175
+ end
176
+
177
+ def fill_circle(x, y, r, style={})
178
+ cmds = []
179
+
180
+ if style.key?(:color)
181
+ cmds << [:fillStyle, style[:color]]
182
+ end
183
+
184
+ cmds += [
185
+ [:beginPath],
186
+ [:arc,
187
+ x, y,
188
+ r,
189
+ 0, Math::PI * 2, false
190
+ ],
191
+ [:fill]
192
+ ]
193
+
194
+ draw(*cmds)
195
+ end
196
+ end
@@ -0,0 +1,43 @@
1
+ <html>
2
+
3
+ <head>
4
+ <meta charset="utf-8" />
5
+
6
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
7
+ <script src="./plumo.js"></script>
8
+
9
+ <style>
10
+ * {
11
+ font-family: monospace;
12
+ }
13
+ </style>
14
+ </head>
15
+
16
+ <body
17
+ style="
18
+ background: #222;
19
+ color: #ccc;
20
+ "
21
+ >
22
+ <div>
23
+ ping: <span id="ping"></span>
24
+ </div>
25
+
26
+ <div>
27
+ requests/sec: <span id="rps">-</span>
28
+ </div>
29
+
30
+ <div>
31
+ queue size: <span id="qsize">-</span>
32
+ <pre
33
+ id="qsize_bar"
34
+ style="font-size: 40%;"> </pre>
35
+ </div>
36
+
37
+ <canvas
38
+ width="100px" height="100px"
39
+ style="border: solid 1px #000;"
40
+ />
41
+ </body>
42
+
43
+ </html>
@@ -0,0 +1,164 @@
1
+ const puts = (...args)=>{
2
+ console.log(...args);
3
+ };
4
+
5
+ class Comet {
6
+ constructor(){
7
+ this.timer = null;
8
+ this.connected = true;
9
+ this.sessionId = new Date().getTime();
10
+ }
11
+
12
+ _open(){
13
+ $.post("/comet", { sessionid: this.sessionId })
14
+ .then((data)=>{
15
+ if (this.connected) {
16
+ this.onmessage(data);
17
+ this.open();
18
+ }
19
+ })
20
+ .catch((...args)=>{
21
+ if (this.connected) {
22
+ console.info("comet > catch", args);
23
+ } else {
24
+ // ignore
25
+ }
26
+ });
27
+ }
28
+
29
+ open(){
30
+ clearTimeout(this.timer);
31
+ this.timer = setTimeout(()=>{
32
+ this._open();
33
+ }, 0);
34
+ }
35
+
36
+ onclose(){
37
+ this.connected = false;
38
+ console.info("onclose");
39
+ $.post("/close");
40
+ clearTimeout(this.timer);
41
+ }
42
+ }
43
+
44
+ class CanvasWrapper {
45
+ constructor(el){
46
+ this.el = el;
47
+ this.$el = $(el);
48
+ this.ctx = el.getContext("2d");
49
+ }
50
+
51
+ reset(w, h){
52
+ this.$el.attr("width" , w);
53
+ this.$el.attr("height", h);
54
+ this.$el.show();
55
+ }
56
+
57
+ execCmd(cmd){
58
+ const _cmd = cmd[0];
59
+ const args = cmd.slice(1);
60
+
61
+ if (_cmd in this.ctx) {
62
+ if (typeof this.ctx[_cmd] === "function") {
63
+ this.ctx[_cmd](...args);
64
+ } else {
65
+ this.ctx[_cmd] = args[0];
66
+ }
67
+ } else {
68
+ console.error("invalid command", _cmd);
69
+ }
70
+ }
71
+ }
72
+
73
+ class App {
74
+ constructor(){
75
+ this.comet = new Comet();
76
+ this.cw = new CanvasWrapper($("canvas").get(0));
77
+ this.resTimes = [];
78
+ }
79
+
80
+ start(){
81
+ // comet
82
+ this.comet.onmessage = this.onmessage.bind(this);
83
+ this.comet.open();
84
+
85
+ // ping thread
86
+ setInterval(()=>{
87
+ this.ping();
88
+ }, 1000);
89
+ }
90
+
91
+ onmessage(data){
92
+ this.refreshRps();
93
+ this.refreshQsize(data.qsize, data.events.length);
94
+
95
+ data.events.forEach((event)=>{
96
+ switch (event.type) {
97
+ case "reset":
98
+ this.cw.reset(event.payload.w, event.payload.h);
99
+ break;
100
+
101
+ case "cmds":
102
+ event.cmds.forEach((cmd)=>{
103
+ this.cw.execCmd(cmd);
104
+ });
105
+ break;
106
+
107
+ case "close":
108
+ this.comet.onclose();
109
+ break;
110
+
111
+ default:
112
+ console.error("unknown event", event);
113
+ }
114
+ });
115
+ }
116
+
117
+ refreshRps(){
118
+ const currentTime = new Date().getTime();
119
+ this.resTimes.push(currentTime);
120
+ const limit = currentTime - 1000;
121
+
122
+ const recent =
123
+ this.resTimes.filter((time)=> limit <= time );
124
+
125
+ this.resTimes = recent;
126
+
127
+ $("#rps").text(recent.length);
128
+ }
129
+
130
+ refreshQsize(qsize, numEvents){
131
+ $("#qsize").text(qsize);
132
+
133
+ let qsizeBar = "#".repeat(numEvents);
134
+ if (numEvents < qsize) {
135
+ qsizeBar += "-".repeat(qsize - numEvents);
136
+ }
137
+ $("#qsize_bar").text(qsizeBar);
138
+ }
139
+
140
+ ping(){
141
+ if (this.comet.connected) {
142
+ return;
143
+ }
144
+
145
+ $.post("/ping")
146
+ .then((data)=>{
147
+ if (data.status === "running") {
148
+ location.reload();
149
+ } else {
150
+ console.info("ping > " + data.status);
151
+ throw new Error("ping ng");
152
+ }
153
+ })
154
+ .catch(()=>{
155
+ const text = $("#ping").text();
156
+ $("#ping").text(text + ".");
157
+ });
158
+ }
159
+ }
160
+
161
+ $(()=>{
162
+ const app = new App();
163
+ app.start();
164
+ });
@@ -0,0 +1,3 @@
1
+ module Plumo
2
+ VERSION = "0.0.1"
3
+ end
data/plumo.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'plumo/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "plumo"
8
+ spec.version = Plumo::VERSION
9
+ spec.authors = ["sonota88"]
10
+ spec.email = ["yosiot8753@gmail.com"]
11
+
12
+ spec.summary = %q{Easy 2d-graphincs using Ruby and Canvas}
13
+ spec.description = %q{Easy 2d-graphincs using Ruby and Canvas}
14
+ spec.homepage = "https://github.com/sonota88/plumo"
15
+ spec.license = 'BSD 2-Clause "Simplified"'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.bindir = "exe"
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.17"
25
+ spec.add_development_dependency "rake", "~> 12.0"
26
+ # spec.add_development_dependency "minitest", "~> 5.0"
27
+ end
data/sample_1.rb ADDED
@@ -0,0 +1,50 @@
1
+ $LOAD_PATH.unshift './lib/plumo'
2
+
3
+ require 'plumo'
4
+
5
+ def rand_color
6
+ "rgba(%f, %f, %f, %f)" % [
7
+ rand * 240,
8
+ rand * 200,
9
+ rand * 120,
10
+ 0.5 + rand / 2
11
+ ]
12
+ end
13
+
14
+ def rand_x
15
+ rand * $canvas_w
16
+ end
17
+
18
+ def rand_y
19
+ rand * $canvas_h
20
+ end
21
+
22
+ # --------------------------------
23
+
24
+ $canvas_w = 400
25
+ $canvas_h = 200
26
+
27
+ plumo = Plumo.new($canvas_w, $canvas_h)
28
+ plumo.start
29
+
30
+ loop do
31
+ plumo.line(
32
+ rand_x, rand_y,
33
+ rand_x, rand_y,
34
+ color: rand_color
35
+ )
36
+
37
+ plumo.fill_circle(
38
+ rand_x, rand_y, rand * 50,
39
+ color: rand_color
40
+ )
41
+
42
+ width = rand * 50
43
+ plumo.fill_rect(
44
+ rand_x, rand_y,
45
+ width, width,
46
+ color: rand_color
47
+ )
48
+
49
+ sleep 0.1
50
+ end
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: plumo
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - sonota88
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-04-14 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.17'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.17'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '12.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '12.0'
41
+ description: Easy 2d-graphincs using Ruby and Canvas
42
+ email:
43
+ - yosiot8753@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".gitignore"
49
+ - Gemfile
50
+ - LICENSE
51
+ - README.md
52
+ - Rakefile
53
+ - bin/plumo-reloader
54
+ - lib/plumo/plumo.rb
55
+ - lib/plumo/public/index.html
56
+ - lib/plumo/public/plumo.js
57
+ - lib/plumo/version.rb
58
+ - plumo.gemspec
59
+ - sample_1.rb
60
+ homepage: https://github.com/sonota88/plumo
61
+ licenses:
62
+ - BSD 2-Clause "Simplified"
63
+ metadata: {}
64
+ post_install_message:
65
+ rdoc_options: []
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ requirements: []
79
+ rubygems_version: 3.0.1
80
+ signing_key:
81
+ specification_version: 4
82
+ summary: Easy 2d-graphincs using Ruby and Canvas
83
+ test_files: []