linked_rails 0.0.1 → 0.0.4.pre.ge1306665a

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 (174) 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 +71 -21
  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 +16 -21
  36. data/app/models/linked_rails/manifest.rb +8 -2
  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 +5 -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/serializers/linked_rails/actions/item_serializer.rb +5 -5
  51. data/app/serializers/linked_rails/actions/object_serializer.rb +9 -0
  52. data/app/serializers/linked_rails/collection/filter_field_serializer.rb +3 -2
  53. data/app/serializers/linked_rails/collection/filter_option_serializer.rb +1 -1
  54. data/app/serializers/linked_rails/collection/filter_serializer.rb +1 -1
  55. data/app/serializers/linked_rails/collection/sorting_serializer.rb +1 -1
  56. data/app/serializers/linked_rails/collection/view_serializer.rb +3 -3
  57. data/app/serializers/linked_rails/collection_serializer.rb +8 -7
  58. data/app/serializers/linked_rails/condition_serializer.rb +3 -3
  59. data/app/serializers/linked_rails/entry_point_serializer.rb +3 -3
  60. data/app/serializers/linked_rails/enum_value_serializer.rb +1 -0
  61. data/app/serializers/linked_rails/form/field/association_input_serializer.rb +1 -0
  62. data/app/serializers/linked_rails/form/field/file_input_serializer.rb +11 -0
  63. data/app/serializers/linked_rails/form/field_serializer.rb +3 -1
  64. data/app/serializers/linked_rails/form/group_serializer.rb +1 -1
  65. data/app/serializers/linked_rails/form/page_serializer.rb +1 -1
  66. data/app/serializers/linked_rails/menus/item_serializer.rb +3 -3
  67. data/app/serializers/linked_rails/menus/list_serializer.rb +1 -1
  68. data/app/serializers/linked_rails/ontology_serializer.rb +2 -2
  69. data/app/serializers/linked_rails/property_query_serializer.rb +7 -0
  70. data/app/serializers/linked_rails/sequence_serializer.rb +2 -5
  71. data/app/serializers/linked_rails/shacl/node_shape_serializer.rb +1 -1
  72. data/app/serializers/linked_rails/shacl/property_shape_serializer.rb +1 -1
  73. data/app/serializers/linked_rails/shacl/shape_serializer.rb +5 -5
  74. data/app/serializers/linked_rails/web_page_serializer.rb +3 -3
  75. data/app/serializers/linked_rails/web_site_serializer.rb +1 -1
  76. data/app/serializers/linked_rails/widget_serializer.rb +3 -3
  77. data/lib/generators/linked_rails/install/install_generator.rb +7 -8
  78. data/lib/generators/linked_rails/install/templates/README +7 -0
  79. data/lib/generators/linked_rails/install/templates/app_menu_list.rb +36 -7
  80. data/lib/generators/linked_rails/install/templates/application_menu_list.rb +40 -1
  81. data/lib/generators/linked_rails/install/templates/initializer.rb +1 -2
  82. data/lib/generators/linked_rails/install/templates/locales.yml +14 -0
  83. data/lib/generators/linked_rails/install/templates/rdf_serializers_initializer.rb +1 -1
  84. data/lib/generators/linked_rails/install/templates/vocab.rb +1 -0
  85. data/lib/generators/linked_rails/install/templates/vocab.yml +2 -2
  86. data/lib/generators/linked_rails/model/model_generator.rb +0 -1
  87. data/lib/generators/linked_rails/model/templates/controller.rb.tt +5 -1
  88. data/lib/generators/linked_rails/model/templates/form.rb.tt +3 -0
  89. data/lib/generators/linked_rails/model/templates/menu_list.rb.tt +15 -0
  90. data/lib/generators/linked_rails/model/templates/policy.rb.tt +13 -0
  91. data/lib/generators/linked_rails/model/templates/serializer.rb.tt +5 -1
  92. data/lib/linked_rails/active_response/controller/collections.rb +1 -1
  93. data/lib/linked_rails/active_response/controller/crud_defaults.rb +4 -4
  94. data/lib/linked_rails/active_response/controller/params.rb +10 -10
  95. data/lib/linked_rails/active_response/controller.rb +8 -18
  96. data/lib/linked_rails/active_response/responders/rdf.rb +19 -10
  97. data/lib/linked_rails/callable_variable.rb +1 -1
  98. data/lib/linked_rails/collection_params_parser.rb +93 -0
  99. data/lib/linked_rails/controller/actionable.rb +121 -0
  100. data/lib/linked_rails/controller/authorization.rb +6 -0
  101. data/lib/linked_rails/controller/default_actions/create.rb +52 -0
  102. data/lib/linked_rails/controller/default_actions/destroy.rb +42 -0
  103. data/lib/linked_rails/controller/default_actions/update.rb +43 -0
  104. data/lib/linked_rails/controller/delta.rb +78 -0
  105. data/lib/linked_rails/controller/error_handling.rb +11 -9
  106. data/lib/linked_rails/controller/rendering.rb +48 -0
  107. data/lib/linked_rails/controller.rb +24 -4
  108. data/lib/linked_rails/enhanceable.rb +1 -1
  109. data/lib/linked_rails/enhancements/creatable/controller.rb +1 -1
  110. data/lib/linked_rails/enhancements/destroyable/controller.rb +1 -1
  111. data/lib/linked_rails/enhancements/updatable/controller.rb +1 -1
  112. data/lib/linked_rails/enhancements.rb +0 -16
  113. data/lib/linked_rails/helpers/delta_helper.rb +26 -57
  114. data/lib/linked_rails/helpers/ontola_actions_helper.rb +2 -2
  115. data/lib/linked_rails/helpers/resource_helper.rb +4 -2
  116. data/lib/linked_rails/iri_mapper.rb +17 -39
  117. data/lib/linked_rails/middleware/error_handling.rb +51 -0
  118. data/lib/linked_rails/middleware/linked_data_params.rb +30 -151
  119. data/lib/linked_rails/model/actionable.rb +68 -0
  120. data/lib/linked_rails/model/collections.rb +201 -39
  121. data/lib/linked_rails/model/dirty.rb +7 -8
  122. data/lib/linked_rails/model/enhancements.rb +1 -6
  123. data/lib/linked_rails/model/filtering.rb +4 -6
  124. data/lib/linked_rails/model/indexable.rb +6 -16
  125. data/lib/linked_rails/model/iri.rb +28 -19
  126. data/lib/linked_rails/model/iri_mapping.rb +37 -8
  127. data/lib/linked_rails/model/menuable.rb +28 -0
  128. data/lib/linked_rails/model/serialization.rb +2 -15
  129. data/lib/linked_rails/model/singularable.rb +57 -0
  130. data/lib/linked_rails/model/sorting.rb +0 -5
  131. data/lib/linked_rails/model/tables.rb +26 -0
  132. data/lib/linked_rails/model.rb +17 -7
  133. data/lib/linked_rails/params_parser.rb +133 -55
  134. data/lib/linked_rails/policy/attribute_conditions.rb +2 -2
  135. data/lib/linked_rails/policy.rb +40 -46
  136. data/lib/linked_rails/railtie.rb +11 -0
  137. data/lib/linked_rails/rdf_error.rb +2 -2
  138. data/lib/linked_rails/renderers.rb +1 -0
  139. data/lib/linked_rails/routes.rb +38 -22
  140. data/lib/linked_rails/serializer/actionable.rb +27 -0
  141. data/lib/linked_rails/serializer/menuable.rb +31 -0
  142. data/lib/linked_rails/serializer/singularable.rb +26 -0
  143. data/lib/linked_rails/serializer.rb +28 -11
  144. data/lib/linked_rails/test_methods.rb +114 -0
  145. data/lib/linked_rails/translate.rb +31 -9
  146. data/lib/linked_rails/types/iri_type.rb +37 -0
  147. data/lib/linked_rails/uri_template.rb +30 -0
  148. data/lib/linked_rails/version.rb +1 -1
  149. data/lib/linked_rails/vocab.rb +9 -0
  150. data/lib/linked_rails.rb +30 -13
  151. data/lib/rails/welcome_controller.rb +3 -2
  152. data/lib/rdf/list.rb +9 -0
  153. data/lib/rdf/query_fix.rb +15 -0
  154. metadata +58 -33
  155. data/app/models/linked_rails/actions/default_actions/create.rb +0 -60
  156. data/app/models/linked_rails/actions/default_actions/destroy.rb +0 -45
  157. data/app/models/linked_rails/actions/default_actions/update.rb +0 -50
  158. data/app/models/linked_rails/actions/default_actions.rb +0 -17
  159. data/lib/generators/linked_rails/install/templates/application_action_list.rb +0 -3
  160. data/lib/generators/linked_rails/model/templates/action_list.rb.tt +0 -6
  161. data/lib/linked_rails/enhancements/actionable/model.rb +0 -71
  162. data/lib/linked_rails/enhancements/actionable/serializer.rb +0 -25
  163. data/lib/linked_rails/enhancements/creatable/action.rb +0 -15
  164. data/lib/linked_rails/enhancements/destroyable/action.rb +0 -15
  165. data/lib/linked_rails/enhancements/destroyable/routing.rb +0 -19
  166. data/lib/linked_rails/enhancements/menuable/model.rb +0 -36
  167. data/lib/linked_rails/enhancements/menuable/serializer.rb +0 -33
  168. data/lib/linked_rails/enhancements/route_concerns.rb +0 -56
  169. data/lib/linked_rails/enhancements/singularable/controller.rb +0 -43
  170. data/lib/linked_rails/enhancements/singularable/model.rb +0 -47
  171. data/lib/linked_rails/enhancements/singularable/serializer.rb +0 -28
  172. data/lib/linked_rails/enhancements/tableable/model.rb +0 -28
  173. data/lib/linked_rails/enhancements/updatable/action.rb +0 -15
  174. data/lib/linked_rails/enhancements/updatable/routing.rb +0 -20
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'pundit'
4
4
 
