linked_rails 0.0.1

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 (207) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +674 -0
  3. data/README.md +65 -0
  4. data/Rakefile +34 -0
  5. data/app/controllers/linked_rails/actions/items_controller.rb +9 -0
  6. data/app/controllers/linked_rails/bulk_controller.rb +195 -0
  7. data/app/controllers/linked_rails/current_user_controller.rb +13 -0
  8. data/app/controllers/linked_rails/enum_values_controller.rb +49 -0
  9. data/app/controllers/linked_rails/forms_controller.rb +13 -0
  10. data/app/controllers/linked_rails/manifests_controller.rb +21 -0
  11. data/app/controllers/linked_rails/menus/items_controller.rb +9 -0
  12. data/app/controllers/linked_rails/menus/lists_controller.rb +9 -0
  13. data/app/controllers/linked_rails/not_found_controller.rb +15 -0
  14. data/app/controllers/linked_rails/ontologies_controller.rb +7 -0
  15. data/app/models/linked_rails/actions/default_actions/create.rb +60 -0
  16. data/app/models/linked_rails/actions/default_actions/destroy.rb +45 -0
  17. data/app/models/linked_rails/actions/default_actions/update.rb +50 -0
  18. data/app/models/linked_rails/actions/default_actions.rb +17 -0
  19. data/app/models/linked_rails/actions/item.rb +234 -0
  20. data/app/models/linked_rails/actions/list.rb +113 -0
  21. data/app/models/linked_rails/collection/filter.rb +16 -0
  22. data/app/models/linked_rails/collection/filter_field.rb +30 -0
  23. data/app/models/linked_rails/collection/filter_option.rb +17 -0
  24. data/app/models/linked_rails/collection/filterable.rb +92 -0
  25. data/app/models/linked_rails/collection/infinite_view.rb +98 -0
  26. data/app/models/linked_rails/collection/iri.rb +74 -0
  27. data/app/models/linked_rails/collection/iri_mapping.rb +33 -0
  28. data/app/models/linked_rails/collection/paginated_view.rb +41 -0
  29. data/app/models/linked_rails/collection/sortable.rb +60 -0
  30. data/app/models/linked_rails/collection/sorting.rb +72 -0
  31. data/app/models/linked_rails/collection/view.rb +101 -0
  32. data/app/models/linked_rails/collection.rb +220 -0
  33. data/app/models/linked_rails/condition.rb +7 -0
  34. data/app/models/linked_rails/creative_work.rb +21 -0
  35. data/app/models/linked_rails/current_user.rb +28 -0
  36. data/app/models/linked_rails/entry_point.rb +53 -0
  37. data/app/models/linked_rails/enum_value.rb +33 -0
  38. data/app/models/linked_rails/form/field/association_input.rb +23 -0
  39. data/app/models/linked_rails/form/field/checkbox_group.rb +10 -0
  40. data/app/models/linked_rails/form/field/checkbox_input.rb +10 -0
  41. data/app/models/linked_rails/form/field/color_input.rb +17 -0
  42. data/app/models/linked_rails/form/field/date_input.rb +10 -0
  43. data/app/models/linked_rails/form/field/date_time_input.rb +10 -0
  44. data/app/models/linked_rails/form/field/email_input.rb +10 -0
  45. data/app/models/linked_rails/form/field/file_input.rb +10 -0
  46. data/app/models/linked_rails/form/field/location_input.rb +11 -0
  47. data/app/models/linked_rails/form/field/markdown_input.rb +10 -0
  48. data/app/models/linked_rails/form/field/number_input.rb +10 -0
  49. data/app/models/linked_rails/form/field/password_input.rb +10 -0
  50. data/app/models/linked_rails/form/field/postal_range_input.rb +10 -0
  51. data/app/models/linked_rails/form/field/radio_group.rb +10 -0
  52. data/app/models/linked_rails/form/field/resource_field.rb +23 -0
  53. data/app/models/linked_rails/form/field/select_input.rb +11 -0
  54. data/app/models/linked_rails/form/field/slider_input.rb +10 -0
  55. data/app/models/linked_rails/form/field/text_area_input.rb +10 -0
  56. data/app/models/linked_rails/form/field/text_input.rb +10 -0
  57. data/app/models/linked_rails/form/field/toggle_button_group.rb +10 -0
  58. data/app/models/linked_rails/form/field.rb +117 -0
  59. data/app/models/linked_rails/form/field_factory.rb +219 -0
  60. data/app/models/linked_rails/form/group.rb +39 -0
  61. data/app/models/linked_rails/form/page.rb +31 -0
  62. data/app/models/linked_rails/form.rb +156 -0
  63. data/app/models/linked_rails/manifest.rb +102 -0
  64. data/app/models/linked_rails/media_object.rb +31 -0
  65. data/app/models/linked_rails/menus/item.rb +92 -0
  66. data/app/models/linked_rails/menus/list.rb +138 -0
  67. data/app/models/linked_rails/ontology/base.rb +50 -0
  68. data/app/models/linked_rails/ontology/class.rb +43 -0
  69. data/app/models/linked_rails/ontology/property.rb +19 -0
  70. data/app/models/linked_rails/ontology.rb +34 -0
  71. data/app/models/linked_rails/property_query.rb +11 -0
  72. data/app/models/linked_rails/resource.rb +17 -0
  73. data/app/models/linked_rails/sequence.rb +64 -0
  74. data/app/models/linked_rails/shacl/node_shape.rb +21 -0
  75. data/app/models/linked_rails/shacl/property_shape.rb +53 -0
  76. data/app/models/linked_rails/shacl/shape.rb +33 -0
  77. data/app/models/linked_rails/web_page.rb +22 -0
  78. data/app/models/linked_rails/web_site.rb +17 -0
  79. data/app/models/linked_rails/widget.rb +55 -0
  80. data/app/policies/linked_rails/actions/item_policy.rb +11 -0
  81. data/app/policies/linked_rails/actions/list_policy.rb +11 -0
  82. data/app/policies/linked_rails/collection/view_policy.rb +13 -0
  83. data/app/policies/linked_rails/collection_policy.rb +41 -0
  84. data/app/policies/linked_rails/enum_value_policy.rb +32 -0
  85. data/app/policies/linked_rails/menus/item_policy.rb +11 -0
  86. data/app/policies/linked_rails/menus/list_policy.rb +11 -0
  87. data/app/policies/linked_rails/sequence_policy.rb +9 -0
  88. data/app/serializers/linked_rails/actions/item_serializer.rb +28 -0
  89. data/app/serializers/linked_rails/collection/filter_field_serializer.rb +12 -0
  90. data/app/serializers/linked_rails/collection/filter_option_serializer.rb +12 -0
  91. data/app/serializers/linked_rails/collection/filter_serializer.rb +13 -0
  92. data/app/serializers/linked_rails/collection/sorting_serializer.rb +13 -0
  93. data/app/serializers/linked_rails/collection/view_serializer.rb +22 -0
  94. data/app/serializers/linked_rails/collection_serializer.rb +44 -0
  95. data/app/serializers/linked_rails/condition_serializer.rb +9 -0
  96. data/app/serializers/linked_rails/creative_work_serializer.rb +10 -0
  97. data/app/serializers/linked_rails/current_user_serializer.rb +7 -0
  98. data/app/serializers/linked_rails/entry_point_serializer.rb +20 -0
  99. data/app/serializers/linked_rails/enum_value_serializer.rb +12 -0
  100. data/app/serializers/linked_rails/form/field/association_input_serializer.rb +13 -0
  101. data/app/serializers/linked_rails/form/field/resource_field_serializer.rb +11 -0
  102. data/app/serializers/linked_rails/form/field/select_input_serializer.rb +11 -0
  103. data/app/serializers/linked_rails/form/field_serializer.rb +39 -0
  104. data/app/serializers/linked_rails/form/group_serializer.rb +14 -0
  105. data/app/serializers/linked_rails/form/page_serializer.rb +13 -0
  106. data/app/serializers/linked_rails/form_serializer.rb +9 -0
  107. data/app/serializers/linked_rails/media_object_serializer.rb +17 -0
  108. data/app/serializers/linked_rails/menus/item_serializer.rb +35 -0
  109. data/app/serializers/linked_rails/menus/list_serializer.rb +13 -0
  110. data/app/serializers/linked_rails/ontology/class_serializer.rb +19 -0
  111. data/app/serializers/linked_rails/ontology/property_serializer.rb +16 -0
  112. data/app/serializers/linked_rails/ontology_serializer.rb +8 -0
  113. data/app/serializers/linked_rails/rdf_error_serializer.rb +8 -0
  114. data/app/serializers/linked_rails/sequence_serializer.rb +14 -0
  115. data/app/serializers/linked_rails/shacl/node_shape_serializer.rb +12 -0
  116. data/app/serializers/linked_rails/shacl/property_shape_serializer.rb +38 -0
  117. data/app/serializers/linked_rails/shacl/shape_serializer.rb +25 -0
  118. data/app/serializers/linked_rails/web_page_serializer.rb +10 -0
  119. data/app/serializers/linked_rails/web_site_serializer.rb +11 -0
  120. data/app/serializers/linked_rails/widget_serializer.rb +15 -0
  121. data/config/initializers/inflections.rb +5 -0
  122. data/lib/generators/linked_rails/install/install_generator.rb +65 -0
  123. data/lib/generators/linked_rails/install/templates/app_menu_list.rb +41 -0
  124. data/lib/generators/linked_rails/install/templates/application_action_list.rb +3 -0
  125. data/lib/generators/linked_rails/install/templates/application_form.rb +3 -0
  126. data/lib/generators/linked_rails/install/templates/application_menu_list.rb +3 -0
  127. data/lib/generators/linked_rails/install/templates/application_policy.rb +18 -0
  128. data/lib/generators/linked_rails/install/templates/application_serializer.rb +5 -0
  129. data/lib/generators/linked_rails/install/templates/initializer.rb +9 -0
  130. data/lib/generators/linked_rails/install/templates/locales.yml +12 -0
  131. data/lib/generators/linked_rails/install/templates/rdf_responder.rb +5 -0
  132. data/lib/generators/linked_rails/install/templates/rdf_serializers_initializer.rb +5 -0
  133. data/lib/generators/linked_rails/install/templates/vocab.rb +5 -0
  134. data/lib/generators/linked_rails/install/templates/vocab.yml +26 -0
  135. data/lib/generators/linked_rails/model/model_generator.rb +58 -0
  136. data/lib/generators/linked_rails/model/templates/action_list.rb.tt +6 -0
  137. data/lib/generators/linked_rails/model/templates/controller.rb.tt +7 -0
  138. data/lib/generators/linked_rails/model/templates/create_table_migration.rb.tt +24 -0
  139. data/lib/generators/linked_rails/model/templates/form.rb.tt +6 -0
  140. data/lib/generators/linked_rails/model/templates/menu_list.rb.tt +6 -0
  141. data/lib/generators/linked_rails/model/templates/model.rb.tt +15 -0
  142. data/lib/generators/linked_rails/model/templates/module.rb.tt +7 -0
  143. data/lib/generators/linked_rails/model/templates/policy.rb.tt +6 -0
  144. data/lib/generators/linked_rails/model/templates/serializer.rb.tt +9 -0
  145. data/lib/linked_rails/active_response/controller/collections.rb +43 -0
  146. data/lib/linked_rails/active_response/controller/crud_defaults.rb +92 -0
  147. data/lib/linked_rails/active_response/controller/params.rb +51 -0
  148. data/lib/linked_rails/active_response/controller.rb +37 -0
  149. data/lib/linked_rails/active_response/responders/rdf.rb +158 -0
  150. data/lib/linked_rails/cache.rb +35 -0
  151. data/lib/linked_rails/callable_variable.rb +25 -0
  152. data/lib/linked_rails/constraints/whitelist.rb +36 -0
  153. data/lib/linked_rails/controller/authorization.rb +15 -0
  154. data/lib/linked_rails/controller/error_handling.rb +76 -0
  155. data/lib/linked_rails/controller.rb +43 -0
  156. data/lib/linked_rails/engine.rb +7 -0
  157. data/lib/linked_rails/enhanceable.rb +21 -0
  158. data/lib/linked_rails/enhancements/actionable/model.rb +71 -0
  159. data/lib/linked_rails/enhancements/actionable/serializer.rb +25 -0
  160. data/lib/linked_rails/enhancements/creatable/action.rb +15 -0
  161. data/lib/linked_rails/enhancements/creatable/controller.rb +15 -0
  162. data/lib/linked_rails/enhancements/destroyable/action.rb +15 -0
  163. data/lib/linked_rails/enhancements/destroyable/controller.rb +15 -0
  164. data/lib/linked_rails/enhancements/destroyable/routing.rb +19 -0
  165. data/lib/linked_rails/enhancements/indexable/model.rb +10 -0
  166. data/lib/linked_rails/enhancements/menuable/model.rb +36 -0
  167. data/lib/linked_rails/enhancements/menuable/serializer.rb +33 -0
  168. data/lib/linked_rails/enhancements/route_concerns.rb +56 -0
  169. data/lib/linked_rails/enhancements/singularable/controller.rb +43 -0
  170. data/lib/linked_rails/enhancements/singularable/model.rb +47 -0
  171. data/lib/linked_rails/enhancements/singularable/serializer.rb +28 -0
  172. data/lib/linked_rails/enhancements/tableable/model.rb +28 -0
  173. data/lib/linked_rails/enhancements/updatable/action.rb +15 -0
  174. data/lib/linked_rails/enhancements/updatable/controller.rb +15 -0
  175. data/lib/linked_rails/enhancements/updatable/routing.rb +20 -0
  176. data/lib/linked_rails/enhancements/updatable/serializer.rb +17 -0
  177. data/lib/linked_rails/enhancements.rb +22 -0
  178. data/lib/linked_rails/helpers/delta_helper.rb +86 -0
  179. data/lib/linked_rails/helpers/ontola_actions_helper.rb +32 -0
  180. data/lib/linked_rails/helpers/resource_helper.rb +70 -0
  181. data/lib/linked_rails/iri_mapper.rb +125 -0
  182. data/lib/linked_rails/middleware/linked_data_params.rb +224 -0
  183. data/lib/linked_rails/model/collections.rb +82 -0
  184. data/lib/linked_rails/model/dirty.rb +82 -0
  185. data/lib/linked_rails/model/enhancements.rb +61 -0
  186. data/lib/linked_rails/model/filtering.rb +89 -0
  187. data/lib/linked_rails/model/indexable.rb +51 -0
  188. data/lib/linked_rails/model/iri.rb +121 -0
  189. data/lib/linked_rails/model/iri_mapping.rb +69 -0
  190. data/lib/linked_rails/model/serialization.rb +88 -0
  191. data/lib/linked_rails/model/sorting.rb +20 -0
  192. data/lib/linked_rails/model.rb +68 -0
  193. data/lib/linked_rails/params_parser.rb +93 -0
  194. data/lib/linked_rails/policy/attribute_conditions.rb +53 -0
  195. data/lib/linked_rails/policy.rb +189 -0
  196. data/lib/linked_rails/rdf_error.rb +36 -0
  197. data/lib/linked_rails/renderers.rb +46 -0
  198. data/lib/linked_rails/routes.rb +108 -0
  199. data/lib/linked_rails/serializer.rb +137 -0
  200. data/lib/linked_rails/translate.rb +176 -0
  201. data/lib/linked_rails/version.rb +5 -0
  202. data/lib/linked_rails/vocab.rb +81 -0
  203. data/lib/linked_rails.rb +86 -0
  204. data/lib/nill_class_renderer.rb +3 -0
  205. data/lib/rails/welcome_controller.rb +45 -0
  206. data/lib/tasks/linked_rails_tasks.rake +6 -0
  207. metadata +416 -0
