linked_rails 0.0.3 → 0.0.4.pre.g0ec6e30ca

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 (180) 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 +107 -22
  5. data/app/controllers/linked_rails/enum_values_controller.rb +0 -42
  6. data/app/models/linked_rails/actions/item.rb +72 -55
  7. data/app/models/linked_rails/actions/list.rb +6 -31
  8. data/app/models/linked_rails/actions/object.rb +40 -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 +53 -15
  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 +5 -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 +49 -25
  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 +11 -14
  38. data/app/models/linked_rails/menus/list.rb +30 -10
  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 +3 -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/collection/filter_field_serializer.rb +3 -2
  54. data/app/serializers/linked_rails/collection/filter_option_serializer.rb +1 -1
  55. data/app/serializers/linked_rails/collection/filter_serializer.rb +1 -1
  56. data/app/serializers/linked_rails/collection/sorting_serializer.rb +1 -1
  57. data/app/serializers/linked_rails/collection/view_serializer.rb +4 -3
  58. data/app/serializers/linked_rails/collection_serializer.rb +8 -7
  59. data/app/serializers/linked_rails/condition_serializer.rb +3 -3
  60. data/app/serializers/linked_rails/entry_point_serializer.rb +3 -3
  61. data/app/serializers/linked_rails/enum_value_serializer.rb +1 -0
  62. data/app/serializers/linked_rails/form/field/association_input_serializer.rb +1 -0
  63. data/app/serializers/linked_rails/form/field/file_input_serializer.rb +11 -0
  64. data/app/serializers/linked_rails/form/field_serializer.rb +3 -1
  65. data/app/serializers/linked_rails/form/group_serializer.rb +1 -1
  66. data/app/serializers/linked_rails/form/page_serializer.rb +1 -1
  67. data/app/serializers/linked_rails/menus/item_serializer.rb +3 -3
  68. data/app/serializers/linked_rails/menus/list_serializer.rb +1 -1
  69. data/app/serializers/linked_rails/ontology_serializer.rb +2 -2
  70. data/app/serializers/linked_rails/property_query_serializer.rb +7 -0
  71. data/app/serializers/linked_rails/sequence_serializer.rb +2 -5
  72. data/app/serializers/linked_rails/shacl/node_shape_serializer.rb +1 -1
  73. data/app/serializers/linked_rails/shacl/property_shape_serializer.rb +1 -1
  74. data/app/serializers/linked_rails/shacl/shape_serializer.rb +5 -5
  75. data/app/serializers/linked_rails/web_page_serializer.rb +3 -3
  76. data/app/serializers/linked_rails/web_site_serializer.rb +1 -1
  77. data/app/serializers/linked_rails/widget_serializer.rb +3 -3
  78. data/app/workers/linked_rails/invalidation_stream_worker.rb +16 -0
  79. data/lib/generators/linked_rails/install/install_generator.rb +5 -8
  80. data/lib/generators/linked_rails/install/templates/README +2 -0
  81. data/lib/generators/linked_rails/install/templates/app_menu_list.rb +36 -7
  82. data/lib/generators/linked_rails/install/templates/application_menu_list.rb +40 -1
  83. data/lib/generators/linked_rails/install/templates/initializer.rb +1 -2
  84. data/lib/generators/linked_rails/install/templates/locales.yml +16 -0
  85. data/lib/generators/linked_rails/install/templates/rdf_serializers_initializer.rb +1 -1
  86. data/lib/generators/linked_rails/install/templates/vocab.rb +1 -0
  87. data/lib/generators/linked_rails/install/templates/vocab.yml +2 -2
  88. data/lib/generators/linked_rails/model/model_generator.rb +0 -1
  89. data/lib/generators/linked_rails/model/templates/controller.rb.tt +5 -1
  90. data/lib/generators/linked_rails/model/templates/form.rb.tt +3 -0
  91. data/lib/generators/linked_rails/model/templates/menu_list.rb.tt +15 -0
  92. data/lib/generators/linked_rails/model/templates/policy.rb.tt +13 -0
  93. data/lib/generators/linked_rails/model/templates/serializer.rb.tt +5 -1
  94. data/lib/linked_rails/active_response/controller/collections.rb +1 -1
  95. data/lib/linked_rails/active_response/controller/crud_defaults.rb +4 -4
  96. data/lib/linked_rails/active_response/controller/params.rb +10 -10
  97. data/lib/linked_rails/active_response/controller.rb +8 -18
  98. data/lib/linked_rails/active_response/responders/rdf.rb +19 -10
  99. data/lib/linked_rails/callable_variable.rb +1 -1
  100. data/lib/linked_rails/collection_params_parser.rb +93 -0
  101. data/lib/linked_rails/controller/actionable.rb +121 -0
  102. data/lib/linked_rails/controller/authorization.rb +6 -0
  103. data/lib/linked_rails/controller/default_actions/create.rb +52 -0
  104. data/lib/linked_rails/controller/default_actions/destroy.rb +42 -0
  105. data/lib/linked_rails/controller/default_actions/update.rb +43 -0
  106. data/lib/linked_rails/controller/delta.rb +58 -0
  107. data/lib/linked_rails/controller/error_handling.rb +12 -7
  108. data/lib/linked_rails/controller/rendering.rb +48 -0
  109. data/lib/linked_rails/controller.rb +24 -4
  110. data/lib/linked_rails/enhancements/creatable/controller.rb +1 -1
  111. data/lib/linked_rails/enhancements/destroyable/controller.rb +1 -1
  112. data/lib/linked_rails/enhancements/updatable/controller.rb +1 -1
  113. data/lib/linked_rails/enhancements.rb +0 -16
  114. data/lib/linked_rails/errors/forbidden.rb +37 -0
  115. data/lib/linked_rails/errors.rb +3 -0
  116. data/lib/linked_rails/helpers/delta_helper.rb +28 -57
  117. data/lib/linked_rails/helpers/ontola_actions_helper.rb +2 -2
  118. data/lib/linked_rails/helpers/resource_helper.rb +14 -2
  119. data/lib/linked_rails/iri_mapper.rb +18 -40
  120. data/lib/linked_rails/middleware/error_handling.rb +51 -0
  121. data/lib/linked_rails/middleware/linked_data_params.rb +30 -151
  122. data/lib/linked_rails/model/actionable.rb +68 -0
  123. data/lib/linked_rails/model/cacheable.rb +45 -0
  124. data/lib/linked_rails/model/collections.rb +203 -39
  125. data/lib/linked_rails/model/dirty.rb +6 -18
  126. data/lib/linked_rails/model/enhancements.rb +1 -6
  127. data/lib/linked_rails/model/filtering.rb +4 -6
  128. data/lib/linked_rails/model/indexable.rb +6 -16
  129. data/lib/linked_rails/model/iri.rb +29 -19
  130. data/lib/linked_rails/model/iri_mapping.rb +37 -8
  131. data/lib/linked_rails/model/menuable.rb +28 -0
  132. data/lib/linked_rails/model/serialization.rb +2 -15
  133. data/lib/linked_rails/model/singularable.rb +57 -0
  134. data/lib/linked_rails/model/sorting.rb +0 -5
  135. data/lib/linked_rails/model/tables.rb +26 -0
  136. data/lib/linked_rails/model.rb +18 -7
  137. data/lib/linked_rails/params_parser.rb +140 -53
  138. data/lib/linked_rails/policy/attribute_conditions.rb +2 -2
  139. data/lib/linked_rails/policy.rb +44 -46
  140. data/lib/linked_rails/railtie.rb +11 -0
  141. data/lib/linked_rails/rdf_error.rb +2 -2
  142. data/lib/linked_rails/renderers.rb +1 -0
  143. data/lib/linked_rails/routes.rb +38 -22
  144. data/lib/linked_rails/serializer/actionable.rb +27 -0
  145. data/lib/linked_rails/serializer/menuable.rb +31 -0
  146. data/lib/linked_rails/serializer/singularable.rb +26 -0
  147. data/lib/linked_rails/serializer.rb +35 -12
  148. data/lib/linked_rails/storage.rb +32 -0
  149. data/lib/linked_rails/test_methods.rb +114 -0
  150. data/lib/linked_rails/translate.rb +31 -9
  151. data/lib/linked_rails/types/iri_type.rb +37 -0
  152. data/lib/linked_rails/uri_template.rb +30 -0
  153. data/lib/linked_rails/url.rb +11 -0
  154. data/lib/linked_rails/version.rb +1 -1
  155. data/lib/linked_rails/vocab.rb +9 -0
  156. data/lib/linked_rails.rb +40 -14
  157. data/lib/rails/welcome_controller.rb +3 -3
  158. data/lib/rdf/list.rb +9 -0
  159. data/lib/rdf/query_fix.rb +15 -0
  160. metadata +78 -33
  161. data/app/models/linked_rails/actions/default_actions/create.rb +0 -60
  162. data/app/models/linked_rails/actions/default_actions/destroy.rb +0 -45
  163. data/app/models/linked_rails/actions/default_actions/update.rb +0 -50
  164. data/app/models/linked_rails/actions/default_actions.rb +0 -17
  165. data/lib/generators/linked_rails/install/templates/application_action_list.rb +0 -3
  166. data/lib/generators/linked_rails/model/templates/action_list.rb.tt +0 -6
  167. data/lib/linked_rails/enhancements/actionable/model.rb +0 -71
  168. data/lib/linked_rails/enhancements/actionable/serializer.rb +0 -25
  169. data/lib/linked_rails/enhancements/creatable/action.rb +0 -15
  170. data/lib/linked_rails/enhancements/destroyable/action.rb +0 -15
  171. data/lib/linked_rails/enhancements/destroyable/routing.rb +0 -19
  172. data/lib/linked_rails/enhancements/menuable/model.rb +0 -36
  173. data/lib/linked_rails/enhancements/menuable/serializer.rb +0 -33
  174. data/lib/linked_rails/enhancements/route_concerns.rb +0 -56
  175. data/lib/linked_rails/enhancements/singularable/controller.rb +0 -43
  176. data/lib/linked_rails/enhancements/singularable/model.rb +0 -47
  177. data/lib/linked_rails/enhancements/singularable/serializer.rb +0 -28
  178. data/lib/linked_rails/enhancements/tableable/model.rb +0 -28
  179. data/lib/linked_rails/enhancements/updatable/action.rb +0 -15
  180. 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,34 @@ 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
