glib-web 0.5.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (158) hide show
  1. checksums.yaml +7 -0
  2. data/app/controllers/concerns/glib/auth/policy.rb +148 -0
  3. data/app/controllers/concerns/glib/json/dynamic_text.rb +126 -0
  4. data/app/controllers/concerns/glib/json/libs.rb +144 -0
  5. data/app/controllers/concerns/glib/json/new_dynamic_text.rb +122 -0
  6. data/app/controllers/concerns/glib/json/transformation.rb +11 -0
  7. data/app/controllers/concerns/glib/json/traversal.rb +85 -0
  8. data/app/controllers/concerns/glib/json/ui.rb +70 -0
  9. data/app/controllers/concerns/glib/json/validation.rb +13 -0
  10. data/app/controllers/glib/home_controller.rb +16 -0
  11. data/app/helpers/glib/app_feature_support_helper.rb +16 -0
  12. data/app/helpers/glib/dynamic_images_helper.rb +52 -0
  13. data/app/helpers/glib/dynamic_texts_helper.rb +42 -0
  14. data/app/helpers/glib/forms_helper.rb +15 -0
  15. data/app/helpers/glib/json_ui/abstract_builder.rb +281 -0
  16. data/app/helpers/glib/json_ui/action_builder.rb +81 -0
  17. data/app/helpers/glib/json_ui/action_builder/dialogs.rb +58 -0
  18. data/app/helpers/glib/json_ui/action_builder/http.rb +19 -0
  19. data/app/helpers/glib/json_ui/action_builder/sheets.rb +15 -0
  20. data/app/helpers/glib/json_ui/action_builder/snackbars.rb +41 -0
  21. data/app/helpers/glib/json_ui/action_builder/windows.rb +25 -0
  22. data/app/helpers/glib/json_ui/dynamic_field_builders.rb +25 -0
  23. data/app/helpers/glib/json_ui/generic_builders.rb +28 -0
  24. data/app/helpers/glib/json_ui/list_builders.rb +87 -0
  25. data/app/helpers/glib/json_ui/menu_builder.rb +52 -0
  26. data/app/helpers/glib/json_ui/page_helper.rb +187 -0
  27. data/app/helpers/glib/json_ui/response_helper.rb +23 -0
  28. data/app/helpers/glib/json_ui/split_builders.rb +32 -0
  29. data/app/helpers/glib/json_ui/styling_helper.rb +25 -0
  30. data/app/helpers/glib/json_ui/table_builders.rb +74 -0
  31. data/app/helpers/glib/json_ui/view_builder.rb +185 -0
  32. data/app/helpers/glib/json_ui/view_builder/banners.rb +24 -0
  33. data/app/helpers/glib/json_ui/view_builder/charts.rb +33 -0
  34. data/app/helpers/glib/json_ui/view_builder/fields.rb +213 -0
  35. data/app/helpers/glib/json_ui/view_builder/panels.rb +219 -0
  36. data/app/models/glib/active_storage/attachment.rb +9 -0
  37. data/app/models/glib/active_storage/blob.rb +9 -0
  38. data/app/models/glib/dynamic_text_record.rb +9 -0
  39. data/app/models/glib/text.rb +96 -0
  40. data/app/policies/glib/application_policy.rb +164 -0
  41. data/app/validators/email_typo_validator.rb +38 -0
  42. data/app/validators/email_validator.rb +7 -0
  43. data/app/validators/url_validator.rb +20 -0
  44. data/app/views/app/views/json_ui/vue/renderer.html.erb +35 -0
  45. data/app/views/json_ui/garage/_nav_menu.json.jbuilder +71 -0
  46. data/app/views/json_ui/garage/actions/_dialogs.json.jbuilder +104 -0
  47. data/app/views/json_ui/garage/actions/_http.json.jbuilder +18 -0
  48. data/app/views/json_ui/garage/actions/_reload.json.jbuilder +17 -0
  49. data/app/views/json_ui/garage/actions/_sheets.json.jbuilder +19 -0
  50. data/app/views/json_ui/garage/actions/_snackbars.json.jbuilder +33 -0
  51. data/app/views/json_ui/garage/actions/_timeouts.json.jbuilder +18 -0
  52. data/app/views/json_ui/garage/actions/_windows.json.jbuilder +24 -0
  53. data/app/views/json_ui/garage/actions/dialogs_oauth_post.json.jbuilder +6 -0
  54. data/app/views/json_ui/garage/actions/index.json.jbuilder +23 -0
  55. data/app/views/json_ui/garage/forms/_alert_post_data.json.jbuilder +7 -0
  56. data/app/views/json_ui/garage/forms/basic.json.jbuilder +34 -0
  57. data/app/views/json_ui/garage/forms/basic_post.json.jbuilder +8 -0
  58. data/app/views/json_ui/garage/forms/checkboxes.json.jbuilder +44 -0
  59. data/app/views/json_ui/garage/forms/dynamic_group.json.jbuilder +41 -0
  60. data/app/views/json_ui/garage/forms/dynamic_select.json.jbuilder +25 -0
  61. data/app/views/json_ui/garage/forms/dynamic_select_data.json.jbuilder +38 -0
  62. data/app/views/json_ui/garage/forms/file_upload.json.jbuilder +58 -0
  63. data/app/views/json_ui/garage/forms/floating_submit.json.jbuilder +31 -0
  64. data/app/views/json_ui/garage/forms/generic_post.json.jbuilder +3 -0
  65. data/app/views/json_ui/garage/forms/get_request.json.jbuilder +28 -0
  66. data/app/views/json_ui/garage/forms/index.json.jbuilder +101 -0
  67. data/app/views/json_ui/garage/forms/pickers.json.jbuilder +46 -0
  68. data/app/views/json_ui/garage/forms/rich_text.json.jbuilder +40 -0
  69. data/app/views/json_ui/garage/forms/selects.json.jbuilder +70 -0
  70. data/app/views/json_ui/garage/forms/show_hide.json.jbuilder +88 -0
  71. data/app/views/json_ui/garage/forms/styled_boxes.json.jbuilder +32 -0
  72. data/app/views/json_ui/garage/forms/submission_flow.json.jbuilder +17 -0
  73. data/app/views/json_ui/garage/forms/submission_flow_post.json.jbuilder +24 -0
  74. data/app/views/json_ui/garage/forms/submission_indicator.json.jbuilder +63 -0
  75. data/app/views/json_ui/garage/forms/submission_indicator_post.json.jbuilder +25 -0
  76. data/app/views/json_ui/garage/forms/text_validation.json.jbuilder +22 -0
  77. data/app/views/json_ui/garage/home/blank.json.jbuilder +11 -0
  78. data/app/views/json_ui/garage/home/index.json.jbuilder +32 -0
  79. data/app/views/json_ui/garage/home/slow.json.jbuilder +11 -0
  80. data/app/views/json_ui/garage/lists/_infinite_scroll_section.json.jbuilder +20 -0
  81. data/app/views/json_ui/garage/lists/edit_actions.json.jbuilder +19 -0
  82. data/app/views/json_ui/garage/lists/fab.json.jbuilder +14 -0
  83. data/app/views/json_ui/garage/lists/index.json.jbuilder +23 -0
  84. data/app/views/json_ui/garage/lists/infinite_scroll.json.jbuilder +38 -0
  85. data/app/views/json_ui/garage/lists/templating.json.jbuilder +35 -0
  86. data/app/views/json_ui/garage/notifications/index.json.jbuilder +18 -0
  87. data/app/views/json_ui/garage/notifications/web_socket.json.jbuilder +60 -0
  88. data/app/views/json_ui/garage/pages/flat_centered.json.jbuilder +29 -0
  89. data/app/views/json_ui/garage/pages/full_width.json.jbuilder +29 -0
  90. data/app/views/json_ui/garage/pages/full_width_height.json.jbuilder +16 -0
  91. data/app/views/json_ui/garage/pages/index.json.jbuilder +47 -0
  92. data/app/views/json_ui/garage/pages/layout.json.jbuilder +19 -0
  93. data/app/views/json_ui/garage/pages/loading_indicator.json.jbuilder +10 -0
  94. data/app/views/json_ui/garage/pages/nav_buttons.json.jbuilder +21 -0
  95. data/app/views/json_ui/garage/pages/tab_bar.json.jbuilder +27 -0
  96. data/app/views/json_ui/garage/panels/_styled.json.jbuilder +78 -0
  97. data/app/views/json_ui/garage/panels/card.json.jbuilder +4 -0
  98. data/app/views/json_ui/garage/panels/carousel.json.jbuilder +16 -0
  99. data/app/views/json_ui/garage/panels/custom.json.jbuilder +17 -0
  100. data/app/views/json_ui/garage/panels/flow.json.jbuilder +49 -0
  101. data/app/views/json_ui/garage/panels/horizontal.json.jbuilder +91 -0
  102. data/app/views/json_ui/garage/panels/index.json.jbuilder +132 -0
  103. data/app/views/json_ui/garage/panels/outlined.json.jbuilder +4 -0
  104. data/app/views/json_ui/garage/panels/responsive.json.jbuilder +88 -0
  105. data/app/views/json_ui/garage/panels/split.json.jbuilder +183 -0
  106. data/app/views/json_ui/garage/panels/vertical.json.jbuilder +50 -0
  107. data/app/views/json_ui/garage/services/dynamic_text.json.jbuilder +13 -0
  108. data/app/views/json_ui/garage/services/image.json.jbuilder +47 -0
  109. data/app/views/json_ui/garage/services/index.json.jbuilder +17 -0
  110. data/app/views/json_ui/garage/tables/_autoload_section.json.jbuilder +13 -0
  111. data/app/views/json_ui/garage/tables/autoload_all.json.jbuilder +38 -0
  112. data/app/views/json_ui/garage/tables/autoload_as_needed.json.jbuilder +39 -0
  113. data/app/views/json_ui/garage/tables/export_import.json.jbuilder +29 -0
  114. data/app/views/json_ui/garage/tables/horizontal_scroll.json.jbuilder +26 -0
  115. data/app/views/json_ui/garage/tables/index.json.jbuilder +26 -0
  116. data/app/views/json_ui/garage/tables/layout.json.jbuilder +38 -0
  117. data/app/views/json_ui/garage/views/_chart_data.json.jbuilder +17 -0
  118. data/app/views/json_ui/garage/views/banners.json.jbuilder +51 -0
  119. data/app/views/json_ui/garage/views/calendar_data.json.jbuilder +30 -0
  120. data/app/views/json_ui/garage/views/carousels.json.jbuilder +37 -0
  121. data/app/views/json_ui/garage/views/charts.json.jbuilder +115 -0
  122. data/app/views/json_ui/garage/views/images.json.jbuilder +89 -0
  123. data/app/views/json_ui/garage/views/index.json.jbuilder +48 -0
  124. data/app/views/json_ui/garage/views/links.json.jbuilder +70 -0
  125. data/app/views/json_ui/garage/views/map_data.json.jbuilder +43 -0
  126. data/app/views/json_ui/garage/views/markdowns.json.jbuilder +41 -0
  127. data/app/views/json_ui/garage/views/misc.json.jbuilder +34 -0
  128. data/app/views/json_ui/garage/views/texts.json.jbuilder +41 -0
  129. data/app/views/layouts/json_ui/renderer.html.erb +32 -0
  130. data/config/routes.rb +8 -0
  131. data/lib/generators/glib/install_generator.rb +24 -0
  132. data/lib/generators/templates/20191017062519_create_texts.rb +12 -0
  133. data/lib/generators/templates/20191024063257_add_scope_to_texts.rb +7 -0
  134. data/lib/generators/templates/20191112095018_add_lang_to_texts.rb +7 -0
  135. data/lib/generators/templates/20191126071051_create_active_storage_tables.active_storage.rb +27 -0
  136. data/lib/generators/templates/database.yml +107 -0
  137. data/lib/generators/templates/dynamic_text.rb +2 -0
  138. data/lib/glib-web.rb +8 -0
  139. data/lib/glib/crypt.rb +1 -0
  140. data/lib/glib/crypt/utils.rb +26 -0
  141. data/lib/glib/dynamic_text.rb +1 -0
  142. data/lib/glib/dynamic_text/config.rb +21 -0
  143. data/lib/glib/engine.rb +7 -0
  144. data/lib/glib/json_crawler.rb +10 -0
  145. data/lib/glib/json_crawler/action_crawler.rb +20 -0
  146. data/lib/glib/json_crawler/action_crawlers/action_http.rb +14 -0
  147. data/lib/glib/json_crawler/action_crawlers/forms_submit.rb +48 -0
  148. data/lib/glib/json_crawler/action_crawlers/menu.rb +12 -0
  149. data/lib/glib/json_crawler/action_crawlers/nav_initiate.rb +15 -0
  150. data/lib/glib/json_crawler/action_crawlers/windows_open.rb +28 -0
  151. data/lib/glib/json_crawler/coverage.rb +20 -0
  152. data/lib/glib/json_crawler/http.rb +120 -0
  153. data/lib/glib/json_crawler/router.rb +86 -0
  154. data/lib/glib/test_helpers.rb +40 -0
  155. data/lib/glib/value.rb +7 -0
  156. data/lib/glib/version.rb +5 -0
  157. data/lib/tasks/db.rake +95 -0
  158. metadata +246 -0
