linked_rails 0.0.4.pre.gfe77aae19 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +674 -21
  3. data/app/controllers/linked_rails/bulk_controller.rb +16 -81
  4. data/app/models/linked_rails/actions/item.rb +30 -23
  5. data/app/models/linked_rails/actions/list.rb +6 -0
  6. data/app/models/linked_rails/collection/filter_field.rb +3 -16
  7. data/app/models/linked_rails/collection/filterable.rb +4 -6
  8. data/app/models/linked_rails/collection/infinite.rb +6 -17
  9. data/app/models/linked_rails/collection/iri.rb +2 -25
  10. data/app/models/linked_rails/collection/iri_mapping.rb +1 -1
  11. data/app/models/linked_rails/collection/paginated.rb +3 -4
  12. data/app/models/linked_rails/collection/view.rb +10 -38
  13. data/app/models/linked_rails/collection.rb +25 -27
  14. data/app/models/linked_rails/enum_value.rb +1 -1
  15. data/app/models/linked_rails/form/field/association_input.rb +1 -7
  16. data/app/models/linked_rails/form/field/file_input.rb +0 -1
  17. data/app/models/linked_rails/form/field.rb +5 -31
  18. data/app/models/linked_rails/form/field_factory.rb +16 -29
  19. data/app/models/linked_rails/form/group.rb +4 -2
  20. data/app/models/linked_rails/form/page.rb +0 -4
  21. data/app/models/linked_rails/form.rb +6 -4
  22. data/app/models/linked_rails/manifest.rb +20 -94
  23. data/app/models/linked_rails/menus/item.rb +13 -10
  24. data/app/models/linked_rails/menus/list.rb +5 -13
  25. data/app/models/linked_rails/ontology/base.rb +0 -2
  26. data/app/models/linked_rails/ontology.rb +0 -1
  27. data/app/models/linked_rails/property_query.rb +0 -2
  28. data/app/models/linked_rails/sequence.rb +11 -2
  29. data/app/models/linked_rails/web_page.rb +4 -0
  30. data/app/models/linked_rails/web_site.rb +4 -0
  31. data/app/models/linked_rails/widget.rb +10 -3
  32. data/app/policies/linked_rails/collection_policy.rb +2 -2
  33. data/app/serializers/linked_rails/actions/item_serializer.rb +4 -3
  34. data/app/serializers/linked_rails/collection/filter_field_serializer.rb +2 -3
  35. data/app/serializers/linked_rails/collection/filter_option_serializer.rb +1 -1
  36. data/app/serializers/linked_rails/collection/filter_serializer.rb +1 -1
  37. data/app/serializers/linked_rails/collection/sorting_serializer.rb +1 -1
  38. data/app/serializers/linked_rails/collection/view_serializer.rb +3 -3
  39. data/app/serializers/linked_rails/collection_serializer.rb +13 -9
  40. data/app/serializers/linked_rails/condition_serializer.rb +3 -3
  41. data/app/serializers/linked_rails/entry_point_serializer.rb +2 -2
  42. data/app/serializers/linked_rails/enum_value_serializer.rb +0 -1
  43. data/app/serializers/linked_rails/form/field/association_input_serializer.rb +0 -1
  44. data/app/serializers/linked_rails/form/field_serializer.rb +1 -3
  45. data/app/serializers/linked_rails/form/group_serializer.rb +1 -1
  46. data/app/serializers/linked_rails/form/page_serializer.rb +1 -1
  47. data/app/serializers/linked_rails/menus/item_serializer.rb +3 -3
  48. data/app/serializers/linked_rails/menus/list_serializer.rb +1 -1
  49. data/app/serializers/linked_rails/sequence_serializer.rb +5 -2
  50. data/app/serializers/linked_rails/shacl/node_shape_serializer.rb +1 -1
  51. data/app/serializers/linked_rails/shacl/property_shape_serializer.rb +1 -1
  52. data/app/serializers/linked_rails/shacl/shape_serializer.rb +5 -5
  53. data/app/serializers/linked_rails/web_page_serializer.rb +3 -3
  54. data/app/serializers/linked_rails/web_site_serializer.rb +1 -1
  55. data/app/serializers/linked_rails/widget_serializer.rb +3 -3
  56. data/lib/generators/linked_rails/install/templates/locales.yml +0 -4
  57. data/lib/generators/linked_rails/install/templates/rdf_serializers_initializer.rb +1 -1
  58. data/lib/generators/linked_rails/model/templates/form.rb.tt +1 -1
  59. data/lib/generators/linked_rails/model/templates/policy.rb.tt +1 -1
  60. data/lib/generators/linked_rails/model/templates/serializer.rb.tt +1 -5
  61. data/lib/linked_rails/active_response/controller/collections.rb +1 -1
  62. data/lib/linked_rails/active_response/controller/crud_defaults.rb +1 -1
  63. data/lib/linked_rails/active_response/controller/params.rb +6 -6
  64. data/lib/linked_rails/active_response/controller.rb +22 -1
  65. data/lib/linked_rails/collection_params_parser.rb +1 -1
  66. data/lib/linked_rails/controller/actionable.rb +2 -5
  67. data/lib/linked_rails/controller/default_actions/create.rb +2 -2
  68. data/lib/linked_rails/controller/error_handling.rb +7 -12
  69. data/lib/linked_rails/controller.rb +1 -4
  70. data/lib/linked_rails/helpers/delta_helper.rb +58 -4
  71. data/lib/linked_rails/helpers/resource_helper.rb +1 -13
  72. data/lib/linked_rails/iri_mapper.rb +1 -1
  73. data/lib/linked_rails/middleware/linked_data_params.rb +27 -26
  74. data/lib/linked_rails/model/actionable.rb +8 -7
  75. data/lib/linked_rails/model/collections.rb +4 -10
  76. data/lib/linked_rails/model/dirty.rb +18 -6
  77. data/lib/linked_rails/model/filtering.rb +2 -2
  78. data/lib/linked_rails/model/indexable.rb +1 -1
  79. data/lib/linked_rails/model/iri.rb +2 -14
  80. data/lib/linked_rails/model/iri_mapping.rb +3 -3
  81. data/lib/linked_rails/model/menuable.rb +7 -1
  82. data/lib/linked_rails/model/serialization.rb +6 -2
  83. data/lib/linked_rails/model/singularable.rb +5 -7
  84. data/lib/linked_rails/model/tables.rb +1 -1
  85. data/lib/linked_rails/model.rb +2 -5
  86. data/lib/linked_rails/params_parser.rb +47 -48
  87. data/lib/linked_rails/policy.rb +30 -21
  88. data/lib/linked_rails/renderers.rb +0 -1
  89. data/lib/linked_rails/routes.rb +0 -1
  90. data/lib/linked_rails/serializer/singularable.rb +1 -1
  91. data/lib/linked_rails/serializer.rb +2 -6
  92. data/lib/linked_rails/translate.rb +0 -12
  93. data/lib/linked_rails/vocab.rb +0 -1
  94. data/lib/linked_rails.rb +3 -16
  95. data/lib/rdf/query_fix.rb +1 -1
  96. metadata +10 -59
  97. data/app/controllers/linked_rails/actions/objects_controller.rb +0 -9
  98. data/app/models/linked_rails/actions/object.rb +0 -38
  99. data/app/models/linked_rails/form/field/url_input.rb +0 -10
  100. data/app/policies/linked_rails/actions/object_policy.rb +0 -11
  101. data/app/policies/linked_rails/form_policy.rb +0 -13
  102. data/app/policies/linked_rails/ontology_policy.rb +0 -13
  103. data/app/serializers/linked_rails/actions/object_serializer.rb +0 -9
  104. data/app/serializers/linked_rails/form/field/file_input_serializer.rb +0 -11
  105. data/app/serializers/linked_rails/property_query_serializer.rb +0 -7
  106. data/app/workers/linked_rails/invalidation_stream_worker.rb +0 -16
  107. data/lib/linked_rails/controller/delta.rb +0 -58
  108. data/lib/linked_rails/controller/rendering.rb +0 -48
  109. data/lib/linked_rails/errors/forbidden.rb +0 -37
  110. data/lib/linked_rails/errors.rb +0 -3
  111. data/lib/linked_rails/middleware/error_handling.rb +0 -51
  112. data/lib/linked_rails/model/cacheable.rb +0 -45
  113. data/lib/linked_rails/railtie.rb +0 -11
  114. data/lib/linked_rails/storage.rb +0 -32
  115. data/lib/linked_rails/types/iri_type.rb +0 -37
  116. data/lib/linked_rails/url.rb +0 -11
  117. data/lib/rdf/list.rb +0 -9
