glib-web 4.39.0 → 4.39.2

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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/concerns/glib/auth/policy.rb +13 -0
  3. data/app/controllers/glib/api_docs_controller.rb +145 -0
  4. data/app/helpers/glib/json_ui/abstract_builder.rb +16 -0
  5. data/app/helpers/glib/json_ui/action_builder/dialogs.rb +4 -0
  6. data/app/helpers/glib/json_ui/list_builders.rb +2 -0
  7. data/app/helpers/glib/json_ui/view_builder/fields.rb +15 -0
  8. data/app/helpers/glib/json_ui/view_builder/panels.rb +450 -11
  9. data/app/helpers/glib/json_ui/view_builder.rb +1 -1
  10. data/app/views/glib/api_docs/component.json.jbuilder +215 -0
  11. data/app/views/glib/api_docs/index.json.jbuilder +103 -0
  12. data/app/views/glib/api_docs/show.json.jbuilder +111 -0
  13. data/app/views/json_ui/garage/actions/_dialogs.json.jbuilder +2 -2
  14. data/app/views/json_ui/garage/forms/dynamic_group.json.jbuilder +2 -2
  15. data/app/views/json_ui/garage/forms/file_upload_new.json.jbuilder +1 -1
  16. data/app/views/json_ui/garage/forms/partial_update.json.jbuilder +12 -12
  17. data/app/views/json_ui/garage/forms/selects.json.jbuilder +1 -1
  18. data/app/views/json_ui/garage/forms/show_hide.json.jbuilder +62 -7
  19. data/app/views/json_ui/garage/lists/edit_mode.json.jbuilder +4 -4
  20. data/app/views/json_ui/garage/lists/templating.json.jbuilder +68 -44
  21. data/app/views/json_ui/garage/pages/custom_style_class.json.jbuilder +1 -1
  22. data/app/views/json_ui/garage/pages/nav_buttons.json.jbuilder +31 -13
  23. data/app/views/json_ui/garage/panels/_styled.json.jbuilder +8 -8
  24. data/app/views/json_ui/garage/panels/hover.json.jbuilder +2 -2
  25. data/app/views/json_ui/garage/panels/timeline.json.jbuilder +5 -5
  26. data/app/views/json_ui/garage/panels/ul.json.jbuilder +1 -1
  27. data/app/views/json_ui/garage/tables/bulk_edit.json.jbuilder +1 -1
  28. data/app/views/json_ui/garage/test_page/file_upload_new.json.jbuilder +1 -1
  29. data/app/views/json_ui/garage/test_page/form.json.jbuilder +6 -6
  30. data/app/views/json_ui/garage/test_page/form_dynamic.json.jbuilder +2 -2
  31. data/app/views/json_ui/garage/test_page/logics_set.json.jbuilder +94 -0
  32. data/app/views/json_ui/garage/views/components_replace.json.jbuilder +13 -13
  33. data/app/views/json_ui/garage/views/components_set.json.jbuilder +6 -6
  34. data/app/views/json_ui/garage/views/fields_focus.json.jbuilder +22 -22
  35. data/app/views/json_ui/garage/views/markdowns.json.jbuilder +2 -0
  36. data/config/routes.rb +4 -0
  37. data/lib/glib/doc_generator.rb +386 -0
  38. data/lib/glib/json_crawler/router.rb +45 -24
  39. data/lib/glib/rubocop/cops/json_ui/base_nested_parameter.rb +145 -0
  40. data/lib/glib/rubocop/cops/json_ui/nested_action_parameter.rb +55 -0
  41. data/lib/glib/rubocop/cops/json_ui/nested_block_parameter.rb +51 -0
  42. data/lib/glib/rubocop/cops/multiline_method_call_style.rb +74 -5
  43. data/lib/glib/rubocop/cops/test_name_parentheses.rb +33 -0
  44. data/lib/glib/rubocop.rb +4 -0
  45. data/lib/glib/test/parallel_coverage.rb +38 -0
  46. data/lib/glib/test_helpers.rb +12 -0
  47. data/lib/glib-web.rb +1 -0
  48. data/lib/tasks/db.rake +1 -1
  49. data/lib/tasks/docs.rake +59 -0
  50. metadata +13 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 04bdbf508e9bae2d875e880a1ccff951cacb80d7eae24a543e82a816d9eaff4f
4
- data.tar.gz: 3f209d236fafbb17c2a40a5cf570bfd5166be900059abbf71b0b652260e035ec
3
+ metadata.gz: 93226f57a31d7506df878201485705c4bddf72c12e76701cf256dbcbea1b9f50
4
+ data.tar.gz: 535de1bcc5ffcee41c5296425d69e10bbaf86a9cd76920396e876ac7be3a2416
5
5
  SHA512:
