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
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ module Actions
5
+ class ObjectsController < LinkedRails.controller_parent_class
6
+ active_response :show
7
+ end
8
+ end
9
+ end
@@ -11,7 +11,8 @@ module LinkedRails
11
11
  def show
12
12
  render json: authorized_resources
13
13
 
14
- print_timings
14
+ print_timings unless Rails.env.production?
15
+ send_timings if client
15
16
  end
16
17
 
17
18
  private
@@ -20,8 +21,11 @@ module LinkedRails
20
21
  return response_for_wrong_host(opts) if wrong_host?(opts[:iri])
21
22
 
22
23
  include = opts[:include].to_s == 'true'
24
+ resource = LinkedRails.iri_mapper.resource_from_iri(request_path_to_url(opts[:iri]), user_context)
23
25
 
24
- response_from_request(include, RDF::URI(opts[:iri]))
26
+ return response_from_request(include, RDF::URI(opts[:iri])) if resource.blank?
27
+
28
+ response_from_resource(include, opts[:iri], resource)
25
29
  rescue StandardError => e
26
30
  handle_resource_error(opts, e)
27
31
  end
@@ -30,12 +34,16 @@ module LinkedRails
30
34
  @authorized_resources ||=
31
35
  params
32
36
  .require(:resources)
33
- .map { |param| param.permit(:include, :iri) }
37
+ .map { |param| resource_params(param) }
34
38
  .map(&method(:timed_authorized_resource))
35
39
  end
36
40
 
41
+ def client
42
+ nil
43
+ end
44
+
37
45
  def handle_resource_error(opts, error)
38
- log_resource_error(error)
46
+ log_resource_error(error, opts[:iri])
39
47
  status = error_status(error)
40
48
  body = error_body(status, error, opts[:iri]) if opts[:include].to_s == 'true'
41
49
 
@@ -50,7 +58,7 @@ module LinkedRails
50
58
  resource_body(error_resource(status, error, iri))
51
59
  end
52
60
 
53
- def log_resource_error(error)
61
+ def log_resource_error(error, _iri)
54
62
  return unless log_resource_error?(error)
55
63
 
56
64
  Rails.logger.error(error)
@@ -64,23 +72,32 @@ module LinkedRails
64
72
  def print_timings
65
73
  Rails.logger.debug(
66
74
  "\n CPU system user+system real inc status cache iri\n" \
67
- "#{timings.join("\n")}\n" \
75
+ "#{timing_lines.join("\n")}\n" \
68
76
  " User: #{current_user.class}(#{current_user.id})"
69
77
  )
70
78
  end
71
79
 
80
+ def send_timings
81
+ timings.each do |timing|
82
+ client.send_json(timing_json(timing))
83
+ end
84
+ end
85
+
72
86
  def require_doorkeeper_token?
73
87
  false
74
88
  end
75
89
 
76
- def resource_body(resource)
77
- return if resource.nil?
90
+ def resource_cache_control(cacheable, status, resource_policy)
91
+ return :private unless status == 200 && cacheable
92
+ return 'no-cache' unless resource_policy.try(:public_resource?)
78
93
 
79
- serializer_options = RDF::Serializers::Renderers.transform_opts(
80
- {include: resource&.try(:preview_includes)},
81
- serializer_params
82
- )
83
- RDF::Serializers.serializer_for(resource).new(resource, serializer_options).send(:render_hndjson)
94
+ :public
95
+ end
96
+
97
+ def resource_params(param)
98
+ params = param.permit(:include, :iri)
99
+ params[:iri] = URI(params[:iri])
100
+ params
84
101
  end
85
102
 
86
103
  def resource_response_body(iri, rack_body, status)
@@ -109,7 +126,7 @@ module LinkedRails
109
126
  fullpath = iri.query.blank? ? iri.path : "#{iri.path}?#{iri.query}"
110
127
 
111
128
  request.env.slice(*REQUEST_HEADERS).merge(
112
- 'HTTP_ACCEPT' => 'application/hex+x-ndjson',
129
+ 'HTTP_ACCEPT' => 'application/empathy+json',
113
130
  'HTTP_OPERATOR_ARG_GRAPH' => 'true',
114
131
  'ORIGINAL_FULLPATH' => fullpath
115
132
  )
