linked_rails 0.0.4.pre.g9b7bc9073 → 0.0.4.pre.g9e3bcac55
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/app/controllers/linked_rails/bulk_controller.rb +37 -2
- data/app/models/linked_rails/actions/item.rb +15 -7
- data/app/models/linked_rails/actions/object.rb +5 -3
- data/app/models/linked_rails/collection/view.rb +9 -8
- data/app/models/linked_rails/form/field/file_input.rb +5 -1
- data/app/models/linked_rails/form/field_factory.rb +1 -7
- data/app/models/linked_rails/form.rb +1 -0
- data/app/models/linked_rails/manifest.rb +94 -20
- data/app/models/linked_rails/menus/item.rb +1 -1
- data/app/models/linked_rails/menus/list.rb +15 -4
- data/app/models/linked_rails/ontology.rb +1 -0
- data/app/policies/linked_rails/collection_policy.rb +1 -0
- 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/collection/view_serializer.rb +1 -0
- data/app/workers/linked_rails/invalidation_stream_worker.rb +16 -0
- data/lib/generators/linked_rails/install/templates/locales.yml +2 -0
- data/lib/linked_rails/controller/delta.rb +1 -21
- data/lib/linked_rails/controller/error_handling.rb +5 -0
- 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 +3 -1
- data/lib/linked_rails/helpers/resource_helper.rb +11 -1
- data/lib/linked_rails/iri_mapper.rb +1 -1
- data/lib/linked_rails/middleware/linked_data_params.rb +1 -1
- data/lib/linked_rails/model/cacheable.rb +45 -0
- data/lib/linked_rails/model/collections.rb +3 -1
- data/lib/linked_rails/model/dirty.rb +0 -11
- data/lib/linked_rails/model/iri.rb +1 -0
- data/lib/linked_rails/model.rb +1 -0
- data/lib/linked_rails/params_parser.rb +20 -11
- data/lib/linked_rails/policy.rb +4 -0
- data/lib/linked_rails/serializer/actionable.rb +3 -3
- data/lib/linked_rails/serializer.rb +7 -1
- data/lib/linked_rails/storage.rb +32 -0
- data/lib/linked_rails/url.rb +11 -0
- data/lib/linked_rails.rb +11 -2
- metadata +26 -5
- data/app/serializers/linked_rails/actions/object_serializer.rb +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c66380d7f4ca34d26bae943972d7db0bf4df315bee70c2f676dff5a67e9b8d48
|
4
|
+
data.tar.gz: 1e914271f291b9498bb12c8cbf98acc9641b8730e8e4538887bde8e3a03e36b3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0c9c6d3dd7e7938372aa92f714513f9d3b5374710362afa3a9c6df6a1e683706909c98459c302d3fab7a40d8e604119ad7889190e3c92652fc33da18aa9c7a42
|
7
|
+
data.tar.gz: 6d78dad18fe110f9c52d42db45d58f5ca06eb74301b6684d63f447f8c0f33b440e78bca7067e4ea6f0f7a300baf6966006d9e5f87ff7bd11d042c2bc0e30961c
|
@@ -21,8 +21,11 @@ module LinkedRails
|
|
21
21
|
return response_for_wrong_host(opts) if wrong_host?(opts[:iri])
|
22
22
|
|
23
23
|
include = opts[:include].to_s == 'true'
|
24
|
+
resource = LinkedRails.iri_mapper.resource_from_iri(request_path_to_url(opts[:iri]), user_context)
|
24
25
|
|
25
|
-
response_from_request(include, RDF::URI(opts[:iri]))
|
26
|
+
return response_from_request(include, RDF::URI(opts[:iri])) if resource.blank?
|
27
|
+
|
28
|
+
response_from_resource(include, opts[:iri], resource)
|
26
29
|
rescue StandardError => e
|
27
30
|
handle_resource_error(opts, e)
|
28
31
|
end
|
@@ -84,10 +87,19 @@ module LinkedRails
|
|
84
87
|
false
|
85
88
|
end
|
86
89
|
|
90
|
+
def resource_cache_control(cacheable, status, resource_policy)
|
91
|
+
return :private unless status == 200 && cacheable
|
92
|
+
return 'no-cache' unless resource_policy.try(:public_resource?)
|
93
|
+
|
94
|
+
:public
|
95
|
+
end
|
96
|
+
|
87
97
|
def resource_params(param)
|
88
98
|
params = param.permit(:include, :iri)
|
89
99
|
params[:iri] = URI(params[:iri])
|
90
100
|
params
|
101
|
+
rescue URI::InvalidURIError
|
102
|
+
params.except(:iri)
|
91
103
|
end
|
92
104
|
|
93
105
|
def resource_response_body(iri, rack_body, status)
|
@@ -131,6 +143,12 @@ module LinkedRails
|
|
131
143
|
}.merge(opts)
|
132
144
|
end
|
133
145
|
|
146
|
+
def resource_status(resource_policy)
|
147
|
+
raise(LinkedRails::Errors::Forbidden.new(query: :show?)) unless resource_policy.show?
|
148
|
+
|
149
|
+
200
|
150
|
+
end
|
151
|
+
|
134
152
|
def response_for_wrong_host(opts)
|
135
153
|
iri = opts[:iri]
|
136
154
|
term = term_from_vocab(iri)
|
@@ -139,6 +157,23 @@ module LinkedRails
|
|
139
157
|
ontology_term_response(iri, term, opts[:include])
|
140
158
|
end
|
141
159
|
|
160
|
+
def response_from_resource(include, iri, resource)
|
161
|
+
resource_policy = policy(resource)
|
162
|
+
status = resource_status(resource_policy)
|
163
|
+
|
164
|
+
resource_response(
|
165
|
+
iri,
|
166
|
+
body: response_from_resource_body(include, iri, resource, status),
|
167
|
+
cache: resource_cache_control(resource.try(:cacheable?), status, resource_policy),
|
168
|
+
language: I18n.locale,
|
169
|
+
status: status
|
170
|
+
)
|
171
|
+
end
|
172
|
+
|
173
|
+
def response_from_resource_body(include, _iri, resource, status)
|
174
|
+
include && status == 200 ? resource_body(resource) : nil
|
175
|
+
end
|
176
|
+
|
142
177
|
def term_from_vocab(iri)
|
143
178
|
vocab = Vocab.for(iri)
|
144
179
|
tag = iri.to_s.split(vocab.to_s).last
|
@@ -216,7 +251,7 @@ module LinkedRails
|
|
216
251
|
|
217
252
|
def sanitized_relative_path(iri) # rubocop:disable Metrics/AbcSize
|
218
253
|
iri.path = "#{iri.path}/" unless iri.path&.ends_with?('/')
|
219
|
-
uri = URI(LinkedRails.iri.path.present? ? iri.to_s.split("#{LinkedRails.iri.path}/").last : iri)
|
254
|
+
uri = URI(LinkedRails.iri.path.chomp('/').present? ? iri.to_s.split("#{LinkedRails.iri.path}/").last : iri)
|
220
255
|
|
221
256
|
[uri.path, uri.query].compact.join('?')
|
222
257
|
end
|
@@ -61,13 +61,15 @@ module LinkedRails
|
|
61
61
|
end
|
62
62
|
|
63
63
|
def object
|
64
|
-
|
65
|
-
|
66
|
-
@object || resource
|
64
|
+
object_from_var || resource
|
67
65
|
end
|
68
66
|
|
69
67
|
def object_iri
|
70
|
-
|
68
|
+
iri_with_root(object_root_relative_iri)
|
69
|
+
end
|
70
|
+
|
71
|
+
def object_root_relative_iri
|
72
|
+
object&.iri&.anonymous? ? anonymous_object_root_relative_iri : object&.root_relative_iri
|
71
73
|
end
|
72
74
|
|
73
75
|
def parent
|
@@ -138,8 +140,8 @@ module LinkedRails
|
|
138
140
|
|
139
141
|
private
|
140
142
|
|
141
|
-
def
|
142
|
-
object_iri =
|
143
|
+
def anonymous_object_root_relative_iri
|
144
|
+
object_iri = root_relative_iri.dup
|
143
145
|
object_iri.path += '/action_object'
|
144
146
|
object_iri
|
145
147
|
end
|
@@ -152,6 +154,12 @@ module LinkedRails
|
|
152
154
|
LinkedRails.translate(:action, :label, self)
|
153
155
|
end
|
154
156
|
|
157
|
+
def object_from_var
|
158
|
+
return @object unless @object.respond_to?(:call)
|
159
|
+
|
160
|
+
@object = list.instance_exec(&@object)
|
161
|
+
end
|
162
|
+
|
155
163
|
def policy_expired?
|
156
164
|
@policy_expired ||= policy && resource_policy.try(:expired?)
|
157
165
|
end
|
@@ -187,7 +195,7 @@ module LinkedRails
|
|
187
195
|
|
188
196
|
def target_url_fallback # rubocop:disable Metrics/AbcSize
|
189
197
|
base = (resource.try(:singular_resource?) ? resource.singular_iri : resource.iri).dup
|
190
|
-
base.path = "#{base.path}/#{target_path}" if target_path.present?
|
198
|
+
base.path = "#{base.path.chomp('/')}/#{target_path}" if target_path.present?
|
191
199
|
base.query = Rack::Utils.parse_nested_query(base.query).merge(target_query).to_param if target_query.present?
|
192
200
|
base
|
193
201
|
end
|
@@ -12,8 +12,8 @@ module LinkedRails
|
|
12
12
|
alias parent action
|
13
13
|
delegate :object, to: :action
|
14
14
|
|
15
|
-
def
|
16
|
-
action.
|
15
|
+
def root_relative_iri
|
16
|
+
action.object_root_relative_iri
|
17
17
|
end
|
18
18
|
|
19
19
|
class << self
|
@@ -30,7 +30,9 @@ module LinkedRails
|
|
30
30
|
|
31
31
|
parent = parent_from_params!(params, user_context)
|
32
32
|
|
33
|
-
|
33
|
+
object = parent&.object
|
34
|
+
object.instance_variable_set(:@iri, parent.object_iri)
|
35
|
+
object
|
34
36
|
end
|
35
37
|
end
|
36
38
|
end
|
@@ -8,8 +8,9 @@ module LinkedRails
|
|
8
8
|
include LinkedRails::Model
|
9
9
|
|
10
10
|
attr_accessor :collection, :filter
|
11
|
-
delegate :apply_scope, :association_base, :association_class, :
|
12
|
-
:parent, :policy, :total_page_count, :unfiltered_collection, :user_context,
|
11
|
+
delegate :apply_scope, :association_base, :association_class, :child_resource, :default_page_size, :display,
|
12
|
+
:include_members,:parent, :policy, :total_page_count, :unfiltered_collection, :user_context,
|
13
|
+
to: :collection
|
13
14
|
delegate :count, to: :members
|
14
15
|
|
15
16
|
alias id iri
|
@@ -42,7 +43,7 @@ module LinkedRails
|
|
42
43
|
end
|
43
44
|
|
44
45
|
def preview_includes
|
45
|
-
include_members ? {member_sequence: :members} : %i[member_sequence]
|
46
|
+
include_members ? {members: {}, member_sequence: :members} : %i[member_sequence]
|
46
47
|
end
|
47
48
|
|
48
49
|
def title
|
@@ -75,19 +76,19 @@ module LinkedRails
|
|
75
76
|
end
|
76
77
|
|
77
78
|
def iris_from_scope?
|
78
|
-
members_query.is_a?(ActiveRecord::Relation) && !polymorphic_collection?
|
79
|
+
members_query.is_a?(ActiveRecord::Relation) && !polymorphic_collection?(members_query.klass)
|
79
80
|
end
|
80
81
|
|
81
82
|
def members_array
|
82
83
|
members_query.to_a
|
83
84
|
end
|
84
85
|
|
85
|
-
def polymorphic_collection?
|
86
|
-
column =
|
87
|
-
polymorphic =
|
86
|
+
def polymorphic_collection?(klass)
|
87
|
+
column = klass.inheritance_column
|
88
|
+
polymorphic = klass.columns_hash.include?(column)
|
88
89
|
return false unless polymorphic
|
89
90
|
|
90
|
-
return true if
|
91
|
+
return true if klass.descends_from_active_record?
|
91
92
|
|
92
93
|
members_query.where_values_hash.include?(column) &&
|
93
94
|
members_query.where_values_hash[column] != association_class.to_s
|
@@ -52,13 +52,7 @@ module LinkedRails
|
|
52
52
|
private
|
53
53
|
|
54
54
|
def attr_column(name)
|
55
|
-
|
56
|
-
if model_class.is_delegated_attribute?(name)
|
57
|
-
model_class.class_for_delegated_attribute(name)
|
58
|
-
else
|
59
|
-
model_class
|
60
|
-
end
|
61
|
-
column_model.column_for_attribute(name)
|
55
|
+
model_class.column_for_attribute(name)
|
62
56
|
end
|
63
57
|
|
64
58
|
def attr_to_datatype # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
|
@@ -5,6 +5,27 @@ module LinkedRails
|
|
5
5
|
include ActiveModel::Model
|
6
6
|
include LinkedRails::Model
|
7
7
|
|
8
|
+
def save
|
9
|
+
Storage.hset(
|
10
|
+
:persistent,
|
11
|
+
:manifest,
|
12
|
+
URL.as_href(LinkedRails.iri) => web_manifest.to_json
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
def web_manifest
|
17
|
+
web_manifest_base.merge(
|
18
|
+
ontola: web_manifest_ontola_section,
|
19
|
+
serviceworker: web_manifest_sw_section
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def allowed_external_sources
|
26
|
+
[]
|
27
|
+
end
|
28
|
+
|
8
29
|
def app_name
|
9
30
|
Rails.application.railtie_name.chomp('_application').humanize
|
10
31
|
end
|
@@ -17,8 +38,27 @@ module LinkedRails
|
|
17
38
|
'#eef0f2'
|
18
39
|
end
|
19
40
|
|
41
|
+
def blob_preview_iri
|
42
|
+
return unless ActiveStorage::Blob.service.present?
|
43
|
+
|
44
|
+
"#{LinkedRails.iri(path: 'rails/active_storage/blobs/redirect')}/{signed_id}/preview"
|
45
|
+
end
|
46
|
+
|
47
|
+
def blob_upload_iri
|
48
|
+
return unless ActiveStorage::Blob.service.present?
|
49
|
+
|
50
|
+
LinkedRails.iri(path: 'rails/active_storage/direct_uploads')
|
51
|
+
end
|
52
|
+
|
20
53
|
def css_class; end
|
21
54
|
|
55
|
+
def csp_entries
|
56
|
+
{
|
57
|
+
connectSrc: [ActiveStorage::Blob.service.try(:bucket)&.url].compact,
|
58
|
+
scriptSrc: [ActiveStorage::Blob.service.try(:bucket)&.url].compact
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
22
62
|
def header_background
|
23
63
|
:primary
|
24
64
|
end
|
@@ -27,17 +67,16 @@ module LinkedRails
|
|
27
67
|
:white
|
28
68
|
end
|
29
69
|
|
30
|
-
def
|
31
|
-
[
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
]
|
70
|
+
def icons
|
71
|
+
[]
|
72
|
+
end
|
73
|
+
|
74
|
+
def lang
|
75
|
+
:nl
|
37
76
|
end
|
38
77
|
|
39
78
|
def scope
|
40
|
-
|
79
|
+
LinkedRails.iri.to_s
|
41
80
|
end
|
42
81
|
|
43
82
|
def site_theme_color
|
@@ -52,43 +91,46 @@ module LinkedRails
|
|
52
91
|
app_name
|
53
92
|
end
|
54
93
|
|
94
|
+
def start_url
|
95
|
+
scope == '/' ? scope : "#{scope}/"
|
96
|
+
end
|
97
|
+
|
55
98
|
def theme; end
|
56
99
|
|
57
100
|
def theme_options
|
58
101
|
{}
|
59
102
|
end
|
60
103
|
|
61
|
-
def web_manifest
|
62
|
-
web_manifest_base.merge(
|
63
|
-
ontola: web_manifest_ontola_section,
|
64
|
-
serviceworker: web_manifest_sw_section
|
65
|
-
)
|
66
|
-
end
|
67
|
-
|
68
104
|
def web_manifest_base # rubocop:disable Metrics/MethodLength
|
69
105
|
{
|
70
106
|
background_color: background_color,
|
71
107
|
dir: :rtl,
|
72
108
|
display: :standalone,
|
73
|
-
|
109
|
+
icons: icons,
|
110
|
+
lang: lang,
|
74
111
|
name: app_name,
|
75
112
|
scope: scope,
|
76
113
|
short_name: app_name,
|
77
|
-
start_url:
|
114
|
+
start_url: start_url,
|
78
115
|
theme_color: site_theme_color
|
79
116
|
}
|
80
117
|
end
|
81
118
|
|
82
119
|
def web_manifest_ontola_section # rubocop:disable Metrics/MethodLength
|
83
120
|
{
|
84
|
-
|
121
|
+
allowed_external_sources: allowed_external_sources,
|
122
|
+
blob_preview_iri: blob_preview_iri,
|
123
|
+
blob_upload_iri: blob_upload_iri,
|
124
|
+
csp: csp_entries,
|
85
125
|
header_background: header_background,
|
86
126
|
header_text: header_text,
|
87
|
-
|
127
|
+
preconnect: preconnect,
|
88
128
|
primary_color: site_theme_color,
|
89
129
|
secondary_color: site_secondary_color,
|
130
|
+
styled_headers: styled_headers,
|
90
131
|
theme: theme,
|
91
132
|
theme_options: theme_options.to_query,
|
133
|
+
tracking: tracking,
|
92
134
|
website_iri: LinkedRails.iri.to_s,
|
93
135
|
websocket_path: websocket_path
|
94
136
|
}
|
@@ -96,7 +138,7 @@ module LinkedRails
|
|
96
138
|
|
97
139
|
def web_manifest_sw_section
|
98
140
|
{
|
99
|
-
src: "#{scope}/sw.js
|
141
|
+
src: "#{scope.chomp('/')}/sw.js",
|
100
142
|
scope: scope
|
101
143
|
}
|
102
144
|
end
|
@@ -104,5 +146,37 @@ module LinkedRails
|
|
104
146
|
def websocket_path
|
105
147
|
Rails.application.config.try(:action_cable).try(:mount_path).try(:[], 1..-1)
|
106
148
|
end
|
149
|
+
|
150
|
+
def preconnect
|
151
|
+
[]
|
152
|
+
end
|
153
|
+
|
154
|
+
def styled_headers
|
155
|
+
false
|
156
|
+
end
|
157
|
+
|
158
|
+
def tracking
|
159
|
+
[]
|
160
|
+
end
|
161
|
+
|
162
|
+
class << self
|
163
|
+
def destroy(iri)
|
164
|
+
Storage.hdel(:persistent, :manifest, URL.as_href(iri))
|
165
|
+
end
|
166
|
+
|
167
|
+
def move(from, to)
|
168
|
+
Storage.hset(
|
169
|
+
:persistent,
|
170
|
+
:redirect_prefix,
|
171
|
+
URL.as_href(from) => URL.as_href(to)
|
172
|
+
)
|
173
|
+
|
174
|
+
data = Storage.hget(:persistent, :manifest, URL.as_href(from))
|
175
|
+
|
176
|
+
Storage.hset(:persistent, :manifest, URL.as_href(to), data) if data
|
177
|
+
|
178
|
+
destroy(from)
|
179
|
+
end
|
180
|
+
end
|
107
181
|
end
|
108
182
|
end
|
@@ -26,7 +26,7 @@ module LinkedRails
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def menus
|
29
|
-
@menus ||= available_menus.map(&method(:
|
29
|
+
@menus ||= available_menus.map(&method(:memoized_menu_item))
|
30
30
|
end
|
31
31
|
|
32
32
|
def menu(tag)
|
@@ -49,9 +49,8 @@ module LinkedRails
|
|
49
49
|
end
|
50
50
|
|
51
51
|
def menu_item(tag, options) # rubocop:disable Metrics/AbcSize
|
52
|
-
|
53
|
-
|
54
|
-
end
|
52
|
+
return unless show_menu_item?(tag, options)
|
53
|
+
|
55
54
|
options[:label_params] ||= {}
|
56
55
|
options[:label_params][:default] ||= ["menus.default.#{tag}".to_sym, tag.to_s.capitalize]
|
57
56
|
options[:label] ||= default_label(tag, options)
|
@@ -60,6 +59,10 @@ module LinkedRails
|
|
60
59
|
LinkedRails.menus_item_class.new(resource: resource, tag: tag, parent: self, **options)
|
61
60
|
end
|
62
61
|
|
62
|
+
def policy_verdict(policy, options)
|
63
|
+
policy.send(options[:policy], *options[:policy_arguments])
|
64
|
+
end
|
65
|
+
|
63
66
|
def resource_policy(policy_resource)
|
64
67
|
policy_resource ||= resource
|
65
68
|
policy_resource = instance_exec(&policy_resource) if policy_resource.respond_to?(:call)
|
@@ -69,6 +72,14 @@ module LinkedRails
|
|
69
72
|
@resource_policy[policy_resource] ||= Pundit.policy(user_context, policy_resource)
|
70
73
|
end
|
71
74
|
|
75
|
+
def show_menu_item?(_tag, options)
|
76
|
+
return true if options[:policy].blank?
|
77
|
+
|
78
|
+
policy = resource_policy(options[:policy_resource])
|
79
|
+
|
80
|
+
policy_verdict(policy, options)
|
81
|
+
end
|
82
|
+
|
72
83
|
def iri_template
|
73
84
|
base_template = resource.send(resource.try(:singular_resource?) ? :singular_iri_template : :iri_template)
|
74
85
|
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module LinkedRails
|
4
|
+
class InvalidationStreamWorker < ActiveJob::Base
|
5
|
+
def perform(type, iri, resource_type)
|
6
|
+
entry = {
|
7
|
+
type: type,
|
8
|
+
resource: iri,
|
9
|
+
resourceType: resource_type
|
10
|
+
}
|
11
|
+
id = Storage.xadd(:stream, LinkedRails.cache_stream, entry)
|
12
|
+
|
13
|
+
raise('No message id returned, implies failure') if id.blank?
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -5,20 +5,8 @@ module LinkedRails
|
|
5
5
|
module Delta
|
6
6
|
include LinkedRails::Helpers::DeltaHelper
|
7
7
|
|
8
|
-
def changes_triples(resource)
|
8
|
+
def changes_triples(resource)
|
9
9
|
resource_serializer(resource)&.send(:repository).statements
|
10
|
-
|
11
|
-
# resource.previous_changes_by_predicate.map do |predicate, (_old_value, _new_value)|
|
12
|
-
# serializer_attributes = resource.class.predicate_mapping[predicate]
|
13
|
-
# next if serializer_attributes.is_a?(FastJsonapi::Relationship)
|
14
|
-
#
|
15
|
-
# attr_name = serializer_attributes.key
|
16
|
-
# serialized_value =
|
17
|
-
# serializer.class.attributes_to_serialize[attr_name]&.serialize(resource, serializer_params, {})
|
18
|
-
# (serialized_value.is_a?(Array) ? serialized_value : [serialized_value]).map do |value|
|
19
|
-
# change_triple(resource, predicate, value)
|
20
|
-
# end
|
21
|
-
# end.compact.flatten
|
22
10
|
end
|
23
11
|
|
24
12
|
def changed_relations_triples(resource, inverted = nil) # rubocop:disable Metrics/AbcSize
|
@@ -35,14 +23,6 @@ module LinkedRails
|
|
35
23
|
|
36
24
|
private
|
37
25
|
|
38
|
-
def change_triple(resource, predicate, value)
|
39
|
-
if value.nil?
|
40
|
-
RDF::Statement.new(resource.iri, predicate, Vocab.sp[:Variable], graph_name: delta_iri(:remove))
|
41
|
-
else
|
42
|
-
RDF::Statement.new(resource.iri, predicate, value, graph_name: delta_iri(:replace))
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
26
|
def changed_relation_triples(predicate, destructed, resources, inverted)
|
47
27
|
related_resource_invalidations =
|
48
28
|
resources.flat_map do |resource|
|
@@ -6,6 +6,10 @@ module LinkedRails
|
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
include ActiveSupport::Rescuable
|
8
8
|
|
9
|
+
included do
|
10
|
+
rescue_from LinkedRails::Errors::Forbidden, with: :handle_error
|
11
|
+
end
|
12
|
+
|
9
13
|
private
|
10
14
|
|
11
15
|
def add_error_snackbar(error)
|
@@ -69,6 +73,7 @@ module LinkedRails
|
|
69
73
|
'Doorkeeper::Errors::InvalidGrantReuse' => 422,
|
70
74
|
'LinkedRails::Auth::Errors::Expired' => 410,
|
71
75
|
'LinkedRails::Auth::Errors::Unauthorized' => 401,
|
76
|
+
'LinkedRails::Errors::Forbidden' => 403,
|
72
77
|
'Pundit::NotAuthorizedError' => 403
|
73
78
|
}
|
74
79
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module LinkedRails
|
4
|
+
module Errors
|
5
|
+
class Forbidden < StandardError
|
6
|
+
attr_reader :query, :record, :policy, :action
|
7
|
+
|
8
|
+
# @param [Hash] options
|
9
|
+
# @option options [String] query The action of the request
|
10
|
+
# @option options [ActiveRecord::Base] record The record that was requested
|
11
|
+
# @option options [Policy] policy The policy that raised the exception
|
12
|
+
# @option options [String] message Override the default error message
|
13
|
+
# @return [String] the message
|
14
|
+
def initialize(**options)
|
15
|
+
@query = options.fetch(:query).to_s
|
16
|
+
@record = options[:record]
|
17
|
+
@policy = options[:policy]
|
18
|
+
@action = @query[-1] == '?' ? @query[0..-2] : @query
|
19
|
+
@message = options[:message]
|
20
|
+
|
21
|
+
raise StandardError if @query.blank? && @message.blank?
|
22
|
+
|
23
|
+
super(@message || default_message)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def default_message
|
29
|
+
I18n.t(
|
30
|
+
"pundit.#{@policy.class.to_s.underscore}.#{@query}",
|
31
|
+
action: @action,
|
32
|
+
default: I18n.t('errors.access_denied')
|
33
|
+
)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -8,7 +8,9 @@ module LinkedRails
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def invalidate_collection_delta(collection)
|
11
|
-
|
11
|
+
iri = collection.is_a?(RDF::Resource) ? collection : collection.iri
|
12
|
+
|
13
|
+
[Vocab.sp[:Variable], Vocab.ontola[:baseCollection], iri, delta_iri(:invalidate)]
|
12
14
|
end
|
13
15
|
|
14
16
|
def invalidate_parent_collections_delta(resource)
|
@@ -46,12 +46,22 @@ module LinkedRails
|
|
46
46
|
|
47
47
|
private
|
48
48
|
|
49
|
+
def request_path_to_url(path)
|
50
|
+
return path unless path.present? && URI(path).relative?
|
51
|
+
|
52
|
+
port = [80, 443].include?(request.port) ? nil : request.port
|
53
|
+
URI::Generic.new(request.scheme, nil, request.host, port, nil, path, nil, nil, nil).to_s
|
54
|
+
end
|
55
|
+
|
49
56
|
def build_new_resource
|
50
57
|
controller_class.build_new(user_context: user_context)
|
51
58
|
end
|
52
59
|
|
53
60
|
def new_resource_from_parent
|
54
|
-
|
61
|
+
if requested_resource.is_a?(LinkedRails.collection_class) ||
|
62
|
+
requested_resource.is_a?(LinkedRails.collection_view_class)
|
63
|
+
return requested_resource.child_resource
|
64
|
+
end
|
55
65
|
|
56
66
|
parent_resource.build_child(
|
57
67
|
controller_class,
|
@@ -38,7 +38,7 @@ module LinkedRails
|
|
38
38
|
params = Rails.application.routes.recognize_path(iri.to_s, method: method)
|
39
39
|
|
40
40
|
route_params_to_opts(params.merge(query), iri.to_s)
|
41
|
-
rescue ActionController::RoutingError
|
41
|
+
rescue ActionController::RoutingError, SystemStackError
|
42
42
|
EMPTY_IRI_OPTS.dup
|
43
43
|
end
|
44
44
|
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module LinkedRails
|
4
|
+
module Model
|
5
|
+
module Cacheable
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
if respond_to?(:after_commit)
|
10
|
+
after_commit :publish_create, on: :create, if: :should_publish_changes
|
11
|
+
after_commit :publish_update, on: :update, if: :should_publish_changes
|
12
|
+
after_commit :publish_delete, on: :destroy, if: :should_publish_changes
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def cacheable?
|
17
|
+
true
|
18
|
+
end
|
19
|
+
|
20
|
+
def publish_create
|
21
|
+
publish_message('io.ontola.transactions.Created')
|
22
|
+
end
|
23
|
+
|
24
|
+
def publish_update
|
25
|
+
publish_message('io.ontola.transactions.Updated')
|
26
|
+
end
|
27
|
+
|
28
|
+
def publish_delete
|
29
|
+
publish_message('io.ontola.transactions.Deleted')
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def publish_message(type)
|
35
|
+
LinkedRails::InvalidationStreamWorker.perform_now(type, iri.to_s, self.class.iri.to_s)
|
36
|
+
rescue StandardError
|
37
|
+
LinkedRails::InvalidationStreamWorker.perform_later(type, iri.to_s, self.class.iri.to_s)
|
38
|
+
end
|
39
|
+
|
40
|
+
def should_publish_changes
|
41
|
+
cacheable? && !Rails.env.test?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -70,7 +70,9 @@ module LinkedRails
|
|
70
70
|
|
71
71
|
module ClassMethods
|
72
72
|
def collection_iri(**opts)
|
73
|
-
|
73
|
+
fragment = opts.delete(:fragment)
|
74
|
+
|
75
|
+
LinkedRails.iri(path: collection_root_relative_iri(**opts), fragment: fragment)
|
74
76
|
end
|
75
77
|
|
76
78
|
# Sets the defaults for all collections for this class.
|
@@ -22,17 +22,6 @@ module LinkedRails
|
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
-
def previous_changes_by_predicate
|
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| [self.class.predicate_for_key(k.to_sym), v] }
|
32
|
-
.select { |k, _v| k.present? }
|
33
|
-
]
|
34
|
-
end
|
35
|
-
|
36
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)
|
data/lib/linked_rails/model.rb
CHANGED
@@ -3,8 +3,7 @@
|
|
3
3
|
module LinkedRails
|
4
4
|
class ParamsParser # rubocop:disable Metrics/ClassLength
|
5
5
|
include ::Empathy::EmpJson::Helpers::Slices
|
6
|
-
include ::Empathy::EmpJson::Helpers::
|
7
|
-
include ::Empathy::EmpJson::Helpers::Values
|
6
|
+
include ::Empathy::EmpJson::Helpers::Parsing
|
8
7
|
|
9
8
|
attr_accessor :params, :slice, :user_context
|
10
9
|
delegate :filter_params, to: :collection_params_parser
|
@@ -63,11 +62,11 @@ module LinkedRails
|
|
63
62
|
def associated_class_from_params(reflection, object)
|
64
63
|
return reflection.klass unless reflection.polymorphic?
|
65
64
|
|
66
|
-
|
65
|
+
type = values_from_slice(slice, object, Vocab.rdfv[:type])
|
67
66
|
|
68
|
-
raise("No type given for '#{object}' referenced by polymorphic association '#{reflection.name}'") if
|
67
|
+
raise("No type given for '#{object}' referenced by polymorphic association '#{reflection.name}'") if type.blank?
|
69
68
|
|
70
|
-
iri_to_class(
|
69
|
+
iri_to_class(type)
|
71
70
|
end
|
72
71
|
|
73
72
|
def collection_params_parser
|
@@ -78,11 +77,11 @@ module LinkedRails
|
|
78
77
|
options = serializer_field(klass, predicate)
|
79
78
|
return unless value.count == 1 && options.present?
|
80
79
|
|
81
|
-
parsed_value = value.first.start_with?('http') ? RDF::URI(value.first) :
|
80
|
+
parsed_value = value.first.start_with?('http') ? RDF::URI(value.first) : value.first
|
82
81
|
parse_param(
|
83
82
|
klass,
|
84
83
|
options.predicate,
|
85
|
-
parsed_value
|
84
|
+
object_to_value(parsed_value)
|
86
85
|
)
|
87
86
|
end
|
88
87
|
|
@@ -119,9 +118,13 @@ module LinkedRails
|
|
119
118
|
end
|
120
119
|
|
121
120
|
def parse_attribute(klass, field_options, value)
|
122
|
-
|
121
|
+
[field_options.key, parse_attribute_value(klass, field_options, value)]
|
122
|
+
end
|
123
|
+
|
124
|
+
def parse_attribute_value(klass, field_options, value)
|
125
|
+
return nil if value == Vocab.libro[:null]
|
123
126
|
|
124
|
-
|
127
|
+
(parse_enum_attribute(klass, field_options.key, value) || value).to_s
|
125
128
|
end
|
126
129
|
|
127
130
|
def parse_enum_attribute(klass, key, value)
|
@@ -134,9 +137,15 @@ module LinkedRails
|
|
134
137
|
|
135
138
|
def parse_iri_param(iri, reflection)
|
136
139
|
key = foreign_key_for_reflection(reflection)
|
137
|
-
|
140
|
+
return unless key
|
138
141
|
|
139
|
-
|
142
|
+
if iri == Vocab.libro[:null]
|
143
|
+
[key, nil]
|
144
|
+
else
|
145
|
+
value = parse_iri_param_value(iri, reflection)
|
146
|
+
|
147
|
+
[key, value.to_s] if value
|
148
|
+
end
|
140
149
|
end
|
141
150
|
|
142
151
|
def parse_iri_param_value(iri, reflection)
|
data/lib/linked_rails/policy.rb
CHANGED
@@ -15,10 +15,10 @@ module LinkedRails
|
|
15
15
|
|
16
16
|
module ClassMethods
|
17
17
|
def action_triples(object, _params)
|
18
|
-
if object
|
19
|
-
[]
|
20
|
-
else
|
18
|
+
if named_object?(object) || object.try(:singular_resource?)
|
21
19
|
object.try(:action_triples) || []
|
20
|
+
else
|
21
|
+
[]
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
@@ -115,7 +115,13 @@ module LinkedRails
|
|
115
115
|
collection_name = "#{name.to_s.singularize}_collection"
|
116
116
|
opts[:association] ||= name
|
117
117
|
opts[:polymorphic] ||= true
|
118
|
-
|
118
|
+
|
119
|
+
action_object = opts.delete(:action_object)
|
120
|
+
opts[:if] ||= -> (object) {
|
121
|
+
return action_object if object.iri.path.end_with?('/action_object')
|
122
|
+
|
123
|
+
named_object?(object)
|
124
|
+
}
|
119
125
|
|
120
126
|
collection_opts = {}
|
121
127
|
collection_opts[:page_size] = opts.delete(:page_size) if opts.key?(:page_size)
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'redis'
|
4
|
+
|
5
|
+
module LinkedRails
|
6
|
+
class Storage
|
7
|
+
REDIS_DB = {
|
8
|
+
cache: LinkedRails.cache_redis_database,
|
9
|
+
persistent: LinkedRails.persistent_redis_database,
|
10
|
+
stream: LinkedRails.stream_redis_database
|
11
|
+
}.freeze
|
12
|
+
KEYS = {
|
13
|
+
manifest: 'cache:Manifest',
|
14
|
+
redirect_exact: 'cache:Redirect:Exact',
|
15
|
+
redirect_prefix: 'cache:Redirect:Prefix'
|
16
|
+
}.freeze
|
17
|
+
|
18
|
+
class << self
|
19
|
+
%i[xadd].each do |method|
|
20
|
+
define_method(method) do |db, *args|
|
21
|
+
Redis.new(db: REDIS_DB.fetch(db)).send(method, *args)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
%i[hset hdel hget].each do |method|
|
26
|
+
define_method(method) do |db, key, *args|
|
27
|
+
Redis.new(db: REDIS_DB.fetch(db)).send(method, KEYS.fetch(key), *args)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/linked_rails.rb
CHANGED
@@ -22,6 +22,10 @@ module LinkedRails
|
|
22
22
|
|
23
23
|
mattr_accessor :whitelisted_spi_ips
|
24
24
|
mattr_writer :host, :scheme
|
25
|
+
mattr_accessor :persistent_redis_database, default: ENV['PERSISTENT_REDIS_DATABASE'].presence || 6
|
26
|
+
mattr_accessor :stream_redis_database, default: ENV['STREAM_REDIS_DATABASE'].presence || 7
|
27
|
+
mattr_accessor :cache_redis_database, default: ENV['CACHE_REDIS_DATABASE'].presence || 8
|
28
|
+
mattr_accessor :cache_stream, default: ENV['CACHE_STREAM'].presence || 'transactions'
|
25
29
|
|
26
30
|
def self.configurable_class(parent, klass, default: nil, reader: nil) # rubocop:disable Metrics/AbcSize
|
27
31
|
method = :"#{[parent, klass.to_s.downcase].compact.join('_')}_class"
|
@@ -50,8 +54,10 @@ module LinkedRails
|
|
50
54
|
@@scheme ||= Rails.application.routes.default_url_options[:protocol] || :http # rubocop:disable Style/ClassVars
|
51
55
|
end
|
52
56
|
|
53
|
-
def iri(**
|
54
|
-
|
57
|
+
def iri(**args)
|
58
|
+
opts = {scheme: LinkedRails.scheme, host: LinkedRails.host}.merge(args)
|
59
|
+
opts[:path] = opts[:path].presence || '/'
|
60
|
+
RDF::URI.new(**opts)
|
55
61
|
end
|
56
62
|
end
|
57
63
|
|
@@ -84,6 +90,7 @@ ActiveSupport::Inflector.inflections do |inflect|
|
|
84
90
|
inflect.acronym 'SHACL'
|
85
91
|
end
|
86
92
|
|
93
|
+
require 'linked_rails/errors'
|
87
94
|
require 'linked_rails/uri_template'
|
88
95
|
require 'linked_rails/vocab'
|
89
96
|
require 'linked_rails/cache'
|
@@ -101,3 +108,5 @@ require 'linked_rails/routes'
|
|
101
108
|
require 'linked_rails/serializer'
|
102
109
|
require 'linked_rails/translate'
|
103
110
|
require 'linked_rails/railtie'
|
111
|
+
require 'linked_rails/url'
|
112
|
+
require 'linked_rails/storage'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: linked_rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.4.pre.
|
4
|
+
version: 0.0.4.pre.g9e3bcac55
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Arthur Dingemans
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-08
|
11
|
+
date: 2022-09-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: active_response
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 1.3.0
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 1.3.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: kaminari-activerecord
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: redis
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: rdf
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -285,11 +299,12 @@ files:
|
|
285
299
|
- app/policies/linked_rails/collection/view_policy.rb
|
286
300
|
- app/policies/linked_rails/collection_policy.rb
|
287
301
|
- app/policies/linked_rails/enum_value_policy.rb
|
302
|
+
- app/policies/linked_rails/form_policy.rb
|
288
303
|
- app/policies/linked_rails/menus/item_policy.rb
|
289
304
|
- app/policies/linked_rails/menus/list_policy.rb
|
305
|
+
- app/policies/linked_rails/ontology_policy.rb
|
290
306
|
- app/policies/linked_rails/sequence_policy.rb
|
291
307
|
- app/serializers/linked_rails/actions/item_serializer.rb
|
292
|
-
- app/serializers/linked_rails/actions/object_serializer.rb
|
293
308
|
- app/serializers/linked_rails/collection/filter_field_serializer.rb
|
294
309
|
- app/serializers/linked_rails/collection/filter_option_serializer.rb
|
295
310
|
- app/serializers/linked_rails/collection/filter_serializer.rb
|
@@ -324,6 +339,7 @@ files:
|
|
324
339
|
- app/serializers/linked_rails/web_page_serializer.rb
|
325
340
|
- app/serializers/linked_rails/web_site_serializer.rb
|
326
341
|
- app/serializers/linked_rails/widget_serializer.rb
|
342
|
+
- app/workers/linked_rails/invalidation_stream_worker.rb
|
327
343
|
- config/initializers/inflections.rb
|
328
344
|
- lib/generators/linked_rails/install/install_generator.rb
|
329
345
|
- lib/generators/linked_rails/install/templates/README
|
@@ -373,6 +389,8 @@ files:
|
|
373
389
|
- lib/linked_rails/enhancements/destroyable/controller.rb
|
374
390
|
- lib/linked_rails/enhancements/updatable/controller.rb
|
375
391
|
- lib/linked_rails/enhancements/updatable/serializer.rb
|
392
|
+
- lib/linked_rails/errors.rb
|
393
|
+
- lib/linked_rails/errors/forbidden.rb
|
376
394
|
- lib/linked_rails/helpers/delta_helper.rb
|
377
395
|
- lib/linked_rails/helpers/ontola_actions_helper.rb
|
378
396
|
- lib/linked_rails/helpers/resource_helper.rb
|
@@ -381,6 +399,7 @@ files:
|
|
381
399
|
- lib/linked_rails/middleware/linked_data_params.rb
|
382
400
|
- lib/linked_rails/model.rb
|
383
401
|
- lib/linked_rails/model/actionable.rb
|
402
|
+
- lib/linked_rails/model/cacheable.rb
|
384
403
|
- lib/linked_rails/model/collections.rb
|
385
404
|
- lib/linked_rails/model/dirty.rb
|
386
405
|
- lib/linked_rails/model/enhancements.rb
|
@@ -404,10 +423,12 @@ files:
|
|
404
423
|
- lib/linked_rails/serializer/actionable.rb
|
405
424
|
- lib/linked_rails/serializer/menuable.rb
|
406
425
|
- lib/linked_rails/serializer/singularable.rb
|
426
|
+
- lib/linked_rails/storage.rb
|
407
427
|
- lib/linked_rails/test_methods.rb
|
408
428
|
- lib/linked_rails/translate.rb
|
409
429
|
- lib/linked_rails/types/iri_type.rb
|
410
430
|
- lib/linked_rails/uri_template.rb
|
431
|
+
- lib/linked_rails/url.rb
|
411
432
|
- lib/linked_rails/version.rb
|
412
433
|
- lib/linked_rails/vocab.rb
|
413
434
|
- lib/nill_class_renderer.rb
|