linked_rails 0.0.1 → 0.0.4.pre.g1faca9643

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 (182) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +21 -674
  3. data/app/controllers/linked_rails/actions/objects_controller.rb +9 -0
  4. data/app/controllers/linked_rails/bulk_controller.rb +105 -22
  5. data/app/controllers/linked_rails/enum_values_controller.rb +0 -42
  6. data/app/models/linked_rails/actions/item.rb +64 -55
  7. data/app/models/linked_rails/actions/list.rb +6 -31
  8. data/app/models/linked_rails/actions/object.rb +38 -0
  9. data/app/models/linked_rails/collection/configuration.rb +55 -0
  10. data/app/models/linked_rails/collection/filter.rb +1 -1
  11. data/app/models/linked_rails/collection/filter_field.rb +19 -2
  12. data/app/models/linked_rails/collection/filter_option.rb +1 -1
  13. data/app/models/linked_rails/collection/filterable.rb +6 -9
  14. data/app/models/linked_rails/collection/infinite.rb +113 -0
  15. data/app/models/linked_rails/collection/infinite_view.rb +1 -90
  16. data/app/models/linked_rails/collection/iri.rb +47 -43
  17. data/app/models/linked_rails/collection/iri_mapping.rb +15 -7
  18. data/app/models/linked_rails/collection/paginated.rb +46 -0
  19. data/app/models/linked_rails/collection/paginated_view.rb +1 -33
  20. data/app/models/linked_rails/collection/sortable.rb +1 -9
  21. data/app/models/linked_rails/collection/sorting.rb +1 -1
  22. data/app/models/linked_rails/collection/view.rb +51 -14
  23. data/app/models/linked_rails/collection.rb +53 -85
  24. data/app/models/linked_rails/creative_work.rb +1 -1
  25. data/app/models/linked_rails/entry_point.rb +8 -5
  26. data/app/models/linked_rails/enum_value.rb +40 -2
  27. data/app/models/linked_rails/form/field/association_input.rb +7 -1
  28. data/app/models/linked_rails/form/field/file_input.rb +1 -0
  29. data/app/models/linked_rails/form/field/resource_field.rb +2 -0
  30. data/{lib/linked_rails/enhancements/indexable/model.rb → app/models/linked_rails/form/field/url_input.rb} +3 -3
  31. data/app/models/linked_rails/form/field.rb +37 -13
  32. data/app/models/linked_rails/form/field_factory.rb +48 -18
  33. data/app/models/linked_rails/form/group.rb +4 -6
  34. data/app/models/linked_rails/form/page.rb +8 -4
  35. data/app/models/linked_rails/form.rb +17 -21
  36. data/app/models/linked_rails/manifest.rb +102 -22
  37. data/app/models/linked_rails/menus/item.rb +10 -13
  38. data/app/models/linked_rails/menus/list.rb +16 -7
  39. data/app/models/linked_rails/ontology/base.rb +3 -1
  40. data/app/models/linked_rails/ontology/class.rb +3 -3
  41. data/app/models/linked_rails/ontology.rb +6 -5
  42. data/app/models/linked_rails/property_query.rb +2 -0
  43. data/app/models/linked_rails/sequence.rb +4 -13
  44. data/app/models/linked_rails/shacl/property_shape.rb +1 -1
  45. data/app/models/linked_rails/web_page.rb +0 -4
  46. data/app/models/linked_rails/web_site.rb +0 -4
  47. data/app/models/linked_rails/widget.rb +4 -11
  48. data/app/policies/linked_rails/actions/object_policy.rb +11 -0
  49. data/app/policies/linked_rails/collection_policy.rb +2 -2
  50. data/app/policies/linked_rails/form_policy.rb +13 -0
  51. data/app/policies/linked_rails/ontology_policy.rb +13 -0
  52. data/app/serializers/linked_rails/actions/item_serializer.rb +5 -5
  53. data/app/serializers/linked_rails/actions/object_serializer.rb +9 -0
  54. data/app/serializers/linked_rails/collection/filter_field_serializer.rb +3 -2
  55. data/app/serializers/linked_rails/collection/filter_option_serializer.rb +1 -1
  56. data/app/serializers/linked_rails/collection/filter_serializer.rb +1 -1
  57. data/app/serializers/linked_rails/collection/sorting_serializer.rb +1 -1
  58. data/app/serializers/linked_rails/collection/view_serializer.rb +3 -3
  59. data/app/serializers/linked_rails/collection_serializer.rb +8 -7
  60. data/app/serializers/linked_rails/condition_serializer.rb +3 -3
  61. data/app/serializers/linked_rails/entry_point_serializer.rb +3 -3
  62. data/app/serializers/linked_rails/enum_value_serializer.rb +1 -0
  63. data/app/serializers/linked_rails/form/field/association_input_serializer.rb +1 -0
  64. data/app/serializers/linked_rails/form/field/file_input_serializer.rb +11 -0
  65. data/app/serializers/linked_rails/form/field_serializer.rb +3 -1
  66. data/app/serializers/linked_rails/form/group_serializer.rb +1 -1
  67. data/app/serializers/linked_rails/form/page_serializer.rb +1 -1
  68. data/app/serializers/linked_rails/menus/item_serializer.rb +3 -3
  69. data/app/serializers/linked_rails/menus/list_serializer.rb +1 -1
  70. data/app/serializers/linked_rails/ontology_serializer.rb +2 -2
  71. data/app/serializers/linked_rails/property_query_serializer.rb +7 -0
  72. data/app/serializers/linked_rails/sequence_serializer.rb +2 -5
  73. data/app/serializers/linked_rails/shacl/node_shape_serializer.rb +1 -1
  74. data/app/serializers/linked_rails/shacl/property_shape_serializer.rb +1 -1
  75. data/app/serializers/linked_rails/shacl/shape_serializer.rb +5 -5
  76. data/app/serializers/linked_rails/web_page_serializer.rb +3 -3
  77. data/app/serializers/linked_rails/web_site_serializer.rb +1 -1
  78. data/app/serializers/linked_rails/widget_serializer.rb +3 -3
  79. data/app/workers/linked_rails/invalidation_stream_worker.rb +16 -0
  80. data/lib/generators/linked_rails/install/install_generator.rb +7 -8
  81. data/lib/generators/linked_rails/install/templates/README +7 -0
  82. data/lib/generators/linked_rails/install/templates/app_menu_list.rb +36 -7
  83. data/lib/generators/linked_rails/install/templates/application_menu_list.rb +40 -1
  84. data/lib/generators/linked_rails/install/templates/initializer.rb +1 -2
  85. data/lib/generators/linked_rails/install/templates/locales.yml +16 -0
  86. data/lib/generators/linked_rails/install/templates/rdf_serializers_initializer.rb +1 -1
  87. data/lib/generators/linked_rails/install/templates/vocab.rb +1 -0
  88. data/lib/generators/linked_rails/install/templates/vocab.yml +2 -2
  89. data/lib/generators/linked_rails/model/model_generator.rb +0 -1
  90. data/lib/generators/linked_rails/model/templates/controller.rb.tt +5 -1
  91. data/lib/generators/linked_rails/model/templates/form.rb.tt +3 -0
  92. data/lib/generators/linked_rails/model/templates/menu_list.rb.tt +15 -0
  93. data/lib/generators/linked_rails/model/templates/policy.rb.tt +13 -0
  94. data/lib/generators/linked_rails/model/templates/serializer.rb.tt +5 -1
  95. data/lib/linked_rails/active_response/controller/collections.rb +1 -1
  96. data/lib/linked_rails/active_response/controller/crud_defaults.rb +4 -4
  97. data/lib/linked_rails/active_response/controller/params.rb +10 -10
  98. data/lib/linked_rails/active_response/controller.rb +8 -18
  99. data/lib/linked_rails/active_response/responders/rdf.rb +19 -10
  100. data/lib/linked_rails/callable_variable.rb +1 -1
  101. data/lib/linked_rails/collection_params_parser.rb +93 -0
  102. data/lib/linked_rails/controller/actionable.rb +121 -0
  103. data/lib/linked_rails/controller/authorization.rb +6 -0
  104. data/lib/linked_rails/controller/default_actions/create.rb +52 -0
  105. data/lib/linked_rails/controller/default_actions/destroy.rb +42 -0
  106. data/lib/linked_rails/controller/default_actions/update.rb +43 -0
  107. data/lib/linked_rails/controller/delta.rb +58 -0
  108. data/lib/linked_rails/controller/error_handling.rb +14 -7
  109. data/lib/linked_rails/controller/rendering.rb +48 -0
  110. data/lib/linked_rails/controller.rb +24 -4
  111. data/lib/linked_rails/enhanceable.rb +1 -1
  112. data/lib/linked_rails/enhancements/creatable/controller.rb +1 -1
  113. data/lib/linked_rails/enhancements/destroyable/controller.rb +1 -1
  114. data/lib/linked_rails/enhancements/updatable/controller.rb +1 -1
  115. data/lib/linked_rails/enhancements.rb +0 -16
  116. data/lib/linked_rails/errors/forbidden.rb +37 -0
  117. data/lib/linked_rails/errors.rb +3 -0
  118. data/lib/linked_rails/helpers/delta_helper.rb +26 -57
  119. data/lib/linked_rails/helpers/ontola_actions_helper.rb +2 -2
  120. data/lib/linked_rails/helpers/resource_helper.rb +11 -2
  121. data/lib/linked_rails/iri_mapper.rb +17 -39
  122. data/lib/linked_rails/middleware/error_handling.rb +51 -0
  123. data/lib/linked_rails/middleware/linked_data_params.rb +30 -151
  124. data/lib/linked_rails/model/actionable.rb +68 -0
  125. data/lib/linked_rails/model/cacheable.rb +45 -0
  126. data/lib/linked_rails/model/collections.rb +201 -39
  127. data/lib/linked_rails/model/dirty.rb +6 -18
  128. data/lib/linked_rails/model/enhancements.rb +1 -6
  129. data/lib/linked_rails/model/filtering.rb +4 -6
  130. data/lib/linked_rails/model/indexable.rb +6 -16
  131. data/lib/linked_rails/model/iri.rb +29 -19
  132. data/lib/linked_rails/model/iri_mapping.rb +37 -8
  133. data/lib/linked_rails/model/menuable.rb +28 -0
  134. data/lib/linked_rails/model/serialization.rb +2 -15
  135. data/lib/linked_rails/model/singularable.rb +57 -0
  136. data/lib/linked_rails/model/sorting.rb +0 -5
  137. data/lib/linked_rails/model/tables.rb +26 -0
  138. data/lib/linked_rails/model.rb +18 -7
  139. data/lib/linked_rails/params_parser.rb +131 -54
  140. data/lib/linked_rails/policy/attribute_conditions.rb +2 -2
  141. data/lib/linked_rails/policy.rb +44 -46
  142. data/lib/linked_rails/railtie.rb +11 -0
  143. data/lib/linked_rails/rdf_error.rb +2 -2
  144. data/lib/linked_rails/renderers.rb +1 -0
  145. data/lib/linked_rails/routes.rb +38 -22
  146. data/lib/linked_rails/serializer/actionable.rb +27 -0
  147. data/lib/linked_rails/serializer/menuable.rb +31 -0
  148. data/lib/linked_rails/serializer/singularable.rb +26 -0
  149. data/lib/linked_rails/serializer.rb +28 -11
  150. data/lib/linked_rails/storage.rb +32 -0
  151. data/lib/linked_rails/test_methods.rb +114 -0
  152. data/lib/linked_rails/translate.rb +31 -9
  153. data/lib/linked_rails/types/iri_type.rb +37 -0
  154. data/lib/linked_rails/uri_template.rb +30 -0
  155. data/lib/linked_rails/url.rb +11 -0
  156. data/lib/linked_rails/version.rb +1 -1
  157. data/lib/linked_rails/vocab.rb +9 -0
  158. data/lib/linked_rails.rb +40 -14
  159. data/lib/rails/welcome_controller.rb +3 -2
  160. data/lib/rdf/list.rb +9 -0
  161. data/lib/rdf/query_fix.rb +15 -0
  162. metadata +80 -33
  163. data/app/models/linked_rails/actions/default_actions/create.rb +0 -60
  164. data/app/models/linked_rails/actions/default_actions/destroy.rb +0 -45
  165. data/app/models/linked_rails/actions/default_actions/update.rb +0 -50
  166. data/app/models/linked_rails/actions/default_actions.rb +0 -17
  167. data/lib/generators/linked_rails/install/templates/application_action_list.rb +0 -3
  168. data/lib/generators/linked_rails/model/templates/action_list.rb.tt +0 -6
  169. data/lib/linked_rails/enhancements/actionable/model.rb +0 -71
  170. data/lib/linked_rails/enhancements/actionable/serializer.rb +0 -25
  171. data/lib/linked_rails/enhancements/creatable/action.rb +0 -15
  172. data/lib/linked_rails/enhancements/destroyable/action.rb +0 -15
  173. data/lib/linked_rails/enhancements/destroyable/routing.rb +0 -19
  174. data/lib/linked_rails/enhancements/menuable/model.rb +0 -36
  175. data/lib/linked_rails/enhancements/menuable/serializer.rb +0 -33
  176. data/lib/linked_rails/enhancements/route_concerns.rb +0 -56
  177. data/lib/linked_rails/enhancements/singularable/controller.rb +0 -43
  178. data/lib/linked_rails/enhancements/singularable/model.rb +0 -47
  179. data/lib/linked_rails/enhancements/singularable/serializer.rb +0 -28
  180. data/lib/linked_rails/enhancements/tableable/model.rb +0 -28
  181. data/lib/linked_rails/enhancements/updatable/action.rb +0 -15
  182. data/lib/linked_rails/enhancements/updatable/routing.rb +0 -20
