plumo 0.0.1

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.
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: []