@@ -7,7 +7,9 @@ module LinkedRails
7
7
  include ActiveSupport::Rescuable
8
8
 
9
9
  included do
10
- rescue_from LinkedRails::Errors::Forbidden, with: :handle_error
10
+ rescue_from StandardError, with: :handle_and_report_error
11
+ rescue_from ActiveRecord::RecordNotFound, with: :handle_error
12
+ rescue_from Pundit::NotAuthorizedError, with: :handle_error
11
13
  end
12
14
 
13
15
  private
@@ -20,14 +22,7 @@ module LinkedRails
20
22
  !%w[GET HEAD].include?(request.method)
21
23
  end
22
24
 
23
- def handle_and_report_error(error)
24
- report_error(error) if error_status(error) == 500
25
- handle_error(error)
26
- end
27
-
28
25
  def handle_error(error)
29
- raise(error) if response_body
30
-
31
26
  respond_to do |format|
32
27
  (LinkedRails::Renderers.rdf_content_types + [:json]).each do |type|
33
28
  format.send(type) { error_response_serializer(error, type) }
@@ -35,10 +30,11 @@ module LinkedRails
35
30
  end
36
31
  end
37
32
 
38
- def report_error(error)
39
- raise(error) if Rails.env.development? || Rails.env.test?
33
+ def handle_and_report_error(error)
34
+ raise if Rails.env.development? || Rails.env.test?
35
+ raise if response_body
40
36
 