@@ -3,10 +3,17 @@
3
3
  module LinkedRails
4
4
  class Form
5
5
  class FieldFactory # rubocop:disable Metrics/ClassLength
6
+ DATABASE_ERRORS = [
7
+ 'PG::ConnectionBad'.safe_constantize,
8
+ ActiveRecord::StatementInvalid,
9
+ ActiveRecord::ConnectionNotEstablished
10
+ ].compact.freeze
6
11
  MAX_STR_LEN = 255
7
12
  VALIDATOR_SELECTORS = [
8
13
  [:min_length, ActiveModel::Validations::LengthValidator, :minimum],
14
+ [:min_inclusive, ActiveModel::Validations::NumericalityValidator, :greater_than_or_equal_to],
9
15
  [:max_length, ActiveModel::Validations::LengthValidator, :maximum],
16
+ [:max_inclusive, ActiveModel::Validations::NumericalityValidator, :less_than_or_equal_to],
10
17
  [:pattern, ActiveModel::Validations::FormatValidator, :with],
11
18
  [:presence, ActiveModel::Validations::PresenceValidator, nil],
12
19
  [:sh_in, ActiveModel::Validations::InclusionValidator, :in]
@@ -15,7 +22,8 @@ module LinkedRails
15
22
 
16
23
  attr_accessor :field_options, :form, :key
17
24
 
18
- delegate :form_options_iri,
25
+ delegate :abstract_form,
26
+ :form_options_iri,
19
27
  :model_class,
20
28
  :model_policy!,
21
29
  :serializer_attributes,
@@ -24,12 +32,21 @@ module LinkedRails
24
32
  to: :form
25
33
 
26
34
  def condition_or_field
27
- @condition_or_field ||= model_policy!.condition_for(
35
+ return @condition_or_field if instance_variable_defined?(:@condition_or_field)
36
+
37
+ alternatives = node_shapes_for(
28
38
  key,
29
- field,
30
39
  property: field_options[:if] || [],
31
40
  sh_not: field_options[:unless] || []
32
41
  )
42
+ @condition_or_field =
43
+ if alternatives.count == 1
44
+ Condition.new(shape: alternatives.first, pass: field)
45
+ elsif alternatives.count.positive?
46
+ Condition.new(shape: SHACL::NodeShape.new(or: alternatives), pass: field)
47
+ else
48
+ field
49
+ end
33
50
  end
34
51
 
35
52
  private
@@ -45,8 +62,7 @@ module LinkedRails
45
62
  end
46
63
 
47
64
  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)
65
+ case attribute_type
50
66
  when :string, :text
51
67
  Vocab.xsd.string
52
68
  when :integer
@@ -58,22 +74,26 @@ module LinkedRails
58
74
  when :boolean
59
75
  Vocab.xsd.boolean
60
76
  when :decimal
61
- decimal_data_type(name)
77
+ decimal_data_type(attribute_name)
62
78
  when :file
63
79
  Vocab.ll[:blob]
64
80
  else
65
- Vocab.xsd.string if model_class.try(:defined_enums)&.key?(name)
81
+ Vocab.xsd.string if model_class.try(:defined_enums)&.key?(attribute_name)
66
82
  end
67
83
  end
68
84
 
69
- def attribute_type(name)
70
- model_class.try(:attribute_types).try(:[], name)&.type
71
- rescue PG::ConnectionBad, ActiveRecord::StatementInvalid, ActiveRecord::ConnectionNotEstablished
85
+ def attribute_name
86
+ serializer_attribute&.key&.to_s || key.to_s
87
+ end
88
+
89
+ def attribute_type
90
+ model_class.try(:attribute_types).try(:[], attribute_name)&.type
91
+ rescue *DATABASE_ERRORS
72
92
  :string
73
93
  end
74
94
 
75
95
  def attribute_validators
76
- @attribute_validators ||= model_class.validators.select { |v| v.attributes.include?(model_attribute) }
96
+ @attribute_validators ||= model_class&.validators&.select { |v| v.attributes.include?(model_attribute) }
77
97
  end
78
98
 
79
99
  def datatype
@@ -143,7 +163,7 @@ module LinkedRails
143
163
  when Vocab.ontola['datatype/postalRange']
144
164
  return Form::Field::PostalRangeInput
145
165
  else
146
- max_length && max_length > MAX_STR_LEN ? Form::Field::TextAreaInput : Form::Field::TextInput
166
+ attribute_type == :text ? Form::Field::TextAreaInput : Form::Field::TextInput
147
167
  end
148
168
  end
149
169
  # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
@@ -168,6 +188,15 @@ module LinkedRails
168
188
  @model_attribute ||= (model_class.try(:attribute_alias, key) || key).to_sym
169
189
  end
170
190
 
191
+ def node_shapes_for(attr, property: [], sh_not: [])
192
+ alternatives = abstract_form ? [] : model_policy!.condition_alternatives(attr, field.permission_required?)
193
+ alternatives = [[]] if alternatives.empty? && (property.any? || sh_not.any?)
194
+
195
+ alternatives.map do |props|
196
+ SHACL::NodeShape.new(property: props + property, sh_not: sh_not)
197
+ end
198
+ end
199
+
171
200
  def normalized_key(key)
172
201
  return key.to_s[0...-3].to_sym if key.to_s.ends_with?('_id')
173
202
  return key.to_s[0...-4].pluralize.to_sym if key.to_s.ends_with?('_ids')
@@ -197,14 +226,15 @@ module LinkedRails
197
226
  def validator(klass, option_key)
198
227
  matched_validator = validator_by_class(klass)
199
228
 
200
- option_key ? matched_validator&.options.try(:[], option_key) : matched_validator.present?
229
+ value = option_key ? matched_validator&.options.try(:[], option_key) : matched_validator.present?
230
+
231
+ value unless value.respond_to?(:call)
201
232
  end
202
233
 
203
234
  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?
235
+ attribute_validators&.detect do |validator|
236
+ validator.is_a?(klass) && validator.options[:if].blank? && validator.options[:unless].blank?
237
+ end
208
238
  end
209
239
 
210
240
  def validators
@@ -212,7 +242,7 @@ module LinkedRails
212
242
  VALIDATOR_SELECTORS.map do |key, klass, option_key|
213
243
  [key, validator(klass, option_key)]
214
244
  end
215
- ]
245
+ ].compact
216
246
  end