101
+ rescue URI::InvalidURIError
102
+ params.except(:iri)
84
103
  end
85
104
 
86
105
  def resource_response_body(iri, rack_body, status)
@@ -109,7 +128,7 @@ module LinkedRails
109
128
  fullpath = iri.query.blank? ? iri.path : "#{iri.path}?#{iri.query}"
110
129
 
111
130
  request.env.slice(*REQUEST_HEADERS).merge(
112
- 'HTTP_ACCEPT' => 'application/hex+x-ndjson',
131
+ 'HTTP_ACCEPT' => 'application/empathy+json',
113
132
  'HTTP_OPERATOR_ARG_GRAPH' => 'true',
114
133
  'ORIGINAL_FULLPATH' => fullpath
115
134
  )
@@ -124,6 +143,12 @@ module LinkedRails
124
143
  }.merge(opts)
125
144
  end
126
145
 
146
+ def resource_status(resource_policy)
147
+ raise(LinkedRails::Errors::Forbidden.new(query: :show?)) unless resource_policy.show?
148
+
149
+ 200
150
+ end
151
+
127
152
  def response_for_wrong_host(opts)
128
153
  iri = opts[:iri]
129
154
  term = term_from_vocab(iri)
@@ -132,18 +157,73 @@ module LinkedRails
132
157
  ontology_term_response(iri, term, opts[:include])