41
- Rails.logger.error(error)
37
+ handle_error(error)
42
38
  end
43
39
 
44
40
  def error_mode(exception)
@@ -73,7 +69,6 @@ module LinkedRails
73
69
  'Doorkeeper::Errors::InvalidGrantReuse' => 422,
74
70
  'LinkedRails::Auth::Errors::Expired' => 410,
75
71
  'LinkedRails::Auth::Errors::Unauthorized' => 401,
76
- 'LinkedRails::Errors::Forbidden' => 403,
77
72
  'Pundit::NotAuthorizedError' => 403
78
73
  }
79
74
  end
@@ -4,8 +4,6 @@ require_relative 'active_response/controller'
4
4
  require_relative 'controller/actionable'
5
5
  require_relative 'controller/authorization'
6
6
  require_relative 'controller/error_handling'
7
- require_relative 'controller/delta'
8
- require_relative 'controller/rendering'
9
7
 
10
8
  module LinkedRails
11
9
  module Controller
@@ -22,9 +20,8 @@ module LinkedRails
22
20
  include LinkedRails::Controller::Actionable
23
21
  include LinkedRails::Controller::Authorization
24
22
  include LinkedRails::Controller::ErrorHandling
25
- include LinkedRails::Controller::Rendering
26
- include LinkedRails::Controller::Delta
27
23
  include LinkedRails::Helpers::OntolaActionsHelper
24
+ include LinkedRails::Helpers::DeltaHelper
28
25
  include LinkedRails::Helpers::ResourceHelper
29
26
 
30
27
  before_action :set_manifest_header
@@ -3,8 +3,64 @@
3
3
  module LinkedRails
4
4
  module Helpers
5
5
  module DeltaHelper