217
247
  end
218
248
  end
@@ -6,19 +6,17 @@ module LinkedRails
6
6
  attr_accessor :fields, :collapsible, :hidden, :footer
7
7
  attr_writer :description, :key, :label
8
8
 
9
- def initialize(attrs = {})
10
- super(attrs)
9
+ def initialize(**attrs)
10
+ super(**attrs)
11
11
  self.fields = []
12
12
  end
13
13
 
14
14
  def description
15
- @description = @description.call if @description.respond_to?(:call)
16
- @description
15
+ @description.respond_to?(:call) ? @description.call : @description
17
16
  end
18
17
 
19
18
  def label
20
- @label = @label.call if @label.respond_to?(:call)
21
- @label
19
+ @label.respond_to?(:call) ? @label.call : @label
22
20
  end
23
21
 
24
22
  def rdf_type
@@ -6,13 +6,13 @@ module LinkedRails
6
6
  attr_accessor :groups, :label, :description
7
7
  attr_writer :key
8
8
 
9
- def initialize(attrs = {})
10
- super(attrs)
9
+ def initialize(**attrs)
10
+ super(**attrs)
11
11
  self.groups = []
12
12
  end
13
13
 
14
- def add_group(opts)
15
- group = Form::Group.new(opts)
14
+ def add_group(**opts)
15
+ group = Form::Group.new(**opts)
16
16
  groups << group
