glib-web 4.43.0 → 4.44.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
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a7ef76ca5c9787ab9598a9396f376fb2f336470583724c9980a1ba3c89b4a3fa
|
|
4
|
+
data.tar.gz: 74f73601b2d7f5225ec3b4f7c6f5b555c5262f3bfbf481b5126c93cc7ad5f99a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 195f04ced5dd0cede880f1b097f9602e7a9aed3d4dd1d59fa24e035e38bf3aee781ca85c78eed0fd8a0e35b01f74ded30e416f5d1c0e0017d8d9557bbe366efb
|
|
7
|
+
data.tar.gz: af5383c804394f9e00f2f848f61b69e825f3385d9dd90b18b2f3d3c513a647ab3d2923d1ce7f936dc9c94b8ff4b9df1a0a82e3c65788292d7d4457ec818dc737
|
|
@@ -333,6 +333,24 @@ class Glib::JsonUi::ViewBuilder
|
|
|
333
333
|
end
|
|
334
334
|
end
|
|
335
335
|
|
|
336
|
+
# Hidden field that captures URL fragment (hash) value.
|
|
337
|
+
#
|
|
338
|
+
# Extracts the fragment portion of the URL (everything after #) and
|
|
339
|
+
# populates it as the field value. Useful for secure data passing
|
|
340
|
+
# where the fragment is not sent to the server in HTTP requests.
|
|
341
|
+
#
|
|
342
|
+
# @example Capture URL fragment and auto-submit form
|
|
343
|
+
# form.fields_urlFragment \
|
|
344
|
+
# name: 'url_fragment',
|
|
345
|
+
# onPopulateValue: ->(action) do
|
|
346
|
+
# action.forms_submit
|
|
347
|
+
# end
|
|
348
|
+
class UrlFragment < Hidden
|
|
349
|
+
# Action triggered after the URL fragment value is populated.
|
|
350
|
+
# Typically used to auto-submit the form with the captured value.
|
|
351
|
+
action :onPopulateValue
|
|
352
|
+
end
|
|
353
|
+
|
|
336
354
|
# Timer/stopwatch field for time tracking.
|
|
337
355
|
#
|
|
338
356
|
# Can count up (stopwatch mode) or down (countdown timer mode).
|
|
@@ -0,0 +1,386 @@
|
|
|
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
|
|
@@ -38,6 +38,7 @@ module RuboCop
|
|
|
38
38
|
return if proper_parentheses_style?(node)
|
|
39
39
|
return if proper_backslash_style?(node) && allow_backslash?
|
|
40
40
|
return if inside_correctable_parent?(node)
|
|
41
|
+
return if assignment_method?(node)
|
|
41
42
|
|
|
42
43
|
add_offense(node) do |corrector|
|
|
43
44
|
autocorrect(corrector, node)
|
|
@@ -52,6 +53,13 @@ module RuboCop
|
|
|
52
53
|
node.multiline? && node.arguments.any?
|
|
53
54
|
end
|
|
54
55
|
|
|
56
|
+
def assignment_method?(node)
|
|
57
|
+
# Skip setter methods (e.g., response.body=, foo.bar=)
|
|
58
|
+
# These are written as assignments (response.body = ...) and shouldn't
|
|
59
|
+
# be converted to method call style (response.body(...))
|
|
60
|
+
node.method_name.to_s.end_with?('=')
|
|
61
|
+
end
|
|
62
|
+
|
|
55
63
|
def allow_backslash?
|
|
56
64
|
# Allow backslash style without warnings when configured
|
|
57
65
|
# Set to true to allow both styles (but auto-correct will still convert to parentheses)
|
data/lib/tasks/db.rake
CHANGED
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.44.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: []
|
|
@@ -471,6 +471,7 @@ files:
|
|
|
471
471
|
- lib/glib-web.rb
|
|
472
472
|
- lib/glib/all_helpers.rb
|
|
473
473
|
- lib/glib/crypt/utils.rb
|
|
474
|
+
- lib/glib/doc_generator.rb
|
|
474
475
|
- lib/glib/dynamic_text.rb
|
|
475
476
|
- lib/glib/dynamic_text/config.rb
|
|
476
477
|
- lib/glib/engine.rb
|
|
@@ -506,10 +507,10 @@ files:
|
|
|
506
507
|
- lib/glib/version.rb
|
|
507
508
|
- lib/tasks/db.rake
|
|
508
509
|
- lib/tasks/docs.rake
|
|
509
|
-
homepage:
|
|
510
|
+
homepage:
|
|
510
511
|
licenses: []
|
|
511
512
|
metadata: {}
|
|
512
|
-
post_install_message:
|
|
513
|
+
post_install_message:
|
|
513
514
|
rdoc_options: []
|
|
514
515
|
require_paths:
|
|
515
516
|
- lib
|
|
@@ -525,7 +526,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
525
526
|
version: '0'
|
|
526
527
|
requirements: []
|
|
527
528
|
rubygems_version: 3.4.6
|
|
528
|
-
signing_key:
|
|
529
|
+
signing_key:
|
|
529
530
|
specification_version: 4
|
|
530
531
|
summary: ''
|
|
531
532
|
test_files: []
|