6
+ def changed_relation_triples(predicate, destructed, resources)
7
+ related_resource_invalidations = resources.map(&method(:invalidate_resource_delta))
8
+
9
+ return related_resource_invalidations unless predicate
10
+
11
+ if destructed
12
+ return related_resource_invalidations + [
13
+ [current_resource.iri, predicate, Vocab.sp[:Variable], delta_iri(:remove)]
14
+ ]
15
+ end
16
+
17
+ related_resource_invalidations + resources.map do |resource|
18
+ [current_resource.iri, predicate, resource.iri, delta_iri(:replace)]
19
+ end
20
+ end
21
+
22
+ def changed_relations_triples # rubocop:disable Metrics/AbcSize
23
+ current_resource.previously_changed_relations.flat_map do |key, value|
24
+ if key.to_s.ends_with?('_collection')
25
+ [invalidate_collection_delta(current_resource.send(key))]
26
+ else
27
+ destructed = current_resource.send(:association_has_destructed?, key)
28
+ many = value.relationship_type == :has_many
29
+ relation = current_resource.send(key)
30
+ changed_relation_triples(value.predicate, destructed, many ? relation : [relation])
31
+ end
32
+ end
33
+ end
34
+
35
+ def change_triple(predicate, value)
36
+ if value.nil?
37
+ RDF::Statement.new(current_resource.iri, predicate, Vocab.sp[:Variable], graph_name: delta_iri(:remove))
38
+ else
39
+ RDF::Statement.new(current_resource.iri, predicate, value, graph_name: delta_iri(:replace))
40
+ end
41
+ end
42
+
43
+ def changes_triples # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
44
+ serializer_class = RDF::Serializers.serializer_for(current_resource)
45
+ return [] if serializer_class.blank?
46
+
47
+ serializer = serializer_class.new(current_resource)
48
+
49
+ current_resource.previous_changes_by_predicate.map do |predicate, (_old_value, _new_value)|
50
+ serializer_attributes = current_resource.class.predicate_mapping[predicate]
51
+ next if serializer_attributes.is_a?(FastJsonapi::Relationship)
52
+
53
+ attr_name = serializer_attributes.key
54
+ serialized_value =
55
+ serializer.class.attributes_to_serialize[attr_name]&.serialize(current_resource, serializer_params, {})
56
+ (serialized_value.is_a?(Array) ? serialized_value : [serialized_value]).map do |value|
57
+ change_triple(predicate, value)
58
+ end
59
+ end.compact.flatten
60
+ end
61
+
6
62
  def delta_iri(delta)
7
- %i[remove replace invalidate].include?(delta) ? Vocab.ontola[delta] : Vocab.ld[delta]
63
+ %i[remove replace invalidate].include?(delta) ? Vocab.ontola[delta] : Vocab.ll[delta]
8
64
  end
9
65
 
10
66
  def invalidate_collection_delta(collection)
@@ -12,9 +68,7 @@ module LinkedRails
12
68
  end
13
69
 
14
70
  def invalidate_parent_collections_delta(resource)
15
- context = user_context if respond_to?(:user_context, true)
16
-
17
- resource&.parent_collections(context)&.map(&method(:invalidate_collection_delta)) || []
71
+ resource&.parent_collections(try(:user_context))&.map(&method(:invalidate_collection_delta)) || []
18
72
  end
19
73
 
20
74
  def invalidate_resource_delta(resource)
@@ -41,28 +41,16 @@ module LinkedRails
41
41
  end
42
42
 
43
43
  def requested_resource_parent
44
- requested_resource.try(:parent)
44
+ requested_resource&.parent
45
45
  end
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
-
56
49
  def build_new_resource
57
50
  controller_class.build_new(user_context: user_context)
58
51
  end
59
52
 