5
+ require_relative 'collection/configuration'
5
6
  require_relative 'collection/filter'
6
7
  require_relative 'collection/filterable'
7
8
  require_relative 'collection/iri'
@@ -9,28 +10,38 @@ require_relative 'collection/iri_mapping'
9
10
  require_relative 'collection/sortable'
10
11
  require_relative 'collection/sorting'
11
12
  require_relative 'collection/view'
13
+ require_relative 'collection/paginated'
12
14
  require_relative 'collection/paginated_view'
15
+ require_relative 'collection/infinite'
13
16
  require_relative 'collection/infinite_view'
14
17
 
15
18
  module LinkedRails
16
19
  class Collection # rubocop:disable Metrics/ClassLength
17
20
  include ActiveModel::Model
18
- include LinkedRails::Model::Enhancements
19
- include LinkedRails::Model::Iri
21
+ include LinkedRails::Model::Actionable
22
+ include LinkedRails::Model::IRI
23
+ include LinkedRails::Collection::Configuration
20
24
  include LinkedRails::Collection::Filterable
21
- include LinkedRails::Collection::Iri
22
- include LinkedRails::Collection::IriMapping
25
+ include LinkedRails::Collection::IRI
26
+ include LinkedRails::Collection::IRIMapping
23
27
  include LinkedRails::Collection::Sortable
