drawio_dsl 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DrawioDsl
4
+ # The layout engine is responsible for laying out the elements on the page.
5
+ #
6
+ # The layout engine can automatically place elements on the page.
7
+ #
8
+ # It will keep track of layout boundaries, current position and flow direction.
9
+ # Elements are positioned on the page in the order they are added.
10
+ # Row/column flow objects will hold information about horizontal and vertical element padding
11
+ class LayoutContainer
12
+ DEFAULT_DIRECTION = :vertical
13
+ DEFAULT_PADDING = 20
14
+ DEFAULT_BOUNDARY_HEIGHT = 800
15
+ DEFAULT_BOUNDARY_WIDTH = 1000
16
+
17
+ attr_reader :engine
18
+ attr_accessor :direction
19
+ attr_accessor :padding
20
+
21
+ def initialize(engine, **opts)
22
+ @engine = engine
23
+ @direction = opts[:direction] || DEFAULT_DIRECTION
24
+ @padding = opts[:padding] || DEFAULT_PADDING
25
+ @boundary = opts[:boundary]
26
+ end
27
+
28
+ def vertical?
29
+ @direction == :vertical
30
+ end
31
+
32
+ def horizontal?
33
+ @direction == :horizontal
34
+ end
35
+
36
+ attr_writer :boundary
37
+
38
+ def boundary
39
+ @boundary ||= (vertical? ? DEFAULT_BOUNDARY_HEIGHT : DEFAULT_BOUNDARY_WIDTH)
40
+ end
41
+
42
+ # Place the incoming element by altering it's x, y coordinates based on the rules engine
43
+ def place_element(element)
44
+ place_vertical(element) if vertical?
45
+ end
46
+
47
+ def place_vertical(element)
48
+ element.x = engine.x
49
+ element.y = engine.y
50
+
51
+ engine.y += (element.h + padding)
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DrawioDsl
4
+ # The layout engine is responsible for laying out the elements on the page.
5
+ #
6
+ # The layout engine can automatically place elements on the page.
7
+ #
8
+ # It will keep track of layout boundaries, current position and flow direction.
9
+ # Elements will be placed on the page in the order they are added.
10
+ # Row/column flow objects will hold information about horizontal and vertical element padding
11
+ class LayoutEngine
12
+ attr_accessor :margin_left
13
+ attr_accessor :margin_top
14
+ attr_accessor :x
15
+ attr_accessor :y
16
+
17
+ def initialize(**opts)
18
+ @margin_left = opts[:margin_left] || 20
19
+ @margin_top = opts[:margin_top] || 20
20
+ @x = opts[:x] || @margin_left
21
+ @y = opts[:y] || @margin_top
22
+ end
23
+
24
+ def container
25
+ @container ||= LayoutContainer.new(self)
26
+ end
27
+
28
+ def go_vertical(**opts)
29
+ @container = LayoutContainer.new(self, direction: :vertical, **opts)
30
+ end
31
+
32
+ def go_horizontal(**opts)
33
+ @container = LayoutContainer.new(self, direction: :horizontal, **opts)
34
+ end
35
+
36
+ # # Position the incoming element by altering it's x, y coordinates based on the rules engine
37
+ # def position_element(element)
38
+ # container.horizontal?
39
+ # end
40
+
41
+ # private
42
+
43
+ # def position_horizontally(element)
44
+
45
+ # end
46
+ end
47
+ end
@@ -0,0 +1,346 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DrawioDsl
4
+ module Schema
5
+ # Common Style is the reused on Diagram, Page and Element
6
+ #
7
+ # When styles are not provided at each level, then they will inherit from
8
+ # the parent common style.
9
+ #
10
+ # Elements will use the common style of their page
11
+ class CommonStyle
12
+ attr_accessor :theme
13
+ attr_accessor :white_space
14
+ attr_accessor :html
15
+ attr_accessor :rounded
16
+ attr_accessor :shadow
17
+ attr_accessor :glass
18
+ attr_accessor :sketch
19
+
20
+ def initialize(**args, &block)
21
+ @white_space = args[:white_space]
22
+ @html = args[:html]
23
+ @rounded = args[:rounded]
24
+ @shadow = args[:shadow]
25
+ @sketch = args[:sketch]
26
+ @glass = args[:glass]
27
+
28
+ instance_eval(&block) if block_given?
29
+ end
30
+
31
+ def to_h
32
+ {
33
+ white_space: white_space,
34
+ html: html,
35
+ rounded: rounded,
36
+ shadow: shadow,
37
+ sketch: sketch,
38
+ glass: glass
39
+ }
40
+ end
41
+ end
42
+
43
+ # Default Palette contains palette information that can be inherited at each level
44
+ class DefaultPalette
45
+ attr_accessor :fill_color
46
+ attr_accessor :stroke_color
47
+ attr_accessor :font_color
48
+ attr_accessor :gradient
49
+
50
+ def initialize(owner, **args, &block)
51
+ @fill_color = args[:fill_color]
52
+ @stroke_color = args[:stroke_color]
53
+ @font_color = args[:font_color]
54
+ @gradient = args[:gradient]
55
+
56
+ instance_exec(owner, &block) if block_given?
57
+ end
58
+
59
+ def to_h
60
+ {
61
+ fill_color: fill_color,
62
+ stroke_color: stroke_color,
63
+ font_color: font_color,
64
+ gradient: gradient
65
+ }
66
+ end
67
+ end
68
+
69
+ # Diagram is the root of the schema, it contains pages
70
+ class Diagram
71
+ attr_accessor :host
72
+ attr_accessor :theme
73
+ attr_accessor :style
74
+ attr_accessor :palette
75
+ attr_accessor :pages
76
+
77
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
78
+ def initialize(**args)
79
+ @host = args[:host] || SecureRandom.alphanumeric(3)
80
+
81
+ # Apply a random theme to the diagram if none is specified.
82
+ @theme = args[:theme] || KConfig.configuration.drawio.random_theme
83
+
84
+ @style = DrawioDsl::Schema::CommonStyle.new(**args) do
85
+ default_style = KConfig.configuration.drawio.base_style
86
+
87
+ # Inherit from configured style when specific style not specified.
88
+ @white_space ||= default_style.white_space
89
+ @html ||= default_style.html
90
+ @rounded ||= default_style.rounded
91
+ @shadow ||= default_style.shadow
92
+ @sketch ||= default_style.sketch
93
+ @glass ||= default_style.glass
94
+ end
95
+
96
+ @palette = DrawioDsl::Schema::DefaultPalette.new(self, **args) do |diagram|
97
+ theme_palette = KConfig.configuration.drawio.palette(diagram.theme)
98
+
99
+ # Inherit from theme when specific palette options are not specified.
100
+ @fill_color ||= theme_palette.fill_color
101
+ @stroke_color ||= theme_palette.stroke_color
102
+ @font_color ||= theme_palette.font_color
103
+ @gradient ||= theme_palette.gradient
104
+ end
105
+
106
+ @pages = args[:pages] || []
107
+ end
108
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
109
+
110
+ def to_h
111
+ {
112
+ host: host,
113
+ theme: theme,
114
+ palette: palette.to_h,
115
+ style: style.to_h,
116
+ pages: pages.map(&:to_h)
117
+ }
118
+ end
119
+ end
120
+
121
+ # Page is a container for elements
122
+ class Page
123
+ attr_accessor :diagram
124
+
125
+ attr_accessor :id
126
+ attr_accessor :name
127
+ attr_accessor :theme
128
+ attr_accessor :style
129
+ attr_accessor :palette
130
+ attr_accessor :elements
131
+
132
+ # attr_accessor :dx # dx = "2636"
133
+ # attr_accessor :dy # dy = "2332"
134
+ attr_accessor :grid # grid = "0"
135
+ attr_accessor :grid_size # gridSize = "10"
136
+ attr_accessor :guides # guides = "1"
137
+ attr_accessor :tooltips # tooltips = "1"
138
+ attr_accessor :connect # connect = "1"
139
+ attr_accessor :arrows # arrows = "1"
140
+ attr_accessor :fold # fold = "1"
141
+ attr_accessor :page_no # page = "1"
142
+ attr_accessor :page_scale # pageScale = "1"
143
+ attr_accessor :page_width # pageWidth = "583"
144
+ attr_accessor :page_height # pageHeight = "827"
145
+ attr_accessor :background # background = "#FFFACD"
146
+ attr_accessor :page_shadow # shadow = "0"
147
+ attr_accessor :math # math = "0"
148
+
149
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
150
+ def initialize(diagram, **args)
151
+ @diagram = diagram
152
+
153
+ @id = args[:id]
154
+ @name = args[:name]
155
+ @theme = args[:theme] || diagram.theme
156
+
157
+ @grid = args[:grid] || 0
158
+ @grid_size = args[:grid_size] || 10
159
+ @guides = args[:guides] || 1
160
+ @tooltips = args[:tooltips] || 1
161
+ @connect = args[:connect] || 1
162
+ @arrows = args[:arrows] || 1
163
+ @fold = args[:fold] || 1
164
+ @page_no = args[:page_no] || 1
165
+ @page_scale = args[:page_scale] || 1
166
+ @page_width = args[:page_width] || 1169 # A4
167
+ @page_height = args[:page_height] || 827 # A4
168
+ @background = args[:background] || '#FFFACD'
169
+ @page_shadow = args[:page_shadow] || 0
170
+ @math = args[:math] || 0
171
+
172
+ @style = DrawioDsl::Schema::CommonStyle.new(**args) do
173
+ # Inherit from the diagram style when specific style not specified.
174
+ @white_space ||= diagram.style.white_space
175
+ @html ||= diagram.style.html
176
+ @rounded ||= diagram.style.rounded
177
+ @shadow ||= diagram.style.shadow
178
+ @sketch ||= diagram.style.sketch
179
+ @glass ||= diagram.style.glass
180
+ end
181
+
182
+ @palette = DrawioDsl::Schema::DefaultPalette.new(self, **args) do |page|
183
+ theme_palette = KConfig.configuration.drawio.palette(page.theme)
184
+
185
+ # Inherit from theme when specific palette options are not specified.
186
+ @fill_color ||= theme_palette.fill_color
187
+ @stroke_color ||= theme_palette.stroke_color
188
+ @font_color ||= theme_palette.font_color
189
+ @gradient ||= theme_palette.gradient
190
+ end
191
+
192
+ @elements = args[:elements] || []
193
+ end
194
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
195
+
196
+ def to_h
197
+ {
198
+ id: id,
199
+ name: name,
200
+ theme: theme,
201
+ palette: palette.to_h,
202
+ style: style.to_h,
203
+ elements: elements.map(&:to_h)
204
+ }
205
+ end
206
+ end
207
+
208
+ # Element is a graphical element, it can be a shape, a text, or a group (todo)
209
+ class Element
210
+ attr_accessor :page
211
+
212
+ attr_accessor :theme
213
+ attr_accessor :title
214
+
215
+ # The style of the element, these will derive from the page style if not provided
216
+ attr_accessor :white_space
217
+ attr_accessor :html
218
+ attr_accessor :rounded
219
+ attr_accessor :shadow
220
+ attr_accessor :glass
221
+ attr_accessor :sketch
222
+
223
+ attr_accessor :fill_color
224
+ attr_accessor :stroke_color
225
+ attr_accessor :font_color
226
+ attr_accessor :gradient
227
+
228
+ attr_accessor :id
229
+ attr_accessor :type
230
+ attr_accessor :x
231
+ attr_accessor :y
232
+ attr_accessor :w
233
+ attr_accessor :h
234
+ attr_accessor :style_modifiers
235
+
236
+ def initialize(page, **args)
237
+ @page = page
238
+ @id = args[:id]
239
+
240
+ apply_defaults(args, KConfig.configuration.drawio.element)
241
+ end
242
+
243
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
244
+ def apply_defaults(args, element_defaults)
245
+ @theme = args[:theme] || page.theme # KConfig.configuration.drawio.themes.sample
246
+ theme_palette = KConfig.configuration.drawio.palette(page.theme)
247
+ @title = args[:title] || ''
248
+
249
+ @white_space = args[:white_space] || page.style.white_space # wrap or nil
250
+ @html = args[:html] || page.style.html
251
+ @rounded = args[:rounded] || page.style.rounded
252
+ @shadow = args[:shadow] || page.style.shadow
253
+ @sketch = args[:sketch] || page.style.sketch
254
+ @glass = args[:glass] || page.style.glass
255
+
256
+ @type = args[:type] || element_defaults.type
257
+ @x = args[:x] || element_defaults.x
258
+ @y = args[:y] || element_defaults.y
259
+ @w = args[:w] || element_defaults.w
260
+ @h = args[:h] || element_defaults.h
261
+ @style_modifiers = args[:style_modifiers] || element_defaults.style_modifiers
262
+
263
+ @fill_color = args[:fill_color] || theme_palette.fill_color
264
+ @stroke_color = args[:stroke_color] || theme_palette.stroke_color
265
+ @font_color = args[:font_color] || theme_palette.font_color
266
+ @gradient = args[:gradient] || theme_palette.gradient
267
+ end
268
+
269
+ def style
270
+ key_values = []
271
+ key_values << style_modifiers unless style_modifiers.empty?
272
+ key_values << "whiteSpace=#{white_space}" if white_space
273
+ key_values << "html=#{html}" if html
274
+ key_values << "rounded=#{rounded}" if rounded
275
+ key_values << "shadow=#{shadow}" if shadow
276
+ key_values << "sketch=#{sketch}" if sketch
277
+ key_values << "glass=#{glass}" if glass
278
+ key_values << "fillColor=#{fill_color}" if fill_color
279
+ key_values << "strokeColor=#{stroke_color}" if stroke_color
280
+ key_values << "fontColor=#{font_color}" if font_color
281
+ key_values << "gradient=#{gradient}" if gradient
282
+
283
+ key_values.join(';')
284
+ end
285
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
286
+
287
+ def to_h
288
+ {
289
+ id: id,
290
+ type: type,
291
+ x: x,
292
+ y: y,
293
+ w: w,
294
+ h: h,
295
+ style: style
296
+ }
297
+ end
298
+ end
299
+
300
+ # Graphical element in the shape of a square
301
+ class Square < Element
302
+ def initialize(page, **args)
303
+ super(page, **args)
304
+
305
+ apply_defaults(args, KConfig.configuration.drawio.square)
306
+ end
307
+ end
308
+
309
+ # Graphical element in the shape of a rectangle
310
+ class Rectangle < Element
311
+ def initialize(page, **args)
312
+ super(page, **args)
313
+
314
+ apply_defaults(args, KConfig.configuration.drawio.rectangle)
315
+ end
316
+ end
317
+
318
+ # Graphical element in the shape of a circle
319
+ class Circle < Element
320
+ def initialize(page, **args)
321
+ super(page, **args)
322
+
323
+ apply_defaults(args, KConfig.configuration.drawio.circle)
324
+ end
325
+ end
326
+
327
+ # Graphical element in the shape of an rectangle with to vertical lines
328
+ class Process < Element
329
+ def initialize(page, **args)
330
+ super(page, **args)
331
+
332
+ apply_defaults(args, KConfig.configuration.drawio.process)
333
+ end
334
+ end
335
+
336
+ # Graphical element in the shape of an ellipse
337
+ class Ellipse < Element
338
+ def initialize(page, **args)
339
+ super(page, **args)
340
+
341
+ apply_defaults(args, KConfig.configuration.drawio.ellipse)
342
+ end
343
+ end
344
+ end
345
+ end
346
+ # dx="800" dy="583" background="#FFFACD" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="0" pageScale="1" pageWidth="1169" pageHeight="827" math="0" shadow="0"
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DrawioDsl
4
+ VERSION = '0.1.0'
5
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DrawioDsl
4
+ # DrawioDsl is a DSL for draw-io diagrams.
5
+ class XmlBuilder
6
+ include KLog::Logging
7
+
8
+ attr_reader :diagram
9
+
10
+ def initialize(diagram)
11
+ @diagram = diagram
12
+ end
13
+
14
+ def build
15
+ xml_builder = Nokogiri::XML::Builder.new do |xml|
16
+ build_diagram(xml)
17
+ end
18
+ xml_builder.to_xml.sub('<?xml version="1.0"?>', '').strip
19
+ end
20
+
21
+ private
22
+
23
+ def build_diagram(xml)
24
+ xml.mxfile(host: diagram.host) do
25
+ diagram.pages.each do |page|
26
+ build_page(xml, page)
27
+ end
28
+ end
29
+ end
30
+
31
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/BlockLength
32
+ def build_page(xml, page)
33
+ xml.diagram(id: page.id, name: page.name) do
34
+ xml.mxGraphModel(
35
+ dx: 0,
36
+ dy: 0,
37
+ background: page.background,
38
+ grid: page.grid,
39
+ gridSize: page.grid_size,
40
+ guides: page.guides,
41
+ tooltips: page.tooltips,
42
+ connect: page.connect,
43
+ arrows: page.arrows,
44
+ fold: page.fold,
45
+ page: page.page_no,
46
+ pageScale: page.page_scale,
47
+ pageWidth: page.page_width,
48
+ pageHeight: page.page_height,
49
+ math: page.math,
50
+ shadow: page.page_shadow
51
+ ) do
52
+ xml.root do
53
+ xml.mxCell(id: "#{page.id}-A")
54
+ xml.mxCell(id: "#{page.id}-B", parent: "#{page.id}-A")
55
+ page.elements.each do |element|
56
+ build_element(xml, element)
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/BlockLength
63
+
64
+ def build_element(xml, element)
65
+ # puts "opts: #{opts}"
66
+ puts element.x
67
+ puts element.y
68
+
69
+ xml.mxCell(id: element.id, value: element.title, style: element.style, vertex: 1, parent: "#{element.page.id}-B") do
70
+ xml.mxGeometry(x: element.x, y: element.y, width: element.w, height: element.h, as: 'geometry')
71
+ end
72
+ end
73
+ end
74
+ end
data/lib/drawio_dsl.rb ADDED
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+ require 'nokogiri'
5
+
6
+ require 'k_config'
7
+ require 'k_log'
8
+ require 'k_director'
9
+
10
+ require_relative 'drawio_dsl/configuration'
11
+ require_relative 'drawio_dsl/version'
12
+ require_relative 'drawio_dsl/schema'
13
+ require_relative 'drawio_dsl/dom_builder'
14
+ require_relative 'drawio_dsl/xml_builder'
15
+ require_relative 'drawio_dsl/layout_container'
16
+ require_relative 'drawio_dsl/layout_engine'
17
+ require_relative 'drawio_dsl/drawio'
18
+
19
+ module DrawioDsl
20
+ # raise DrawioDsl::Error, 'Sample message'
21
+ Error = Class.new(StandardError)
22
+
23
+ # Your code goes here...
24
+ end
25
+
26
+ if ENV['KLUE_DEBUG']&.to_s&.downcase == 'true'
27
+ namespace = 'DrawioDsl::Version'
28
+ file_path = $LOADED_FEATURES.find { |f| f.include?('drawio_dsl/version') }
29
+ version = DrawioDsl::VERSION.ljust(9)
30
+ puts "#{namespace.ljust(35)} : #{version.ljust(9)} : #{file_path}"
31
+ end