@@ -124,6 +141,12 @@ module LinkedRails
124
141
  }.merge(opts)
125
142
  end
126
143
 
144
+ def resource_status(resource_policy)
145
+ raise(LinkedRails::Errors::Forbidden.new(query: :show?)) unless resource_policy.show?
146
+
147
+ 200
148
+ end
149
+
127
150
  def response_for_wrong_host(opts)
128
151
  iri = opts[:iri]
129
152
  term = term_from_vocab(iri)
@@ -132,18 +155,73 @@ module LinkedRails
132
155
  ontology_term_response(iri, term, opts[:include])
133
156
  end
134
157
 
158
+ def response_from_resource(include, iri, resource)
159
+ resource_policy = policy(resource)
160
+ status = resource_status(resource_policy)
161
+
162
+ resource_response(
163
+ iri,
164
+ body: response_from_resource_body(include, iri, resource, status),
165
+ cache: resource_cache_control(resource.try(:cacheable?), status, resource_policy),
166
+ language: I18n.locale,
167
+ status: status
168
+ )
169
+ end
170
+
171
+ def response_from_resource_body(include, _iri, resource, status)
172
+ include && status == 200 ? resource_body(resource) : nil
173
+ end
174
+
135
175
  def term_from_vocab(iri)
136
176
  vocab = Vocab.for(iri)
137
- tag = iri.split(vocab.to_s).last
177
+ tag = iri.to_s.split(vocab.to_s).last
138
178
  vocab[tag]
139
179
  rescue NoMethodError
140
180
  nil
141
181
  end
142
182
 
183
+ def timing_json(timing) # rubocop:disable Metrics/MethodLength
184
+ route = Rails.application.routes.recognize_path(timing[:resource].to_s)
185
+
186
+ {
187
+ type: 'web',
188
+ timings: {
189
+ total_duration: timing[:time].total
190
+ },
191
+ default_labels: {
192
+ action: route[:action] || 'show',
193
+ controller: route[:controller] || 'subresource',
194
+ status: timing[:status],
195
+ include: timing[:include]
196
+ }
197
+ }
198
+ end
199
+
200
+ def timing_lines
201
+ timings.map do |timing|
202
+ include = timing[:include].to_s.ljust(5)
203
+ "#{timing[:time].to_s[0..-2]} - #{include} #{timing[:status]} #{timing[:cache]} #{timing[:resource]}"
204
+ end
205
+ end
206
+
207
+ def ontology_class(iri)
208
+ klass = LinkedRails.linked_models.detect do |model|
209
+ (model.iri.is_a?(Array) ? model.iri : [model.iri]).include?(iri)
210
+ end
211
+ LinkedRails.ontology_class_class.new(iri: iri, klass: klass)
212
+ end
213
+
214
+ def ontology_property(iri)
215
+ LinkedRails.ontology_property_class.new(iri: iri)
216
+ end
217
+
143
218
  def ontology_term_response(iri, term, include)
219
+ capital = term.relativize(term.vocab).to_s[0] =~ /[A-Z]/
220
+ resource = capital ? ontology_class(term) : ontology_property(term)
221
+
144
222
  resource_response(
145
223
  iri,
146
- body: include ? resource_body(LinkedRails.ontology_property_class.new(iri: term)) : nil,
224
+ body: include ? resource_body(resource) : nil,
147
225
  cache: :public,
148
226
  language: I18n.locale,
149
227
  status: 200
@@ -161,6 +239,8 @@ module LinkedRails
161
239
  language: response_language(headers),
162
240
  status: status
163
241
  )
242
+ rescue URI::InvalidURIError, ActionController::BadRequest
243
+ resource_response(iri)
164
244
  end
165
245
 
166
246
  def response_language(headers)
@@ -169,7 +249,7 @@ module LinkedRails
169
249
 
170
250
  def sanitized_relative_path(iri) # rubocop:disable Metrics/AbcSize
171
251
  iri.path = "#{iri.path}/" unless iri.path&.ends_with?('/')
172
- uri = URI(LinkedRails.iri.path.present? ? iri.to_s.split("#{LinkedRails.iri.path}/").last : iri)
252
+ uri = URI(LinkedRails.iri.path.chomp('/').present? ? iri.to_s.split("#{LinkedRails.iri.path}/").last : iri)
173
253
 