60
53
  def new_resource_from_parent
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
65
-
66
54
  parent_resource.build_child(
67
55
  controller_class,
68
56
  user_context: user_context
@@ -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, SystemStackError
41
+ rescue ActionController::RoutingError
42
42
  EMPTY_IRI_OPTS.dup
43
43
  end
44
44
 
@@ -1,13 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # require 'empathy/emp_json'
4
-
5
3
  module LinkedRails
6
4
  module Middleware
7
5
  class LinkedDataParams
8
- include ::Empathy::EmpJson::Helpers::Slices
9
- include ::Empathy::EmpJson::Helpers::Parsing
10
-
11
6
  def initialize(app)
12
7
  @app = app
13
8
  end
@@ -15,7 +10,7 @@ module LinkedRails
15
10
  def call(env)
16
11
  req = Rack::Request.new(env)
17
12
  params_from_query(req)
18
- params_from_slice(req)
13
+ params_from_graph(req)
19
14
 
20
15
  @app.call(env)
21
16
  end
@@ -36,32 +31,38 @@ module LinkedRails
36
31
  request.update_param(class_key, data) if data.present?
37
32
  end
38
33
 
39
- def slice_from_request(request)
40
- return unless request.content_type == Mime::Type.lookup_by_extension(:empjson).to_s
41
-
42
- body = request.body.read
34
+ def graph_from_request(request)
35
+ request_graph = request.delete_param("<#{Vocab.ll[:graph].value}>")
36
+ return if request_graph.blank?
43
37
 
44
- JSON.parse(body) if body.present?
38
+ RDF::Graph.load(
39
+ request_graph[:tempfile].path,
40
+ content_type: request_graph[:type],
41
+ canonicalize: true,
42
+ intern: false
43
+ )
45
44
  end
46
45
 
47
- # Converts a emp slice from to a nested attributes hash.
46
+ # Converts a serialized graph from a multipart request body to a nested
47
+ # attributes hash.
48
48
  #
49
- # The entrypoint for the slice is the `.` subject, which is
49
+ # The graph sent to the server should be sent under the `ll:graph` form name.
50
+ # The entrypoint for the graph is the `ll:targetResource` subject, which is
50
51
  # assumed to be the resource intended to be targeted by the request (i.e. the
51
52
  # resource to be created, updated, or deleted).
52
53
  #
53
54
  # @return [Hash] A hash of attributes, empty if no statements were given.
54
- def params_from_slice(request)
55
- slice = slice_from_request(request)
55
+ def params_from_graph(request)
56
+ graph = graph_from_request(request)
56
57
 
57
- return unless slice
58
+ return unless graph
58
59
 
59
- request.env['emp_json'] = slice
60
+ request.update_param(:body_graph, graph)
60
61
  target_class = target_class_from_path(request)
61
62
  return if target_class.blank?
62
63
 
63
- update_actor_param(request, slice)
64
- update_target_params(request, slice, target_class)
64
+ update_actor_param(request, graph)
65
+ update_target_params(request, graph, target_class)
65
66
  end
66
67
 
67
68
  def params_from_query(request)
@@ -82,19 +83,19 @@ module LinkedRails
82
83
  opts[:class]
83
84
  end
84
85
 
85
- def update_actor_param(request, slice)
86
- actor = values_from_slice(slice, '.', Vocab.schema.creator)
87
-
86
+ def update_actor_param(request, graph)
87
+ actor = graph.query([Vocab.ll[:targetResource], Vocab.schema.creator]).first
88
88
  return if actor.blank?
89
89
 
90
- request.update_param(:actor_iri, emp_to_primitive(actor))
90
+ request.update_param(:actor_iri, actor.object)
91
+ graph.delete(actor)
91
92
  end
92
93
 
93
- def update_target_params(request, slice, target_class)
94
+ def update_target_params(request, graph, target_class)
94
95
  key = target_class.to_s.demodulize.underscore
95
96
 
96
- parser = ParamsParser.new(slice: slice, params: request.params)
97
- from_body = parser.parse_resource('.', target_class)
97
+ parser = ParamsParser.new(graph: graph, params: request.params)
98
+ from_body = parser.parse_resource(Vocab.ll[:targetResource], target_class)
98
99
 
99
100
  request.update_param(key, from_body.merge(request.params[key] || {}))
100
101
  end
@@ -45,7 +45,9 @@ module LinkedRails
45
45
 
46
46
  module ClassMethods
47
47
  def action_list
48
- @action_list ||= define_action_list
48
+ return @action_list if @action_list.try(:actionable_class) == self
49
+
50
+ @action_list = defined_action_list || define_action_list
49
51
  end
50
52
 
51
53
  private
@@ -54,13 +56,12 @@ module LinkedRails
54
56
  superclass.try(:action_list) || LinkedRails.action_list_parent_class
55
57
  end
56
58
 
59
+ def defined_action_list
60
+ 'ActionList'.safe_constantize
61
+ end
62
+
57
63
  def define_action_list
58
- klass = Class.new(action_superclass)
59
- actionable_class = self
60
- klass.define_singleton_method(:actionable_class) do
61
- actionable_class
62
- end
63
- klass
64
+ const_set('ActionList', Class.new(action_superclass))
64
65
  end
65
66
  end
66
67
  end
@@ -37,8 +37,6 @@ module LinkedRails
37
37
  association_class: nil,
38
38
  # association_scope [Sym] The scope applied to the collection.
39
39
  association_scope: nil,
40
- # call_to_action [String] The label shown as call to action.
41
- call_to_action: nil,
42
40
  # collection_class [Class] The base class of the collection.
43
41
  # If you want to use a class other than LinkedRails.collection_class.
44
42
  collection_class: nil,
@@ -46,8 +44,6 @@ module LinkedRails
46
44
  default_filters: {},
47
45
  # collection_class [Array<Hash>] The default sortings applied to the collection.
48
46
  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
47
  # iri_template_keys [Array<Sym>] Custom query keys for the iri template
52
48
  iri_template_keys: [],
53
49
  # joins [Array<Sym>, Sym] The associations to join
@@ -62,9 +58,7 @@ module LinkedRails
62
58
  # Set to false to skip scoping
63
59
  policy_scope: -> { policy ? policy::Scope : Pundit::PolicyFinder.new(filtered_association).scope! },
64
60
  # 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
61
+ route_key: nil
68
62
  }.freeze
