linked_rails 0.0.3 → 0.0.4.pre.g0ec6e30ca

Sign up to get free protection for your applications and to get access to all the features.
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