linked_rails 0.0.1
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 +7 -0
- data/LICENSE +674 -0
- data/README.md +65 -0
- data/Rakefile +34 -0
- data/app/controllers/linked_rails/actions/items_controller.rb +9 -0
- data/app/controllers/linked_rails/bulk_controller.rb +195 -0
- data/app/controllers/linked_rails/current_user_controller.rb +13 -0
- data/app/controllers/linked_rails/enum_values_controller.rb +49 -0
- data/app/controllers/linked_rails/forms_controller.rb +13 -0
- data/app/controllers/linked_rails/manifests_controller.rb +21 -0
- data/app/controllers/linked_rails/menus/items_controller.rb +9 -0
- data/app/controllers/linked_rails/menus/lists_controller.rb +9 -0
- data/app/controllers/linked_rails/not_found_controller.rb +15 -0
- data/app/controllers/linked_rails/ontologies_controller.rb +7 -0
- data/app/models/linked_rails/actions/default_actions/create.rb +60 -0
- data/app/models/linked_rails/actions/default_actions/destroy.rb +45 -0
- data/app/models/linked_rails/actions/default_actions/update.rb +50 -0
- data/app/models/linked_rails/actions/default_actions.rb +17 -0
- data/app/models/linked_rails/actions/item.rb +234 -0
- data/app/models/linked_rails/actions/list.rb +113 -0
- data/app/models/linked_rails/collection/filter.rb +16 -0
- data/app/models/linked_rails/collection/filter_field.rb +30 -0
- data/app/models/linked_rails/collection/filter_option.rb +17 -0
- data/app/models/linked_rails/collection/filterable.rb +92 -0
- data/app/models/linked_rails/collection/infinite_view.rb +98 -0
- data/app/models/linked_rails/collection/iri.rb +74 -0
- data/app/models/linked_rails/collection/iri_mapping.rb +33 -0
- data/app/models/linked_rails/collection/paginated_view.rb +41 -0
- data/app/models/linked_rails/collection/sortable.rb +60 -0
- data/app/models/linked_rails/collection/sorting.rb +72 -0
- data/app/models/linked_rails/collection/view.rb +101 -0
- data/app/models/linked_rails/collection.rb +220 -0
- data/app/models/linked_rails/condition.rb +7 -0
- data/app/models/linked_rails/creative_work.rb +21 -0
- data/app/models/linked_rails/current_user.rb +28 -0
- data/app/models/linked_rails/entry_point.rb +53 -0
- data/app/models/linked_rails/enum_value.rb +33 -0
- data/app/models/linked_rails/form/field/association_input.rb +23 -0
- data/app/models/linked_rails/form/field/checkbox_group.rb +10 -0
- data/app/models/linked_rails/form/field/checkbox_input.rb +10 -0
- data/app/models/linked_rails/form/field/color_input.rb +17 -0
- data/app/models/linked_rails/form/field/date_input.rb +10 -0
- data/app/models/linked_rails/form/field/date_time_input.rb +10 -0
- data/app/models/linked_rails/form/field/email_input.rb +10 -0
- data/app/models/linked_rails/form/field/file_input.rb +10 -0
- data/app/models/linked_rails/form/field/location_input.rb +11 -0
- data/app/models/linked_rails/form/field/markdown_input.rb +10 -0
- data/app/models/linked_rails/form/field/number_input.rb +10 -0
- data/app/models/linked_rails/form/field/password_input.rb +10 -0
- data/app/models/linked_rails/form/field/postal_range_input.rb +10 -0
- data/app/models/linked_rails/form/field/radio_group.rb +10 -0
- data/app/models/linked_rails/form/field/resource_field.rb +23 -0
- data/app/models/linked_rails/form/field/select_input.rb +11 -0
- data/app/models/linked_rails/form/field/slider_input.rb +10 -0
- data/app/models/linked_rails/form/field/text_area_input.rb +10 -0
- data/app/models/linked_rails/form/field/text_input.rb +10 -0
- data/app/models/linked_rails/form/field/toggle_button_group.rb +10 -0
- data/app/models/linked_rails/form/field.rb +117 -0
- data/app/models/linked_rails/form/field_factory.rb +219 -0
- data/app/models/linked_rails/form/group.rb +39 -0
- data/app/models/linked_rails/form/page.rb +31 -0
- data/app/models/linked_rails/form.rb +156 -0
- data/app/models/linked_rails/manifest.rb +102 -0
- data/app/models/linked_rails/media_object.rb +31 -0
- data/app/models/linked_rails/menus/item.rb +92 -0
- data/app/models/linked_rails/menus/list.rb +138 -0
- data/app/models/linked_rails/ontology/base.rb +50 -0
- data/app/models/linked_rails/ontology/class.rb +43 -0
- data/app/models/linked_rails/ontology/property.rb +19 -0
- data/app/models/linked_rails/ontology.rb +34 -0
- data/app/models/linked_rails/property_query.rb +11 -0
- data/app/models/linked_rails/resource.rb +17 -0
- data/app/models/linked_rails/sequence.rb +64 -0
- data/app/models/linked_rails/shacl/node_shape.rb +21 -0
- data/app/models/linked_rails/shacl/property_shape.rb +53 -0
- data/app/models/linked_rails/shacl/shape.rb +33 -0
- data/app/models/linked_rails/web_page.rb +22 -0
- data/app/models/linked_rails/web_site.rb +17 -0
- data/app/models/linked_rails/widget.rb +55 -0
- data/app/policies/linked_rails/actions/item_policy.rb +11 -0
- data/app/policies/linked_rails/actions/list_policy.rb +11 -0
- data/app/policies/linked_rails/collection/view_policy.rb +13 -0
- data/app/policies/linked_rails/collection_policy.rb +41 -0
- data/app/policies/linked_rails/enum_value_policy.rb +32 -0
- data/app/policies/linked_rails/menus/item_policy.rb +11 -0
- data/app/policies/linked_rails/menus/list_policy.rb +11 -0
- data/app/policies/linked_rails/sequence_policy.rb +9 -0
- data/app/serializers/linked_rails/actions/item_serializer.rb +28 -0
- data/app/serializers/linked_rails/collection/filter_field_serializer.rb +12 -0
- data/app/serializers/linked_rails/collection/filter_option_serializer.rb +12 -0
- data/app/serializers/linked_rails/collection/filter_serializer.rb +13 -0
- data/app/serializers/linked_rails/collection/sorting_serializer.rb +13 -0
- data/app/serializers/linked_rails/collection/view_serializer.rb +22 -0
- data/app/serializers/linked_rails/collection_serializer.rb +44 -0
- data/app/serializers/linked_rails/condition_serializer.rb +9 -0
- data/app/serializers/linked_rails/creative_work_serializer.rb +10 -0
- data/app/serializers/linked_rails/current_user_serializer.rb +7 -0
- data/app/serializers/linked_rails/entry_point_serializer.rb +20 -0
- data/app/serializers/linked_rails/enum_value_serializer.rb +12 -0
- data/app/serializers/linked_rails/form/field/association_input_serializer.rb +13 -0
- data/app/serializers/linked_rails/form/field/resource_field_serializer.rb +11 -0
- data/app/serializers/linked_rails/form/field/select_input_serializer.rb +11 -0
- data/app/serializers/linked_rails/form/field_serializer.rb +39 -0
- data/app/serializers/linked_rails/form/group_serializer.rb +14 -0
- data/app/serializers/linked_rails/form/page_serializer.rb +13 -0
- data/app/serializers/linked_rails/form_serializer.rb +9 -0
- data/app/serializers/linked_rails/media_object_serializer.rb +17 -0
- data/app/serializers/linked_rails/menus/item_serializer.rb +35 -0
- data/app/serializers/linked_rails/menus/list_serializer.rb +13 -0
- data/app/serializers/linked_rails/ontology/class_serializer.rb +19 -0
- data/app/serializers/linked_rails/ontology/property_serializer.rb +16 -0
- data/app/serializers/linked_rails/ontology_serializer.rb +8 -0
- data/app/serializers/linked_rails/rdf_error_serializer.rb +8 -0
- data/app/serializers/linked_rails/sequence_serializer.rb +14 -0
- data/app/serializers/linked_rails/shacl/node_shape_serializer.rb +12 -0
- data/app/serializers/linked_rails/shacl/property_shape_serializer.rb +38 -0
- data/app/serializers/linked_rails/shacl/shape_serializer.rb +25 -0
- data/app/serializers/linked_rails/web_page_serializer.rb +10 -0
- data/app/serializers/linked_rails/web_site_serializer.rb +11 -0
- data/app/serializers/linked_rails/widget_serializer.rb +15 -0
- data/config/initializers/inflections.rb +5 -0
- data/lib/generators/linked_rails/install/install_generator.rb +65 -0
- data/lib/generators/linked_rails/install/templates/app_menu_list.rb +41 -0
- data/lib/generators/linked_rails/install/templates/application_action_list.rb +3 -0
- data/lib/generators/linked_rails/install/templates/application_form.rb +3 -0
- data/lib/generators/linked_rails/install/templates/application_menu_list.rb +3 -0
- data/lib/generators/linked_rails/install/templates/application_policy.rb +18 -0
- data/lib/generators/linked_rails/install/templates/application_serializer.rb +5 -0
- data/lib/generators/linked_rails/install/templates/initializer.rb +9 -0
- data/lib/generators/linked_rails/install/templates/locales.yml +12 -0
- data/lib/generators/linked_rails/install/templates/rdf_responder.rb +5 -0
- data/lib/generators/linked_rails/install/templates/rdf_serializers_initializer.rb +5 -0
- data/lib/generators/linked_rails/install/templates/vocab.rb +5 -0
- data/lib/generators/linked_rails/install/templates/vocab.yml +26 -0
- data/lib/generators/linked_rails/model/model_generator.rb +58 -0
- data/lib/generators/linked_rails/model/templates/action_list.rb.tt +6 -0
- data/lib/generators/linked_rails/model/templates/controller.rb.tt +7 -0
- data/lib/generators/linked_rails/model/templates/create_table_migration.rb.tt +24 -0
- data/lib/generators/linked_rails/model/templates/form.rb.tt +6 -0
- data/lib/generators/linked_rails/model/templates/menu_list.rb.tt +6 -0
- data/lib/generators/linked_rails/model/templates/model.rb.tt +15 -0
- data/lib/generators/linked_rails/model/templates/module.rb.tt +7 -0
- data/lib/generators/linked_rails/model/templates/policy.rb.tt +6 -0
- data/lib/generators/linked_rails/model/templates/serializer.rb.tt +9 -0
- data/lib/linked_rails/active_response/controller/collections.rb +43 -0
- data/lib/linked_rails/active_response/controller/crud_defaults.rb +92 -0
- data/lib/linked_rails/active_response/controller/params.rb +51 -0
- data/lib/linked_rails/active_response/controller.rb +37 -0
- data/lib/linked_rails/active_response/responders/rdf.rb +158 -0
- data/lib/linked_rails/cache.rb +35 -0
- data/lib/linked_rails/callable_variable.rb +25 -0
- data/lib/linked_rails/constraints/whitelist.rb +36 -0
- data/lib/linked_rails/controller/authorization.rb +15 -0
- data/lib/linked_rails/controller/error_handling.rb +76 -0
- data/lib/linked_rails/controller.rb +43 -0
- data/lib/linked_rails/engine.rb +7 -0
- data/lib/linked_rails/enhanceable.rb +21 -0
- data/lib/linked_rails/enhancements/actionable/model.rb +71 -0
- data/lib/linked_rails/enhancements/actionable/serializer.rb +25 -0
- data/lib/linked_rails/enhancements/creatable/action.rb +15 -0
- data/lib/linked_rails/enhancements/creatable/controller.rb +15 -0
- data/lib/linked_rails/enhancements/destroyable/action.rb +15 -0
- data/lib/linked_rails/enhancements/destroyable/controller.rb +15 -0
- data/lib/linked_rails/enhancements/destroyable/routing.rb +19 -0
- data/lib/linked_rails/enhancements/indexable/model.rb +10 -0
- data/lib/linked_rails/enhancements/menuable/model.rb +36 -0
- data/lib/linked_rails/enhancements/menuable/serializer.rb +33 -0
- data/lib/linked_rails/enhancements/route_concerns.rb +56 -0
- data/lib/linked_rails/enhancements/singularable/controller.rb +43 -0
- data/lib/linked_rails/enhancements/singularable/model.rb +47 -0
- data/lib/linked_rails/enhancements/singularable/serializer.rb +28 -0
- data/lib/linked_rails/enhancements/tableable/model.rb +28 -0
- data/lib/linked_rails/enhancements/updatable/action.rb +15 -0
- data/lib/linked_rails/enhancements/updatable/controller.rb +15 -0
- data/lib/linked_rails/enhancements/updatable/routing.rb +20 -0
- data/lib/linked_rails/enhancements/updatable/serializer.rb +17 -0
- data/lib/linked_rails/enhancements.rb +22 -0
- data/lib/linked_rails/helpers/delta_helper.rb +86 -0
- data/lib/linked_rails/helpers/ontola_actions_helper.rb +32 -0
- data/lib/linked_rails/helpers/resource_helper.rb +70 -0
- data/lib/linked_rails/iri_mapper.rb +125 -0
- data/lib/linked_rails/middleware/linked_data_params.rb +224 -0
- data/lib/linked_rails/model/collections.rb +82 -0
- data/lib/linked_rails/model/dirty.rb +82 -0
- data/lib/linked_rails/model/enhancements.rb +61 -0
- data/lib/linked_rails/model/filtering.rb +89 -0
- data/lib/linked_rails/model/indexable.rb +51 -0
- data/lib/linked_rails/model/iri.rb +121 -0
- data/lib/linked_rails/model/iri_mapping.rb +69 -0
- data/lib/linked_rails/model/serialization.rb +88 -0
- data/lib/linked_rails/model/sorting.rb +20 -0
- data/lib/linked_rails/model.rb +68 -0
- data/lib/linked_rails/params_parser.rb +93 -0
- data/lib/linked_rails/policy/attribute_conditions.rb +53 -0
- data/lib/linked_rails/policy.rb +189 -0
- data/lib/linked_rails/rdf_error.rb +36 -0
- data/lib/linked_rails/renderers.rb +46 -0
- data/lib/linked_rails/routes.rb +108 -0
- data/lib/linked_rails/serializer.rb +137 -0
- data/lib/linked_rails/translate.rb +176 -0
- data/lib/linked_rails/version.rb +5 -0
- data/lib/linked_rails/vocab.rb +81 -0
- data/lib/linked_rails.rb +86 -0
- data/lib/nill_class_renderer.rb +3 -0
- data/lib/rails/welcome_controller.rb +45 -0
- data/lib/tasks/linked_rails_tasks.rake +6 -0
- metadata +416 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module LinkedRails
|
|
4
|
+
module Model
|
|
5
|
+
module IriMapping
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
module ClassMethods
|
|
9
|
+
def parent_from_params(params, user_context)
|
|
10
|
+
LinkedRails.iri_mapper.parent_from_params(params, user_context)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def requested_index_resource(params, user_context)
|
|
14
|
+
if params.key?(:parent_iri)
|
|
15
|
+
collection_from_parent(index_collection_params(params, user_context))
|
|
16
|
+
else
|
|
17
|
+
root_collection(index_collection_params(params, user_context))
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def requested_index_resource!(params, user_context)
|
|
22
|
+
requested_index_resource(params, user_context) || raise(ActiveRecord::RecordNotFound)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def requested_resource(opts, user_context)
|
|
26
|
+
if collection_action?(opts)
|
|
27
|
+
requested_index_resource(opts[:params], user_context)
|
|
28
|
+
elsif singular_action?(opts)
|
|
29
|
+
resource = requested_singular_resource(opts[:params], user_context)
|
|
30
|
+
resource&.singular_resource = true
|
|
31
|
+
resource
|
|
32
|
+
else
|
|
33
|
+
requested_single_resource(opts[:params], user_context)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def requested_single_resource(params, _user_context)
|
|
38
|
+
if self < ActiveRecord::Base
|
|
39
|
+
find_by(primary_key => params[:id]) if params.key?(:id)
|
|
40
|
+
else
|
|
41
|
+
new(params)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def requested_single_resource!(params, user_context)
|
|
46
|
+
requested_single_resource(params, user_context) || raise(ActiveRecord::RecordNotFound)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def collection_action?(opts)
|
|
52
|
+
%w[index create].include?(opts[:action]) && !opts[:params][:singular_route]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def index_collection_params(params, user_context)
|
|
56
|
+
params_hash = params.is_a?(ActionController::Parameters) ? params.to_unsafe_h : params
|
|
57
|
+
|
|
58
|
+
{
|
|
59
|
+
user_context: user_context
|
|
60
|
+
}.merge(params_hash).with_indifferent_access
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def singular_action?(opts)
|
|
64
|
+
opts[:params][:singular_route]
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module LinkedRails
|
|
4
|
+
module Model
|
|
5
|
+
module Serialization
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
alias read_attribute_for_serialization send
|
|
9
|
+
|
|
10
|
+
def preview_includes
|
|
11
|
+
self.class.preview_includes
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def show_includes
|
|
15
|
+
preview_includes
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
module ClassMethods
|
|
19
|
+
def attributes_from_filters(filters)
|
|
20
|
+
filters.each_with_object({}) do |(key, value), hash|
|
|
21
|
+
next unless value.count == 1
|
|
22
|
+
|
|
23
|
+
attribute = predicate_mapping[key]&.key
|
|
24
|
+
hash[attribute] = value.first if attribute
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def include_in_collection?
|
|
29
|
+
false
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def input_select_property
|
|
33
|
+
Vocab.schema.name
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# The associations to preload when serializing multiple records
|
|
37
|
+
def includes_for_serializer
|
|
38
|
+
{}
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def predicate_for_key(key)
|
|
42
|
+
return if key.blank?
|
|
43
|
+
|
|
44
|
+
predicate_mapping.detect { |_key, value| value.key.to_sym == key.to_sym }&.first ||
|
|
45
|
+
predicate_for_key(try(:attribute_aliases)&.key(key.to_s))
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def predicate_mapping
|
|
49
|
+
@predicate_mapping ||= Hash[attribute_mapping + reflection_mapping]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# The associations to include when serializing multiple records
|
|
53
|
+
def preview_includes
|
|
54
|
+
[]
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# The associations to include when serializing one record
|
|
58
|
+
def show_includes
|
|
59
|
+
preview_includes
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def attribute_mapping
|
|
65
|
+
serializer = RDF::Serializers.serializer_for(self)
|
|
66
|
+
return [] if serializer.try(:attributes_to_serialize).blank?
|
|
67
|
+
|
|
68
|
+
serializer
|
|
69
|
+
.attributes_to_serialize
|
|
70
|
+
.values
|
|
71
|
+
.select { |attr| attr.predicate.present? }
|
|
72
|
+
.map { |attr| [attr.predicate, attr] }
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def reflection_mapping
|
|
76
|
+
serializer = RDF::Serializers.serializer_for(self)
|
|
77
|
+
return [] if serializer.try(:relationships_to_serialize).blank?
|
|
78
|
+
|
|
79
|
+
serializer
|
|
80
|
+
.relationships_to_serialize
|
|
81
|
+
.values
|
|
82
|
+
.select { |value| value.predicate.present? }
|
|
83
|
+
.map { |value| [value.predicate, value] }
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module LinkedRails
|
|
4
|
+
module Model
|
|
5
|
+
module Sorting
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
included do
|
|
9
|
+
class_attribute :default_sortings, instance_accessor: false, instance_predicate: false
|
|
10
|
+
self.default_sortings = [{key: Vocab.schema.dateCreated, direction: :desc}]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
module ClassMethods
|
|
14
|
+
def default_sort_column
|
|
15
|
+
:created_at
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'model/collections'
|
|
4
|
+
require_relative 'model/dirty'
|
|
5
|
+
require_relative 'model/enhancements'
|
|
6
|
+
require_relative 'model/filtering'
|
|
7
|
+
require_relative 'model/indexable'
|
|
8
|
+
require_relative 'model/iri'
|
|
9
|
+
require_relative 'model/iri_mapping'
|
|
10
|
+
require_relative 'model/serialization'
|
|
11
|
+
require_relative 'model/sorting'
|
|
12
|
+
|
|
13
|
+
module LinkedRails
|
|
14
|
+
module Model
|
|
15
|
+
extend ActiveSupport::Concern
|
|
16
|
+
include Collections
|
|
17
|
+
include Dirty
|
|
18
|
+
include Enhancements
|
|
19
|
+
include Filtering
|
|
20
|
+
include Indexable
|
|
21
|
+
include Iri
|
|
22
|
+
include IriMapping
|
|
23
|
+
include Serialization
|
|
24
|
+
include Sorting
|
|
25
|
+
|
|
26
|
+
def build_child(klass, user_context: nil)
|
|
27
|
+
klass.build_new(parent: self, user_context: user_context)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def singular_resource?
|
|
31
|
+
false
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
module ClassMethods
|
|
35
|
+
def build_new(parent: nil, user_context: nil)
|
|
36
|
+
new(attributes_for_new(parent: parent, user_context: user_context))
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def form_class
|
|
40
|
+
@form_class ||= "#{name}Form".safe_constantize || superclass.try(:form_class)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def label
|
|
44
|
+
obj = iri.is_a?(Array) ? iri.first : iri
|
|
45
|
+
LinkedRails.translate(:class, :label, obj) if obj
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def plural_label
|
|
49
|
+
obj = iri.is_a?(Array) ? iri.first : iri
|
|
50
|
+
LinkedRails.translate(:class, :plural_label, obj) if obj
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def policy_class
|
|
54
|
+
@policy_class ||= "#{name}Policy".safe_constantize || superclass.try(:policy_class)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def attribute_from_filter(filter, predicate)
|
|
60
|
+
filter[predicate]&.first if filter
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def attributes_for_new(_opts)
|
|
64
|
+
{}
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module LinkedRails
|
|
4
|
+
class ParamsParser
|
|
5
|
+
attr_reader :params, :user_context
|
|
6
|
+
|
|
7
|
+
def initialize(params)
|
|
8
|
+
@user_context = params[:user_context]
|
|
9
|
+
@params = params.is_a?(Hash) ? ActionController::Parameters.new(params) : params
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def before_params
|
|
13
|
+
return @before_params if instance_variable_defined?(:@before_params)
|
|
14
|
+
|
|
15
|
+
values = permit_params(before: [])[:before]
|
|
16
|
+
return @before_params = nil if values.blank?
|
|
17
|
+
|
|
18
|
+
@before_params = values.map do |f|
|
|
19
|
+
key, value = f.split('=')
|
|
20
|
+
{key: RDF::URI(CGI.unescape(key)), value: value}
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def collection_params
|
|
25
|
+
return @collection_params if instance_variable_defined?(:@collection_params)
|
|
26
|
+
|
|
27
|
+
values = permit_params(:display, :page_size, :title, :type)
|
|
28
|
+
|
|
29
|
+
filter = filter_params
|
|
30
|
+
values[:filter] = filter if filter
|
|
31
|
+
|
|
32
|
+
sort = sorting_params
|
|
33
|
+
values[:sort] = sort if sort
|
|
34
|
+
|
|
35
|
+
values[:user_context] = user_context if user_context
|
|
36
|
+
|
|
37
|
+
@collection_params = values
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def collection_view_params
|
|
41
|
+
return @collection_view_params if instance_variable_defined?(:@collection_view_params)
|
|
42
|
+
|
|
43
|
+
values = permit_params(:page)
|
|
44
|
+
|
|
45
|
+
before = before_params
|
|
46
|
+
values[:before] = before if before
|
|
47
|
+
|
|
48
|
+
@collection_view_params = values
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def filter_params # rubocop:disable Metrics/AbcSize
|
|
52
|
+
return @filter_params if instance_variable_defined?(:@filter_params)
|
|
53
|
+
|
|
54
|
+
values = permit_params(filter: [])[:filter] || permit_params(filter: {})[:filter]
|
|
55
|
+
return @filter_params = values if values.is_a?(Hash)
|
|
56
|
+
return @filter_params = {} if values.blank?
|
|
57
|
+
|
|
58
|
+
@filter_params = values.each_with_object({}) do |f, hash|
|
|
59
|
+
values = f.split('=')
|
|
60
|
+
key = RDF::URI(CGI.unescape(values.first))
|
|
61
|
+
hash[key] ||= []
|
|
62
|
+
hash[key] << CGI.unescape(values.second)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def sorting_params
|
|
67
|
+
return @sorting_params if instance_variable_defined?(:@sorting_params)
|
|
68
|
+
|
|
69
|
+
values = permit_params(sort: [])[:sort]
|
|
70
|
+
return @sorting_params = nil if values.blank?
|
|
71
|
+
|
|
72
|
+
@sorting_params = values.map do |f|
|
|
73
|
+
parse_filter_value(f)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def parse_filter_value(value)
|
|
80
|
+
return value if value.is_a?(Hash)
|
|
81
|
+
|
|
82
|
+
key, value = value.split('=')
|
|
83
|
+
{key: RDF::URI(CGI.unescape(key)), direction: value}
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def permit_params(*keys)
|
|
87
|
+
params
|
|
88
|
+
.permit(*keys)
|
|
89
|
+
.to_h
|
|
90
|
+
.with_indifferent_access
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module LinkedRails
|
|
4
|
+
module Policy
|
|
5
|
+
module AttributeConditions
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def check_has_properties(properties)
|
|
11
|
+
properties.all? { |k, v| @record.send(k).nil? != v }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def check_has_values(values)
|
|
15
|
+
values.all? { |k, v| @record.send(k) == v }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def check_new_record(boolean)
|
|
19
|
+
@record.new_record? == boolean
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
module ClassMethods
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def has_properties_shapes(properties) # rubocop:disable Naming/PredicateName
|
|
26
|
+
properties.map do |key, boolean|
|
|
27
|
+
SHACL::PropertyShape.new(
|
|
28
|
+
path: policy_class.predicate_for_key(key),
|
|
29
|
+
max_count: boolean ? nil : 0,
|
|
30
|
+
min_count: boolean ? 1 : nil
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def has_values_shapes(values) # rubocop:disable Naming/PredicateName
|
|
36
|
+
values.map do |key, value|
|
|
37
|
+
enum = RDF::Serializers.serializer_for(policy_class).enum_options(key).try(:[], value)
|
|
38
|
+
santized_value = enum ? -> { enum.iri } : value
|
|
39
|
+
|
|
40
|
+
SHACL::PropertyShape.new(
|
|
41
|
+
path: policy_class.predicate_for_key(key),
|
|
42
|
+
has_value: santized_value
|
|
43
|
+
)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def new_record_shapes(boolean)
|
|
48
|
+
has_properties_shapes(created_at: !boolean)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'active_response/controller'
|
|
4
|
+
require_relative 'controller/error_handling'
|
|
5
|
+
require_relative 'policy/attribute_conditions'
|
|
6
|
+
|
|
7
|
+
module LinkedRails
|
|
8
|
+
module Policy
|
|
9
|
+
extend ActiveSupport::Concern
|
|
10
|
+
|
|
11
|
+
included do
|
|
12
|
+
extend Enhanceable
|
|
13
|
+
include AttributeConditions
|
|
14
|
+
|
|
15
|
+
enhanceable :policy_class, :Policy
|
|
16
|
+
class_attribute :_permitted_attributes
|
|
17
|
+
|
|
18
|
+
attr_reader :message, :user_context, :record
|
|
19
|
+
|
|
20
|
+
def initialize(user_context, record)
|
|
21
|
+
@user_context = user_context
|
|
22
|
+
@record = record
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def create_child?(klass, opts = {})
|
|
27
|
+
child_policy(klass, opts).create?
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def index_children?(klass, opts = {})
|
|
31
|
+
child_policy(klass, opts).show?
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def permitted_attributes
|
|
35
|
+
self.class.permitted_attributes
|
|
36
|
+
.select { |opts| attribute_permitted?(opts[:conditions]) }
|
|
37
|
+
.map { |opts| sanitized_attributes(opts[:attributes], opts[:options] || {}) }
|
|
38
|
+
.flatten
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def show?
|
|
42
|
+
false
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def update?
|
|
46
|
+
false
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def create?
|
|
50
|
+
false
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def destroy?
|
|
54
|
+
false
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def attribute_permitted?(conditions)
|
|
60
|
+
conditions.all? do |key, opts|
|
|
61
|
+
raise "Unknown attribute condition #{key}" unless respond_to?("check_#{key}", true)
|
|
62
|
+
|
|
63
|
+
send(:"check_#{key}", opts)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def child_policy(klass, opts = {})
|
|
68
|
+
Pundit.policy(user_context, record.build_child(klass, opts.merge(user_context: user_context)))
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def forbid_with_message(message)
|
|
72
|
+
@message = message
|
|
73
|
+
false
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def parent_policy
|
|
77
|
+
return if record.try(:parent).blank?
|
|
78
|
+
|
|
79
|
+
@parent_policy ||= Pundit.policy(user_context, record.parent)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def policy_class
|
|
83
|
+
self.class.policy_class
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def sanitize_array_attribute(attr)
|
|
87
|
+
[attr, attr => []]
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def sanitize_attribute(attr)
|
|
91
|
+
attr
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def sanitized_attributes(attributes, opts)
|
|
95
|
+
if opts[:nested]
|
|
96
|
+
attributes.map(&method(:sanitize_nested_attribute))
|
|
97
|
+
elsif opts[:array]
|
|
98
|
+
attributes.map(&method(:sanitize_array_attribute))
|
|
99
|
+
else
|
|
100
|
+
attributes.map(&method(:sanitize_attribute))
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def sanitize_nested_attribute(key) # rubocop:disable Metrics/AbcSize
|
|
105
|
+
association = record.class.reflect_on_association(key)
|
|
106
|
+
|
|
107
|
+
return nil if association.blank? || (!association.polymorphic? && !association.klass)
|
|
108
|
+
|
|
109
|
+
nested_attributes =
|
|
110
|
+
if association.polymorphic?
|
|
111
|
+
Pundit.policy(user_context, record).try("#{association.name}_attributes") || []
|
|
112
|
+
else
|
|
113
|
+
child = record.build_child(association.klass, user_context: user_context)
|
|
114
|
+
Pundit.policy(user_context, child).permitted_attributes
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
{"#{key}_attributes" => nested_attributes + %i[id _destroy]}
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
module ClassMethods
|
|
121
|
+
def condition_for(attr, pass, shape_opts = {})
|
|
122
|
+
raise("#{attr} not permitted by #{self}") if attribute_options(attr).blank? && pass.permission_required?
|
|
123
|
+
|
|
124
|
+
alternatives = node_shapes_for(attr, **shape_opts)
|
|
125
|
+
if alternatives.count == 1
|
|
126
|
+
Condition.new(shape: alternatives.first, pass: pass)
|
|
127
|
+
elsif alternatives.count.positive?
|
|
128
|
+
Condition.new(shape: SHACL::NodeShape.new(or: alternatives), pass: pass)
|
|
129
|
+
else
|
|
130
|
+
pass
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def permitted_attributes
|
|
135
|
+
initialize_permitted_attributes
|
|
136
|
+
|
|
137
|
+
_permitted_attributes
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
private
|
|
141
|
+
|
|
142
|
+
def attribute_options(attr)
|
|
143
|
+
permitted_attributes.select { |opts| opts[:attributes].include?(attr) }
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def condition_alternatives(attr)
|
|
147
|
+
attribute_options(attr)
|
|
148
|
+
.select { |opts| opts[:conditions].present? }
|
|
149
|
+
.map { |opts| opts[:conditions] }
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def node_shapes_for(attr, property: [], sh_not: [])
|
|
153
|
+
alternatives = condition_alternatives(attr)
|
|
154
|
+
alternatives = [[]] if alternatives.empty? && (property.any? || sh_not.any?)
|
|
155
|
+
|
|
156
|
+
alternatives.map do |props|
|
|
157
|
+
properties = property_shapes(props) + property
|
|
158
|
+
SHACL::NodeShape.new(property: properties, sh_not: sh_not)
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def initialize_permitted_attributes
|
|
163
|
+
return if _permitted_attributes && method(:_permitted_attributes).owner == singleton_class
|
|
164
|
+
|
|
165
|
+
self._permitted_attributes = superclass.try(:_permitted_attributes)&.dup || []
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def permit_attributes(attrs, conditions = {})
|
|
169
|
+
permitted_attributes << {attributes: attrs, conditions: conditions, options: {}}
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def permit_array_attributes(attrs, conditions = {})
|
|
173
|
+
permitted_attributes << {attributes: attrs, conditions: conditions, options: {array: true}}
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def permit_nested_attributes(attrs, conditions = {})
|
|
177
|
+
permitted_attributes << {attributes: attrs, conditions: conditions, options: {nested: true}}
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def policy_class
|
|
181
|
+
@policy_class ||= name.sub(/Policy/, '').classify.safe_constantize
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def property_shapes(conditions)
|
|
185
|
+
conditions.map { |key, options| send("#{key}_shapes", options) }.flatten.compact
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|