17
17
  group
18
18
  end
@@ -21,6 +21,10 @@ module LinkedRails
21
21
  @footer_group ||= add_group(collapsible: false, footer: true, key: :footer)
22
22
  end
23
23
 
24
+ def footer_group!
25
+ @footer_group
26
+ end
27
+
24
28
  class << self
25
29
  def iri
26
30
  Vocab.form[:Page]
@@ -5,10 +5,11 @@ require 'pundit'
5
5
  module LinkedRails
6
6
  class Form # rubocop:disable Metrics/ClassLength
7
7
  include LinkedRails::Model
8
+ include LinkedRails::Model::Cacheable
8
9
 
9
- class_attribute :pages, :model_class
10
+ class_attribute :abstract_form, :pages, :model_class
10
11
 
11
- def root_relative_iri(_opts = {})
12
+ def root_relative_iri(**_opts)
12
13
  self.class.form_iri_path
13
14
  end
14
15
 
@@ -63,10 +64,6 @@ module LinkedRails
63
64
  @serializer_reflections ||= serializer_class&.relationships_to_serialize || {}
64
65
  end
65
66
 
66
- def preview_includes
67
- [pages: {groups: [fields: [:fail, :pass, shape: [:property, nested_shapes: :property]]]}]
68
- end
69
-
70
67
  private