24
28
 
25
- enhance LinkedRails::Enhancements::Actionable
26
-
27
- attr_accessor :association, :association_class, :association_scope, :grid_max_columns, :joins,
28
- :name, :page_size, :parent, :part_of, :policy, :user_context, :view
29
- attr_writer :association_base, :table_type, :default_display, :default_title, :default_type,
30
- :display, :policy_scope, :title, :type, :views
29
+ attr_accessor :name, :policy, :user_context
30
+ attr_writer :association_base, :views
31
31
 
32
32
  alias id iri
33
33
 
34
+ def initialize(**opts)
35
+ opts = opts.with_indifferent_access
36
+
37
+ %i[association_class iri_template].each do |key|
38
+ raise("No #{key} given") if opts[key].blank?
39
+ end
40
+ opts[:route_key] ||= opts[:association_class].collection_route_key
41
+
42
+ super
43
+ end
44
+
34
45
  def action_list(user_context)
35
46
  @action_list ||= {}
36
47
  @action_list[user_context] ||= association_class.try(:action_list)&.new(
@@ -56,21 +67,8 @@ module LinkedRails
56
67
  super(options.merge(except: %w[association_class unfiltered_collection collection]))
57
68
  end
58
69
 
59
- def association_base
60
- @association_base ||= apply_scope(sorted_association(filtered_association))
61
- end
62
-
63
- def build_child # rubocop:disable Metrics/AbcSize
64
- child =
65
- parent&.build_child(association_class, user_context: user_context) ||
66
- association_class.build_new(parent: parent, user_context: user_context)
67
-
68
- attributes_from_filters = ActionController::Parameters.new(association_class.attributes_from_filters(@filter))
69
- permitted_child_keys = Pundit.policy(user_context, child)&.permitted_attributes || []
70
- permitted_attributes_from_filters = attributes_from_filters.permit(permitted_child_keys)
71
-
72
- child.assign_attributes(permitted_attributes_from_filters)
73
- child
70
+ def child_resource
71
+ @child_resource ||= build_child
74
72
  end
75
73
 
76
74
  def columns
@@ -79,18 +77,10 @@ module LinkedRails
79
77
  RDF::List[*columns_list] if columns_list.present?
80
78
  end
81
79
 
82
- def default_page_size
83
- association_class.try(:default_per_page) || 20
84
- end
85
-
86
80
  def default_view
87
81
  @default_view ||= view_with_opts(default_view_opts)
88
82
  end
89
83
 
90
- def display
91
- @display&.to_sym || default_display
92
- end
93
-
94
84
  def first
95
85
  case type
96
86
  when :paginated
@@ -110,32 +100,7 @@ module LinkedRails
110
100
 
111
101
  def new_child(options)
112
102
  attrs = options.merge(new_child_values)
113
- self.class.new(attrs)
114
- end
115
-
116
- def preview_includes
117
- {
118
- default_view: default_view.preview_includes,
119
- filter_fields: :options,
120
- filters: [],
121
- sortings: []
122
- }
123
- end
124
-
125
- def title
126
- var = @title || @default_title
127
- return var.call(parent) if var.respond_to?(:call)
128
-
129
- var || title_from_translation
130
- end
131
-
132
- def title_from_translation
133
- plural = association_class.name.tableize
134
- I18n.t(
135
- "#{plural}.collection.#{@filter&.values&.join('.').presence || name || :default}",
136
- count: ->(_opts) { total_count },
137
- default: association_class.plural_label
138
- )
103
+ self.class.new(**attrs)
139
104
  end
140
105
 
141
106
  def total_count
@@ -143,11 +108,7 @@ module LinkedRails
143
108
  end
144
109
 
145
110
  def total_page_count
146
- (total_count / (page_size || default_page_size).to_f).ceil if total_count
147
- end
148
-
149
- def type
150
- @type&.to_sym || default_type
111
+ (total_count / page_size.to_f).ceil if total_count
151
112
  end
152
113
 
153
114
  def unscoped_association
@@ -160,21 +121,25 @@ module LinkedRails
160
121
 
161
122
  def view_with_opts(opts)
162
123
  @views ||= []
163
- view = LinkedRails.collection_view_class.new({collection: self, type: type}.merge(opts))
124
+ view = collection_view_class.new(**{collection: self, type: type}.merge(opts))
164
125
  @views << view
165
126
  view
166
127
  end
167
128
 
168
129
  private
169
130
 
170
- def default_display
171
- @default_display = @default_display.call(parent) if @default_display.respond_to?(:call)
131
+ def build_child
132
+ child =
133
+ parent&.build_child(association_class, user_context: user_context) ||
134
+ association_class.build_new(parent: parent, user_context: user_context)
172
135
 
173
- @default_display || association_class.try(:default_collection_display)
136
+ child.assign_attributes(association_base.where_values_hash) if association_base.respond_to?(:where_values_hash)
137
+ child.assign_attributes(permitted_attributes_from_filters(child))
138
+ child
174
139
  end
175
140
 
176
- def default_type
177
- @default_type || association_class.try(:default_collection_type) || :paginated
141
+ def collection_view_class
142
+ LinkedRails.collection_view_class
178
143
  end
179
144
 
180
145
  def default_view_opts
@@ -187,30 +152,33 @@ module LinkedRails
187
152
  opts
188
153
  end
189
154
 
190
- def policy_scope
191
- return @policy_scope if @policy_scope == false
192
-
193
- @policy_scope = policy ? policy::Scope : Pundit::PolicyFinder.new(filtered_association).scope!
194
- end
195
-
196
155
  def paginated?
197
156
  type == :paginated
198
157
  end
199
158
 
200
- def table_type
201
- return @table_type if @table_type
159
+ def permitted_attributes_from_filters(child)
160
+ return {} if @filter.empty?
202
161
 
203
- case display&.to_sym
204
- when :table
205
- :default
206
- when :settingsTable
207
- :settings
208
- end
162
+ Pundit.policy(user_context, child).try(:permitted_attributes_from_filters, @filter) || {}
163
+ end
164
+
165
+ def title_from_translation
166
+ plural = association_class.name.tableize
167
+
168
+ I18n.t(
169
+ "#{plural}.collection.#{filter&.values&.join('.').presence || name || :default}",
170
+ count: ->(_opts) { total_count },
171
+ default: association_class.plural_label
172
+ )
209
173
  end
210
174
 
211
175
  def new_child_values
212
176
  instance_values
213
- .slice('association', 'association_class', 'association_scope', 'iri_template', 'parent')
177
+ .slice(
178
+ 'iri_template',
179
+ *LinkedRails::Model::Collections::COLLECTION_STATIC_OPTIONS.keys.map(&:to_s),
180
+ *LinkedRails::Model::Collections::COLLECTION_CUSTOMIZABLE_OPTIONS.keys.map { |key| "default_#{key}" }
181
+ )
214
182
  .merge(
215
183
  unfiltered_collection: filtered? ? @unfiltered_collection : self,
216
184
  user_context: user_context
@@ -8,7 +8,7 @@ module LinkedRails
8
8
  attr_writer :iri
9
9
  attr_accessor :description, :name, :text, :url
10
10
 
11
- def iri(_opts = {})
11
+ def iri(**_opts)
12
12
  @iri ||= RDF::Node.new
13
13
  end
14
14
 
@@ -6,7 +6,7 @@ module LinkedRails
6
6
  include LinkedRails::Model
7
7
 
8
8
  attr_accessor :parent
9
- attr_writer :url
9
+ attr_writer :target_url
10
10
  delegate :form, :description, :http_method, :image, :user_context,
11
11
  :resource, :tag, to: :parent
12
12
 
@@ -16,12 +16,15 @@ module LinkedRails
16
16
  form&.form_iri
17
17
  end
18
18
 
19
- def as_json(_opts = {})
19
+ def as_json(**_opts)
20
20
  {}
21
21
  end
22
22
 
23
23
  def iri_template
24
- @iri_template ||= iri_template_with_fragment(URITemplate.new(parent.root_relative_iri.to_s), :EntryPoint)
24
+ @iri_template ||= iri_template_with_fragment(
25
+ LinkedRails::URITemplate.new(parent.root_relative_iri.to_s),
26
+ :EntryPoint
27
+ )
25
28
  end
26
29
 
27
30
  def label
@@ -30,8 +33,8 @@ module LinkedRails
30
33
  value || label_fallback
31
34
  end
32
35
 
33
- def url
34
- @url || parent.url
36
+ def target_url
37
+ @target_url || parent.target_url
35
38
  end
36
39
 
37
40
  private
@@ -5,14 +5,14 @@ module LinkedRails
5
5
  include ActiveModel::Model
6
6
  include LinkedRails::Model
7
7
 
8
- attr_accessor :attr, :close_match, :exact_match, :group_by, :key, :klass, :type
8
+ attr_accessor :attr, :close_match, :exact_match, :group_by, :identifier, :key, :klass, :type
9
9
  attr_writer :iri, :label
10
10
 
11
11
  def label
12
12
  label_from_variable || LinkedRails.translate(:enum, :label, self)
13
13
  end
14
14
 
15
- def iri(_opts = {})
15
+ def iri(**_opts)
16
16
  @iri || iri_with_root(RDF::URI("/enums/#{klass.name.tableize}/#{attr}##{key}"))
17
17
  end
18
18
 
@@ -29,5 +29,43 @@ module LinkedRails
29
29
  def label_from_variable
30
30
  @label.respond_to?(:call) ? @label.call : @label
31
31
  end
32
+
33
+ class << self
34
+ def class_for_params(params)
35
+ LinkedRails.linked_models.detect do |klass|
36
+ klass.to_s == ([params[:module]].compact + [params[:klass]&.singularize]).join('/').classify
37
+ end
38
+ end
39
+
40
+ def enum_options(params)
41
+ serializer_for_params(params)&.enum_options(params[:attribute])
42
+ end
43
+
44
+ def requested_resource(opts, _user_context)
45
+ options = enum_options(opts[:params])
46
+
47
+ return unless options
48
+
49
+ LinkedRails::Sequence.new(
50
+ options.values,
51
+ id: sanitized_sequence_iri(opts[:iri]),
52
+ scope: LinkedRails::EnumValuePolicy::Scope
53
+ )
54
+ end
55
+
56
+ def sanitized_sequence_iri(raw_iri)
57
+ iri = RDF::URI(raw_iri)
58
+ iri.query = nil
59
+ iri.fragment = nil
60
+ iri.path = iri.path.split('.').first
61
+ iri
62
+ end
63
+
64
+ def serializer_for_params(params)
65
+ klass = class_for_params(params)
66
+
67
+ RDF::Serializers.serializer_for(klass) if klass
68
+ end
69
+ end
32
70
  end
33
71
  end
@@ -9,7 +9,13 @@ module LinkedRails
9
9
  def datatype; end
10
10
 
11
11
  def nested_form
12
- @nested_form ||= klass_name.constantize.form_class
12
+ @nested_form ||= target_class.form_class
13
+ end
14
+
15
+ def target_class
16
+ return @target_class if @target_class
17
+
18
+ klass_name.constantize unless form.abstract_form
13
19
  end
14
20
 
15
21
  private
@@ -4,6 +4,7 @@ module LinkedRails
4
4
  class Form
5
5
  class Field
6
6
  class FileInput < Field
7
+ attr_accessor :max_size
7
8
  end
8
9
  end
9
10
  end
@@ -10,6 +10,8 @@ module LinkedRails
10
10
 
11
11
  attr_reader :path
12
12
 
13
+ def label_from_property; end
14
+
13
15
  def permission_required?
14
16
  false
15
17
  end
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LinkedRails
4
- module Enhancements
5
- module Indexable
6
- module Model
4
+ class Form
5
+ class Field
6
+ class UrlInput < Field
7
7
  end
8
8
  end
9
9
  end
@@ -2,25 +2,27 @@
2
2
 
3
3
  module LinkedRails
4
4
  class Form
5
- class Field < LinkedRails::Resource
6
- attr_writer :datatype,
7
- :description,
5
+ class Field < LinkedRails::Resource # rubocop:disable Metrics/ClassLength
6
+ attr_writer :description,
8
7
  :helper_text,
9
8
  :label,
9
+ :max_inclusive,
10
10
  :max_length,
11
11
  :min_count,
12
+ :min_inclusive,
12
13
  :min_length,
13
- :sh_in,
14
14
  :pattern,
15
- :path
16
- attr_accessor :default_value,
15
+ :path,
16
+ :placeholder,
17
+ :sh_in,
18
+ :start_adornment
19
+ attr_accessor :datatype,
20
+ :default_value,
17
21
  :max_count,
18
22
  :max_count_prop,
19
- :max_inclusive,
20
23
  :max_inclusive_prop,
21
24
  :max_length_prop,
22
25
  :min_count_prop,
23
- :min_inclusive,
24
26
  :min_inclusive_prop,
25
27
  :min_length_prop,
26
28
  :model_attribute,
@@ -31,9 +33,6 @@ module LinkedRails
31
33
  :validators,
32
34
  :sh_in_prop,
33
35
  :key
34
- def datatype
35
- @datatype || raise("No datatype found for #{key} in #{form.name}")
36
- end
37
36
 
38
37
  def description
39
38
  description_from_attribute || description_fallback
@@ -43,6 +42,10 @@ module LinkedRails
43
42
  helper_text_from_attribute || helper_text_fallback
44
43
  end
45
44
 
45
+ def max_inclusive
46
+ @max_inclusive || validators[:max_inclusive]
47
+ end
48
+
46
49
  def max_length
47
50
  @max_length || validators[:max_length]
48
51
  end
@@ -51,6 +54,10 @@ module LinkedRails
51
54
  @min_count || (validators[:presence] ? 1 : nil)
52
55
  end
53
56
 
57
+ def min_inclusive
58
+ @min_inclusive || validators[:min_inclusive]
59
+ end
60
+
54
61
  def min_length
55
62
  @min_length || validators[:min_length]
56
63
  end
@@ -72,12 +79,20 @@ module LinkedRails
72
79
  true
73
80
  end
74
81
 
82
+ def placeholder
83
+ placeholder_from_attribute || placeholder_fallback
84
+ end
85
+
75
86
  def sh_in
76
87
  return validators[:sh_in] if @sh_in.blank?
77
88
 
78
89
  @sh_in.respond_to?(:call) ? @sh_in.call : @sh_in
79
90
  end
80
91
 
92
+ def start_adornment
93
+ @start_adornment.respond_to?(:call) ? @start_adornment.call : @start_adornment
94
+ end
95
+
81
96
  private
82
97
 
83
98
  def description_from_attribute
@@ -101,12 +116,21 @@ module LinkedRails
101
116
  end
102
117
 
103
118
  def label_fallback
104
- field_label = LinkedRails.translate(:field, :label, self, false)
105
- return field_label if field_label.present?
119
+ LinkedRails.translate(:field, :label, self, false).presence || label_from_property
120
+ end
106
121
 
122
+ def label_from_property
107
123
  LinkedRails.translate(:property, :label, path) if path
108
124
  end
109
125
 
126
+ def placeholder_from_attribute
127
+ @placeholder.respond_to?(:call) ? @placeholder.call : @placeholder
128
+ end
129
+
130
+ def placeholder_fallback
131
+ LinkedRails.translate(:field, :placeholder, self, false)
132
+ end
133
+
110
134
  class << self
111
135
  def iri
112
136
  Vocab.form[name.demodulize]
@@ -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