133
158
  end
134
159
 
160
+ def response_from_resource(include, iri, resource)
161
+ resource_policy = policy(resource)
162
+ status = resource_status(resource_policy)
163
+
164
+ resource_response(
165
+ iri,
166
+ body: response_from_resource_body(include, iri, resource, status),
167
+ cache: resource_cache_control(resource.try(:cacheable?), status, resource_policy),
168
+ language: I18n.locale,
169
+ status: status
170
+ )
171
+ end
172
+
173
+ def response_from_resource_body(include, _iri, resource, status)
174
+ include && status == 200 ? resource_body(resource) : nil
175
+ end
176
+
135
177
  def term_from_vocab(iri)
136
178
  vocab = Vocab.for(iri)
137
- tag = iri.split(vocab.to_s).last
179
+ tag = iri.to_s.split(vocab.to_s).last
138
180
  vocab[tag]
139
181
  rescue NoMethodError
140
182
  nil
141
183
  end
142
184
 
185
+ def timing_json(timing) # rubocop:disable Metrics/MethodLength
186
+ route = Rails.application.routes.recognize_path(timing[:resource].to_s)
187
+
188
+ {
189
+ type: 'web',
190
+ timings: {
191
+ total_duration: timing[:time].total
192
+ },
193
+ default_labels: {
194
+ action: route[:action] || 'show',
195
+ controller: route[:controller] || 'subresource',
196
+ status: timing[:status],
197
+ include: timing[:include]
198
+ }
199
+ }
200
+ end
201
+
202
+ def timing_lines
203
+ timings.map do |timing|
204
+ include = timing[:include].to_s.ljust(5)
205
+ "#{timing[:time].to_s[0..-2]} - #{include} #{timing[:status]} #{timing[:cache]} #{timing[:resource]}"
206
+ end
207
+ end
208
+
209
+ def ontology_class(iri)
210
+ klass = LinkedRails.linked_models.detect do |model|
211
+ (model.iri.is_a?(Array) ? model.iri : [model.iri]).include?(iri)
212
+ end
213
+ LinkedRails.ontology_class_class.new(iri: iri, klass: klass)
214
+ end
215
+
216
+ def ontology_property(iri)
217
+ LinkedRails.ontology_property_class.new(iri: iri)
218
+ end
219
+
143
220
  def ontology_term_response(iri, term, include)
