glib-web 4.39.2 → 4.40.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 +4 -4
- data/app/helpers/glib/json_ui/page_helper.rb +6 -0
- data/app/helpers/glib/json_ui/view_builder/fields.rb +540 -35
- data/app/helpers/glib/json_ui/view_builder/panels.rb +5 -1
- data/app/views/json_ui/garage/lists/edit_actions.json.jbuilder +96 -66
- data/app/views/json_ui/garage/lists/edit_mode.json.jbuilder +58 -41
- data/app/views/json_ui/garage/panels/timeline.json.jbuilder +82 -73
- data/app/views/json_ui/garage/test_page/lifecycle.json.jbuilder +3 -0
- data/lib/glib/snapshot.rb +75 -17
- data/lib/tasks/db.rake +1 -1
- metadata +6 -7
- data/lib/glib/doc_generator.rb +0 -386
metadata
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: glib-web
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 4.
|
|
4
|
+
version: 4.40.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- ''
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
11
|
date: 2019-10-04 00:00:00.000000000 Z
|
|
@@ -136,7 +136,7 @@ dependencies:
|
|
|
136
136
|
- - ">="
|
|
137
137
|
- !ruby/object:Gem::Version
|
|
138
138
|
version: '0'
|
|
139
|
-
description:
|
|
139
|
+
description:
|
|
140
140
|
email: ''
|
|
141
141
|
executables: []
|
|
142
142
|
extensions: []
|
|
@@ -427,7 +427,6 @@ files:
|
|
|
427
427
|
- lib/glib-web.rb
|
|
428
428
|
- lib/glib/all_helpers.rb
|
|
429
429
|
- lib/glib/crypt/utils.rb
|
|
430
|
-
- lib/glib/doc_generator.rb
|
|
431
430
|
- lib/glib/dynamic_text.rb
|
|
432
431
|
- lib/glib/dynamic_text/config.rb
|
|
433
432
|
- lib/glib/engine.rb
|
|
@@ -463,10 +462,10 @@ files:
|
|
|
463
462
|
- lib/glib/version.rb
|
|
464
463
|
- lib/tasks/db.rake
|
|
465
464
|
- lib/tasks/docs.rake
|
|
466
|
-
homepage:
|
|
465
|
+
homepage:
|
|
467
466
|
licenses: []
|
|
468
467
|
metadata: {}
|
|
469
|
-
post_install_message:
|
|
468
|
+
post_install_message:
|
|
470
469
|
rdoc_options: []
|
|
471
470
|
require_paths:
|
|
472
471
|
- lib
|
|
@@ -482,7 +481,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
482
481
|
version: '0'
|
|
483
482
|
requirements: []
|
|
484
483
|
rubygems_version: 3.4.6
|
|
485
|
-
signing_key:
|
|
484
|
+
signing_key:
|
|
486
485
|
specification_version: 4
|
|
487
486
|
summary: ''
|
|
488
487
|
test_files: []
|
data/lib/glib/doc_generator.rb
DELETED
|
@@ -1,386 +0,0 @@
|
|
|
1
|
-
require 'parser/current'
|
|
2
|
-
require 'yaml'
|
|
3
|
-
require 'fileutils'
|
|
4
|
-
require 'time'
|
|
5
|
-
|
|
6
|
-
module Glib
|
|
7
|
-
class DocGenerator
|
|
8
|
-
def initialize
|
|
9
|
-
@parser = Parser::CurrentRuby
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
# Generate YAML documentation for a single Ruby file
|
|
13
|
-
def generate_for_file(file_path, output_path)
|
|
14
|
-
unless File.exist?(file_path)
|
|
15
|
-
puts "Warning: File not found: #{file_path}"
|
|
16
|
-
return
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
source = File.read(file_path)
|
|
20
|
-
ast = @parser.parse(source)
|
|
21
|
-
|
|
22
|
-
components = extract_components(ast, source)
|
|
23
|
-
|
|
24
|
-
# Generate YAML
|
|
25
|
-
yaml_data = {
|
|
26
|
-
'meta' => {
|
|
27
|
-
'source_file' => file_path,
|
|
28
|
-
'generated_at' => Time.now.iso8601,
|
|
29
|
-
'generator_version' => '1.0.0'
|
|
30
|
-
},
|
|
31
|
-
'components' => components
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
# Ensure output directory exists
|
|
35
|
-
FileUtils.mkdir_p(File.dirname(output_path))
|
|
36
|
-
|
|
37
|
-
# Write YAML file
|
|
38
|
-
File.write(output_path, yaml_data.to_yaml)
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
private
|
|
42
|
-
|
|
43
|
-
# Extract component information from AST
|
|
44
|
-
def extract_components(node, source)
|
|
45
|
-
components = {}
|
|
46
|
-
|
|
47
|
-
traverse_ast(node, source) do |class_node, class_name, parent_class, comment|
|
|
48
|
-
next unless class_node.type == :class
|
|
49
|
-
|
|
50
|
-
# Parse YARD comment
|
|
51
|
-
yard_doc = parse_yard_comment(comment)
|
|
52
|
-
|
|
53
|
-
# Extract properties
|
|
54
|
-
properties = extract_properties(class_node, source)
|
|
55
|
-
|
|
56
|
-
# Build component hash
|
|
57
|
-
component_key = underscore(class_name)
|
|
58
|
-
components[component_key] = {
|
|
59
|
-
'class_name' => class_name,
|
|
60
|
-
'extends' => parent_class,
|
|
61
|
-
'description' => yard_doc[:description],
|
|
62
|
-
'properties' => properties,
|
|
63
|
-
'examples' => yard_doc[:examples],
|
|
64
|
-
'references' => yard_doc[:references],
|
|
65
|
-
'notes' => yard_doc[:notes],
|
|
66
|
-
'deprecated' => yard_doc[:deprecated]
|
|
67
|
-
}.compact
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
components
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
# Traverse AST and yield class nodes with their comments
|
|
74
|
-
def traverse_ast(node, source, &block)
|
|
75
|
-
return unless node.is_a?(Parser::AST::Node)
|
|
76
|
-
|
|
77
|
-
if node.type == :class
|
|
78
|
-
class_name = extract_class_name(node)
|
|
79
|
-
parent_class = extract_parent_class(node)
|
|
80
|
-
comment = extract_comment(node, source)
|
|
81
|
-
|
|
82
|
-
yield node, class_name, parent_class, comment
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
# Recursively traverse child nodes
|
|
86
|
-
node.children.each do |child|
|
|
87
|
-
traverse_ast(child, source, &block)
|
|
88
|
-
end
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
# Extract class name from class node
|
|
92
|
-
def extract_class_name(class_node)
|
|
93
|
-
const_node = class_node.children[0]
|
|
94
|
-
if const_node.type == :const
|
|
95
|
-
const_node.children[1].to_s
|
|
96
|
-
else
|
|
97
|
-
'Unknown'
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
# Extract parent class name
|
|
102
|
-
def extract_parent_class(class_node)
|
|
103
|
-
parent_node = class_node.children[1]
|
|
104
|
-
return nil unless parent_node
|
|
105
|
-
|
|
106
|
-
if parent_node.type == :const
|
|
107
|
-
parent_node.children[1].to_s
|
|
108
|
-
else
|
|
109
|
-
nil
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
# Extract comment before a node
|
|
114
|
-
def extract_comment(node, source)
|
|
115
|
-
return '' unless node.location
|
|
116
|
-
|
|
117
|
-
# Get all lines before the node
|
|
118
|
-
lines = source.lines
|
|
119
|
-
node_line = node.location.line - 1
|
|
120
|
-
|
|
121
|
-
# Walk backwards to collect comment lines
|
|
122
|
-
comment_lines = []
|
|
123
|
-
(node_line - 1).downto(0) do |i|
|
|
124
|
-
line = lines[i].strip
|
|
125
|
-
break unless line.start_with?('#') || line.empty?
|
|
126
|
-
|
|
127
|
-
if line.start_with?('#')
|
|
128
|
-
# Remove leading # and whitespace
|
|
129
|
-
comment_lines.unshift(line.sub(/^#\s?/, ''))
|
|
130
|
-
end
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
comment_lines.join("\n")
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
# Parse YARD comment into structured data
|
|
137
|
-
def parse_yard_comment(comment)
|
|
138
|
-
return {} if comment.empty?
|
|
139
|
-
|
|
140
|
-
result = {
|
|
141
|
-
description: '',
|
|
142
|
-
examples: [],
|
|
143
|
-
references: [],
|
|
144
|
-
notes: [],
|
|
145
|
-
deprecated: nil
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
current_section = :description
|
|
149
|
-
current_example = nil
|
|
150
|
-
description_lines = []
|
|
151
|
-
|
|
152
|
-
comment.lines.each do |line|
|
|
153
|
-
line = line.chomp
|
|
154
|
-
|
|
155
|
-
# Check for YARD tags
|
|
156
|
-
if line =~ /^@example\s*(.*)/
|
|
157
|
-
# Save current example if exists
|
|
158
|
-
result[:examples] << current_example if current_example
|
|
159
|
-
|
|
160
|
-
current_example = {
|
|
161
|
-
'label' => $1.strip,
|
|
162
|
-
'code' => ''
|
|
163
|
-
}
|
|
164
|
-
current_section = :example
|
|
165
|
-
elsif line =~ /^@see\s+(.*)/
|
|
166
|
-
reference = $1.strip
|
|
167
|
-
# Parse URL and description
|
|
168
|
-
if reference =~ /^(https?:\/\/\S+)\s+(.*)/
|
|
169
|
-
result[:references] << { 'url' => $1, 'description' => $2 }
|
|
170
|
-
elsif reference =~ /^(https?:\/\/\S+)/
|
|
171
|
-
result[:references] << { 'url' => $1 }
|
|
172
|
-
else
|
|
173
|
-
result[:references] << { 'text' => reference }
|
|
174
|
-
end
|
|
175
|
-
current_section = :description
|
|
176
|
-
elsif line =~ /^@note\s+(.*)/
|
|
177
|
-
result[:notes] << $1.strip
|
|
178
|
-
current_section = :description
|
|
179
|
-
elsif line =~ /^@deprecated\s*(.*)/
|
|
180
|
-
result[:deprecated] = $1.strip
|
|
181
|
-
result[:deprecated] = true if result[:deprecated].empty?
|
|
182
|
-
current_section = :description
|
|
183
|
-
else
|
|
184
|
-
# Regular content
|
|
185
|
-
if current_section == :example && current_example
|
|
186
|
-
# Remove leading spaces from example code (preserve relative indentation)
|
|
187
|
-
current_example['code'] += line + "\n"
|
|
188
|
-
elsif current_section == :description
|
|
189
|
-
description_lines << line unless line.strip.empty? && description_lines.empty?
|
|
190
|
-
end
|
|
191
|
-
end
|
|
192
|
-
end
|
|
193
|
-
|
|
194
|
-
# Save last example
|
|
195
|
-
result[:examples] << current_example if current_example
|
|
196
|
-
|
|
197
|
-
# Clean up example code (remove extra leading whitespace)
|
|
198
|
-
result[:examples].each do |example|
|
|
199
|
-
example['code'] = unindent(example['code'])
|
|
200
|
-
end
|
|
201
|
-
|
|
202
|
-
# Join description lines
|
|
203
|
-
result[:description] = description_lines.join("\n").strip
|
|
204
|
-
|
|
205
|
-
# Remove empty arrays/nils
|
|
206
|
-
result.delete(:examples) if result[:examples].empty?
|
|
207
|
-
result.delete(:references) if result[:references].empty?
|
|
208
|
-
result.delete(:notes) if result[:notes].empty?
|
|
209
|
-
result.delete(:deprecated) if result[:deprecated].nil?
|
|
210
|
-
|
|
211
|
-
result
|
|
212
|
-
end
|
|
213
|
-
|
|
214
|
-
# Extract properties from class body
|
|
215
|
-
def extract_properties(class_node, source)
|
|
216
|
-
properties = {}
|
|
217
|
-
|
|
218
|
-
class_body = class_node.children[2]
|
|
219
|
-
return properties unless class_body
|
|
220
|
-
|
|
221
|
-
traverse_class_body(class_body, source) do |method_name, args, comment|
|
|
222
|
-
# These are the DSL property definition methods
|
|
223
|
-
if [:string, :int, :bool, :float, :action, :views, :array, :hash,
|
|
224
|
-
:icon, :color, :length, :date, :date_time, :text, :any,
|
|
225
|
-
:panels_builder, :menu, :singleton_array].include?(method_name)
|
|
226
|
-
|
|
227
|
-
property_name = args.first
|
|
228
|
-
next unless property_name
|
|
229
|
-
|
|
230
|
-
# Parse property comment
|
|
231
|
-
prop_doc = parse_property_comment(comment)
|
|
232
|
-
|
|
233
|
-
# Extract options if present (for hash, singleton_array, etc.)
|
|
234
|
-
options = extract_property_options(args)
|
|
235
|
-
|
|
236
|
-
properties[property_name] = {
|
|
237
|
-
'type' => method_name.to_s,
|
|
238
|
-
'description' => prop_doc[:description],
|
|
239
|
-
'required' => options[:required] || false,
|
|
240
|
-
'options' => options[:options],
|
|
241
|
-
'examples' => prop_doc[:examples],
|
|
242
|
-
'notes' => prop_doc[:notes],
|
|
243
|
-
'deprecated' => prop_doc[:deprecated]
|
|
244
|
-
}.compact
|
|
245
|
-
|
|
246
|
-
# Remove empty arrays
|
|
247
|
-
properties[property_name].delete('examples') if properties[property_name]['examples']&.empty?
|
|
248
|
-
properties[property_name].delete('notes') if properties[property_name]['notes']&.empty?
|
|
249
|
-
end
|
|
250
|
-
end
|
|
251
|
-
|
|
252
|
-
properties
|
|
253
|
-
end
|
|
254
|
-
|
|
255
|
-
# Traverse class body to find method calls (property definitions)
|
|
256
|
-
def traverse_class_body(node, source, &block)
|
|
257
|
-
return unless node.is_a?(Parser::AST::Node)
|
|
258
|
-
|
|
259
|
-
if node.type == :send
|
|
260
|
-
receiver = node.children[0]
|
|
261
|
-
method_name = node.children[1]
|
|
262
|
-
args = node.children[2..-1].map { |arg| extract_symbol_or_string(arg) }
|
|
263
|
-
comment = extract_comment(node, source)
|
|
264
|
-
|
|
265
|
-
# Only process calls without a receiver (DSL methods)
|
|
266
|
-
yield method_name, args, comment if receiver.nil?
|
|
267
|
-
end
|
|
268
|
-
|
|
269
|
-
node.children.each do |child|
|
|
270
|
-
traverse_class_body(child, source, &block)
|
|
271
|
-
end
|
|
272
|
-
end
|
|
273
|
-
|
|
274
|
-
# Extract symbol or string value from AST node
|
|
275
|
-
def extract_symbol_or_string(node)
|
|
276
|
-
return nil unless node.is_a?(Parser::AST::Node)
|
|
277
|
-
|
|
278
|
-
case node.type
|
|
279
|
-
when :sym
|
|
280
|
-
node.children[0].to_s
|
|
281
|
-
when :str
|
|
282
|
-
node.children[0]
|
|
283
|
-
when :hash
|
|
284
|
-
extract_hash(node)
|
|
285
|
-
else
|
|
286
|
-
nil
|
|
287
|
-
end
|
|
288
|
-
end
|
|
289
|
-
|
|
290
|
-
# Extract hash from AST node
|
|
291
|
-
def extract_hash(node)
|
|
292
|
-
return nil unless node.type == :hash
|
|
293
|
-
|
|
294
|
-
result = {}
|
|
295
|
-
node.children.each do |pair|
|
|
296
|
-
next unless pair.type == :pair
|
|
297
|
-
key = extract_symbol_or_string(pair.children[0])
|
|
298
|
-
value = extract_symbol_or_string(pair.children[1]) || extract_array(pair.children[1])
|
|
299
|
-
result[key] = value if key
|
|
300
|
-
end
|
|
301
|
-
result
|
|
302
|
-
end
|
|
303
|
-
|
|
304
|
-
# Extract array from AST node
|
|
305
|
-
def extract_array(node)
|
|
306
|
-
return nil unless node.is_a?(Parser::AST::Node) && node.type == :array
|
|
307
|
-
|
|
308
|
-
node.children.map { |child| extract_symbol_or_string(child) }.compact
|
|
309
|
-
end
|
|
310
|
-
|
|
311
|
-
# Extract property options from arguments
|
|
312
|
-
def extract_property_options(args)
|
|
313
|
-
options = { required: false }
|
|
314
|
-
|
|
315
|
-
args.each do |arg|
|
|
316
|
-
if arg.is_a?(Hash)
|
|
317
|
-
if arg['required']
|
|
318
|
-
options[:required] = true
|
|
319
|
-
end
|
|
320
|
-
if arg['optional']
|
|
321
|
-
options[:options] = { 'optional' => arg['optional'] }
|
|
322
|
-
end
|
|
323
|
-
end
|
|
324
|
-
end
|
|
325
|
-
|
|
326
|
-
options
|
|
327
|
-
end
|
|
328
|
-
|
|
329
|
-
# Parse property comment
|
|
330
|
-
def parse_property_comment(comment)
|
|
331
|
-
return {} if comment.empty?
|
|
332
|
-
|
|
333
|
-
result = {
|
|
334
|
-
description: '',
|
|
335
|
-
examples: [],
|
|
336
|
-
notes: [],
|
|
337
|
-
deprecated: nil
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
description_lines = []
|
|
341
|
-
|
|
342
|
-
comment.lines.each do |line|
|
|
343
|
-
line = line.chomp
|
|
344
|
-
|
|
345
|
-
if line =~ /^@example\s+(.*)/
|
|
346
|
-
result[:examples] << $1.strip
|
|
347
|
-
elsif line =~ /^@note\s+(.*)/
|
|
348
|
-
result[:notes] << $1.strip
|
|
349
|
-
elsif line =~ /^@deprecated\s*(.*)/
|
|
350
|
-
result[:deprecated] = $1.strip
|
|
351
|
-
result[:deprecated] = true if result[:deprecated].empty?
|
|
352
|
-
else
|
|
353
|
-
description_lines << line unless line.strip.empty? && description_lines.empty?
|
|
354
|
-
end
|
|
355
|
-
end
|
|
356
|
-
|
|
357
|
-
result[:description] = description_lines.join("\n").strip
|
|
358
|
-
|
|
359
|
-
result
|
|
360
|
-
end
|
|
361
|
-
|
|
362
|
-
# Convert CamelCase to snake_case
|
|
363
|
-
def underscore(string)
|
|
364
|
-
string.gsub(/::/, '_')
|
|
365
|
-
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
|
366
|
-
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
|
367
|
-
.tr('-', '_')
|
|
368
|
-
.downcase
|
|
369
|
-
end
|
|
370
|
-
|
|
371
|
-
# Remove common leading whitespace from multi-line strings
|
|
372
|
-
def unindent(text)
|
|
373
|
-
return text if text.empty?
|
|
374
|
-
|
|
375
|
-
lines = text.lines
|
|
376
|
-
# Find minimum indentation (ignoring empty lines)
|
|
377
|
-
min_indent = lines
|
|
378
|
-
.reject { |line| line.strip.empty? }
|
|
379
|
-
.map { |line| line.match(/^(\s*)/)[1].length }
|
|
380
|
-
.min || 0
|
|
381
|
-
|
|
382
|
-
# Remove that amount of indentation from each line
|
|
383
|
-
lines.map { |line| line.strip.empty? ? line : line[min_indent..-1] }.join
|
|
384
|
-
end
|
|
385
|
-
end
|
|
386
|
-
end
|