174
254
  [uri.path, uri.query].compact.join('?')
175
255
  end
@@ -181,15 +261,18 @@ module LinkedRails
181
261
  def timed_authorized_resource(resource)
182
262
  res = nil
183
263
  time = Benchmark.measure { res = authorized_resource(resource) }
184
- unless Rails.env.production?
185
- include = resource[:include].to_s.ljust(5)
186
- timings << "#{time.to_s[0..-2]} - #{include} #{res[:status]} #{res[:cache]} #{resource[:iri]}"
187
- end
264
+ timings << {
265
+ time: time,
266
+ include: resource[:include],
267
+ status: res[:status],
268
+ cache: res[:cache],
269
+ resource: resource[:iri]
270
+ }
188
271
  res
189
272
  end
190
273
 
191
274
  def wrong_host?(iri)
192
- !iri.starts_with?(LinkedRails.iri)
275
+ !iri.to_s.starts_with?(LinkedRails.iri)
193
276
  end
194
277
  end
195
278
  end
@@ -3,47 +3,5 @@
3
3
  module LinkedRails
4
4
  class EnumValuesController < LinkedRails.controller_parent_class
5
5
  active_response :index
6
-
7
- private
8
-
9
- def enum_options!
10
- serializer_class!.enum_options(params[:attribute]) || raise(ActiveRecord::RecordNotFound)
11
- end
12
-
13
- def requested_resource
14
- return super unless action_name == 'index'
15
-
16
- @requested_resource ||= LinkedRails::Sequence.new(
17
- enum_options!.values,
18
- id: index_iri,
19
- scope: LinkedRails::EnumValuePolicy::Scope
20
- )
21
- end
22
-
23
- def model_class
24
- @model_class ||= self.class.linked_models.detect do |klass|
25
- klass.to_s == ([params[:module]].compact + [params[:klass]&.singularize]).join('/').classify
26
- end
27
- end
28
-
29
- def model_class!
30
- model_class || raise(ActiveRecord::RecordNotFound)
31
- end
32
-
33
- def serializer_class
34
- @serializer_class ||= RDF::Serializers.serializer_for(model_class!)
35
- end
36
-
37
- def serializer_class!
38
- serializer_class || raise(ActiveRecord::RecordNotFound)
39
- end
40
-
41
- class << self
42
- def linked_models
43
- @linked_models ||= ObjectSpace.each_object(Class).select do |c|
44
- c.included_modules.include?(LinkedRails::Model)
45
- end
46
- end
47
- end
48
6
  end
49
7
  end
@@ -7,15 +7,23 @@ module LinkedRails
7
7
  class Item # rubocop:disable Metrics/ClassLength
8
8
  include ActiveModel::Model
9
9
  include LinkedRails::Model
10
- enhance LinkedRails::Enhancements::Singularable
11
10
 
12
- attr_accessor :list, :policy_arguments, :submit_label
11
+ attr_accessor :inherit, :list, :one_click, :policy_arguments, :submit_label, :target_path, :target_query
13
12
  attr_writer :parent, :resource, :root_relative_iri, :user_context, :object,
14
13
  :target
15
14
  delegate :user_context, to: :list, allow_nil: true
16
-
17
- %i[description result type policy label image url include_object include_paths collection condition form completed
18
- tag http_method favorite path policy_resource predicate resource].each do |method|
15
+ collection_options(
16
+ association_base: lambda {
17
+ action_list = association_class.get_action_list(parent, user_context)
18
+
19
+ action_list.actions
20
+ },
21
+ display: :grid,
22
+ title: -> { I18n.t('actions.plural') }
23
+ )
24
+
25
+ %i[condition description result type policy label image target_url collection form
26
+ tag http_method favorite action_path policy_resource predicate resource].each do |method|
19
27
  attr_writer method
20
28
  define_method method do
21
29
  var = instance_variable_get(:"@#{method}")
@@ -28,10 +36,10 @@ module LinkedRails
28
36
 
29
37
  def action_status
30
38
  @action_status ||=
31
- if completed
32
- Vocab.schema.CompletedActionStatus
33
- elsif policy_valid?
39
+ if policy_valid?
34
40
  Vocab.schema.PotentialActionStatus
41
+ elsif policy_status
42
+ policy_status
35
43
  elsif policy_expired?