71
68
 
72
69
  def current_group
@@ -85,7 +82,7 @@ module LinkedRails
85
82
  @default_page ||= page(:default)
86
83
  end
87
84
 
88
- def field(key, opts = {})
85
+ def field(key, **opts)
89
86
  current_group.fields << Form::FieldFactory.new(
90
87
  field_options: opts,
91
88
  form: self,
@@ -94,9 +91,10 @@ module LinkedRails
94
91
  end
95
92
 
96
93
  def find_form_class(params)
97
- requested_class = [params[:module], "#{params[:id]&.singularize}_forms"].compact.join('/').classify
94
+ form_name = [params[:module], "#{params[:id]&.singularize}_forms"].compact.join('/').classify
95
+ requested_class = form_name.safe_constantize
98
96
 
99
- LinkedRails::Form.descendants.detect { |klass| klass.to_s == requested_class }
97
+ requested_class if requested_class && requested_class < LinkedRails::Form
100
98
  end
101
99
 
102
100
  def footer
@@ -108,10 +106,10 @@ module LinkedRails
108
106
  current_page.footer_group
109
107
  end
110
108
 
111
- def group(key, opts = {})
109
+ def group(key, **opts)
112
110
  opts[:collapsible] = true unless opts.key?(:collapsible)
113
111
  opts[:key] = key
114
- group = current_page.add_group(opts)
112
+ group = current_page.add_group(**opts)
115
113
  @current_group = group
116
114
 
117
115
  yield if block_given?
@@ -120,26 +118,24 @@ module LinkedRails
120
118
  group
121
119
  end
122
120
 
123
- # rubocop:disable Naming/PredicateName
124
- def has_many(key, opts = {})
121
+ def has_many(key, **opts)
125
122
  opts[:input_field] = Form::Field::AssociationInput
126
123
  opts[:max_count] = 99
127
- field(key, opts)
124
+ field(key, **opts)
128
125
  end
129
126
 
130
- def has_one(key, opts = {})
127
+ def has_one(key, **opts)
131
128
  opts[:input_field] = Form::Field::AssociationInput
132
129
  opts[:max_count] = 1
133
- field(key, opts)
130
+ field(key, **opts)
134
131
  end
135
- # rubocop:enable Naming/PredicateName
136
132
 
137
133
  def hidden(&block)
138
134
  group(:hidden, collapsible: false, hidden: true, &block)
139
135
  end
140
136
 
141
- def page(key, opts = {})
142
- page = Form::Page.new(opts.merge(key: key))
137
+ def page(key, **opts)
138
+ page = Form::Page.new(**opts.merge(key: key))
143
139
  @current_page = page
144
140
  pages << @current_page
145
141
  yield if block_given?
@@ -147,9 +143,9 @@ module LinkedRails
147
143
  page
148
144
  end
149
145
 
150
- def resource(key, opts = {})
146
+ def resource(key, **opts)
151
147
  opts[:input_field] = Form::Field::ResourceField
152
- field(key, opts)
148
+ field(key, **opts)
153
149
  end
154
150
  end
155
151
  end
@@ -5,6 +5,27 @@ module LinkedRails
5
5
  include ActiveModel::Model
6
6
  include LinkedRails::Model
7
7
 
8
+ def save
9
+ Storage.hset(
10
+ :persistent,
11
+ :manifest,
12
+ URL.as_href(LinkedRails.iri) => web_manifest.to_json
13
+ )
14
+ end
15
+
16
+ def web_manifest
17
+ web_manifest_base.merge(
18
+ ontola: web_manifest_ontola_section,
19
+ serviceworker: web_manifest_sw_section
20
+ )
21
+ end
22
+
23
+ private
24
+
25
+ def allowed_external_sources
26
+ []
27
+ end
28
+
8
29
  def app_name
9
30
  Rails.application.railtie_name.chomp('_application').humanize
10
31
  end
@@ -17,8 +38,27 @@ module LinkedRails
17
38
  '#eef0f2'
18
39
  end
19
40
 
41
+ def blob_preview_iri
42
+ return unless ActiveStorage::Blob.service.present?
43
+
44
+ "#{LinkedRails.iri(path: 'rails/active_storage/blobs/redirect')}/{signed_id}/preview')"
45
+ end
46
+
47
+ def blob_upload_iri
48
+ return unless ActiveStorage::Blob.service.present?
49
+
50
+ LinkedRails.iri(path: 'rails/active_storage/direct_uploads')
51
+ end
52
+
20
53
  def css_class; end
21
54
 
55
+ def csp_entries
56
+ {
57
+ connectSrc: [ActiveStorage::Blob.service.try(:bucket)&.url].compact,
58
+ scriptSrc: [ActiveStorage::Blob.service.try(:bucket)&.url].compact
59
+ }
60
+ end
61
+
22
62
  def header_background
23
63
  :primary
24
64
  end
@@ -27,17 +67,16 @@ module LinkedRails
27
67
  :white
28
68
  end
29
69
 
30
- def preload_iris
31
- [
32
- scope,
33
- LinkedRails.iri(path: 'ns/core').to_s,
34
- LinkedRails.iri(path: 'c_a').to_s,
35
- LinkedRails.iri(path: 'menus').to_s
36
- ]
70
+ def icons
71
+ []
72
+ end
73
+
74
+ def lang
75
+ :nl
37
76
  end
38
77
 
39
78
  def scope
40
- @scope ||= LinkedRails.iri.to_s
79
+ LinkedRails.iri.to_s
41
80
  end
42
81
 
43
82
  def site_theme_color
@@ -52,51 +91,92 @@ module LinkedRails
52
91
  app_name
53
92
  end
54
93
 
94
+ def start_url
95
+ scope == '/' ? scope : "#{scope}/"
96
+ end
97
+
55
98
  def theme; end
56
99
 
57
100
  def theme_options
58
101
  {}
59
102
  end
60
103
 
61
- def web_manifest
62
- web_manifest_base.merge(
63
- ontola: web_manifest_ontola_section,
64
- serviceworker: web_manifest_sw_section
65
- )
66
- end
67
-
68
104
  def web_manifest_base # rubocop:disable Metrics/MethodLength
69
105
  {
70
106
  background_color: background_color,
71
107
  dir: :rtl,
72
108
  display: :standalone,
73
- lang: :nl,
109
+ icons: icons,
110
+ lang: lang,
74
111
  name: app_name,
75
112
  scope: scope,
76
113
  short_name: app_name,
77
- start_url: scope,
114
+ start_url: start_url,
78
115
  theme_color: site_theme_color
79
116
  }
80
117
  end
81
118
 
82
- def web_manifest_ontola_section
119
+ def web_manifest_ontola_section # rubocop:disable Metrics/MethodLength
83
120
  {
84
- css_class: css_class,
121
+ allowed_external_sources: allowed_external_sources,
122
+ blob_preview_iri: blob_preview_iri,
123
+ blob_upload_iri: blob_upload_iri,
124
+ csp: csp_entries,
85
125
  header_background: header_background,
86
126
  header_text: header_text,
87
- preload: preload_iris,
127
+ preconnect: preconnect,
88
128
  primary_color: site_theme_color,
89
129
  secondary_color: site_secondary_color,
130
+ styled_headers: styled_headers,
90
131
  theme: theme,
91
- theme_options: theme_options.to_query
132
+ theme_options: theme_options.to_query,
133
+ tracking: tracking,
134
+ website_iri: LinkedRails.iri.to_s,
135
+ websocket_path: websocket_path
92
136
  }
93
137
  end
94
138
 
95
139
  def web_manifest_sw_section
96
140
  {
97
- src: "#{scope}/sw.js?manifestLocation=#{Rack::Utils.escape("#{scope}/manifest.json")}",
141
+ src: "#{scope.chomp('/')}/sw.js",
98
142
  scope: scope
99
143
  }
100
144
  end
145
+
146
+ def websocket_path
147
+ Rails.application.config.try(:action_cable).try(:mount_path).try(:[], 1..-1)
148
+ end
149
+
150
+ def preconnect
151
+ []
152
+ end
153
+
154
+ def styled_headers
155
+ false
156
+ end
157
+
158
+ def tracking
159
+ []
160
+ end
161
+
162
+ class << self
163
+ def destroy(iri)
164
+ Storage.hdel(:persistent, :manifest, URL.as_href(iri))
165
+ end
166
+
167
+ def move(from, to)
168
+ Storage.hset(
169
+ :persistent,
170
+ :redirect_prefix,
171
+ URL.as_href(from) => URL.as_href(to)
172
+ )
173
+
174
+ data = Storage.hget(:persistent, :manifest, URL.as_href(from))
175
+
176
+ Storage.hset(:persistent, :manifest, URL.as_href(to), data) if data
177
+
178
+ destroy(from)
179
+ end
180
+ end
101
181
  end
102
182
  end
@@ -12,7 +12,7 @@ module LinkedRails
12
12
  delegate :iri_opts, to: :parent
13
13
 
14
14
  alias id iri
15
- %i[action href image label menus iri_base].each do |method|
15
+ %i[action href image label iri_base].each do |method|
16
16
  callable_variable(method, instance: :parent)
17
17
  end
18
18
 
@@ -26,7 +26,7 @@ module LinkedRails
26
26
  def iri_template
27
27
  return parent.send(:iri_template) unless parent.is_a?(LinkedRails::Menus::List)
28
28
 
29
- return URITemplate.new("#{iri_base}{/tag}{#fragment}") if iri_base
29
+ return LinkedRails::URITemplate.new("#{iri_base}{/tag}{#fragment}") if iri_base
30
30
 
31
31
  iri_template_expand_path(parent.send(:iri_template), '{/tag}')
32
32
  end
@@ -36,7 +36,7 @@ module LinkedRails
36
36
 
37
37
  @menu_sequence ||=
38
38
  LinkedRails::Sequence.new(
39
- -> { menus&.compact&.each { |menu| menu.parent = self } },
39
+ -> { menus },
40
40
  id: menu_sequence_iri,
41
41
  parent: self,
42
42
  scope: false
@@ -52,6 +52,12 @@ module LinkedRails
52
52
  sequence_iri
53
53
  end
54
54
 
55
+ def menus
56
+ return @menus unless @menus.respond_to?(:call)
57
+
58
+ @menus = parent.instance_exec(&@menus).compact.each { |menu| menu.parent = self }
59
+ end
60
+
55
61
  def rdf_type
56
62
  type || Vocab.ontola[:MenuItem]
57
63
  end
@@ -67,17 +73,8 @@ module LinkedRails
67
73
  end
68
74
 
69
75
  class << self
70
- def base_includes
71
- [action: :target]
72
- end
73
-
74
76
  def preview_includes
75
- base_includes + [
76
- menu_sequence: [
77
- members: base_includes +
78
- [menu_sequence: [members: base_includes]]
79
- ]
80
- ]
77
+ [menu_sequence: [members: [menu_sequence: :members]]]
81
78
  end
82
79
 
83
80
  def requested_index_resource(params, user_context)
@@ -20,21 +20,28 @@ module LinkedRails
20
20
  end
21
21
 
22
22
  def iri_opts
23
- resource&.iri_opts || {}
23
+ return {} if resource.blank?
24
+
25
+ resource.try(:singular_resource?) ? resource.singular_iri_opts : resource.iri_opts
24
26
  end
25
27
 
26
28
  def menus
27
- @menus ||= available_menus.map(&method(:menu_item))
29
+ @menus ||= available_menus.map(&method(:memoized_menu_item))
28
30
  end
29
31
 
30
32
  def menu(tag)
31
- menu_item(tag, available_menus[tag].dup) if available_menus.key?(tag)
33
+ memoized_menu_item(tag, available_menus[tag].dup) if available_menus.key?(tag)
32
34
  end
33
35
 
34
36
  private
35
37
 
36
38
  def default_label(tag, options)
37
- I18n.t("menus.#{resource&.class&.name&.tableize}.#{tag}", options[:label_params])
39
+ I18n.t("menus.#{resource&.class&.name&.tableize}.#{tag}", **options[:label_params])
40
+ end
41
+
42
+ def memoized_menu_item(tag, options)
43
+ @memoized_menu_item ||= {}
44
+ @memoized_menu_item[tag] ||= menu_item(tag, options)
38
45
  end
39
46
 
40
47
  def menu_available?(_tag, options)
@@ -48,6 +55,7 @@ module LinkedRails
48
55
  options[:label_params] ||= {}
49
56
  options[:label_params][:default] ||= ["menus.default.#{tag}".to_sym, tag.to_s.capitalize]
50
57
  options[:label] ||= default_label(tag, options)
58
+ options[:action] = ontola_dialog_action(options[:href]) if options.delete(:dialog)
51
59
  options.except!(:policy_resource, :policy, :policy_arguments, :label_params)
52
60
  LinkedRails.menus_item_class.new(resource: resource, tag: tag, parent: self, **options)
53
61
  end
@@ -62,7 +70,9 @@ module LinkedRails
62
70
  end
63
71
 
64
72
  def iri_template
65
- @iri_template ||= iri_template_expand_path(resource.send(:iri_template), '/menus')
73
+ base_template = resource.send(resource.try(:singular_resource?) ? :singular_iri_template : :iri_template)
74
+
75
+ @iri_template ||= iri_template_expand_path(base_template, '/menus')
66
76
  end
67
77
 
68
78
  class << self
@@ -88,7 +98,7 @@ module LinkedRails
88
98
  _defined_menus || {}
89
99
  end
90
100
 
91
- def has_menu(tag, opts = {}) # rubocop:disable Naming/PredicateName
101
+ def has_menu(tag, **opts)
92
102
  defined_menus[tag] = opts
93
103
  end
94
104
 
@@ -100,7 +110,6 @@ module LinkedRails
100
110
  LinkedRails::Sequence.new(
101
111
  menu_list.menus,
102
112
  id: menu_list.iri,
103
- member_includes: Item.preview_includes,
104
113
  scope: false
105
114
  )
106
115
  end
@@ -9,7 +9,7 @@ module LinkedRails
9
9
 
10
10
  def data
11
11
  data = []
12
- iri.each_statement do |statement|
12
+ iri.try(:each_statement) do |statement|
13
13
  next unless include_data_statement?(statement)
14
14
 
15
15
  statement.graph_name = ::RDF::Serializers.config.default_graph
@@ -41,6 +41,8 @@ module LinkedRails
41
41
  end
42
42
 
43
43
  def include_data_statement?(statement)
44
+ return false if statement.subject.node? || statement.object.node?
45
+
44
46
  statement.predicate != Vocab.rdfs.label &&
45
47
  statement.predicate != Vocab.rdfs.range &&
46
48
  statement.predicate != Vocab.rdfs.domain
@@ -12,10 +12,10 @@ module LinkedRails
12
12
  end
13
13
 
14
14
  def parent_class
15
- if klass.superclass == ApplicationRecord
15
+ if klass&.superclass == ApplicationRecord
16
16
  Vocab.schema.Thing
17
17
  else
18
- klass.superclass.iri
18
+ klass&.superclass.try(:iri) || Vocab.schema.Thing
19
19
  end
20
20
  end
21
21
 
@@ -26,7 +26,7 @@ module LinkedRails
26
26
  end
27
27
 
28
28
  def properties
29
- @properties ||= klass.predicate_mapping.keys.map { |key| LinkedRails.ontology_property_class.new(iri: key) }
29
+ @properties ||= klass&.predicate_mapping&.keys&.map { |key| LinkedRails.ontology_property_class.new(iri: key) }
30
30
  end
31
31
 
32
32
  class << self
@@ -4,13 +4,14 @@ module LinkedRails
4
4
  class Ontology
5
5
  include ActiveModel::Model
6
6
  include LinkedRails::Model
7
+ include LinkedRails::Model::Cacheable
7
8
 
8
9
  def classes
9
- @classes ||= ApplicationRecord.descendants.map do |klass|
10
+ @classes ||= LinkedRails.linked_models.map do |klass|
10
11
  iri = klass.iri.is_a?(Array) ? klass.iri.first : klass.iri
11
12
 
12
- LinkedRails.ontology_class_class.new(klass: klass, iri: iri)
13
- end
13
+ LinkedRails.ontology_class_class.new(klass: klass, iri: iri) if iri
14
+ end.compact
14
15
  end
15
16
 
16
17
  def properties
@@ -26,8 +27,8 @@ module LinkedRails
26
27
  %i[classes properties]
27
28
  end
28
29
 
29
- def requested_resource(_opts, _user_context)
30
- LinkedRails.ontology_class.new
30
+ def requested_resource(opts, _user_context)
31
+ LinkedRails.ontology_class.new if opts[:iri].include?(new.root_relative_iri)
31
32
  end
32
33
  end
33
34
  end