@@ -0,0 +1,219 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ class Form
5
+ class FieldFactory # rubocop:disable Metrics/ClassLength
6
+ MAX_STR_LEN = 255
7
+ VALIDATOR_SELECTORS = [
8
+ [:min_length, ActiveModel::Validations::LengthValidator, :minimum],
9
+ [:max_length, ActiveModel::Validations::LengthValidator, :maximum],
10
+ [:pattern, ActiveModel::Validations::FormatValidator, :with],
11
+ [:presence, ActiveModel::Validations::PresenceValidator, nil],
12
+ [:sh_in, ActiveModel::Validations::InclusionValidator, :in]
13
+ ].freeze
14
+ include ActiveModel::Model
15
+
16
+ attr_accessor :field_options, :form, :key
17
+
18
+ delegate :form_options_iri,
19
+ :model_class,
20
+ :model_policy!,
21
+ :serializer_attributes,
22
+ :serializer_class,
23
+ :serializer_reflections,
24
+ to: :form
25
+
26
+ def condition_or_field
27
+ @condition_or_field ||= model_policy!.condition_for(
28
+ key,
29
+ field,
30
+ property: field_options[:if] || [],
31
+ sh_not: field_options[:unless] || []
32
+ )
33
+ end
34
+
35
+ private
36
+
37
+ def attr_column(name)
38
+ column_model =
39
+ if model_class.is_delegated_attribute?(name)
40
+ model_class.class_for_delegated_attribute(name)
41
+ else
42
+ model_class
43
+ end
44
+ column_model.column_for_attribute(name)
45
+ end
46
+
47
+ def attr_to_datatype # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
48
+ name = serializer_attribute&.key&.to_s || key.to_s
49
+ case attribute_type(name)
50
+ when :string, :text
51
+ Vocab.xsd.string
52
+ when :integer
53
+ Vocab.xsd.integer
54
+ when :datetime
55
+ Vocab.xsd.dateTime
56
+ when :date
57
+ Vocab.xsd.date
58
+ when :boolean
59
+ Vocab.xsd.boolean
60
+ when :decimal
61
+ decimal_data_type(name)
62
+ when :file
63
+ Vocab.ll[:blob]
64
+ else
65
+ Vocab.xsd.string if model_class.try(:defined_enums)&.key?(name)
66
+ end
67
+ end
68
+
69
+ def attribute_type(name)
70
+ model_class.try(:attribute_types).try(:[], name)&.type
71
+ rescue PG::ConnectionBad, ActiveRecord::StatementInvalid, ActiveRecord::ConnectionNotEstablished
72
+ :string
73
+ end
74
+
75
+ def attribute_validators
76
+ @attribute_validators ||= model_class.validators.select { |v| v.attributes.include?(model_attribute) }
77
+ end
78
+
79
+ def datatype
80
+ @datatype ||= field_options[:datatype] ||
81
+ serializer_attribute.try(:datatype) ||
82
+ (serializer_enum ? Vocab.xsd.string : attr_to_datatype)
83
+ end
84
+
85
+ def decimal_data_type(name) # rubocop:disable Metrics/MethodLength
86
+ case attr_column(name).precision
87
+ when 64
88
+ Vocab.xsd.long
89
+ when 32
90
+ Vocab.xsd.int
91
+ when 16
92
+ Vocab.xsd.short
93
+ when 8
94
+ Vocab.xsd.byte
95
+ else
96
+ Vocab.xsd.decimal
97
+ end
98
+ end
99
+
100
+ def field
101
+ field_class.new(field_attributes)
102
+ end
103
+
104
+ def field_attributes # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
105
+ return @field_attributes if @field_attributes
106
+
107
+ opts = field_options.except(:if, :unless)
108
+ opts[:form] = form
109
+ opts[:nested_form] = field_options[:form] if field_options.key?(:form)
110
+ opts[:model_class] = model_class
111
+ opts[:model_attribute] = model_attribute
112
+ opts[:key] = key
113
+ opts[:validators] ||= validators
114
+ opts[:path] ||= serializer_attribute&.predicate
115
+ opts[:datatype] ||= datatype
116
+ opts[:max_count] ||= 1
117
+ opts[:sh_in] = form_options_iri(serializer_attribute.key.to_s) if serializer_enum && !opts[:sh_in]
118
+
119
+ @field_attributes = opts
120
+ end
121
+
122
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
123
+ def field_class
124
+ return field_attributes[:input_field] if field_attributes.key?(:input_field)
125
+ return max_count > 1 ? Form::Field::CheckboxGroup : Form::Field::SelectInput if sh_in
126
+ return Form::Field::SliderInput if max_inclusive && min_inclusive
127
+
128
+ case datatype
129
+ when Vocab.xsd.boolean
130
+ return Form::Field::CheckboxInput
131
+ when Vocab.xsd.date
132
+ return Form::Field::DateInput
133
+ when Vocab.xsd.dateTime
134
+ return Form::Field::DateTimeInput
135
+ when Vocab.xsd.integer, Vocab.xsd.long, Vocab.xsd.int, Vocab.xsd.short, Vocab.xsd.byte, Vocab.xsd.decimal
136
+ return Form::Field::NumberInput
137
+ when Vocab.ll.blob
138
+ return Form::Field::FileInput
139
+ when Vocab.fhir.markdown
140
+ return Form::Field::MarkdownInput
141
+ when Vocab.ontola['datatype/password']
142
+ return Form::Field::PasswordInput
143
+ when Vocab.ontola['datatype/postalRange']
144
+ return Form::Field::PostalRangeInput
145
+ else
146
+ max_length && max_length > MAX_STR_LEN ? Form::Field::TextAreaInput : Form::Field::TextInput
147
+ end
148
+ end
149
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
150
+
151
+ def max_count
152
+ field_attributes[:max_count] || validators[:max_count]
153
+ end
154
+
155
+ def max_inclusive
156
+ field_attributes[:max_inclusive] || validators[:max_inclusive] || field_attributes[:max_inclusive_prop]
157
+ end
158
+
159
+ def max_length
160
+ field_attributes[:max_length] || validators[:max_length]
161
+ end
162
+
163
+ def min_inclusive
164
+ field_attributes[:min_inclusive] || validators[:min_inclusive] || field_attributes[:min_inclusive_prop]
165
+ end
166
+
167
+ def model_attribute
168
+ @model_attribute ||= (model_class.try(:attribute_alias, key) || key).to_sym
169
+ end
170
+
171
+ def normalized_key(key)
172
+ return key.to_s[0...-3].to_sym if key.to_s.ends_with?('_id')
173
+ return key.to_s[0...-4].pluralize.to_sym if key.to_s.ends_with?('_ids')
174
+
175
+ key
176
+ end
177
+
178
+ def serializer_attribute
179
+ return serializer_attributes[key] if serializer_attributes[key]
180
+
181
+ @serializer_attribute ||= serializer_reflection
182
+ end
183
+
184
+ def serializer_enum
185
+ @serializer_enum ||= serializer_class&.enum_options(serializer_attribute.key.to_sym) if serializer_attribute
186
+ end
187
+
188
+ def serializer_reflection
189
+ k_v = serializer_reflections.find { |_k, v| (v.association || v.key) == normalized_key(key) }
190
+ k_v[1] if k_v
191
+ end
192
+
193
+ def sh_in
194
+ field_attributes[:sh_in] || validators[:sh_in]
195
+ end
196
+
197
+ def validator(klass, option_key)
198
+ matched_validator = validator_by_class(klass)
199
+
200
+ option_key ? matched_validator&.options.try(:[], option_key) : matched_validator.present?
201
+ end
202
+
203
+ def validator_by_class(klass)
204
+ matched_validator = attribute_validators&.detect { |v| v.is_a?(klass) }
205
+ return if matched_validator.blank?
206
+
207
+ matched_validator if matched_validator.options[:if].blank? && matched_validator.options[:unless].blank?
208
+ end
209
+
210
+ def validators
211
+ @validators ||= Hash[
212
+ VALIDATOR_SELECTORS.map do |key, klass, option_key|
213
+ [key, validator(klass, option_key)]
214
+ end
215
+ ]
216
+ end
217
+ end
218
+ end
219
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ class Form
5
+ class Group < LinkedRails::Resource
6
+ attr_accessor :fields, :collapsible, :hidden, :footer
7
+ attr_writer :description, :key, :label
8
+
9
+ def initialize(attrs = {})
10
+ super(attrs)
11
+ self.fields = []
12
+ end
13
+
14
+ def description
15
+ @description = @description.call if @description.respond_to?(:call)
16
+ @description
17
+ end
18
+
19
+ def label
20
+ @label = @label.call if @label.respond_to?(:call)
21
+ @label
22
+ end
23
+
24
+ def rdf_type
25
+ return Vocab.form[:CollapsibleGroup] if collapsible
26
+ return Vocab.form[:FooterGroup] if footer
27
+ return Vocab.form[:HiddenGroup] if hidden
28
+
29
+ self.class.iri
30
+ end
31
+
32
+ class << self
33
+ def iri
34
+ Vocab.form[:Group]
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ class Form
5
+ class Page < LinkedRails::Resource
6
+ attr_accessor :groups, :label, :description
7
+ attr_writer :key
8
+
9
+ def initialize(attrs = {})
10
+ super(attrs)
11
+ self.groups = []
12
+ end
13
+
14
+ def add_group(opts)
15
+ group = Form::Group.new(opts)
16
+ groups << group
17
+ group
18
+ end
19
+
20
+ def footer_group
21
+ @footer_group ||= add_group(collapsible: false, footer: true, key: :footer)
22
+ end
23
+
24
+ class << self
25
+ def iri
26
+ Vocab.form[:Page]
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,156 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pundit'
4
+
5
+ module LinkedRails
6
+ class Form # rubocop:disable Metrics/ClassLength
7
+ include LinkedRails::Model
8
+
9
+ class_attribute :pages, :model_class
10
+
11
+ def root_relative_iri(_opts = {})
12
+ self.class.form_iri_path
13
+ end
14
+
15
+ class << self
16
+ def inherited(target)
17
+ target.pages = []
18
+ end
19
+
20
+ def form_iri
21
+ LinkedRails.iri(path: form_iri_path)
22
+ end
23
+
24
+ def form_iri_path
25
+ RDF::URI("/forms/#{to_s.sub(/Form$/, '').tableize}")
26
+ end
27
+
28
+ def form_options_iri(attr)
29
+ LinkedRails.iri(path: "/enums/#{model_class.to_s.tableize}/#{attr}")
30
+ end
31
+
32
+ def iri
33
+ Vocab.form[:Form]
34
+ end
35
+
36
+ def model_class
37
+ @model_class ||=
38
+ name.sub(/Form$/, '').safe_constantize ||
39
+ name.deconstantize.classify.sub(/Form$/, '').safe_constantize
40
+ end
41
+
42
+ def model_policy
43
+ @model_policy ||= Pundit::PolicyFinder.new(model_class).policy
44
+ end
45
+
46
+ def model_policy!
47
+ model_policy || raise("No policy found for #{model_class}")
48
+ end
49
+
50
+ def requested_single_resource(params, _user_context)
51
+ find_form_class(params)&.new
52
+ end
53
+
54
+ def serializer_attributes
55
+ @serializer_attributes ||= serializer_class&.attributes_to_serialize || {}
56
+ end
57
+
58
+ def serializer_class
59
+ @serializer_class ||= RDF::Serializers.serializer_for(model_class)
60
+ end
61
+
62
+ def serializer_reflections
63
+ @serializer_reflections ||= serializer_class&.relationships_to_serialize || {}
64
+ end
65
+
66
+ def preview_includes
67
+ [pages: {groups: [fields: [:fail, :pass, shape: [:property, nested_shapes: :property]]]}]
68
+ end
69
+
70
+ private
71
+
72
+ def current_group
73
+ @current_group || default_group
74
+ end
75
+
76
+ def current_page
77
+ @current_page || default_page
78
+ end
79
+
80
+ def default_group
81
+ @default_group ||= group(:default, collapsible: false)
82
+ end
83
+
84
+ def default_page
85
+ @default_page ||= page(:default)
86
+ end
87
+
88
+ def field(key, opts = {})
89
+ current_group.fields << Form::FieldFactory.new(
90
+ field_options: opts,
91
+ form: self,
92
+ key: key
93
+ ).condition_or_field
94
+ end
95
+
96
+ def find_form_class(params)
97
+ requested_class = [params[:module], "#{params[:id]&.singularize}_forms"].compact.join('/').classify
98
+
99
+ LinkedRails::Form.descendants.detect { |klass| klass.to_s == requested_class }
100
+ end
101
+
102
+ def footer
103
+ @current_group = current_page.footer_group
104
+
105
+ yield if block_given?
106
+
107
+ @current_group = nil
108
+ current_page.footer_group
109
+ end
110
+
111
+ def group(key, opts = {})
112
+ opts[:collapsible] = true unless opts.key?(:collapsible)
113
+ opts[:key] = key
114
+ group = current_page.add_group(opts)
115
+ @current_group = group
116
+
117
+ yield if block_given?
118
+
119
+ @current_group = nil
120
+ group
121
+ end
122
+
123
+ # rubocop:disable Naming/PredicateName
124
+ def has_many(key, opts = {})
125
+ opts[:input_field] = Form::Field::AssociationInput
126
+ opts[:max_count] = 99
127
+ field(key, opts)
128
+ end
129
+
130
+ def has_one(key, opts = {})
131
+ opts[:input_field] = Form::Field::AssociationInput
132
+ opts[:max_count] = 1
133
+ field(key, opts)
134
+ end
135
+ # rubocop:enable Naming/PredicateName
136
+
137
+ def hidden(&block)
138
+ group(:hidden, collapsible: false, hidden: true, &block)
139
+ end
140
+
141
+ def page(key, opts = {})
142
+ page = Form::Page.new(opts.merge(key: key))
143
+ @current_page = page
144
+ pages << @current_page
145
+ yield if block_given?
146
+ @current_page = nil
147
+ page
148
+ end
149
+
150
+ def resource(key, opts = {})
151
+ opts[:input_field] = Form::Field::ResourceField
152
+ field(key, opts)
153
+ end
154
+ end
155
+ end
156
+ end