6
- metadata.gz: 480b89089e395f1b123bab2e0b161d485406eb537c215fab96c464e8be22bd7e42a44d7ef460cf37d32396642dcf37eb46afe7625bae126e19cae89d3870f1bc
7
- data.tar.gz: 3ad20d4a121a87e14b496ad5e179b4f7db2be6812a7bc086086e000ec12c52558b6768192300cdcd273a676824beed495d5db8a4fa97854aff76957310f6fee3
6
+ metadata.gz: 1939b128493528c5281b6d93af4b792d3301905b8ccdc771886e42d5be153f193f39f6dee34c14c6d59562b813aedf50e8dde913baff7ac81c5322c1e7f6ab9f
7
+ data.tar.gz: 6c8b6491839e5185076831ba5d21bf27901322de5ef8ee7f3e3df71c6751ce95371d91a72af070fe7339d23b246369c13181ba08d8c995c55cc71866ea494806
@@ -9,6 +9,14 @@ module Glib::Auth
9
9
  include Overrides
10
10
  extend ClassMethods
11
11
 
12
+ class_attribute :glib_permission_test_callback
13
+
14
+ # Controllers can override this callback to implement additional permission check logics
15
+ # that are implemented outside of policies, which would have been skipped by the permission test.
16
+ glib_on_permission_test do
17
+ # Do nothing by default
18
+ end
19
+
12
20
  # TODO: Ultimately we want to uncomment this line, but:
13
21
  # - Need to be able to set aside some time to run rspec tests to ensure nothing gets broken
14
22
  # - Need to find a solution where we can reuse a single public policy
@@ -104,6 +112,7 @@ module Glib::Auth
104
112
  permission_test = params[:__glib_permission_test].present?
105
113
 
106
114
  if permission_test
115
+ instance_exec(&self.class.glib_permission_test_callback)
107
116
  render status: 200, json: { status: 'ok' }
108
117
  end
109
118
  end
@@ -112,6 +121,10 @@ module Glib::Auth
112
121
  end
113
122
 
114
123
  module ClassMethods
124
+ def glib_on_permission_test(&block)
125
+ self.glib_permission_test_callback = block
126
+ end
127
+
115
128
  # rubocop:disable Style/ClassVars
116
129
  def glib_auth_init
117
130
  @@__glib_auth_init = true
@@ -0,0 +1,145 @@
1
+ module Glib
2
+ class ApiDocsController < ApplicationController
3
+ prepend_before_action do
4
+ params[:_skip_custom_render] = 'true' if params[:custom_render] != 'true'
5
+ end
6
+
7
+ if try(:glib_auth_inited?)
8
+ skip_before_action :glib_load_resource
9
+ skip_before_action :glib_authorize_resource
10
+ end
11
+
12
+ # Skip JSON injections from the host app
13
+ skip_after_action :json_inject_left_menu, raise: false
14
+ skip_after_action :json_inject_feature_usage_analytics, raise: false
15
+ skip_after_action :json_inject_flash_actions, raise: false
16
+
17
+ before_action :set_path_prefix
18
+ layout false
19
+
20
+ # Override injection methods to do nothing
21
+ def json_inject_left_menu; end
22
+ def json_inject_feature_usage_analytics; end
23
+ def json_inject_flash_actions; end
24
+
25
+ def set_path_prefix
26
+ @path_prefix = 'glib/api_docs'
27
+ end
28
+
29
+ # GET /glib/json_ui_api
30
+ def index
31
+ @categories = load_all_categories
32
+ end
33
+
34
+ # GET /glib/json_ui_api/:category
35
+ def show
36
+ @category = params[:category]
37
+ @doc = load_documentation(@category)
38
+
39
+ unless @doc
40
+ render plain: "Category '#{@category}' not found", status: :not_found
41
+ return
42
+ end
43
+
44
+ @components = @doc['components'] || {}
45
+ end
46
+
47
+ # GET /glib/json_ui_api/:category/:component
48
+ def component
49
+ @category = params[:category]
50
+ @component_key = params[:component]
51
+ @doc = load_documentation(@category)
52
+
53
+ unless @doc
54
+ render plain: "Category '#{@category}' not found", status: :not_found
55
+ return
56
+ end
57
+
58
+ @component = @doc['components'][@component_key]
59
+
60
+ unless @component
61
+ render plain: "Component '#{@component_key}' not found in category '#{@category}'", status: :not_found
62
+ return
63
+ end
64
+ end
65
+
66
+ private
67
+
68
+ def load_all_categories
69
+ # Try to find the doc/components directory
70
+ # First try the gem's path
71
+ doc_path = Glib::Web::Engine.root.join('doc', 'components')
72
+
73
+ # If not found, try relative to Rails root (for development in host app)
74
+ unless Dir.exist?(doc_path)
75
+ doc_path = Rails.root.join('..', 'glib-web', 'doc', 'components')
76
+ end
77
+
78
+ return [] unless Dir.exist?(doc_path)
79
+
80
+ categories = []
81
+ Dir.glob(doc_path.join('*.yml')).each do |file|
82
+ next if File.basename(file) == 'README.md'
83
+
84
+ basename = File.basename(file, '.yml')
85
+ doc = YAML.load_file(file)
86
+
87
+ categories << {
88
+ 'key' => basename,
89
+ 'name' => basename.titleize,
90
+ 'file' => file,
91
+ 'component_count' => doc['components']&.size || 0,
92
+ 'source_file' => doc.dig('meta', 'source_file')
93
+ }
94
+ end
95
+
96
+ categories.sort_by { |c| c['name'] }
97
+ end
98
+
99
+ def load_documentation(category)
100
+ # Try to find the YAML file
101
+ # First try the gem's path
102
+ doc_path = Glib::Web::Engine.root.join('doc', 'components', "#{category}.yml")
103
+
104
+ # If not found, try relative to Rails root (for development in host app)
105
+ unless File.exist?(doc_path)
106
+ doc_path = Rails.root.join('..', 'glib-web', 'doc', 'components', "#{category}.yml")
107
+ end
108
+
109
+ return nil unless File.exist?(doc_path)
110
+
111
+ YAML.load_file(doc_path)
112
+ end
113
+
114
+ helper_method :json_ui_api_url
115
+ def json_ui_api_url(category: nil, component: nil)
116
+ if component && category
117
+ "/glib/json_ui_api/#{category}/#{component}"
118
+ elsif category
119
+ "/glib/json_ui_api/#{category}"
120
+ else
121
+ "/glib/json_ui_api"
122
+ end
123
+ end
124
+
125
+ helper_method :property_type_badge_class
126
+ def property_type_badge_class(type)
127
+ case type
128
+ when 'string', 'text'
129
+ 'badge-primary'
130
+ when 'int', 'float'
131
+ 'badge-success'
132
+ when 'bool'
133
+ 'badge-info'
134
+ when 'action'
135
+ 'badge-warning'
136
+ when 'views', 'panels_builder'
137
+ 'badge-secondary'
138
+ when 'hash', 'array'
139
+ 'badge-dark'
140
+ else
141
+ 'badge-light'
142
+ end
143
+ end
144
+ end
145
+ end
@@ -162,6 +162,22 @@ module Glib
162
162
  end
