linked_rails 0.0.3 → 0.0.4.pre.g0ec6e30ca
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +107 -22
- data/app/controllers/linked_rails/enum_values_controller.rb +0 -42
- data/app/models/linked_rails/actions/item.rb +72 -55
- data/app/models/linked_rails/actions/list.rb +6 -31
- data/app/models/linked_rails/actions/object.rb +40 -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 +53 -15
- 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 +5 -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 +49 -25
- 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 +17 -21
- data/app/models/linked_rails/manifest.rb +102 -22
- data/app/models/linked_rails/menus/item.rb +11 -14
- data/app/models/linked_rails/menus/list.rb +30 -10
- 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 +6 -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 +3 -2
- data/app/policies/linked_rails/form_policy.rb +13 -0
- data/app/policies/linked_rails/ontology_policy.rb +13 -0
- data/app/serializers/linked_rails/actions/item_serializer.rb +5 -5
- 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 +4 -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/app/workers/linked_rails/invalidation_stream_worker.rb +16 -0
- data/lib/generators/linked_rails/install/install_generator.rb +5 -8
- data/lib/generators/linked_rails/install/templates/README +2 -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 +16 -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 +58 -0
- data/lib/linked_rails/controller/error_handling.rb +12 -7
- data/lib/linked_rails/controller/rendering.rb +48 -0
- data/lib/linked_rails/controller.rb +24 -4
- 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/errors/forbidden.rb +37 -0
- data/lib/linked_rails/errors.rb +3 -0
- data/lib/linked_rails/helpers/delta_helper.rb +28 -57
- data/lib/linked_rails/helpers/ontola_actions_helper.rb +2 -2
- data/lib/linked_rails/helpers/resource_helper.rb +14 -2
- data/lib/linked_rails/iri_mapper.rb +18 -40
- 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/cacheable.rb +45 -0
- data/lib/linked_rails/model/collections.rb +203 -39
- data/lib/linked_rails/model/dirty.rb +6 -18
- 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 +29 -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 +18 -7
- data/lib/linked_rails/params_parser.rb +140 -53
- data/lib/linked_rails/policy/attribute_conditions.rb +2 -2
- data/lib/linked_rails/policy.rb +44 -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 +35 -12
- data/lib/linked_rails/storage.rb +32 -0
- 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/url.rb +11 -0
- data/lib/linked_rails/version.rb +1 -1
- data/lib/linked_rails/vocab.rb +9 -0
- data/lib/linked_rails.rb +40 -14
- data/lib/rails/welcome_controller.rb +3 -3
- data/lib/rdf/list.rb +9 -0
- data/lib/rdf/query_fix.rb +15 -0
- metadata +78 -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
@@ -5,8 +5,183 @@ module LinkedRails
|
|
5
5
|
module Collections
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
|
+
COLLECTION_CUSTOMIZABLE_OPTIONS = {
|
9
|
+
# display [Sym] The default display type.
|
10
|
+
# Choose between :grid, :settingsTable, :table, :card, :default
|
11
|
+
display: :default,
|
12
|
+
# grid_max_columns [Integer] The default amount of columns to use in a grid.
|
13
|
+
grid_max_columns: 3,
|
14
|
+
# page_size [Integer] The default page size.
|
15
|
+
page_size: 20,
|
16
|
+
# table_type [Sym] The columns to use in the table.
|
17
|
+
table_type: lambda {
|
18
|
+
case display&.to_sym
|
19
|
+
when :table
|
20
|
+
:default
|
21
|
+
when :settingsTable
|
22
|
+
:settings
|
23
|
+
end
|
24
|
+
},
|
25
|
+
# title String The default title.
|
26
|
+
title: -> { title_from_translation },
|
27
|
+
# type [Sym] The default pagination type.
|
28
|
+
# Choose between :paginated, :infinite.
|
29
|
+
type: :paginated
|
30
|
+
}.freeze
|
31
|
+
COLLECTION_STATIC_OPTIONS = {
|
32
|
+
# association [Sym] The association of the collection items.
|
33
|
+
association: nil,
|
34
|
+
# association_base [Scope, Array] The items of the collection.
|
35
|
+
association_base: -> { apply_scope(sorted_association(filtered_association)) },
|
36
|
+
# association_class [Class] The class of the collection items.
|
37
|
+
association_class: nil,
|
38
|
+
# association_scope [Sym] The scope applied to the collection.
|
39
|
+
association_scope: nil,
|
40
|
+
# call_to_action [String] The label shown as call to action.
|
41
|
+
call_to_action: nil,
|
42
|
+
# collection_class [Class] The base class of the collection.
|
43
|
+
# If you want to use a class other than LinkedRails.collection_class.
|
44
|
+
collection_class: nil,
|
45
|
+
# collection_class [Hash] The default filters applied to the collection.
|
46
|
+
default_filters: {},
|
47
|
+
# collection_class [Array<Hash>] The default sortings applied to the collection.
|
48
|
+
default_sortings: [{key: Vocab.schema.dateCreated, direction: :desc}],
|
49
|
+
# include_members [Boolean] Whether to include the members of this collection.
|
50
|
+
include_members: false,
|
51
|
+
# iri_template_keys [Array<Sym>] Custom query keys for the iri template
|
52
|
+
iri_template_keys: [],
|
53
|
+
# joins [Array<Sym>, Sym] The associations to join
|
54
|
+
joins: nil,
|
55
|
+
# parent [Instance] The default parent of a collection.
|
56
|
+
parent: nil,
|
57
|
+
# parent_iri [Array<String>] The iri elements of the parent
|
58
|
+
parent_iri: -> { parent&.iri_elements },
|
59
|
+
# part_of [Instance] The record to serialize as isPartOf
|
60
|
+
part_of: -> { parent },
|
61
|
+
# policy_scope [Scope] The policy scope class to be used for scoping
|
62
|
+
# Set to false to skip scoping
|
63
|
+
policy_scope: -> { policy ? policy::Scope : Pundit::PolicyFinder.new(filtered_association).scope! },
|
64
|
+
# route_key [Symbol, String] The route key for the association
|
65
|
+
route_key: nil,
|
66
|
+
# view [IRI] The view to use for rendering the members
|
67
|
+
view: nil
|
68
|
+
}.freeze
|
69
|
+
COLLECTION_OPTIONS = COLLECTION_CUSTOMIZABLE_OPTIONS.merge(COLLECTION_STATIC_OPTIONS)
|
70
|
+
|
71
|
+
module ClassMethods
|
72
|
+
def collection_iri(**opts)
|
73
|
+
fragment = opts.delete(:fragment)
|
74
|
+
|
75
|
+
LinkedRails.iri(path: collection_root_relative_iri(**opts), fragment: fragment)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Sets the defaults for all collections for this class.
|
79
|
+
# Can be overridden by #with_collection, called from associated models,
|
80
|
+
# or by passing parameters in an iri.
|
81
|
+
# @param [Hash] options
|
82
|
+
def collection_options(**options)
|
83
|
+
initialize_default_collection_opts
|
84
|
+
|
85
|
+
options.each do |key, value|
|
86
|
+
raise("Invalid key passed to collection_options: #{key}") unless valid_collection_option?(key)
|
87
|
+
|
88
|
+
_default_collection_opts[key] = value
|
89
|
+
end
|
90
|
+
_default_collection_opts[:iri_template] = _default_collection_opts[:collection_class].generate_iri_template(
|
91
|
+
_default_collection_opts[:iri_template_keys]
|
92
|
+
)
|
93
|
+
_default_collection_opts
|
94
|
+
end
|
95
|
+
|
96
|
+
def collection_root_relative_iri(**opts)
|
97
|
+
opts[:filter] = LinkedRails.collection_class.filter_iri_opts(opts[:filter]) if opts.key?(:filter)
|
98
|
+
opts[:route_key] = collection_route_key
|
99
|
+
default_collection_option(:iri_template).expand(**opts)
|
100
|
+
end
|
101
|
+
|
102
|
+
def collection_route_key
|
103
|
+
default_collection_option(:route_key) || route_key
|
104
|
+
end
|
105
|
+
|
106
|
+
def default_collection_options
|
107
|
+
initialize_default_collection_opts
|
108
|
+
|
109
|
+
_default_collection_opts
|
110
|
+
end
|
111
|
+
|
112
|
+
def default_collection_option(key)
|
113
|
+
default_collection_options[key]
|
114
|
+
end
|
115
|
+
|
116
|
+
# Defines a collection to be used in {collection_for}
|
117
|
+
# @see Ldable#collection_for
|
118
|
+
# @note Adds a instance_method <name>_collection
|
119
|
+
# @param [Hash] name as to be used in {collection_for}
|
120
|
+
# @param [Hash] options See COLLECTION_OPTIONS
|
121
|
+
# @return [Collection]
|
122
|
+
def with_collection(name, **options) # rubocop:disable Metrics/AbcSize
|
123
|
+
options[:association] ||= name.to_sym
|
124
|
+
options[:association_class] ||= name.to_s.classify.constantize
|
125
|
+
merged_options = options[:association_class].default_collection_options.merge(options)
|
126
|
+
merged_options[:iri_template] = merged_options[:collection_class].generate_iri_template(
|
127
|
+
merged_options[:iri_template_keys]
|
128
|
+
)
|
129
|
+
collections_add(name: name, options: merged_options)
|
130
|
+
|
131
|
+
define_method "#{name.to_s.singularize}_collection" do |opts = {}|
|
132
|
+
collection_for(name, **opts)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
|
138
|
+
def collections_add(opts)
|
139
|
+
initialize_collections
|
140
|
+
collections.delete_if { |c| c[:name] == opts[:name] }
|
141
|
+
opts[:options] = sanitized_collection_options(opts[:options])
|
142
|
+
collections.append(opts)
|
143
|
+
end
|
144
|
+
|
145
|
+
def initialize_collections
|
146
|
+
return if collections && method(:collections).owner == singleton_class
|
147
|
+
|
148
|
+
self.collections = superclass.try(:collections)&.dup || []
|
149
|
+
end
|
150
|
+
|
151
|
+
def initialize_default_collection_opts # rubocop:disable Metrics/AbcSize
|
152
|
+
return if _default_collection_opts && method(:_default_collection_opts).owner == singleton_class
|
153
|
+
|
154
|
+
self._default_collection_opts = (superclass.try(:_default_collection_opts) || COLLECTION_OPTIONS).dup
|
155
|
+
|
156
|
+
_default_collection_opts[:collection_class] ||= LinkedRails.collection_class
|
157
|
+
_default_collection_opts[:association_class] = self
|
158
|
+
_default_collection_opts[:iri_template] = _default_collection_opts[:collection_class].generate_iri_template(
|
159
|
+
_default_collection_opts[:iri_template_keys]
|
160
|
+
)
|
161
|
+
|
162
|
+
_default_collection_opts
|
163
|
+
end
|
164
|
+
|
165
|
+
def sanitized_collection_options(opts)
|
166
|
+
opts.each_with_object(HashWithIndifferentAccess.new) do |(key, value), hash|
|
167
|
+
raise("Invalid key passed to with_collection: #{key}") unless valid_collection_option?(key.to_sym)
|
168
|
+
|
169
|
+
hash_key = COLLECTION_CUSTOMIZABLE_OPTIONS.key?(key.to_sym) ? "default_#{key}" : key
|
170
|
+
|
171
|
+
hash[hash_key] = value
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def valid_collection_option?(key)
|
176
|
+
COLLECTION_OPTIONS.key?(key) || key == :iri_template
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
8
180
|
included do
|
9
181
|
class_attribute :collections
|
182
|
+
class_attribute :_default_collection_opts,
|
183
|
+
instance_accessor: false,
|
184
|
+
instance_predicate: false
|
10
185
|
end
|
11
186
|
|
12
187
|
# Initialises a {Collection} for one of the collections defined by {has_collection}
|
@@ -18,19 +193,42 @@ module LinkedRails
|
|
18
193
|
# @param [ApplicationRecord] part_of
|
19
194
|
# @param [Hash] opts Additional options to be passed to the collection.
|
20
195
|
# @return [Collection]
|
21
|
-
def collection_for(name, instance_opts
|
22
|
-
collection_opts =
|
196
|
+
def collection_for(name, **instance_opts)
|
197
|
+
collection_opts = collection_options_for(name).dup
|
23
198
|
return if collection_opts.blank?
|
24
199
|
|
25
200
|
collection_opts[:name] = name
|
26
201
|
collection_opts[:parent] = self
|
27
|
-
|
28
|
-
|
202
|
+
collection_class =
|
203
|
+
collection_opts.delete(:collection_class) ||
|
204
|
+
collection_opts[:association_class].default_collection_option(:collection_class) ||
|
205
|
+
LinkedRails.collection_class
|
29
206
|
collection_class.collection_or_view(collection_opts, instance_opts)
|
30
207
|
end
|
31
208
|
|
209
|
+
def collection_iri(collection, **opts)
|
210
|
+
LinkedRails.iri(path: collection_root_relative_iri(collection, **opts))
|
211
|
+
end
|
212
|
+
|
213
|
+
def collection_options_for(name)
|
214
|
+
opts = collections.detect { |c| c[:name] == name.to_sym }
|
215
|
+
raise("Collection #{name} not found for #{self}") unless opts
|
216
|
+
|
217
|
+
opts[:options] || {}
|
218
|
+
end
|
219
|
+
|
220
|
+
def collection_root_relative_iri(collection, **opts)
|
221
|
+
collection_opts = collection_options_for(collection).dup
|
222
|
+
template = collection_opts[:iri_template]
|
223
|
+
klass = collection_opts[:association_class]
|
224
|
+
opts[:route_key] = collection_opts[:route_key] || klass.collection_route_key
|
225
|
+
opts[:parent_iri] = iri_elements
|
226
|
+
|
227
|
+
template.expand(**opts).to_s
|
228
|
+
end
|
229
|
+
|
32
230
|
def parent_collections(user_context)
|
33
|
-
return [] if try(:parent).try(:collections).blank?
|
231
|
+
return [self.class.root_collection(user_context: user_context)] if try(:parent).try(:collections).blank?
|
34
232
|
|
35
233
|
parent_collections_for(parent, user_context)
|
36
234
|
end
|
@@ -43,40 +241,6 @@ module LinkedRails
|
|
43
241
|
.select { |collection| is_a?(collection[:options][:association_class]) }
|
44
242
|
.map { |collection| parent.collection_for(collection[:name], user_context: user_context) }
|
45
243
|
end
|
46
|
-
|
47
|
-
module ClassMethods
|
48
|
-
def collections_add(opts)
|
49
|
-
initialize_collections
|
50
|
-
collections.delete_if { |c| c[:name] == opts[:name] }
|
51
|
-
collections.append(opts)
|
52
|
-
end
|
53
|
-
|
54
|
-
def initialize_collections
|
55
|
-
return if collections && method(:collections).owner == singleton_class
|
56
|
-
|
57
|
-
self.collections = superclass.try(:collections)&.dup || []
|
58
|
-
end
|
59
|
-
|
60
|
-
# Defines a collection to be used in {collection_for}
|
61
|
-
# @see Ldable#collection_for
|
62
|
-
# @note Adds a instance_method <name>_collection
|
63
|
-
# @param [Hash] name as to be used in {collection_for}
|
64
|
-
# @param [Hash] options
|
65
|
-
# @option options [Sym] association the name of the association
|
66
|
-
# @option options [Class] association_class the class of the association
|
67
|
-
# @option options [Sym] joins the associations to join
|
68
|
-
# @return [Collection]
|
69
|
-
def with_collection(name, options = {})
|
70
|
-
options[:association] ||= name.to_sym
|
71
|
-
options[:association_class] ||= name.to_s.classify.constantize
|
72
|
-
|
73
|
-
collections_add(name: name, options: options)
|
74
|
-
|
75
|
-
define_method "#{name.to_s.singularize}_collection" do |opts = {}|
|
76
|
-
collection_for(name, opts)
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
244
|
end
|
81
245
|
end
|
82
246
|
end
|
@@ -22,40 +22,28 @@ module LinkedRails
|
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
-
def
|
26
|
-
serializer_class = RDF::Serializers.serializer_for(self)
|
27
|
-
return {} unless respond_to?(:previous_changes) && serializer_class
|
28
|
-
|
29
|
-
Hash[
|
30
|
-
previous_changes
|
31
|
-
.map { |k, v| [serializer_class.attributes_to_serialize[k.to_sym]&.predicate, v] }
|
32
|
-
.select { |k, _v| k.present? }
|
33
|
-
]
|
34
|
-
end
|
35
|
-
|
36
|
-
def previously_changed_relations
|
25
|
+
def previously_changed_relations(inverted = nil)
|
37
26
|
serializer_class = RDF::Serializers.serializer_for(self)
|
38
27
|
return {} unless serializer_class.try(:relationships_to_serialize)
|
39
28
|
|
40
29
|
serializer_class.relationships_to_serialize.select do |key, _value|
|
41
30
|
if respond_to?(key)
|
42
31
|
association_key = key.to_s.ends_with?('_collection') ? send(key).association : key
|
43
|
-
association_has_destructed?(association_key) || association_changed?(association_key)
|
32
|
+
association_has_destructed?(association_key) || association_changed?(association_key, inverted)
|
44
33
|
end
|
45
34
|
end.with_indifferent_access
|
46
35
|
end
|
47
36
|
|
48
37
|
private
|
49
38
|
|
50
|
-
def association_changed?(association) # rubocop:disable Metrics/AbcSize
|
39
|
+
def association_changed?(association, inverted) # rubocop:disable Metrics/AbcSize
|
51
40
|
ids_method = "#{association.to_s.singularize}_ids"
|
52
41
|
return true if previous_changes.include?("#{association}_id") || previous_changes.include?(ids_method)
|
53
42
|
return false unless try(:association_cached?, association)
|
43
|
+
records = self.class.reflect_on_association(association).collection? ? send(association) : [send(association)]
|
54
44
|
|
55
|
-
|
56
|
-
|
57
|
-
else
|
58
|
-
send(association)&.previous_changes&.present?
|
45
|
+
records.reject { |a| a == inverted }.any? do |a|
|
46
|
+
a&.previous_changes.present? || a&.previously_changed_relations(self).present?
|
59
47
|
end
|
60
48
|
end
|
61
49
|
|
@@ -16,14 +16,13 @@ module LinkedRails
|
|
16
16
|
|
17
17
|
module ClassMethods
|
18
18
|
# Adds an enhancement to a model and includes the Model module.
|
19
|
-
def enhance(enhancement, opts
|
19
|
+
def enhance(enhancement, **opts)
|
20
20
|
initialize_enhancements
|
21
21
|
already_included = enhanced_with?(enhancement)
|
22
22
|
|
23
23
|
self.enhancements[enhancement] = opts
|
24
24
|
return if already_included
|
25
25
|
|
26
|
-
enhance_routing(enhancement) if enhancement.const_defined?(:Routing) && enhanced_with?(enhancement, :Routing)
|
27
26
|
include enhancement::Model if enhancement.const_defined?(:Model) && enhanced_with?(enhancement, :Model)
|
28
27
|
end
|
29
28
|
|
@@ -46,10 +45,6 @@ module LinkedRails
|
|
46
45
|
|
47
46
|
private
|
48
47
|
|
49
|
-
def enhance_routing(enhancement)
|
50
|
-
LinkedRails::Enhancements::RouteConcerns.add_concern(enhancement)
|
51
|
-
end
|
52
|
-
|
53
48
|
def initialize_enhancements
|
54
49
|
return if enhancements && method(:enhancements).owner == singleton_class
|
55
50
|
|
@@ -6,9 +6,7 @@ module LinkedRails
|
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
8
|
included do
|
9
|
-
class_attribute :default_filters, instance_accessor: false, instance_predicate: false
|
10
9
|
class_attribute :filter_options
|
11
|
-
self.default_filters = {}
|
12
10
|
end
|
13
11
|
|
14
12
|
module ClassMethods
|
@@ -16,17 +14,17 @@ module LinkedRails
|
|
16
14
|
class_variables.include?(:@@filter_options) ? super : {}
|
17
15
|
end
|
18
16
|
|
19
|
-
def filterable(options
|
17
|
+
def filterable(**options)
|
20
18
|
initialize_filter_options
|
21
19
|
|
22
|
-
self.filter_options =
|
20
|
+
self.filter_options = filter_options.merge(options)
|
23
21
|
|
24
22
|
options.map { |k, filter| define_filter_method(k, filter) }
|
25
23
|
end
|
26
24
|
|
27
25
|
private
|
28
26
|
|
29
|
-
def boolean_filter(true_filter, false_filter, options
|
27
|
+
def boolean_filter(true_filter, false_filter, **options)
|
30
28
|
{
|
31
29
|
filter: resolve_boolean_filter(true_filter, false_filter),
|
32
30
|
values: [true, false]
|
@@ -36,7 +34,7 @@ module LinkedRails
|
|
36
34
|
def initialize_filter_options
|
37
35
|
return if filter_options && method(:filter_options).owner == singleton_class
|
38
36
|
|
39
|
-
self.filter_options = superclass.try(:filter_options)&.dup ||
|
37
|
+
self.filter_options = superclass.try(:filter_options)&.dup || HashWithIndifferentAccess.new
|
40
38
|
end
|
41
39
|
|
42
40
|
def define_filter_method(key, filter)
|
@@ -15,33 +15,23 @@ module LinkedRails
|
|
15
15
|
parent.send(collection_name, params) if collection_name
|
16
16
|
end
|
17
17
|
|
18
|
-
def root_collection(params
|
18
|
+
def root_collection(**params)
|
19
19
|
return unless root_collection?
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
LinkedRails.collection_class
|
21
|
+
default_collection_option(:collection_class).collection_or_view(
|
22
|
+
default_collection_options,
|
23
|
+
params
|
24
|
+
)
|
26
25
|
end
|
27
26
|
|
28
27
|
private
|
29
28
|
|
30
29
|
def collection_from_parent_name(parent, _params)
|
31
|
-
collection_name = "#{name.underscore}_collection"
|
30
|
+
collection_name = "#{name.demodulize.underscore}_collection"
|
32
31
|
|
33
32
|
collection_name if parent.respond_to?(collection_name, true)
|
34
33
|
end
|
35
34
|
|
36
|
-
def grid_max_columns; end
|
37
|
-
|
38
|
-
def root_collection_opts
|
39
|
-
{
|
40
|
-
association_class: self,
|
41
|
-
grid_max_columns: grid_max_columns
|
42
|
-
}
|
43
|
-
end
|
44
|
-
|
45
35
|
def root_collection?
|
46
36
|
true
|
47
37
|
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module LinkedRails
|
4
4
|
module Model
|
5
|
-
module
|
5
|
+
module IRI
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
8
|
def anonymous_iri
|
@@ -14,13 +14,17 @@ module LinkedRails
|
|
14
14
|
end
|
15
15
|
|
16
16
|
# @return [RDF::URI].
|
17
|
-
def iri(opts
|
17
|
+
def iri(**opts)
|
18
18
|
return anonymous_iri if anonymous_iri?
|
19
|
-
return iri_with_root(root_relative_iri(opts)) if opts.present?
|
19
|
+
return iri_with_root(root_relative_iri(**opts)) if opts.present?
|
20
20
|
|
21
21
|
@iri ||= iri_with_root(root_relative_iri)
|
22
22
|
end
|
23
23
|
|
24
|
+
def iri_elements
|
25
|
+
root_relative_iri.to_s&.split('?')&.first&.split('/')&.map(&:presence)&.compact&.presence
|
26
|
+
end
|
27
|
+
|
24
28
|
# @return [Hash]
|
25
29
|
def iri_opts
|
26
30
|
@iri_opts ||= {
|
@@ -33,14 +37,14 @@ module LinkedRails
|
|
33
37
|
self.class.iri
|
34
38
|
end
|
35
39
|
|
36
|
-
def reload(_opts
|
40
|
+
def reload(**_opts)
|
37
41
|
@iri = nil
|
38
42
|
super
|
39
43
|
end
|
40
44
|
|
41
45
|
# @return [RDF::URI]
|
42
|
-
def root_relative_iri(opts
|
43
|
-
RDF::URI(expand_iri_template(iri_opts.merge(opts)))
|
46
|
+
def root_relative_iri(**opts)
|
47
|
+
RDF::URI(expand_iri_template(**iri_opts.merge(opts)))
|
44
48
|
end
|
45
49
|
|
46
50
|
# @return [String, Symbol]
|
@@ -49,7 +53,7 @@ module LinkedRails
|
|
49
53
|
private
|
50
54
|
|
51
55
|
# @return [String]
|
52
|
-
def expand_iri_template(args
|
56
|
+
def expand_iri_template(**args)
|
53
57
|
iri_template.expand(args)
|
54
58
|
end
|
55
59
|
|
@@ -58,6 +62,7 @@ module LinkedRails
|
|
58
62
|
iri = root_relative_iri.dup
|
59
63
|
iri.scheme = LinkedRails.scheme
|
60
64
|
iri.host = LinkedRails.host
|
65
|
+
iri.path = iri.path.presence || '/'
|
61
66
|
iri
|
62
67
|
end
|
63
68
|
|
@@ -68,7 +73,7 @@ module LinkedRails
|
|
68
73
|
|
69
74
|
# @return [URITemplate]
|
70
75
|
def iri_template_expand_path(template_base, path)
|
71
|
-
tokens = template_base.tokens
|
76
|
+
tokens = (template_base.is_a?(String) ? LinkedRails::URITemplate.new(template_base) : template_base).tokens
|
72
77
|
|
73
78
|
ind = tokens.find_index do |t|
|
74
79
|
t.is_a?(URITemplate::RFC6570::Expression::FormQuery) || t.is_a?(URITemplate::RFC6570::Expression::Fragment)
|
@@ -76,18 +81,12 @@ module LinkedRails
|
|
76
81
|
|
77
82
|
prefix = ind ? tokens[0...ind] : tokens
|
78
83
|
suffix = ind ? tokens[ind..-1] : []
|
79
|
-
URITemplate.new([prefix, path, suffix].flatten.join)
|
84
|
+
LinkedRails::URITemplate.new([prefix, path, suffix].flatten.join)
|
80
85
|
end
|
81
86
|
|
82
87
|
# @return [URITemplate]
|
83
88
|
def iri_template_with_fragment(template_base, fragment)
|
84
|
-
URITemplate.new("#{template_base.to_s.sub(/{#[\w]+}/, '').split('#').first}##{fragment}")
|
85
|
-
end
|
86
|
-
|
87
|
-
def singular_iri; end
|
88
|
-
|
89
|
-
def singular_iri_opts
|
90
|
-
{}
|
89
|
+
LinkedRails::URITemplate.new("#{template_base.to_s.sub(/{#[\w]+}/, '').split('#').first}##{fragment}")
|
91
90
|
end
|
92
91
|
|
93
92
|
module ClassMethods
|
@@ -99,7 +98,7 @@ module LinkedRails
|
|
99
98
|
return if self == ApplicationRecord
|
100
99
|
|
101
100
|
superclass.try(:iri_namespace) ||
|
102
|
-
(linked_rails_module? ? Vocab.ontola :
|
101
|
+
(linked_rails_module? ? Vocab.ontola : Vocab.app)
|
103
102
|
end
|
104
103
|
|
105
104
|
def iri_value
|
@@ -107,14 +106,25 @@ module LinkedRails
|
|
107
106
|
end
|
108
107
|
|
109
108
|
def iri_template
|
110
|
-
@iri_template ||= URITemplate.new("/#{route_key}{/id}{#fragment}")
|
109
|
+
@iri_template ||= LinkedRails::URITemplate.new("/#{route_key}{/id}{#fragment}")
|
110
|
+
end
|
111
|
+
|
112
|
+
def iris_from_scope(scope)
|
113
|
+
ids = ids_for_iris(scope).uniq
|
114
|
+
ids.map { |id| LinkedRails.iri(path: iri_template.expand(id: id)) }
|
115
|
+
end
|
116
|
+
|
117
|
+
def ids_for_iris(scope)
|
118
|
+
scope.pluck(:id)
|
111
119
|
end
|
112
120
|
|
113
121
|
def linked_rails_module?
|
114
122
|
(Rails.version < '6' ? parents : module_parents).include?(LinkedRails)
|
115
123
|
end
|
116
124
|
|
117
|
-
|
125
|
+
def route_key
|
126
|
+
try(:model_name)&.route_key || to_s.tableize
|
127
|
+
end
|
118
128
|
end
|
119
129
|
end
|
120
130
|
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module LinkedRails
|
4
4
|
module Model
|
5
|
-
module
|
5
|
+
module IRIMapping
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
8
|
module ClassMethods
|
@@ -10,11 +10,24 @@ module LinkedRails
|
|
10
10
|
LinkedRails.iri_mapper.parent_from_params(params, user_context)
|
11
11
|
end
|
12
12
|
|
13
|
+
def parent_from_params!(params, user_context)
|
14
|
+
parent_from_params(params, user_context) || raise(ActiveRecord::RecordNotFound)
|
15
|
+
end
|
16
|
+
|
17
|
+
def requested_action(opts, user_context)
|
18
|
+
action_key = opts[:params][:action_key]
|
19
|
+
resource = LinkedRails.iri_mapper.resource_from_iri(
|
20
|
+
iri_without_action(opts[:iri]),
|
21
|
+
user_context
|
22
|
+
)
|
23
|
+
resource&.action(action_key, user_context)
|
24
|
+
end
|
25
|
+
|
13
26
|
def requested_index_resource(params, user_context)
|
14
27
|
if params.key?(:parent_iri)
|
15
28
|
collection_from_parent(index_collection_params(params, user_context))
|
16
29
|
else
|
17
|
-
root_collection(index_collection_params(params, user_context))
|
30
|
+
root_collection(**index_collection_params(params, user_context))
|
18
31
|
end
|
19
32
|
end
|
20
33
|
|
@@ -25,10 +38,10 @@ module LinkedRails
|
|
25
38
|
def requested_resource(opts, user_context)
|
26
39
|
if collection_action?(opts)
|
27
40
|
requested_index_resource(opts[:params], user_context)
|
41
|
+
elsif action_item_action?(opts)
|
42
|
+
requested_action(opts, user_context)
|
28
43
|
elsif singular_action?(opts)
|
29
|
-
|
30
|
-
resource&.singular_resource = true
|
31
|
-
resource
|
44
|
+
singular_resource(requested_singular_resource(opts[:params], user_context))
|
32
45
|
else
|
33
46
|
requested_single_resource(opts[:params], user_context)
|
34
47
|
end
|
@@ -38,7 +51,7 @@ module LinkedRails
|
|
38
51
|
if self < ActiveRecord::Base
|
39
52
|
find_by(primary_key => params[:id]) if params.key?(:id)
|
40
53
|
else
|
41
|
-
new(params)
|
54
|
+
new(params.except(:format))
|
42
55
|
end
|
43
56
|
end
|
44
57
|
|
@@ -46,10 +59,20 @@ module LinkedRails
|
|
46
59
|
requested_single_resource(params, user_context) || raise(ActiveRecord::RecordNotFound)
|
47
60
|
end
|
48
61
|
|
62
|
+
def singular_resource(resource)
|
63
|
+
resource&.singular_resource = true
|
64
|
+
resource
|
65
|
+
end
|
66
|
+
|
49
67
|
private
|
50
68
|
|
69
|
+
def action_item_action?(opts)
|
70
|
+
opts[:params][:action_key].present?
|
71
|
+
end
|
72
|
+
|
51
73
|
def collection_action?(opts)
|
52
|
-
|
74
|
+
opts[:action] == 'index' ||
|
75
|
+
opts[:class].action_list.defined_actions[:collection].key?(opts[:action].to_sym)
|
53
76
|
end
|
54
77
|
|
55
78
|
def index_collection_params(params, user_context)
|
@@ -57,7 +80,13 @@ module LinkedRails
|
|
57
80
|
|
58
81
|
{
|
59
82
|
user_context: user_context
|
60
|
-
}.merge(params_hash).
|
83
|
+
}.merge(params_hash).symbolize_keys
|
84
|
+
end
|
85
|
+
|
86
|
+
def iri_without_action(url)
|
87
|
+
iri = RDF::URI(url)
|
88
|
+
iri.path = iri.path.split('/')[0...-1].join('/') if iri.path
|
89
|
+
iri.to_s
|
61
90
|
end
|
62
91
|
|
63
92
|
def singular_action?(opts)
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module LinkedRails
|
4
|
+
module Model
|
5
|
+
module Menuable
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
def menus(user_context = nil)
|
9
|
+
menu_list(user_context).menus
|
10
|
+
end
|
11
|
+
|
12
|
+
def menu(tag, user_context = nil)
|
13
|
+
menu_list(user_context).menu(tag)
|
14
|
+
end
|
15
|
+
|
16
|
+
def menu_list(user_context = nil)
|
17
|
+
@menu_list ||= {}
|
18
|
+
@menu_list[user_context] ||= self.class.menu_class.new(resource: self, user_context: user_context)
|
19
|
+
end
|
20
|
+
|
21
|
+
module ClassMethods
|
22
|
+
def menu_class
|
23
|
+
@menu_class ||= "#{name}MenuList".safe_constantize || "#{superclass.name}MenuList".safe_constantize
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|