36
44
  Vocab.ontola[:ExpiredActionStatus]
37
45
  else
@@ -39,48 +47,29 @@ module LinkedRails
39
47
  end
40
48
  end
41
49
 
42
- def as_json(_opts = {})
50
+ def as_json(**_opts)
43
51
  {}
44
52
  end
45
53
 
46
54
  def available?
47
- return false unless action_status == Vocab.schema.PotentialActionStatus
48
-
49
55
  @condition.nil? || condition
50
56
  end
51
57
 
52
- def built_associations
53
- included_object
54
- .class
55
- .try(:reflect_on_all_associations)
56
- &.select { |association| included_object.association(association.name).loaded? }
57
- &.map(&:name)
58
- end
59
-
60
58
  def error
61
59
  policy_message ||
62
60
  I18n.t("actions.status.#{action_status.to_s.split('#').last}", default: nil)
63
61
  end
64
62
 
65
- def form_resource_includes # rubocop:disable Metrics/CyclomaticComplexity
66
- return {} if included_object.nil?
67
- return include_paths || {} if iri.anonymous?
68
-
69
- includes = included_object.class.try(:preview_includes)&.presence || []
70
-
71
- (includes.is_a?(Hash) ? [includes] : includes) + (built_associations || [])
72
- end
73
-
74
- def included_object
75
- object if include_object || object&.iri&.anonymous?
76
- end
77
-
78
63
  def object
79
64
  @object = list.instance_exec(&@object) if @object.respond_to?(:call)
80
65
 
81
66
  @object || resource
82
67
  end
83
68
 
69
+ def object_iri
70
+ object&.iri&.anonymous? ? anonymous_object_iri : object&.iri
71
+ end
72
+
84
73
  def parent
85
74
  return @parent if instance_variable_defined?(:@parent)
86
75
 
@@ -92,10 +81,10 @@ module LinkedRails
92
81
  end
93
82
 
94
83
  def policy_message
95
- resource_policy.try(:message) if action_status == Vocab.ontola[:DisabledActionStatus]
84
+ resource_policy.try(:message) unless policy_valid?
96
85
  end
97
86
 
98
- def root_relative_iri(_opts = {})
87
+ def root_relative_iri(**_opts)
99
88
  value = root_relative_iri_from_var
100
89
  value = RDF::URI(value) unless value.blank? || value.is_a?(RDF::URI)
101
90
  return @root_relative_iri = value if value
@@ -109,24 +98,30 @@ module LinkedRails
109
98
  RDF::URI(value)
110
99
  end
111
100
 
112
- def preview_includes
113
- [:target, included_object: form_resource_includes]
114
- end
115
-
116
101
  def singular_resource?
117
102
  resource.try(:singular_resource?)
118
103
  end
119
104
 
105
+ def policy_status
106
+ resource_policy.try(:action_status)
107
+ end
108
+
120
109
  def iri_opts
121
- resource&.iri_opts || {}
110
+ return {} if resource.blank?
111
+
112
+ resource.try(:singular_resource?) ? resource.singular_iri_opts : resource.iri_opts
122
113
  end
123
114
 
124
115
  def iri_template
125
- path_suffix = path.blank? ? "/actions/#{tag}" : "/#{path}"
116
+ path_suffix = "/#{action_path || tag}"
126
117
 
127
- return @iri_template ||= URITemplate.new(path_suffix) if resource.blank?
118
+ return @iri_template ||= LinkedRails::URITemplate.new(path_suffix) if resource.blank?
128
119
 
129
- @iri_template ||= iri_template_expand_path(resource.send(:iri_template), path_suffix)
120
+ base_template = resource.send(resource.try(:singular_resource?) ? :singular_iri_template : :iri_template)
121
+ @iri_template ||= iri_template_expand_path(
122
+ base_template.to_s.sub('display,', '').sub('sort%5B%5D*,', ''),
123
+ path_suffix
124
+ )
130
125
  end
131
126
 
132
127
  def rdf_type
@@ -143,6 +138,12 @@ module LinkedRails
143
138
 
144
139
  private
145
140
 
141
+ def anonymous_object_iri
142
+ object_iri = iri.dup
143
+ object_iri.path += '/action_object'
144
+ object_iri
145
+ end
146
+
146
147
  def description_fallback
