linked_rails 0.0.1 → 0.0.4.pre.ge1306665a
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/LICENSE +21 -674
- data/app/controllers/linked_rails/actions/objects_controller.rb +9 -0
- data/app/controllers/linked_rails/bulk_controller.rb +71 -21
- data/app/controllers/linked_rails/enum_values_controller.rb +0 -42
- data/app/models/linked_rails/actions/item.rb +64 -55
- data/app/models/linked_rails/actions/list.rb +6 -31
- data/app/models/linked_rails/actions/object.rb +38 -0
- data/app/models/linked_rails/collection/configuration.rb +55 -0
- data/app/models/linked_rails/collection/filter.rb +1 -1
- data/app/models/linked_rails/collection/filter_field.rb +19 -2
- data/app/models/linked_rails/collection/filter_option.rb +1 -1
- data/app/models/linked_rails/collection/filterable.rb +6 -9
- data/app/models/linked_rails/collection/infinite.rb +113 -0
- data/app/models/linked_rails/collection/infinite_view.rb +1 -90
- data/app/models/linked_rails/collection/iri.rb +47 -43
- data/app/models/linked_rails/collection/iri_mapping.rb +15 -7
- data/app/models/linked_rails/collection/paginated.rb +46 -0
- data/app/models/linked_rails/collection/paginated_view.rb +1 -33
- data/app/models/linked_rails/collection/sortable.rb +1 -9
- data/app/models/linked_rails/collection/sorting.rb +1 -1
- data/app/models/linked_rails/collection/view.rb +51 -14
- data/app/models/linked_rails/collection.rb +53 -85
- data/app/models/linked_rails/creative_work.rb +1 -1
- data/app/models/linked_rails/entry_point.rb +8 -5
- data/app/models/linked_rails/enum_value.rb +40 -2
- data/app/models/linked_rails/form/field/association_input.rb +7 -1
- data/app/models/linked_rails/form/field/file_input.rb +1 -0
- data/app/models/linked_rails/form/field/resource_field.rb +2 -0
- data/{lib/linked_rails/enhancements/indexable/model.rb → app/models/linked_rails/form/field/url_input.rb} +3 -3
- data/app/models/linked_rails/form/field.rb +37 -13
- data/app/models/linked_rails/form/field_factory.rb +48 -18
- data/app/models/linked_rails/form/group.rb +4 -6
- data/app/models/linked_rails/form/page.rb +8 -4
- data/app/models/linked_rails/form.rb +16 -21
- data/app/models/linked_rails/manifest.rb +8 -2
- data/app/models/linked_rails/menus/item.rb +10 -13
- data/app/models/linked_rails/menus/list.rb +16 -7
- data/app/models/linked_rails/ontology/base.rb +3 -1
- data/app/models/linked_rails/ontology/class.rb +3 -3
- data/app/models/linked_rails/ontology.rb +5 -5
- data/app/models/linked_rails/property_query.rb +2 -0
- data/app/models/linked_rails/sequence.rb +4 -13
- data/app/models/linked_rails/shacl/property_shape.rb +1 -1
- data/app/models/linked_rails/web_page.rb +0 -4
- data/app/models/linked_rails/web_site.rb +0 -4
- data/app/models/linked_rails/widget.rb +4 -11
- data/app/policies/linked_rails/actions/object_policy.rb +11 -0
- data/app/policies/linked_rails/collection_policy.rb +2 -2
- data/app/serializers/linked_rails/actions/item_serializer.rb +5 -5
- data/app/serializers/linked_rails/actions/object_serializer.rb +9 -0
- data/app/serializers/linked_rails/collection/filter_field_serializer.rb +3 -2
- data/app/serializers/linked_rails/collection/filter_option_serializer.rb +1 -1
- data/app/serializers/linked_rails/collection/filter_serializer.rb +1 -1
- data/app/serializers/linked_rails/collection/sorting_serializer.rb +1 -1
- data/app/serializers/linked_rails/collection/view_serializer.rb +3 -3
- data/app/serializers/linked_rails/collection_serializer.rb +8 -7
- data/app/serializers/linked_rails/condition_serializer.rb +3 -3
- data/app/serializers/linked_rails/entry_point_serializer.rb +3 -3
- data/app/serializers/linked_rails/enum_value_serializer.rb +1 -0
- data/app/serializers/linked_rails/form/field/association_input_serializer.rb +1 -0
- data/app/serializers/linked_rails/form/field/file_input_serializer.rb +11 -0
- data/app/serializers/linked_rails/form/field_serializer.rb +3 -1
- data/app/serializers/linked_rails/form/group_serializer.rb +1 -1
- data/app/serializers/linked_rails/form/page_serializer.rb +1 -1
- data/app/serializers/linked_rails/menus/item_serializer.rb +3 -3
- data/app/serializers/linked_rails/menus/list_serializer.rb +1 -1
- data/app/serializers/linked_rails/ontology_serializer.rb +2 -2
- data/app/serializers/linked_rails/property_query_serializer.rb +7 -0
- data/app/serializers/linked_rails/sequence_serializer.rb +2 -5
- data/app/serializers/linked_rails/shacl/node_shape_serializer.rb +1 -1
- data/app/serializers/linked_rails/shacl/property_shape_serializer.rb +1 -1
- data/app/serializers/linked_rails/shacl/shape_serializer.rb +5 -5
- data/app/serializers/linked_rails/web_page_serializer.rb +3 -3
- data/app/serializers/linked_rails/web_site_serializer.rb +1 -1
- data/app/serializers/linked_rails/widget_serializer.rb +3 -3
- data/lib/generators/linked_rails/install/install_generator.rb +7 -8
- data/lib/generators/linked_rails/install/templates/README +7 -0
- data/lib/generators/linked_rails/install/templates/app_menu_list.rb +36 -7
- data/lib/generators/linked_rails/install/templates/application_menu_list.rb +40 -1
- data/lib/generators/linked_rails/install/templates/initializer.rb +1 -2
- data/lib/generators/linked_rails/install/templates/locales.yml +14 -0
- data/lib/generators/linked_rails/install/templates/rdf_serializers_initializer.rb +1 -1
- data/lib/generators/linked_rails/install/templates/vocab.rb +1 -0
- data/lib/generators/linked_rails/install/templates/vocab.yml +2 -2
- data/lib/generators/linked_rails/model/model_generator.rb +0 -1
- data/lib/generators/linked_rails/model/templates/controller.rb.tt +5 -1
- data/lib/generators/linked_rails/model/templates/form.rb.tt +3 -0
- data/lib/generators/linked_rails/model/templates/menu_list.rb.tt +15 -0
- data/lib/generators/linked_rails/model/templates/policy.rb.tt +13 -0
- data/lib/generators/linked_rails/model/templates/serializer.rb.tt +5 -1
- data/lib/linked_rails/active_response/controller/collections.rb +1 -1
- data/lib/linked_rails/active_response/controller/crud_defaults.rb +4 -4
- data/lib/linked_rails/active_response/controller/params.rb +10 -10
- data/lib/linked_rails/active_response/controller.rb +8 -18
- data/lib/linked_rails/active_response/responders/rdf.rb +19 -10
- data/lib/linked_rails/callable_variable.rb +1 -1
- data/lib/linked_rails/collection_params_parser.rb +93 -0
- data/lib/linked_rails/controller/actionable.rb +121 -0
- data/lib/linked_rails/controller/authorization.rb +6 -0
- data/lib/linked_rails/controller/default_actions/create.rb +52 -0
- data/lib/linked_rails/controller/default_actions/destroy.rb +42 -0
- data/lib/linked_rails/controller/default_actions/update.rb +43 -0
- data/lib/linked_rails/controller/delta.rb +78 -0
- data/lib/linked_rails/controller/error_handling.rb +11 -9
- data/lib/linked_rails/controller/rendering.rb +48 -0
- data/lib/linked_rails/controller.rb +24 -4
- data/lib/linked_rails/enhanceable.rb +1 -1
- data/lib/linked_rails/enhancements/creatable/controller.rb +1 -1
- data/lib/linked_rails/enhancements/destroyable/controller.rb +1 -1
- data/lib/linked_rails/enhancements/updatable/controller.rb +1 -1
- data/lib/linked_rails/enhancements.rb +0 -16
- data/lib/linked_rails/helpers/delta_helper.rb +26 -57
- data/lib/linked_rails/helpers/ontola_actions_helper.rb +2 -2
- data/lib/linked_rails/helpers/resource_helper.rb +4 -2
- data/lib/linked_rails/iri_mapper.rb +17 -39
- data/lib/linked_rails/middleware/error_handling.rb +51 -0
- data/lib/linked_rails/middleware/linked_data_params.rb +30 -151
- data/lib/linked_rails/model/actionable.rb +68 -0
- data/lib/linked_rails/model/collections.rb +201 -39
- data/lib/linked_rails/model/dirty.rb +7 -8
- data/lib/linked_rails/model/enhancements.rb +1 -6
- data/lib/linked_rails/model/filtering.rb +4 -6
- data/lib/linked_rails/model/indexable.rb +6 -16
- data/lib/linked_rails/model/iri.rb +28 -19
- data/lib/linked_rails/model/iri_mapping.rb +37 -8
- data/lib/linked_rails/model/menuable.rb +28 -0
- data/lib/linked_rails/model/serialization.rb +2 -15
- data/lib/linked_rails/model/singularable.rb +57 -0
- data/lib/linked_rails/model/sorting.rb +0 -5
- data/lib/linked_rails/model/tables.rb +26 -0
- data/lib/linked_rails/model.rb +17 -7
- data/lib/linked_rails/params_parser.rb +133 -55
- data/lib/linked_rails/policy/attribute_conditions.rb +2 -2
- data/lib/linked_rails/policy.rb +40 -46
- data/lib/linked_rails/railtie.rb +11 -0
- data/lib/linked_rails/rdf_error.rb +2 -2
- data/lib/linked_rails/renderers.rb +1 -0
- data/lib/linked_rails/routes.rb +38 -22
- data/lib/linked_rails/serializer/actionable.rb +27 -0
- data/lib/linked_rails/serializer/menuable.rb +31 -0
- data/lib/linked_rails/serializer/singularable.rb +26 -0
- data/lib/linked_rails/serializer.rb +28 -11
- data/lib/linked_rails/test_methods.rb +114 -0
- data/lib/linked_rails/translate.rb +31 -9
- data/lib/linked_rails/types/iri_type.rb +37 -0
- data/lib/linked_rails/uri_template.rb +30 -0
- data/lib/linked_rails/version.rb +1 -1
- data/lib/linked_rails/vocab.rb +9 -0
- data/lib/linked_rails.rb +30 -13
- data/lib/rails/welcome_controller.rb +3 -2
- data/lib/rdf/list.rb +9 -0
- data/lib/rdf/query_fix.rb +15 -0
- metadata +58 -33
- data/app/models/linked_rails/actions/default_actions/create.rb +0 -60
- data/app/models/linked_rails/actions/default_actions/destroy.rb +0 -45
- data/app/models/linked_rails/actions/default_actions/update.rb +0 -50
- data/app/models/linked_rails/actions/default_actions.rb +0 -17
- data/lib/generators/linked_rails/install/templates/application_action_list.rb +0 -3
- data/lib/generators/linked_rails/model/templates/action_list.rb.tt +0 -6
- data/lib/linked_rails/enhancements/actionable/model.rb +0 -71
- data/lib/linked_rails/enhancements/actionable/serializer.rb +0 -25
- data/lib/linked_rails/enhancements/creatable/action.rb +0 -15
- data/lib/linked_rails/enhancements/destroyable/action.rb +0 -15
- data/lib/linked_rails/enhancements/destroyable/routing.rb +0 -19
- data/lib/linked_rails/enhancements/menuable/model.rb +0 -36
- data/lib/linked_rails/enhancements/menuable/serializer.rb +0 -33
- data/lib/linked_rails/enhancements/route_concerns.rb +0 -56
- data/lib/linked_rails/enhancements/singularable/controller.rb +0 -43
- data/lib/linked_rails/enhancements/singularable/model.rb +0 -47
- data/lib/linked_rails/enhancements/singularable/serializer.rb +0 -28
- data/lib/linked_rails/enhancements/tableable/model.rb +0 -28
- data/lib/linked_rails/enhancements/updatable/action.rb +0 -15
- data/lib/linked_rails/enhancements/updatable/routing.rb +0 -20
@@ -1,22 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative 'enhancements/route_concerns'
|
4
|
-
|
5
|
-
require_relative 'enhancements/actionable/model'
|
6
|
-
require_relative 'enhancements/actionable/serializer'
|
7
|
-
require_relative 'enhancements/creatable/action'
|
8
3
|
require_relative 'enhancements/creatable/controller'
|
9
|
-
require_relative 'enhancements/destroyable/action'
|
10
4
|
require_relative 'enhancements/destroyable/controller'
|
11
|
-
require_relative 'enhancements/destroyable/routing'
|
12
|
-
require_relative 'enhancements/indexable/model'
|
13
|
-
require_relative 'enhancements/menuable/model'
|
14
|
-
require_relative 'enhancements/menuable/serializer'
|
15
|
-
require_relative 'enhancements/singularable/controller'
|
16
|
-
require_relative 'enhancements/singularable/model'
|
17
|
-
require_relative 'enhancements/singularable/serializer'
|
18
|
-
require_relative 'enhancements/tableable/model'
|
19
|
-
require_relative 'enhancements/updatable/action'
|
20
5
|
require_relative 'enhancements/updatable/controller'
|
21
|
-
require_relative 'enhancements/updatable/routing'
|
22
6
|
require_relative 'enhancements/updatable/serializer'
|
@@ -3,61 +3,8 @@
|
|
3
3
|
module LinkedRails
|
4
4
|
module Helpers
|
5
5
|
module DeltaHelper
|
6
|
-
def changed_relation_triples(predicate, destructed, resources)
|
7
|
-
related_resource_invalidations = resources.map(&method(:invalidate_resource_delta))
|
8
|
-
|
9
|
-
return related_resource_invalidations unless predicate
|
10
|
-
|
11
|
-
if destructed
|
12
|
-
return related_resource_invalidations + [
|
13
|
-
[current_resource.iri, predicate, Vocab.sp[:Variable], delta_iri(:remove)]
|
14
|
-
]
|
15
|
-
end
|
16
|
-
|
17
|
-
related_resource_invalidations + resources.map do |resource|
|
18
|
-
[current_resource.iri, predicate, resource.iri, delta_iri(:replace)]
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def changed_relations_triples # rubocop:disable Metrics/AbcSize
|
23
|
-
current_resource.previously_changed_relations.flat_map do |key, value|
|
24
|
-
if key.to_s.ends_with?('_collection')
|
25
|
-
[invalidate_collection_delta(current_resource.send(key))]
|
26
|
-
else
|
27
|
-
destructed = current_resource.send(:association_has_destructed?, key)
|
28
|
-
many = value.relationship_type == :has_many
|
29
|
-
relation = current_resource.send(key)
|
30
|
-
changed_relation_triples(value.predicate, destructed, many ? relation : [relation])
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def change_triple(predicate, value)
|
36
|
-
if value.nil?
|
37
|
-
RDF::Statement.new(current_resource.iri, predicate, Vocab.sp[:Variable], graph_name: delta_iri(:remove))
|
38
|
-
else
|
39
|
-
RDF::Statement.new(current_resource.iri, predicate, value, graph_name: delta_iri(:replace))
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
def changes_triples # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
44
|
-
serializer = RDF::Serializers.serializer_for(current_resource).new(current_resource)
|
45
|
-
|
46
|
-
current_resource.previous_changes_by_predicate.map do |predicate, (_old_value, _new_value)|
|
47
|
-
serializer_attributes = current_resource.class.predicate_mapping[predicate]
|
48
|
-
next if serializer_attributes.is_a?(FastJsonapi::Relationship)
|
49
|
-
|
50
|
-
attr_name = serializer_attributes.key
|
51
|
-
serialized_value =
|
52
|
-
serializer.class.attributes_to_serialize[attr_name]&.serialize(current_resource, serializer_params, {})
|
53
|
-
(serialized_value.is_a?(Array) ? serialized_value : [serialized_value]).map do |value|
|
54
|
-
change_triple(predicate, value)
|
55
|
-
end
|
56
|
-
end.compact.flatten
|
57
|
-
end
|
58
|
-
|
59
6
|
def delta_iri(delta)
|
60
|
-
%i[remove replace invalidate].include?(delta) ? Vocab.ontola[delta] : Vocab.
|
7
|
+
%i[remove replace invalidate].include?(delta) ? Vocab.ontola[delta] : Vocab.ld[delta]
|
61
8
|
end
|
62
9
|
|
63
10
|
def invalidate_collection_delta(collection)
|
@@ -65,7 +12,9 @@ module LinkedRails
|
|
65
12
|
end
|
66
13
|
|
67
14
|
def invalidate_parent_collections_delta(resource)
|
68
|
-
|
15
|
+
context = user_context if respond_to?(:user_context, true)
|
16
|
+
|
17
|
+
resource&.parent_collections(context)&.map(&method(:invalidate_collection_delta)) || []
|
69
18
|
end
|
70
19
|
|
71
20
|
def invalidate_resource_delta(resource)
|
@@ -75,11 +24,31 @@ module LinkedRails
|
|
75
24
|
end
|
76
25
|
|
77
26
|
def resource_added_delta(resource)
|
78
|
-
invalidate_parent_collections_delta(resource)
|
27
|
+
invalidate_parent_collections_delta(resource) + singular_added_delta(resource)
|
79
28
|
end
|
80
29
|
|
81
30
|
def resource_removed_delta(resource)
|
82
|
-
invalidate_parent_collections_delta(resource)
|
31
|
+
invalidate_parent_collections_delta(resource) + singular_removed_delta(resource)
|
32
|
+
end
|
33
|
+
|
34
|
+
def same_as_statement(from, to)
|
35
|
+
[
|
36
|
+
from,
|
37
|
+
Vocab.owl.sameAs,
|
38
|
+
to
|
39
|
+
]
|
40
|
+
end
|
41
|
+
|
42
|
+
def singular_added_delta(resource)
|
43
|
+
return [] unless resource.try(:singular_resource?)
|
44
|
+
|
45
|
+
[same_as_statement(resource.singular_iri, resource.iri)]
|
46
|
+
end
|
47
|
+
|
48
|
+
def singular_removed_delta(resource)
|
49
|
+
return [] unless resource.try(:singular_resource?)
|
50
|
+
|
51
|
+
[invalidate_resource_delta(resource.singular_iri)]
|
83
52
|
end
|
84
53
|
end
|
85
54
|
end
|
@@ -12,8 +12,8 @@ module LinkedRails
|
|
12
12
|
Vocab.libro["actions/copyToClipboard?#{{value: value}.to_param}"]
|
13
13
|
end
|
14
14
|
|
15
|
-
def ontola_dialog_action(resource, opener: nil)
|
16
|
-
Vocab.libro["actions/dialog/alert?#{{resource: resource, opener: opener}.compact.to_param}"]
|
15
|
+
def ontola_dialog_action(resource, opener: nil, size: nil)
|
16
|
+
Vocab.libro["actions/dialog/alert?#{{resource: resource, opener: opener, size: size}.compact.to_param}"]
|
17
17
|
end
|
18
18
|
|
19
19
|
def ontola_dialog_close_action
|
@@ -41,7 +41,7 @@ module LinkedRails
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def requested_resource_parent
|
44
|
-
requested_resource
|
44
|
+
requested_resource.try(:parent)
|
45
45
|
end
|
46
46
|
|
47
47
|
private
|
@@ -51,6 +51,8 @@ module LinkedRails
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def new_resource_from_parent
|
54
|
+
return requested_resource.child_resource if requested_resource.is_a?(Collection)
|
55
|
+
|
54
56
|
parent_resource.build_child(
|
55
57
|
controller_class,
|
56
58
|
user_context: user_context
|
@@ -59,7 +61,7 @@ module LinkedRails
|
|
59
61
|
|
60
62
|
def resolve_current_resource
|
61
63
|
case action_name
|
62
|
-
when 'create'
|
64
|
+
when 'create'
|
63
65
|
new_resource
|
64
66
|
else
|
65
67
|
requested_resource
|
@@ -12,6 +12,7 @@ module LinkedRails
|
|
12
12
|
# => {
|
13
13
|
# action: 'show',
|
14
14
|
# class: Resource,
|
15
|
+
# iri: 'https://example.com/resource/1?foo=bar',
|
15
16
|
# params: {
|
16
17
|
# id: '1',
|
17
18
|
# foo: 'bar'
|
@@ -22,6 +23,7 @@ module LinkedRails
|
|
22
23
|
# => {
|
23
24
|
# action: 'index',
|
24
25
|
# class: Resource,
|
26
|
+
# iri: 'https://example.com/resources?foo=bar',
|
25
27
|
# params: {
|
26
28
|
# foo: 'bar'
|
27
29
|
# }
|
@@ -35,38 +37,21 @@ module LinkedRails
|
|
35
37
|
query = Rack::Utils.parse_nested_query(URI(iri.to_s).query)
|
36
38
|
params = Rails.application.routes.recognize_path(iri.to_s, method: method)
|
37
39
|
|
38
|
-
route_params_to_opts(params.merge(query))
|
40
|
+
route_params_to_opts(params.merge(query), iri.to_s)
|
39
41
|
rescue ActionController::RoutingError
|
40
42
|
EMPTY_IRI_OPTS.dup
|
41
43
|
end
|
42
44
|
|
43
|
-
def index_resource_from_iri(iri, user_context)
|
44
|
-
ensure_absolute_iri!(iri)
|
45
|
-
|
46
|
-
opts = opts_from_iri(iri)
|
47
|
-
index_resource_from_opts(opts, user_context)
|
48
|
-
end
|
49
|
-
|
50
|
-
def index_resource_from_iri!(iri, user_context)
|
51
|
-
index_resource_from_iri(iri, user_context) || raise(ActiveRecord::RecordNotFound)
|
52
|
-
end
|
53
|
-
|
54
|
-
def index_resource_from_opts(opts, user_context)
|
55
|
-
opts[:class]&.requested_index_resource(opts[:params], user_context) if opts[:action] == 'index'
|
56
|
-
end
|
57
|
-
|
58
45
|
def parent_from_params(params, user_context)
|
59
46
|
return unless params.key?(:parent_iri)
|
60
47
|
|
61
|
-
parent_iri = "/#{params[:parent_iri]}"
|
62
|
-
opts = LinkedRails.iri_mapper.opts_from_iri(parent_iri)
|
63
|
-
opts[:params] = params.except(:parent_iri, :singular_route).merge(opts[:params])
|
48
|
+
parent_iri = LinkedRails.iri(path: "/#{params[:parent_iri]}")
|
64
49
|
|
65
|
-
LinkedRails.iri_mapper.
|
50
|
+
LinkedRails.iri_mapper.resource_from_iri(parent_iri, user_context)
|
66
51
|
end
|
67
52
|
|
68
53
|
def resource_from_iri(iri, user_context)
|
69
|
-
|
54
|
+
return nil unless absolute_iri?(iri)
|
70
55
|
|
71
56
|
opts = opts_from_iri(iri)
|
72
57
|
resource_from_opts(opts, user_context)
|
@@ -80,7 +65,7 @@ module LinkedRails
|
|
80
65
|
opts[:class]&.requested_resource(opts, user_context)
|
81
66
|
end
|
82
67
|
|
83
|
-
def route_params_to_opts(params)
|
68
|
+
def route_params_to_opts(params, iri)
|
84
69
|
controller_class = class_for_controller(params[:controller])
|
85
70
|
|
86
71
|
return EMPTY_IRI_OPTS.dup if controller_class.blank?
|
@@ -88,27 +73,17 @@ module LinkedRails
|
|
88
73
|
{
|
89
74
|
action: params[:action],
|
90
75
|
class: controller_class,
|
91
|
-
|
76
|
+
iri: iri,
|
77
|
+
params: sanitized_route_params(controller_class, params)
|
92
78
|
}.with_indifferent_access
|
93
79
|
end
|
94
80
|
|
95
|
-
|
96
|
-
ensure_absolute_iri!(iri)
|
97
|
-
|
98
|
-
opts = opts_from_iri(iri)
|
99
|
-
single_resource_from_opts(opts, user_context)
|
100
|
-
end
|
101
|
-
|
102
|
-
def single_resource_from_iri!(iri, user_context)
|
103
|
-
single_resource_from_iri(iri, user_context) || raise(ActiveRecord::RecordNotFound)
|
104
|
-
end
|
81
|
+
private
|
105
82
|
|
106
|
-
def
|
107
|
-
|
83
|
+
def absolute_iri?(iri)
|
84
|
+
iri.present? && !URI(iri).relative?
|
108
85
|
end
|
109
86
|
|
110
|
-
private
|
111
|
-
|
112
87
|
def class_for_controller(controller)
|
113
88
|
return if controller.blank?
|
114
89
|
|
@@ -117,8 +92,11 @@ module LinkedRails
|
|
117
92
|
.try(:controller_class)
|
118
93
|
end
|
119
94
|
|
120
|
-
def
|
121
|
-
|
95
|
+
def sanitized_route_params(controller_class, params)
|
96
|
+
new_params = params.except(:action, :controller)
|
97
|
+
nested_key = :"#{controller_class.name.tableize.singularize}_id"
|
98
|
+
new_params[:id] ||= new_params.delete(nested_key) if new_params.key?(nested_key)
|
99
|
+
new_params
|
122
100
|
end
|
123
101
|
end
|
124
102
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module LinkedRails
|
4
|
+
module Middleware
|
5
|
+
class ErrorHandling
|
6
|
+
def initialize(app)
|
7
|
+
@app = app
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
@app.call(env)
|
12
|
+
rescue StandardError => e
|
13
|
+
handle_error(env, e)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def controller_class(req)
|
19
|
+
controller = req.controller_class
|
20
|
+
|
21
|
+
return ApplicationController if controller == ActionDispatch::Request::PASS_NOT_FOUND
|
22
|
+
|
23
|
+
controller
|
24
|
+
rescue StandardError
|
25
|
+
ApplicationController
|
26
|
+
end
|
27
|
+
|
28
|
+
def controller_instance(env)
|
29
|
+
return env['action_controller.instance'] if env['action_controller.instance']
|
30
|
+
|
31
|
+
req = ActionDispatch::Request.new(env)
|
32
|
+
res = ApplicationController.make_response!(req)
|
33
|
+
controller = controller_class(req).new
|
34
|
+
controller.set_request!(req)
|
35
|
+
controller.set_response!(res)
|
36
|
+
|
37
|
+
controller
|
38
|
+
end
|
39
|
+
|
40
|
+
def handle_error(env, error)
|
41
|
+
controller = controller_instance(env)
|
42
|
+
|
43
|
+
controller.send(:handle_and_report_error, error)
|
44
|
+
error_response = controller.response
|
45
|
+
body = ActionDispatch::Response::RackBody.new(error_response)
|
46
|
+
|
47
|
+
[error_response.status, error_response.headers, body]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -1,8 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# require 'empathy/emp_json'
|
4
|
+
|
3
5
|
module LinkedRails
|
4
6
|
module Middleware
|
5
|
-
class LinkedDataParams
|
7
|
+
class LinkedDataParams
|
8
|
+
include ::Empathy::EmpJson::Helpers::Slices
|
9
|
+
include ::Empathy::EmpJson::Helpers::Primitives
|
10
|
+
|
6
11
|
def initialize(app)
|
7
12
|
@app = app
|
8
13
|
end
|
@@ -10,40 +15,15 @@ module LinkedRails
|
|
10
15
|
def call(env)
|
11
16
|
req = Rack::Request.new(env)
|
12
17
|
params_from_query(req)
|
13
|
-
|
18
|
+
params_from_slice(req)
|
14
19
|
|
15
20
|
@app.call(env)
|
16
21
|
end
|
17
22
|
|
18
23
|
private
|
19
24
|
|
20
|
-
def
|
21
|
-
|
22
|
-
when nil
|
23
|
-
hash[key] = value
|
24
|
-
when Hash
|
25
|
-
hash[key].merge!(value)
|
26
|
-
when Array
|
27
|
-
hash[key].append(value)
|
28
|
-
else
|
29
|
-
hash[key] = [hash[key], value]
|
30
|
-
end
|
31
|
-
hash
|
32
|
-
end
|
33
|
-
|
34
|
-
def associated_class_from_params(reflection, graph, subject)
|
35
|
-
return reflection.klass unless reflection.polymorphic?
|
36
|
-
|
37
|
-
query = graph.query(subject: subject, predicate: Vocab.rdfv[:type])
|
38
|
-
if query.empty?
|
39
|
-
raise("No type given for '#{subject}' referenced by polymorphic association '#{reflection.name}'")
|
40
|
-
end
|
41
|
-
|
42
|
-
iri_to_class(query.first.object)
|
43
|
-
end
|
44
|
-
|
45
|
-
def blob_attribute(base_params, value)
|
46
|
-
base_params["<#{value}>"] if value.starts_with?(Vocab.ll['blobs/'])
|
25
|
+
def add_param_from_query(data, target_class, key, value)
|
26
|
+
data[target_class.predicate_mapping[RDF::URI(key)].key] = value
|
47
27
|
end
|
48
28
|
|
49
29
|
def convert_query_params(request, target_class)
|
@@ -56,76 +36,32 @@ module LinkedRails
|
|
56
36
|
request.update_param(class_key, data) if data.present?
|
57
37
|
end
|
58
38
|
|
59
|
-
def
|
60
|
-
|
61
|
-
end
|
62
|
-
|
63
|
-
def enum_attribute(klass, key, value)
|
64
|
-
opts = RDF::Serializers.serializer_for(klass).try(:enum_options, key)
|
65
|
-
return if opts.blank?
|
66
|
-
|
67
|
-
opts.detect { |_k, options| options.iri == value }&.first&.to_s
|
68
|
-
end
|
69
|
-
|
70
|
-
def graph_from_request(request)
|
71
|
-
request_graph = request.delete_param("<#{Vocab.ll[:graph].value}>")
|
72
|
-
return if request_graph.blank?
|
39
|
+
def slice_from_request(request)
|
40
|
+
return unless request.content_type == Mime::Type.lookup_by_extension(:empjson).to_s
|
73
41
|
|
74
|
-
|
75
|
-
request_graph[:tempfile].path,
|
76
|
-
content_type: request_graph[:type],
|
77
|
-
canonicalize: true,
|
78
|
-
intern: false
|
79
|
-
)
|
80
|
-
end
|
81
|
-
|
82
|
-
def iri_to_class(iri)
|
83
|
-
iri.to_s.split(LinkedRails.app_vocab.to_s).pop&.classify&.safe_constantize ||
|
84
|
-
ApplicationRecord.descendants.detect { |klass| klass.iri == iri }
|
85
|
-
end
|
86
|
-
|
87
|
-
def logger
|
88
|
-
Rails.logger
|
89
|
-
end
|
42
|
+
body = request.body.read
|
90
43
|
|
91
|
-
|
92
|
-
nested_resources =
|
93
|
-
if graph.query([subject, Vocab.rdfv[:first], nil]).present?
|
94
|
-
nested_attributes_from_list(base_params, graph, subject, klass)
|
95
|
-
else
|
96
|
-
parsed = parse_nested_resource(base_params, graph, subject, klass)
|
97
|
-
collection ? {rand(1_000_000_000).to_s => parsed} : parsed
|
98
|
-
end
|
99
|
-
["#{association}_attributes", nested_resources]
|
44
|
+
JSON.parse(body) if body.present?
|
100
45
|
end
|
101
46
|
|
102
|
-
|
103
|
-
Hash[
|
104
|
-
RDF::List.new(subject: subject, graph: graph)
|
105
|
-
.map { |nested| [rand(1_000_000_000).to_s, parse_nested_resource(base_params, graph, nested, klass)] }
|
106
|
-
]
|
107
|
-
end
|
108
|
-
|
109
|
-
# Converts a serialized graph from a multipart request body to a nested
|
110
|
-
# attributes hash.
|
47
|
+
# Converts a emp slice from to a nested attributes hash.
|
111
48
|
#
|
112
|
-
# The
|
113
|
-
# The entrypoint for the graph is the `ll:targetResource` subject, which is
|
49
|
+
# The entrypoint for the slice is the `.` subject, which is
|
114
50
|
# assumed to be the resource intended to be targeted by the request (i.e. the
|
115
51
|
# resource to be created, updated, or deleted).
|
116
52
|
#
|
117
53
|
# @return [Hash] A hash of attributes, empty if no statements were given.
|
118
|
-
def
|
119
|
-
|
54
|
+
def params_from_slice(request)
|
55
|
+
slice = slice_from_request(request)
|
120
56
|
|
121
|
-
return unless
|
57
|
+
return unless slice
|
122
58
|
|
123
|
-
request.
|
59
|
+
request.env['emp_json'] = slice
|
124
60
|
target_class = target_class_from_path(request)
|
125
61
|
return if target_class.blank?
|
126
62
|
|
127
|
-
update_actor_param(request,
|
128
|
-
update_target_params(request,
|
63
|
+
update_actor_param(request, slice)
|
64
|
+
update_target_params(request, slice, target_class)
|
129
65
|
end
|
130
66
|
|
131
67
|
def params_from_query(request)
|
@@ -135,87 +71,30 @@ module LinkedRails
|
|
135
71
|
convert_query_params(request, target_class)
|
136
72
|
end
|
137
73
|
|
138
|
-
def parse_nested_resource(base_params, graph, subject, klass)
|
139
|
-
resource = parse_resource(base_params, graph, subject, klass)
|
140
|
-
resource[:id] ||= LinkedRails.iri_mapper.opts_from_iri(subject)[:params][:id] if subject.iri?
|
141
|
-
resource
|
142
|
-
end
|
143
|
-
|
144
|
-
# Recursively parses a resource from graph
|
145
|
-
def parse_resource(base_params, graph, subject, klass)
|
146
|
-
graph
|
147
|
-
.query([subject])
|
148
|
-
.map { |statement| parse_statement(base_params, graph, statement, klass) }
|
149
|
-
.compact
|
150
|
-
.reduce({}) { |h, (k, v)| add_param(h, k, v) }
|
151
|
-
end
|
152
|
-
|
153
|
-
def parse_statement(base_params, graph, statement, klass)
|
154
|
-
field = serializer_field(klass, statement.predicate)
|
155
|
-
if field.is_a?(FastJsonapi::Attribute)
|
156
|
-
parsed_attribute(base_params, klass, field.key, statement.object.value)
|
157
|
-
elsif field.is_a?(FastJsonapi::Relationship)
|
158
|
-
parsed_association(base_params, graph, statement.object, klass, field.association || field.key)
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
def parsed_association(base_params, graph, object, klass, association)
|
163
|
-
reflection = klass.reflect_on_association(association) || raise("#{association} not found for #{klass}")
|
164
|
-
|
165
|
-
if graph.has_subject?(object)
|
166
|
-
association_klass = associated_class_from_params(reflection, graph, object)
|
167
|
-
nested_attributes(base_params, graph, object, association_klass, association, reflection.collection?)
|
168
|
-
elsif object.iri?
|
169
|
-
attributes_from_iri(object, association, reflection)
|
170
|
-
end
|
171
|
-
end
|
172
|
-
|
173
|
-
def attributes_from_iri(object, association, reflection)
|
174
|
-
if reflection.options[:through]
|
175
|
-
key = reflection.has_one? ? "#{association}_id" : "#{association.to_s.singularize}_ids"
|
176
|
-
elsif reflection.belongs_to?
|
177
|
-
key = reflection.foreign_key
|
178
|
-
end
|
179
|
-
return unless key
|
180
|
-
|
181
|
-
resource = LinkedRails.iri_mapper.resource_from_iri(object, nil)
|
182
|
-
value = resource&.send(reflection.association_primary_key)
|
183
|
-
|
184
|
-
[key, value] if value
|
185
|
-
end
|
186
|
-
|
187
|
-
def parsed_attribute(base_params, klass, key, value)
|
188
|
-
[key, blob_attribute(base_params, value) || enum_attribute(klass, key, value) || value]
|
189
|
-
end
|
190
|
-
|
191
|
-
def serializer_field(klass, predicate)
|
192
|
-
field = klass.try(:predicate_mapping).try(:[], predicate)
|
193
|
-
logger.info("#{predicate} not found for #{klass || 'nil'}") if field.blank?
|
194
|
-
field
|
195
|
-
end
|
196
|
-
|
197
74
|
def target_class_from_path(request) # rubocop:disable Metrics/AbcSize
|
198
75
|
opts = LinkedRails.iri_mapper.opts_from_iri(
|
199
76
|
request.base_url + request.env['REQUEST_URI'],
|
200
77
|
method: request.request_method
|
201
78
|
)
|
202
79
|
|
203
|
-
logger.info("No class found for #{request.base_url + request.env['REQUEST_URI']}") unless opts[:class]
|
80
|
+
Rails.logger.info("No class found for #{request.base_url + request.env['REQUEST_URI']}") unless opts[:class]
|
204
81
|
|
205
82
|
opts[:class]
|
206
83
|
end
|
207
84
|
|
208
|
-
def update_actor_param(request,
|
209
|
-
actor =
|
85
|
+
def update_actor_param(request, slice)
|
86
|
+
actor = values_from_slice(slice, '.', Vocab.schema.creator)
|
87
|
+
|
210
88
|
return if actor.blank?
|
211
89
|
|
212
|
-
request.update_param(:actor_iri, actor
|
213
|
-
graph.delete(actor)
|
90
|
+
request.update_param(:actor_iri, emp_to_primitive(actor))
|
214
91
|
end
|
215
92
|
|
216
|
-
def update_target_params(request,
|
93
|
+
def update_target_params(request, slice, target_class)
|
217
94
|
key = target_class.to_s.demodulize.underscore
|
218
|
-
|
95
|
+
|
96
|
+
parser = ParamsParser.new(slice: slice, params: request.params)
|
97
|
+
from_body = parser.parse_resource('.', target_class)
|
219
98
|
|
220
99
|
request.update_param(key, from_body.merge(request.params[key] || {}))
|
221
100
|
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module LinkedRails
|
4
|
+
module Model
|
5
|
+
module Actionable
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
def actions(user_context = nil)
|
9
|
+
action_list(user_context).actions
|
10
|
+
end
|
11
|
+
|
12
|
+
def action(tag, user_context = nil)
|
13
|
+
actions(user_context).find { |a| a.tag == tag }
|
14
|
+
end
|
15
|
+
|
16
|
+
def action_list(user_context)
|
17
|
+
@action_list ||= {}
|
18
|
+
@action_list[user_context] ||= self.class.action_list.new(resource: self, user_context: user_context)
|
19
|
+
end
|
20
|
+
|
21
|
+
def action_triples
|
22
|
+
@action_triples ||= triples_for_actions(actions) + triples_for_actions(collection_actions)
|
23
|
+
end
|
24
|
+
|
25
|
+
def collection_actions
|
26
|
+
(try(:collections) || []).map do |opts|
|
27
|
+
collection_for(opts[:name]).actions
|
28
|
+
end.flatten
|
29
|
+
end
|
30
|
+
|
31
|
+
def favorite_actions
|
32
|
+
actions.filter(&:favorite)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def triples_for_actions(actions)
|
38
|
+
actions.flat_map do |action|
|
39
|
+
[
|
40
|
+
[iri, action.predicate, action.iri],
|
41
|
+
[iri, Vocab.schema.potentialAction, action.iri]
|
42
|
+
]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
module ClassMethods
|
47
|
+
def action_list
|
48
|
+
@action_list ||= define_action_list
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def action_superclass
|
54
|
+
superclass.try(:action_list) || LinkedRails.action_list_parent_class
|
55
|
+
end
|
56
|
+
|
57
|
+
def define_action_list
|
58
|
+
klass = Class.new(action_superclass)
|
59
|
+
actionable_class = self
|
60
|
+
klass.define_singleton_method(:actionable_class) do
|
61
|
+
actionable_class
|
62
|
+
end
|
63
|
+
klass
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|