69
63
  COLLECTION_OPTIONS = COLLECTION_CUSTOMIZABLE_OPTIONS.merge(COLLECTION_STATIC_OPTIONS)
70
64
 
@@ -85,7 +79,7 @@ module LinkedRails
85
79
 
86
80
  _default_collection_opts[key] = value
87
81
  end
88
- _default_collection_opts[:iri_template] = _default_collection_opts[:collection_class].generate_iri_template(
82
+ _default_collection_opts[:iri_template] = LinkedRails.collection_class.generate_iri_template(
89
83
  _default_collection_opts[:iri_template_keys]
90
84
  )
91
85
  _default_collection_opts
@@ -121,7 +115,7 @@ module LinkedRails
121
115
  options[:association] ||= name.to_sym
122
116
  options[:association_class] ||= name.to_s.classify.constantize
123
117
  merged_options = options[:association_class].default_collection_options.merge(options)
124
- merged_options[:iri_template] = merged_options[:collection_class].generate_iri_template(
118
+ merged_options[:iri_template] = LinkedRails.collection_class.generate_iri_template(
125
119
  merged_options[:iri_template_keys]
126
120
  )
127
121
  collections_add(name: name, options: merged_options)
@@ -153,7 +147,7 @@ module LinkedRails
153
147
 
154
148
  _default_collection_opts[:collection_class] ||= LinkedRails.collection_class
155
149
  _default_collection_opts[:association_class] = self
156
- _default_collection_opts[:iri_template] = _default_collection_opts[:collection_class].generate_iri_template(
150
+ _default_collection_opts[:iri_template] = LinkedRails.collection_class.generate_iri_template(
157
151
  _default_collection_opts[:iri_template_keys]
158
152
  )
159
153
 
@@ -22,28 +22,40 @@ module LinkedRails
22
22
  end
23
23
  end
24
24
 
25
- def previously_changed_relations(inverted = nil)
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
+ def previously_changed_relations
26
37
  serializer_class = RDF::Serializers.serializer_for(self)
27
38
  return {} unless serializer_class.try(:relationships_to_serialize)
28
39
 
29
40
  serializer_class.relationships_to_serialize.select do |key, _value|
30
41
  if respond_to?(key)
31
42
  association_key = key.to_s.ends_with?('_collection') ? send(key).association : key
32
- association_has_destructed?(association_key) || association_changed?(association_key, inverted)
43
+ association_has_destructed?(association_key) || association_changed?(association_key)
33
44
  end
34
45
  end.with_indifferent_access
35
46
  end
36
47
 
37
48
  private
38
49
 
39
- def association_changed?(association, inverted) # rubocop:disable Metrics/AbcSize
50
+ def association_changed?(association) # rubocop:disable Metrics/AbcSize
40
51
  ids_method = "#{association.to_s.singularize}_ids"
41
52
  return true if previous_changes.include?("#{association}_id") || previous_changes.include?(ids_method)
42
53
  return false unless try(:association_cached?, association)
43
- records = self.class.reflect_on_association(association).collection? ? send(association) : [send(association)]
44
54
 
45
- records.reject { |a| a == inverted }.any? do |a|
46
- a&.previous_changes.present? || a&.previously_changed_relations(self).present?
55
+ if self.class.reflect_on_association(association).collection?
56
+ send(association).any? { |a| a.previous_changes.present? }
57
+ else
58
+ send(association)&.previous_changes&.present?
47
59
  end
48
60
  end
49
61
 
@@ -17,7 +17,7 @@ module LinkedRails
17
17
  def filterable(**options)
18
18
  initialize_filter_options
19
19
 
20
- self.filter_options = filter_options.merge(options)
20
+ self.filter_options = HashWithIndifferentAccess.new(options).merge(filter_options)
21
21
 
22
22
  options.map { |k, filter| define_filter_method(k, filter) }
23
23
  end
@@ -34,7 +34,7 @@ module LinkedRails
34
34
  def initialize_filter_options
35
35
  return if filter_options && method(:filter_options).owner == singleton_class
36
36
 
37
- self.filter_options = superclass.try(:filter_options)&.dup || HashWithIndifferentAccess.new
37
+ self.filter_options = superclass.try(:filter_options)&.dup || {}
38
38
  end
39
39
 
40
40
  def define_filter_method(key, filter)
@@ -27,7 +27,7 @@ module LinkedRails
27
27
  private
28
28
 
29
29
  def collection_from_parent_name(parent, _params)
30
- collection_name = "#{name.demodulize.underscore}_collection"
30
+ collection_name = "#{name.underscore}_collection"
31
31
 
32
32
  collection_name if parent.respond_to?(collection_name, true)
33
33
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module LinkedRails
4
4
  module Model
5
- module IRI
5
+ module Iri
6
6
  extend ActiveSupport::Concern
7
7
 
8
8
  def anonymous_iri
@@ -62,7 +62,6 @@ module LinkedRails
62
62
  iri = root_relative_iri.dup
63
63
  iri.scheme = LinkedRails.scheme
64
64
  iri.host = LinkedRails.host
65
- iri.path = iri.path.presence || '/'
66
65
  iri
67
66
  end
68
67
 
@@ -109,22 +108,11 @@ module LinkedRails
109
108
  @iri_template ||= LinkedRails::URITemplate.new("/#{route_key}{/id}{#fragment}")
110
109
  end
111
110
 
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)
119
- end
120
-
121
111
  def linked_rails_module?
122
112
  (Rails.version < '6' ? parents : module_parents).include?(LinkedRails)
123
113
  end
124
114
 
125
- def route_key
126
- try(:model_name)&.route_key || to_s.tableize
127
- end
115
+ delegate :route_key, to: :model_name
128
116
  end
129
117
  end
130
118
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module LinkedRails
4
4
  module Model
5
- module IRIMapping
5
+ module IriMapping
6
6
  extend ActiveSupport::Concern
7
7
 
8
8
  module ClassMethods
@@ -51,7 +51,7 @@ module LinkedRails
51
51
  if self < ActiveRecord::Base
52
52
  find_by(primary_key => params[:id]) if params.key?(:id)
53
53
  else
54
- new(params.except(:format))
54
+ new(params)
55
55
  end
56
56
  end
57
57
 
@@ -85,7 +85,7 @@ module LinkedRails
85
85
 
86
86
  def iri_without_action(url)
87
87
  iri = RDF::URI(url)
88
- iri.path = iri.path.split('/')[0...-1].join('/') if iri.path
88
+ iri.path = iri.path.split('/')[0...-1].join('/')
89
89
  iri.to_s
90
90
  end
91
91
 
@@ -14,7 +14,7 @@ module LinkedRails
14
14
  end
15
15
 
16
16
  def menu_list(user_context = nil)
17
- @menu_list ||= {}
17
+ @menu_list = {}
18
18
  @menu_list[user_context] ||= self.class.menu_class.new(resource: self, user_context: user_context)
19
19
  end
20
20
 
@@ -22,6 +22,12 @@ module LinkedRails
22
22
  def menu_class
23
23
  @menu_class ||= "#{name}MenuList".safe_constantize || "#{superclass.name}MenuList".safe_constantize
24
24
  end
25
+
26
+ def preview_includes
27
+ return super if menu_class.blank?
28
+
29
+ super + menu_class.defined_menus.keys.map { |tag| "#{tag}_menu" }
30
+ end
25
31
  end
26
32
  end
27
33
  end
@@ -16,6 +16,10 @@ module LinkedRails
16
16
  end
17
17
 
18
18
  module ClassMethods
19
+ def include_in_collection?
20
+ false
21
+ end
22
+
19
23
  def input_select_property
20
24
  Vocab.schema.name
21
25
  end
@@ -55,7 +59,7 @@ module LinkedRails
55
59
  serializer
56
60
  .attributes_to_serialize
57
61
  .values
58
- .select { |attr| attr.predicate.present? && !attr.predicate.is_a?(Proc) }
62
+ .select { |attr| attr.predicate.present? }
59
63
  .map { |attr| [attr.predicate, attr] }
60
64
  end
61
65
 
@@ -66,7 +70,7 @@ module LinkedRails
66
70
  serializer
67
71
  .relationships_to_serialize
68
72
  .values
69
- .select { |value| value.predicate.present? && !value.predicate.is_a?(Proc) }
73
+ .select { |value| value.predicate.present? }
70
74
  .map { |value| [value.predicate, value] }
71
75
  end
72
76
  end
@@ -14,7 +14,7 @@ module LinkedRails
14
14
  end
15
15
 
16
16
  def root_relative_iri(**opts)
17
- return super unless singular_iri?
17
+ return super unless anonymous_iri?
18
18
 
19
19
  root_relative_singular_iri(**opts)
20
20
  end
@@ -25,10 +25,6 @@ module LinkedRails
25
25
  @singular_iri ||= iri_with_root(root_relative_singular_iri)
26
26
  end
27
27
 
28
- def singular_iri?
29
- anonymous_iri?
30
- end
31
-
32
28
  def singular_iri_opts
33
29
  {}
34
30
  end
@@ -42,8 +38,6 @@ module LinkedRails
42
38
  end
43
39
 
44
40
  class_methods do
45
- delegate :singular_route_key, to: :model_name
46
-
47
41
  def requested_singular_resource(_params, _user_context)
48
42
  raise(NotImplementedError)
49
43
  end
@@ -51,6 +45,10 @@ module LinkedRails
51
45
  def singular_iri_template
52
46
  @singular_iri_template ||= LinkedRails::URITemplate.new("{/parent_iri*}/#{singular_route_key}{#fragment}")
53
47
  end
48
+
49
+ def singular_route_key
50
+ name.underscore
51
+ end
54
52
  end
55
53
  end
56
54
  end
@@ -18,7 +18,7 @@ module LinkedRails
18
18
  def initialize_columns
19
19
  return if defined_columns && method(:defined_columns).owner == singleton_class
20
20
 
21
- self.defined_columns = superclass.try(:defined_columns)&.dup || {}.with_indifferent_access
21
+ self.defined_columns = superclass.try(:defined_columns)&.dup || {}
22
22
  end
23
23
  end
24
24
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'model/actionable'
4
- require_relative 'model/cacheable'
5
4
  require_relative 'model/collections'
6
5
  require_relative 'model/dirty'
7
6
  require_relative 'model/enhancements'
@@ -24,8 +23,8 @@ module LinkedRails
24
23
  include Enhancements
25
24
  include Filtering
26
25
  include Indexable
27
- include IRI
28
- include IRIMapping
26
+ include Iri
27
+ include IriMapping
29
28
  include Menuable
30
29
  include Serialization
31
30
  include Singularable
@@ -38,8 +37,6 @@ module LinkedRails
38
37
 
39
38
  module ClassMethods
40
39
  def build_new(parent: nil, user_context: nil)
41
- raise(ActiveRecord::RecordNotFound) if try(:abstract_class?)
42
-
43
40
  new(**attributes_for_new(parent: parent, user_context: user_context))
44
41
  end
45
42