163
163
  end
164
164
 
165
+ def self.enum(propName, options = {})
166
+ define_method(propName) do |value|
167
+ str = to_string(value)
168
+
169
+ if !Rails.env.production?
170
+ allowed_options = options[:options] || []
171
+ if allowed_options.present? && !allowed_options.map(&:to_s).include?(str)
172
+ raise "Invalid value for #{propName}: '#{str}'. Allowed values: #{allowed_options.map(&:inspect).join(', ')}"
173
+ end
174
+ end
175
+
176
+ instance_variable_set("@#{propName}", str) if options[:cache]
177
+ json.set! propName, str
178
+ end
179
+ end
180
+
165
181
  # Support either string or dynamic_text hash
166
182
  def self.text(propName, options = {})
167
183
  define_method(propName) do |value|
@@ -2,7 +2,11 @@ class Glib::JsonUi::ActionBuilder
2
2
  module Dialogs
3
3
  class Alert < Action
4
4
  string :title
5
+
6
+ # Alert message text to display.
7
+ # @note Does not support markdown formatting. For markdown support, use dialogs/show with a custom body instead.
5
8
  string :message
9
+
6
10
  action :onLoad
7
11
  action :onClose
8
12
  singleton_array :styleClass, :styleClasses
@@ -48,6 +48,8 @@ module Glib
48
48
  string :paramNameForNewSectionedRowIndex
49
49
 
50
50
  panels_builder :accessory, :header, :body, :footer
51
+
52
+ enum :textFormat, options: [:plain, :markdown]
51
53
  end
52
54
 
53
55
  class Featured < Standard
@@ -18,9 +18,24 @@ class Glib::JsonUi::ViewBuilder
18
18
  { only_path: true }
19
19
  end
20
20
 
21
+ # Validates field input according to specified rules.
22
+ # @param validation [Hash] Hash of validation types and their options
23
+ # @example
24
+ # validation presence: { message: 'Required' }
25
+ # validation format: { with: /\A\d+\z/, message: 'Must be a number' }
26
+ # validation length: { minimum: 3, maximum: 20, message: { too_short: 'Too short', too_long: 'Too long' } }
27
+ # @note Supported validation types: presence, required, absence, acceptance, numericality, format, inclusion, exclusion, length
28
+ # @see /home/hgani/workspace/glib-web-npm/components/validation.js Frontend validation implementation
21
29
  def validation(validation)
22
30
  return if validation.blank?
23
31
 
32
+ # Validate that all keys are supported validation types
33
+ supported_validations = %i[presence required absence acceptance numericality format inclusion exclusion length]
34
+ invalid_keys = validation.keys - supported_validations
35
+ if invalid_keys.any?
36
+ raise ArgumentError, "Unsupported validation type(s): #{invalid_keys.join(', ')}. Supported types: #{supported_validations.join(', ')}"
37
+ end
38
+
24
39
  if validation[:format].present?
25
40
  context.cast_to_js_regex(validation[:format])
26
41
  end