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