@@ -0,0 +1,11 @@
1
+ module Glib::Json::Transformation
2
+ def json_transformation_start
3
+ if request.format == 'json'
4
+ @__transformed_json ||= JSON.parse(response.body) rescue nil
5
+ end
6
+ end
7
+
8
+ def __json_transformation_commit
9
+ response.body = @__transformed_json.to_json if @__transformed_json
10
+ end
11
+ end
@@ -0,0 +1,85 @@
1
+ module Glib::Json::Traversal
2
+ def __json_traversal_on_traverse(&block)
3
+ @__json_traversal_on_traverses ||= []
4
+ @__json_traversal_on_traverses << block
5
+ end
6
+
7
+ def __json_traversal_on_complete(&block)
8
+ @__json_traversal_on_completes ||= []
9
+ @__json_traversal_on_completes << block
10
+ end
11
+
12
+ def __json_traversal_perform
13
+ if (hash = json_transformation_start).is_a?(Hash)
14
+ traverse hash['header']&.[]('childViews')
15
+ traverse hash['body']&.[]('childViews')
16
+ traverse hash['footer']&.[]('childViews')
17
+
18
+ @__json_traversal_on_completes.each do |block|
19
+ block.call
20
+ end
21
+ end
22
+ end
23
+
24
+ def traverse(views)
25
+ Visitor.new.traverse_multiple views, ->(view) do
26
+ @__json_traversal_on_traverses.each do |block|
27
+ block.call(view)
28
+ end
29
+ end
30
+ end
31
+
32
+
33
+
34
+ class Visitor
35
+ # Used in test crawler
36
+ attr_reader :forms
37
+
38
+ def initialize
39
+ @forms = []
40
+ end
41
+
42
+ def traverse_multiple(views, block)
43
+ if views.is_a? Array
44
+ views.each do |view|
45
+ case view['view']
46
+ when 'panels/form-v1'
47
+ @forms << view
48
+ traverse_single view, block
49
+ @forms.pop
50
+ else
51
+ traverse_single view, block
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ def traverse_vertical_content(view, block)
58
+ traverse_multiple view['childViews'], block if view
59
+ end
60
+
61
+ def traverse_single(view, block)
62
+ block.call view
63
+
64
+ # Generic view children
65
+ traverse_multiple view['childViews'], block
66
+
67
+ # Generic menu buttons
68
+ traverse_multiple view['buttons'], block
69
+
70
+ # Split panel
71
+ traverse_vertical_content view['left'], block
72
+ traverse_vertical_content view['center'], block
73
+ traverse_vertical_content view['right'], block
74
+
75
+ # TODO: crawl header and footer
76
+ # Table/List
77
+ if (sections = view['sections']).is_a? Array
78
+ sections.each do |section|
79
+ # Table
80
+ traverse_multiple section['rows'], block
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,70 @@
1
+ module Glib::Json::Ui
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ rescue_from ActionController::UnknownFormat do |exception|
6
+ if json_ui_activated?
7
+ # Tell `__json_ui_start()` to avoid rendering this page while still retaining the `_render` param
8
+ # so that the page remains linking to other json_ui pages.
9
+ redirect_to url_for(format: nil, _skip_render: true)
10
+ else
11
+ raise exception
12
+ end
13
+ end
14
+ end
15
+
16
+ # Override
17
+ def form_authenticity_token(*args)
18
+ Rails.env.test? ? 'test_token' : super
19
+ end
20
+
21
+ # NOTE: Override default_url_options and call this method
22
+ def json_ui_url_options
23
+ options = {}
24
+ options[:_render] = params[:_render]
25
+ options[:_locale] = params[:_locale]
26
+ options[:format] = :json if request.format == :json
27
+ options
28
+ end
29
+
30
+ def json_ui_activated?
31
+ @__json_ui_activated
32
+ end
33
+
34
+ def __json_ui_start(options)
35
+ @__json_ui_activated = false
36
+ @__json_ui_rendering = nil
37
+ if options[:when] == :always || params[:_render] == 'v1'
38
+ @__json_ui_activated = true
39
+ request.variant = :ui
40
+
41
+ if request.format.html? && params[:_skip_render] != 'true'
42
+ @__json_ui_rendering = :vue
43
+ request.format = 'json'
44
+ end
45
+ end
46
+ end
47
+
48
+ def __json_ui_commit(options)
49
+ return if response.status >= 300
50
+
51
+ case @__json_ui_rendering
52
+ when :vue
53
+ if (hash = json_transformation_start).is_a?(Hash)
54
+ __json_ui_vue(hash, options)
55
+ else
56
+ raise "Invalid JSON UI payload: #{hash}"
57
+ end
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def __json_ui_vue(hash, options)
64
+ renderer_path = options[:renderer_path]
65
+ @__json_ui_orig_page = response.body
66
+ response.body = render_to_string(template: renderer_path, layout: 'json_ui/renderer', content_type: 'text/html', locals: { page: hash, options: options })
67
+
68
+ # response.body = render_to_string(template: renderer_path, layout: false, content_type: 'text/html', locals: { page: hash, options: options })
69
+ end
70
+ end
@@ -0,0 +1,13 @@
1
+ module Glib::Json::Validation
2
+ def __json_validate_perform
3
+ if Rails.env.development? && params[:_validate] == 'true'
4
+ if (hash = json_transformation_start).is_a?(Hash)
5
+ json_validate = JSONValidate.new(hash)
6
+ response_message = json_validate.valid?
7
+ hash[:_json] = {
8
+ validationErrors: response_message
9
+ }
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ module Glib
2
+ class HomeController < ApplicationController
3
+ if try(:glib_auth_inited?)
4
+ skip_before_action :glib_load_resource
5
+ skip_before_action :glib_authorize_resource
6
+ end
7
+
8
+ def json_ui_garage
9
+ @path_prefix = 'json_ui/garage'
10
+
11
+ # We can't use prepend_view_path because it affects the app, not the gem
12
+ path = "#{@path_prefix}/#{params[:path] || 'home/index'}"
13
+ render path
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module Glib
2
+ module AppFeatureSupportHelper
3
+ def glib_app_feature_supported?(name, app_version, dictionary)
4
+ device = (app_device_os || :default).to_sym
5
+
6
+ version = dictionary[name.to_sym].try(:[], device)
7
+ except = dictionary[name.to_sym].try(:[], :except).try(:[], device)
8
+
9
+ !version.nil? && (version == :all || glib_app_version_gte(version, app_version)) && (except.nil? || except.to_s != app_version)
10
+ end
11
+
12
+ def glib_app_version_gte(feature_version, app_version)
13
+ app_version.blank? || Gem::Version.new(app_version) >= Gem::Version.new(feature_version)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,52 @@
1
+ module Glib
2
+ module DynamicImagesHelper
3
+ # NOTE: The bucket should probably be set as a parameter for json_libs
4
+ def dynamic_image_url(blob_key, width: 100, height: 100, fit: 'clip', bucket: Rails.application.config.try(:aws_s3_bucket))
5
+ return unless blob_key.present?
6
+
7
+ full_params_hash = {
8
+ bucket_name: bucket,
9
+ blob_key: blob_key,
10
+ w: width,
11
+ h: height,
12
+ fit: fit,
13
+ expires: 1.hours.from_now.to_i
14
+ }
15
+ request_params_hash = full_params_hash.except(:bucket_name, :blob_key)
16
+
17
+ if (private_key = ENV['PRIVATE_API_KEY'])
18
+ encryption_service = EncryptionService.new(bucket, private_key)
19
+ signature = encryption_service.encrypt(full_params_hash)
20
+ request_params_hash = request_params_hash.merge(signature: signature)
21
+ end
22
+
23
+ # TODO: This should probably be set as a parameter for json_libs
24
+ uri = URI::HTTPS.build(
25
+ host: 'imageserver-demo.herokuapp.com',
26
+ path: "/image/#{bucket}/#{blob_key}",
27
+ query: request_params_hash.to_param
28
+ )
29
+
30
+ uri.to_s
31
+ end
32
+
33
+ EncryptionService = Struct.new(:salt, :private_key) do
34
+ def encrypt(value)
35
+ encryptor.encrypt_and_sign(value)
36
+ end
37
+
38
+ def decrypt(value)
39
+ encryptor.decrypt_and_verify(value)
40
+ end
41
+
42
+ private
43
+ def encryptor
44
+ @encryptor ||= begin
45
+ key_len = ActiveSupport::MessageEncryptor.key_len
46
+ key = ActiveSupport::KeyGenerator.new(private_key).generate_key(salt, key_len)
47
+ ActiveSupport::MessageEncryptor.new(key)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,42 @@
1
+ module Glib
2
+ module DynamicTextsHelper
3
+ def dt(key, default_value = '', **args)
4
+ new_key = key
5
+ options = {
6
+ scope: args.fetch(:scope, 'itinerarybuilder'),
7
+ lang: args.fetch(:lang, 'en')
8
+ }
9
+
10
+ if key.starts_with?('.')
11
+ new_key = "#{controller_name}.#{action_name}#{key}"
12
+ end
13
+
14
+ content, text_object = Glib::Text.get_content(new_key, default_value, options: options)
15
+ content = content.gsub(/\{\{(\w+)\}\}/) { args.fetch($1.to_sym, "{{#{$1}}}") }
16
+
17
+ if text_object.images.attached?
18
+ content = content.gsub(/\{\{image(\d)\}\}/) {
19
+ if image = text_object.images[$1.to_i - 1]
20
+ dynamic_image_url(image.blob.key)
21
+ else
22
+ "{{image#{$1}}}"
23
+ end
24
+ }
25
+ end
26
+
27
+ content
28
+ end
29
+
30
+ def dt_json(key, default_value = '', **args)
31
+ new_key = key
32
+
33
+ if key.starts_with?('.')
34
+ new_key = "#{controller_name}.#{action_name}#{key}"
35
+ end
36
+
37
+ {
38
+ dt_key: new_key
39
+ }.merge(args)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,15 @@
1
+ module Glib
2
+ module FormsHelper
3
+ def glib_form_field_label(model_name, prop, args = {})
4
+ I18n.t("dt_models.#{model_name}.#{prop}.label", args.merge(default: nil)) || I18n.t("activerecord.attributes.#{model_name}.#{prop}", args)
5
+ end
6
+
7
+ def glib_form_hint_label(model_name, prop, args = {})
8
+ I18n.t("dt_models.#{model_name}.#{prop}.hint", args.merge(default: nil))
9
+ end
10
+
11
+ def glib_form_placeholder_label(model_name, prop, args = {})
12
+ I18n.t("dt_models.#{model_name}.#{prop}.placeholder", args.merge(default: nil))
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,281 @@
1
+ module Glib
2
+ module JsonUi
3
+ class AbstractBuilder
4
+ attr_reader :json, :page
5
+
6
+ def initialize(json, page)
7
+ @json = json
8
+ @page = page
9
+ end
10
+
11
+ private
12
+ def capitalize_first_letter_and_leave_others_alone(str)
13
+ str.slice(0,1).capitalize + str.slice(1..-1)
14
+ end
15
+
16
+ def add_singleton_element type, name, *args
17
+ name_components = name.to_s.split('_')
18
+ json.set! type, "#{name_components.join('/')}" if type
19
+ args = [yield] if block_given?
20
+
21
+ # TODO: consider caching these instances
22
+ "#{self.class.name}::#{name_components.map{ |str| capitalize_first_letter_and_leave_others_alone(str) }.join('::')}".constantize.new(json, @page).props(*args)
23
+ end
24
+
25
+ def add_element_to_array type, name, *args
26
+ json.child! do
27
+ add_singleton_element type, name, *args
28
+ end
29
+ end
30
+
31
+ # TODO: Consider removing this in favour of non 'v1' suffix
32
+ def add_singleton_element_v1 type, name, *args
33
+ name_components = name.to_s.split('_')
34
+ json.set! type, "#{name_components.join('/')}-v1" if type
35
+ args = [yield] if block_given?
36
+ "#{self.class.name}::#{name_components.map{ |str| capitalize_first_letter_and_leave_others_alone(str) }.join('::')}".constantize.new(json, @page).props(*args)
37
+ end
38
+
39
+ # TODO: Consider removing this in favour of non 'v1' suffix
40
+ def add_element_to_array_v1 type, name, *args
41
+ json.child! do
42
+ add_singleton_element_v1 type, name, *args
43
+ end
44
+ end
45
+
46
+ end
47
+
48
+ class JsonUiElement
49
+ attr_reader :json, :page
50
+
51
+ def initialize(json, page)
52
+ @json = json
53
+ @page = page
54
+ end
55
+
56
+ def props(args = {})
57
+ @args = args
58
+
59
+ if args.is_a? Hash
60
+ args.each do |k, v|
61
+ send(k, v)
62
+ end
63
+ else
64
+ raise "Invalid properties: #{args}"
65
+ end
66
+
67
+ created
68
+ end
69
+
70
+ private
71
+
72
+ def self.date(propName)
73
+ define_method(propName) do |value|
74
+ json.set! propName, value&.to_date
75
+
76
+ # if (value = value&.to_s)
77
+ # if !Rails.env.production?
78
+ # raise "Invalid date for #{propName}: #{value}" unless value =~ /^\d{4}-\d{2}-\d{2}$/
79
+ # end
80
+
81
+ # json.set! propName, value
82
+ # end
83
+ end
84
+ end
85
+
86
+ def self.date_time(propName)
87
+ define_method(propName) do |value|
88
+ json.set! propName, value&.to_datetime
89
+
90
+ # if (value = value&.to_s)
91
+ # if !Rails.env.production?
92
+ # try Date.parse(value) rescue raise "Invalid date: #{value}"
93
+ # end
94
+
95
+ # json.set! propName, value
96
+ # end
97
+ end
98
+ end
99
+
100
+ def self.color propName
101
+ define_method(propName) do |value|
102
+ if (value = value&.to_s)
103
+ if !Rails.env.production?
104
+ if !value.match /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/i
105
+ raise "Invalid color: #{value}"
106
+ end
107
+ end
108
+
109
+ json.set! propName, value
110
+ end
111
+ end
112
+ end
113
+
114
+ def self.length propName
115
+ define_method(propName) do |value|
116
+ if value
117
+ if !Rails.env.production?
118
+ case value
119
+ when 'matchParent'
120
+ when 'wrapContent'
121
+ else
122
+ if !value.is_a? Integer
123
+ raise "Invalid length: #{value}"
124
+ end
125
+ end
126
+ end
127
+
128
+ json.set! propName, value
129
+ end
130
+ end
131
+ end
132
+
133
+ def self.string propName, options = {}
134
+ define_method(propName) do |value|
135
+ str = value&.to_s
136
+ instance_variable_set("@#{propName}", str) if options[:cache]
137
+ json.set! propName, str
138
+ end
139
+ end
140
+
141
+ # Support either string or dynamic_text hash
142
+ def self.text propName, options = {}
143
+ define_method(propName) do |value|
144
+ if value.is_a?(Hash)
145
+ json.set! propName, value
146
+ else
147
+ json.set! propName, value&.to_s
148
+ end
149
+ end
150
+ end
151
+
152
+ def self.int propName
153
+ define_method(propName) do |value|
154
+ json.set! propName, value&.to_i
155
+ end
156
+ end
157
+
158
+ def self.float propName
159
+ define_method(propName) do |value|
160
+ json.set! propName, value&.to_f
161
+ end
162
+ end
163
+
164
+ def self.bool propName, options = {}
165
+ define_method(propName) do |value|
166
+ bool = value == true
167
+ instance_variable_set("@#{propName}", bool) if options[:cache]
168
+ json.set! propName, bool
169
+ end
170
+ end
171
+
172
+ def self.icon propName, options = {}
173
+ define_method(propName) do |value|
174
+
175
+ if value.is_a?(Hash)
176
+ data = value
177
+ name = data[:name]
178
+ else
179
+ data = {}
180
+ name = value
181
+ # data = { material: { name: value } }
182
+
183
+ # name = value
184
+
185
+ # TODO: deprecated
186
+ # json.set!(propName) do
187
+ # json.name name
188
+ # end
189
+ end
190
+
191
+ json.set!(propName) do
192
+ if name
193
+ data[:material] ||= {}
194
+ data[:material][:name] = name
195
+ end
196
+
197
+ if (material = data[:material])
198
+ json.material do
199
+ json.name material[:name]
200
+ json.size material[:size] if material[:size]
201
+ end
202
+ end
203
+
204
+ if (fa = data[:fa])
205
+ json.fa do
206
+ json.name fa[:name]
207
+ json.size fa[:size] if fa[:size]
208
+ end
209
+ end
210
+
211
+ if (custom = data[:custom])
212
+ json.custom do
213
+ json.name custom[:name]
214
+ json.size custom[:size] if custom[:size]
215
+ end
216
+ end
217
+
218
+ if (badge = data[:badge])
219
+ if badge.is_a?(Hash)
220
+ json.badge badge
221
+ else
222
+ json.badge do
223
+ json.text badge
224
+ end
225
+ end
226
+ end
227
+
228
+ end
229
+
230
+ # raise "Invalid properties: #{options.keys}" if options.size > 0
231
+
232
+ # instance_variable_set("@#{propName}", name) if options[:cache]
233
+
234
+ end
235
+ end
236
+
237
+ def self.action propName
238
+ define_method(propName) do |value|
239
+ json.set!(propName) { value.call(page.action_builder) }
240
+ end
241
+ end
242
+
243
+ def self.views propName
244
+ define_method(propName) do |value|
245
+ json.set!(propName) { value.call(page.view_builder) }
246
+ end
247
+ end
248
+
249
+ def self.array propName
250
+ define_method(propName) do |value|
251
+ json.set! propName, value&.to_a
252
+ end
253
+ end
254
+
255
+ def self.hash propName
256
+ define_method(propName) do |value|
257
+ json.set! propName, value
258
+ end
259
+ end
260
+
261
+ def self.singleton_array singletonName, arrayName
262
+ array arrayName
263
+
264
+ define_method(singletonName) do |value|
265
+ json.set! arrayName, [value]
266
+ end
267
+ end
268
+
269
+ def self.panels_builder(propName, *panelNames)
270
+ define_method(propName) do |value|
271
+ value.call(page.content_builder(panelNames))
272
+ end
273
+ end
274
+
275
+ def created
276
+ # To be overridden
277
+ end
278
+
279
+ end
280
+ end
281
+ end