plot 0.1.0

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: 67d6a3629080da18f0e3a73f6d8c49454c32698e54581c8b7bf8744c4773649c
4
+ data.tar.gz: c4456dfbd322ae85cfe26e26e063122942922d9781eb4649a82351c8811b54d8
5
+ SHA512:
6
+ metadata.gz: 6afb6dd820edb324e75f3478610dcee2fb2798e909471c11b91dad7152ace8bd336b837fc646b7a5d1d6911c4d72d6ff223d91d5b82e61df1411da8a249a1d29
7
+ data.tar.gz: 665f0c522e9e9c37ba2093972367ff5a5115df26efa34300ecaef5606170a281a857333dfb2010cea1a2d5426029815fc21b1d928bfeb28fef7b339e1b6b3d5d
@@ -0,0 +1,33 @@
1
+ (function(program, execJS) { (function() {execJS(program) }).call({}); })(async function() { #{source}
2
+ }, function(program) {
3
+ // Force BunJS to use sloppy mode see https://github.com/oven-sh/bun/issues/4527#issuecomment-1709520894
4
+ exports.abc = function(){}
5
+ var __process__ = process;
6
+ var printFinal = function(string) {
7
+ process.stdout.write('' + string, function() {
8
+ __process__.exit(0);
9
+ });
10
+ };
11
+ try {
12
+ //delete this.process;
13
+ //delete this.console;
14
+ (program()).then((result) => {
15
+ process = __process__;
16
+ if (typeof result == 'undefined' && result !== null) {
17
+ printFinal('["ok"]');
18
+ } else {
19
+ try {
20
+ printFinal(JSON.stringify(['ok', result]));
21
+ } catch (err) {
22
+ printFinal(JSON.stringify(['err', '' + err, err.stack]));
23
+ }
24
+ }
25
+ }).catch((err) => {
26
+ process = __process__;
27
+ printFinal(JSON.stringify(['err', '' + err, err.stack]));
28
+ })
29
+ } catch (err) {
30
+ process = __process__;
31
+ printFinal(JSON.stringify(['err', '' + err, err.stack]));
32
+ }
33
+ });
data/lib/js/context.js ADDED
@@ -0,0 +1,16 @@
1
+ const Plot = await import('@observablehq/plot');
2
+ const d3 = await import('d3');
3
+ const { JSDOM } = (await import('jsdom')).default;
4
+
5
+ const jsdom = new JSDOM("")
6
+
7
+ global.window = jsdom.window
8
+ global.document = jsdom.window.document
9
+ global.navigator = jsdom.window.navigator
10
+ global.Event = jsdom.window.Event
11
+ global.Node = jsdom.window.Node
12
+ global.NodeList = jsdom.window.NodeList
13
+ global.HTMLCollection = jsdom.window.HTMLCollection
14
+
15
+ global.d3 = d3
16
+ global.Plot = Plot
data/lib/plot/mark.rb ADDED
@@ -0,0 +1,89 @@
1
+ class Plot::MarkProxy
2
+ def initialize(mark, type)
3
+ @mark = mark
4
+ @mark.type = type
5
+ end
6
+
7
+ def channel(name, value, **obj)
8
+ # TODO: add checking if option is a channel
9
+ obj[:value] = value
10
+ @mark.add_option(name, obj)
11
+ end
12
+
13
+ def option(name, value)
14
+ if(value.class == Proc || [:title, :href, :ariaLabel].include?(name)) then
15
+ raise "A channel should be used in place of option"
16
+ end
17
+ @mark.add_option(name, { value: value })
18
+ end
19
+
20
+ def style(name, value)
21
+ if([:fill,
22
+ :fillOpacity,
23
+ :stroke,
24
+ :strokeWidth,
25
+ :strokeOpacity,
26
+ :strokeLinejoin,
27
+ :strokeLinecap,
28
+ :strokeMiterlimit,
29
+ :strokeDasharray,
30
+ :strokeDashoffset,
31
+ :opacity,
32
+ :mixBlendMode,
33
+ :imageFilter,
34
+ :shapeRendering,
35
+ :paintOrder,
36
+ :dx,
37
+ :dy,
38
+ :target,
39
+ :ariaDescription,
40
+ :ariaHidden,
41
+ :pointerEvents,
42
+ :clip,
43
+ :tip].include? name)
44
+ then
45
+ @mark.add_option(name, value)
46
+ else
47
+ raise "Not a style option"
48
+ end
49
+ end
50
+
51
+
52
+ end
53
+
54
+ class Plot::Mark
55
+
56
+ attr_accessor :type, :options
57
+
58
+ def initialize(type, **options)
59
+ @type = type
60
+ @options = options
61
+ end
62
+
63
+ def add_option(k, v)
64
+ @options[k.to_s] = v
65
+ end
66
+
67
+
68
+ def render(data)
69
+ @options.values do |o|
70
+ if o.try(:value).try(:lambda?)
71
+ output = []
72
+ data.each? do |obj|
73
+ output.push(o.call(obj))
74
+ end
75
+
76
+ o.value = output
77
+ end
78
+
79
+ if o.try(:transform).try(:lambda?)
80
+ o.value = o.transform.call(data).to_a
81
+ o.transform = nil
82
+ end
83
+ end
84
+
85
+ "Plot['#{@type}'](JSON.parse('#{JSON.generate(data)}'), JSON.parse('#{JSON.generate(@options)}'))"
86
+ end
87
+
88
+
89
+ end
data/lib/plot/plot.rb ADDED
@@ -0,0 +1,54 @@
1
+ class Plot::Plot
2
+
3
+ attr_accessor :data
4
+
5
+ def initialize(**data)
6
+ @data = data
7
+ end
8
+
9
+ def render
10
+ self.class::definition.render(@data)
11
+ end
12
+
13
+ # Rails renderable
14
+ def render_in(view_context)
15
+ view_context.render html: render.html_safe
16
+ end
17
+ end
18
+
19
+ class Plot::PlotProxy
20
+
21
+ def initialize(plot)
22
+ @plot = plot
23
+ end
24
+
25
+ def mark(type, id, &block)
26
+ mark = Plot::Mark.new(nil)
27
+ mark_proxy = Plot::MarkProxy.new(mark, type)
28
+ mark_proxy.instance_eval(&block)
29
+ @plot.add_mark(id, mark)
30
+ end
31
+
32
+ def plot_options(**options)
33
+ @plot.options = options
34
+ end
35
+ end
36
+
37
+
38
+ class Plot::PlotDefinition
39
+
40
+ attr_accessor :marks, :options
41
+
42
+ def initialize(marks, **options)
43
+ @marks = marks.to_h
44
+ @options = options
45
+ end
46
+
47
+ def add_mark(id, mark)
48
+ @marks[id.to_s] = mark
49
+ end
50
+
51
+ def render(data)
52
+ Plot.plot(@marks, data, @options)
53
+ end
54
+ end
data/lib/plot.rb ADDED
@@ -0,0 +1,46 @@
1
+ require "execjs"
2
+ require "json"
3
+
4
+ module Plot
5
+ @source = open(Gem.loaded_specs["plot"].gem_dir + "/lib/js/context.js").read
6
+
7
+ @runtime = ExecJS::ExternalRuntime.new(
8
+ name: "Bun.sh Async",
9
+ command: ["bun"],
10
+ runner_path: Gem.loaded_specs["plot"].gem_dir + "/lib/js/bun_runner.js",
11
+ encoding: 'UTF-8'
12
+ )
13
+
14
+ @context = @runtime.compile(@source)
15
+
16
+ def self._context
17
+ @context
18
+ end
19
+
20
+ def self.encode_source(source)
21
+ @runtime.send(:encode_source,source)
22
+ end
23
+
24
+
25
+ def self.define(&block)
26
+ definition = PlotDefinition.new(nil)
27
+ plot_proxy = PlotProxy.new(definition)
28
+ plot_proxy.instance_eval(&block)
29
+ return definition
30
+ end
31
+
32
+
33
+ def self.call(method, *args)
34
+ puts *args
35
+ @context.call("Plot['#{method}']", *args)
36
+ end
37
+
38
+ def self.plot(marks, data, options)
39
+ rendered_marks = marks.keys.map { |m| marks[m.to_s].render(data[m.to_sym]) }
40
+ @context.call("function(marks, options) { const rendered_marks = marks.map((m) => eval(m)); return Plot.plot({ marks: rendered_marks, ...options }).outerHTML.toString() }", rendered_marks, options)
41
+ end
42
+
43
+ require "plot/plot"
44
+ require "plot/mark"
45
+
46
+ end
metadata ADDED
@@ -0,0 +1,49 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: plot
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Reese Armstrong
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-10-10 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: An unofficial DSL for making charts with Observable Plot. You define
14
+ plot definitions with the DSL that create full charts when data is added. This gem
15
+ relies upon the installation of the Bun JavaScript runtime.
16
+ email: me@reeseric.ci
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - lib/js/bun_runner.js
22
+ - lib/js/context.js
23
+ - lib/plot.rb
24
+ - lib/plot/mark.rb
25
+ - lib/plot/plot.rb
26
+ homepage: https://sr.ht/~reesericci/plot
27
+ licenses:
28
+ - ISC
29
+ metadata: {}
30
+ post_install_message:
31
+ rdoc_options: []
32
+ require_paths:
33
+ - lib
34
+ required_ruby_version: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ">="
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ required_rubygems_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ requirements: []
45
+ rubygems_version: 3.3.7
46
+ signing_key:
47
+ specification_version: 4
48
+ summary: DSL for making charts with Observable Plot
49
+ test_files: []