147
148
  LinkedRails.translate(:action, :description, self, false)
148
149
  end
@@ -156,7 +157,7 @@ module LinkedRails
156
157
  end
157
158
 
158
159
  def policy_resource_fallback
159
- resource
160
+ object
160
161
  end
161
162
 
162
163
  def policy_valid?
@@ -184,6 +185,13 @@ module LinkedRails
184
185
  @root_relative_iri
185
186
  end
186
187
 
188
+ def target_url_fallback # rubocop:disable Metrics/AbcSize
189
+ base = (resource.try(:singular_resource?) ? resource.singular_iri : resource.iri).dup
190
+ base.path = "#{base.path.chomp('/')}/#{target_path}" if target_path.present?
191
+ base.query = Rack::Utils.parse_nested_query(base.query).merge(target_query).to_param if target_query.present?
192
+ base
193
+ end
194
+
187
195
  def type_fallback
188
196
  Vocab.schema.UpdateAction
189
197
  end
@@ -202,25 +210,26 @@ module LinkedRails
202
210
  )
203
211
  end
204
212
 
213
+ def get_action_list(parent, user_context)
214
+ return parent.action_list(user_context) if parent.respond_to?(:action_list)
215
+
216
+ app_action_list(user_context)
217
+ end
218
+
205
219
  def requested_index_resource(params, user_context)