221
+ capital = term.relativize(term.vocab).to_s[0] =~ /[A-Z]/
222
+ resource = capital ? ontology_class(term) : ontology_property(term)
223
+
144
224
  resource_response(
145
225
  iri,
146
- body: include ? resource_body(LinkedRails.ontology_property_class.new(iri: term)) : nil,
226
+ body: include ? resource_body(resource) : nil,
147
227
  cache: :public,
148
228
  language: I18n.locale,
149
229
  status: 200
@@ -161,6 +241,8 @@ module LinkedRails
161
241
  language: response_language(headers),
162
242
  status: status
163
243
  )
244
+ rescue URI::InvalidURIError, ActionController::BadRequest
245
+ resource_response(iri)
164
246
  end
165
247
 
166
248
  def response_language(headers)
@@ -169,7 +251,7 @@ module LinkedRails
169
251
 
170
252
  def sanitized_relative_path(iri) # rubocop:disable Metrics/AbcSize
171
253
  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)
254
+ uri = URI(LinkedRails.iri.path.chomp('/').present? ? iri.to_s.split("#{LinkedRails.iri.path}/").last : iri)
173
255
 
174
256
  [uri.path, uri.query].compact.join('?')
175
257
  end
@@ -181,15 +263,18 @@ module LinkedRails
181
263
  def timed_authorized_resource(resource)
182
264
  res = nil
183
265
  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
266
+ timings << {
267
+ time: time,
268
+ include: resource[:include],
269
+ status: res[:status],
270
+ cache: res[:cache],
271
+ resource: resource[:iri]
272
+ }
188
273
  res
189
274
  end
190
275
 
191
276
  def wrong_host?(iri)
192
- !iri.starts_with?(LinkedRails.iri)
277
+ !iri.to_s.starts_with?(LinkedRails.iri)
193
278
  end
194
279
  end
195
280
  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,46 +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 || [])
63
+ def object
64
+ object_from_var || resource
72
65
  end
73
66
 
74
- def included_object
75
- object if include_object || object&.iri&.anonymous?
67
+ def object_iri
68
+ iri_with_root(object_root_relative_iri)
76
69
  end
77
70
 
78
- def object
79
- @object = list.instance_exec(&@object) if @object.respond_to?(:call)
80
-
81
- @object || resource
71
+ def object_root_relative_iri
72
+ object&.iri&.anonymous? ? anonymous_object_root_relative_iri : object&.root_relative_iri
82
73
  end
83
74
 
84
75
  def parent
@@ -92,10 +83,10 @@ module LinkedRails
92
83
  end
93
84
 
94
85
  def policy_message
95
- resource_policy.try(:message) if action_status == Vocab.ontola[:DisabledActionStatus]
86
+ resource_policy.try(:message) unless policy_valid?
96
87
  end
97
88
 
98
- def root_relative_iri(_opts = {})
89
+ def root_relative_iri(**_opts)
99
90
  value = root_relative_iri_from_var
100
91
  value = RDF::URI(value) unless value.blank? || value.is_a?(RDF::URI)
101
92
  return @root_relative_iri = value if value
@@ -109,24 +100,30 @@ module LinkedRails
109
100
  RDF::URI(value)
110
101
  end
111
102
 
112
- def preview_includes
113
- [:target, included_object: form_resource_includes]
114
- end
115
-
116
103
  def singular_resource?