206
- parent = parent_from_params(params, user_context)
207
- action_list = parent ? parent.action_list(user_context) : app_action_list(user_context)
208
-
209
- LinkedRails.collection_class.new(
210
- association_base: action_list.actions,
211
- association_class: ::Actions::Item,
212
- default_display: :grid,
213
- default_title: I18n.t('actions.plural'),
214
- parent: parent,
215
- title: params[:title]
220
+ parent = parent_from_params!(params, user_context) if params.key?(:parent_iri)
221
+
222
+ default_collection_option(:collection_class).collection_or_view(
223
+ default_collection_options.merge(parent: parent),
224
+ index_collection_params(params, user_context)
216
225
  )
217
226
  end
218
227
 
219
228
  def requested_single_resource(params, user_context)
220
229
  return nil if params[:id].blank?
221
230
 
222
- parent = parent_from_params(params, user_context)
223
- action_list = parent ? parent.action_list(user_context) : app_action_list(user_context)
231
+ parent = parent_from_params!(params, user_context) if params.key?(:parent_iri)
232
+ action_list = get_action_list(parent, user_context)
224
233
 
225
234
  action_list&.action(params[:id].to_sym)
226
235
  end
@@ -7,14 +7,12 @@ module LinkedRails
7
7
  class List
8
8
  include ActiveModel::Model
9
9
  include LinkedRails::Model
10
- include DefaultActions
11
- extend LinkedRails::Enhanceable
12
10
 
13
11
  attr_accessor :resource, :user_context
14
12
  class_attribute :_defined_actions, instance_reader: false, instance_writer: false
15
13
 
16
14
  def actions
17
- @actions ||= defined_actions.map { |tag, opts| action_item(tag, opts.dup) }
15
+ @actions ||= defined_actions.map { |tag, opts| action_item(tag, opts.dup) }.select(&:available?)
18
16
  end
19
17
 
20
18
  def action(tag)
@@ -34,9 +32,9 @@ module LinkedRails
34
32
  private
35
33
 
36
34
  def action_item(tag, options)
37
- options[:tag] ||= options[:action_tag] || tag
35
+ options[:tag] ||= tag
38
36
  options[:list] ||= self
39
- LinkedRails.actions_item_class.new(options.except(:action_tag))
37
+ LinkedRails.actions_item_class.new(options.except(:action_name))
40
38
  end
41
39
 
42
40
  def association
@@ -52,12 +50,6 @@ module LinkedRails
52
50
  end
53
51
 
54
52
  class << self
55
- def actionable_class
56
- @actionable_class ||=
57
- name.gsub('ActionList', '').safe_constantize ||
58
- name.demodulize.gsub('ActionList', '').safe_constantize
59
- end
60
-
61
53
  def collection_actions
62
54
  defined_actions[:collection]
63
55
  end
@@ -77,26 +69,11 @@ module LinkedRails
77
69
 
78
70
  private
79
71
 
80
- def has_collection_action(action, opts = {}) # rubocop:disable Naming/PredicateName
81
- opts[:http_method] ||= 'POST'
82
- defined_actions[:collection][action] = opts
83
- end
84
-
85
- def has_resource_action(action, opts = {}) # rubocop:disable Naming/PredicateName
86
- opts[:http_method] ||= 'POST'
87
- defined_actions[:resource][action] = opts
88
- end
89
-
90
- def has_singular_action(action, opts = {}) # rubocop:disable Naming/PredicateName
91
- opts[:http_method] ||= 'POST'
92
- defined_actions[:singular][action] = opts
93
- end
94
-
95
72
  def initial_defined_actions(clone_from = {})
96
73
  {
97
- collection: clone_from[:collection].dup || {},
98
- resource: clone_from[:resource].dup || {},
99
- singular: clone_from[:singular].dup || {}
74
+ collection: clone_from[:collection].dup&.select { |_, value| value[:inherit] != false } || {},
75
+ resource: clone_from[:resource].dup&.select { |_, value| value[:inherit] != false } || {},
76
+ singular: clone_from[:singular].dup&.select { |_, value| value[:inherit] != false } || {}
100
77
  }
101
78
  end
102
79
 
@@ -106,8 +83,6 @@ module LinkedRails
106
83
  self._defined_actions = initial_defined_actions(superclass.try(:_defined_actions) || {})
107
84
  end
108
85
  end
109
-
110
- enhanceable :actionable_class, :Action
111
86
  end
112
87
  end
113
88
  end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pundit'
4
+
5
+ module LinkedRails
6
+ module Actions
7
+ class Object
8
+ include ActiveModel::Model
9
+ include LinkedRails::Model
10
+
11
+ attr_accessor :action
12
+ alias parent action
13
+ delegate :object, to: :action
14
+
15
+ def iri
16
+ action.object_iri
17
+ end
18
+
19
+ class << self
20
+ def requested_resource(opts, user_context)
21
+ return super unless opts[:iri].include?('?')
22
+
23
+ parent_iri_with_query = [opts[:params][:parent_iri], opts[:iri].split('?').last].compact.join('?')
24
+
25
+ super(opts.merge(params: opts[:params].merge(parent_iri: parent_iri_with_query)), user_context)
26
+ end
27
+
28
+ def requested_single_resource(params, user_context)
29
+ return unless params.key?(:parent_iri)
30
+
31
+ parent = parent_from_params!(params, user_context)
32
+
33
+ new(action: parent) if parent.object.anonymous_iri?
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ class Collection
5
+ module Configuration
6
+ extend ActiveSupport::Concern
7
+
8
+ module ClassMethods
9
+ private
10
+
11
+ def define_default_option_method(option)
12
+ attr_accessor "default_#{option}"
13
+ end
14
+
15
+ def define_getter_option_method(option)
16
+ attr_writer option
17
+
18
+ define_method(option) do
19
+ resolve_collection_option(option)
20
+ end
21
+ end
22
+ end
23
+
24
+ included do
25
+ attr_accessor :iri_template
26
+
27
+ LinkedRails::Model::Collections::COLLECTION_CUSTOMIZABLE_OPTIONS.each_key do |option|
28
+ define_default_option_method(option)
29
+ end
30
+ LinkedRails::Model::Collections::COLLECTION_OPTIONS.each_key do |option|
31
+ define_getter_option_method(option)
32
+ end
33
+
34
+ def display
35
+ resolve_collection_option(:display)&.to_sym
36
+ end
37
+
38
+ def type
39
+ resolve_collection_option(:type)&.to_sym
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def resolve_collection_option(option)
46
+ var_name = :"@#{option}"
47
+ default_method_name = "default_#{option}"
48
+
49
+ var = instance_variable_defined?(var_name) ? instance_variable_get(var_name) : try(default_method_name)
50
+
51
+ var.respond_to?(:call) ? instance_exec(&var) : var
52
+ end
53
+ end
54
+ end
55
+ end
@@ -8,7 +8,7 @@ module LinkedRails
8
8
 
9
9
  attr_accessor :default_filter, :key, :value, :collection
10
10
 
11
- def iri(_opts = {})
11
+ def iri(**_opts)
12
12
  self
13
13
  end
14
14
  end