117
104
  resource.try(:singular_resource?)
118
105
  end
119
106
 
107
+ def policy_status
108
+ resource_policy.try(:action_status)
109
+ end
110
+
120
111
  def iri_opts
121
- resource&.iri_opts || {}
112
+ return {} if resource.blank?
113
+
114
+ resource.try(:singular_resource?) ? resource.singular_iri_opts : resource.iri_opts
122
115
  end
123
116
 
124
117
  def iri_template
125
- path_suffix = path.blank? ? "/actions/#{tag}" : "/#{path}"
118
+ path_suffix = "/#{action_path || tag}"
126
119
 
127
- return @iri_template ||= URITemplate.new(path_suffix) if resource.blank?
120
+ return @iri_template ||= LinkedRails::URITemplate.new(path_suffix) if resource.blank?
128
121
 
129
- @iri_template ||= iri_template_expand_path(resource.send(:iri_template), path_suffix)
122
+ base_template = resource.send(resource.try(:singular_resource?) ? :singular_iri_template : :iri_template)
123
+ @iri_template ||= iri_template_expand_path(
124
+ base_template.to_s.sub('display,', '').sub('sort%5B%5D*,', ''),
125
+ path_suffix
126
+ )
130
127
  end
131
128
 
132
129
  def rdf_type
@@ -143,6 +140,12 @@ module LinkedRails
143
140
 
144
141
  private
145
142
 
143
+ def anonymous_object_root_relative_iri
144
+ object_iri = root_relative_iri.dup
145
+ object_iri.path += '/action_object'
146
+ object_iri
147
+ end
148
+
146
149
  def description_fallback
147
150
  LinkedRails.translate(:action, :description, self, false)
148
151
  end
@@ -151,12 +154,18 @@ module LinkedRails
151
154
  LinkedRails.translate(:action, :label, self)
152
155
  end
153
156
 
157
+ def object_from_var
158
+ return @object unless @object.respond_to?(:call)
159
+
160
+ @object = list.instance_exec(&@object)
161
+ end
162
+
154
163
  def policy_expired?
155
164
  @policy_expired ||= policy && resource_policy.try(:expired?)
156
165
  end
157
166
 
158
167
  def policy_resource_fallback
159
- resource
168
+ object
160
169
  end
161
170
 
162
171
  def policy_valid?
@@ -184,6 +193,13 @@ module LinkedRails
184
193
  @root_relative_iri
185
194
  end
186
195
 
196
+ def target_url_fallback # rubocop:disable Metrics/AbcSize
197
+ base = (resource.try(:singular_resource?) ? resource.singular_iri : resource.iri).dup
198
+ base.path = "#{base.path.chomp('/')}/#{target_path}" if target_path.present?
199
+ base.query = Rack::Utils.parse_nested_query(base.query).merge(target_query).to_param if target_query.present?
200
+ base
201
+ end
202
+
187
203
  def type_fallback
188
204
  Vocab.schema.UpdateAction
189
205
  end
@@ -202,25 +218,26 @@ module LinkedRails
202
218
  )
203
219
  end
204
220
 
221
+ def get_action_list(parent, user_context)
222
+ return parent.action_list(user_context) if parent.respond_to?(:action_list)
223
+
224
+ app_action_list(user_context)
225
+ end
226
+
205
227
  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]
228
+ parent = parent_from_params!(params, user_context) if params.key?(:parent_iri)
229
+
230
+ default_collection_option(:collection_class).collection_or_view(
231
+ default_collection_options.merge(parent: parent),
232
+ index_collection_params(params, user_context)
216
233
  )
217
234
  end
218
235
 
219
236
  def requested_single_resource(params, user_context)
220
237
  return nil if params[:id].blank?
221
238
 
222
- parent = parent_from_params(params, user_context)
223
- action_list = parent ? parent.action_list(user_context) : app_action_list(user_context)
239
+ parent = parent_from_params!(params, user_context) if params.key?(:parent_iri)
240
+ action_list = get_action_list(parent, user_context)
224
241
 
225
242
  action_list&.action(params[:id].to_sym)
226
243
  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,40 @@
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 root_relative_iri
16
+ action.object_root_relative_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
+ object = parent&.object
34
+ object.instance_variable_set(:@iri, parent.object_iri)
35
+ object
36
+ end
37
+ end
38
+ end
39
+ end
40
+ 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