linked_rails 0.0.1 → 0.0.4.pre.g1faca9643

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.
Files changed (182) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +21 -674
  3. data/app/controllers/linked_rails/actions/objects_controller.rb +9 -0
  4. data/app/controllers/linked_rails/bulk_controller.rb +105 -22
  5. data/app/controllers/linked_rails/enum_values_controller.rb +0 -42
  6. data/app/models/linked_rails/actions/item.rb +64 -55
  7. data/app/models/linked_rails/actions/list.rb +6 -31
  8. data/app/models/linked_rails/actions/object.rb +38 -0
  9. data/app/models/linked_rails/collection/configuration.rb +55 -0
  10. data/app/models/linked_rails/collection/filter.rb +1 -1
  11. data/app/models/linked_rails/collection/filter_field.rb +19 -2
  12. data/app/models/linked_rails/collection/filter_option.rb +1 -1
  13. data/app/models/linked_rails/collection/filterable.rb +6 -9
  14. data/app/models/linked_rails/collection/infinite.rb +113 -0
  15. data/app/models/linked_rails/collection/infinite_view.rb +1 -90
  16. data/app/models/linked_rails/collection/iri.rb +47 -43
  17. data/app/models/linked_rails/collection/iri_mapping.rb +15 -7
  18. data/app/models/linked_rails/collection/paginated.rb +46 -0
  19. data/app/models/linked_rails/collection/paginated_view.rb +1 -33
  20. data/app/models/linked_rails/collection/sortable.rb +1 -9
  21. data/app/models/linked_rails/collection/sorting.rb +1 -1
  22. data/app/models/linked_rails/collection/view.rb +51 -14
  23. data/app/models/linked_rails/collection.rb +53 -85
  24. data/app/models/linked_rails/creative_work.rb +1 -1
  25. data/app/models/linked_rails/entry_point.rb +8 -5
  26. data/app/models/linked_rails/enum_value.rb +40 -2
  27. data/app/models/linked_rails/form/field/association_input.rb +7 -1
  28. data/app/models/linked_rails/form/field/file_input.rb +1 -0
  29. data/app/models/linked_rails/form/field/resource_field.rb +2 -0
  30. data/{lib/linked_rails/enhancements/indexable/model.rb → app/models/linked_rails/form/field/url_input.rb} +3 -3
  31. data/app/models/linked_rails/form/field.rb +37 -13
  32. data/app/models/linked_rails/form/field_factory.rb +48 -18
  33. data/app/models/linked_rails/form/group.rb +4 -6
  34. data/app/models/linked_rails/form/page.rb +8 -4
  35. data/app/models/linked_rails/form.rb +17 -21
  36. data/app/models/linked_rails/manifest.rb +102 -22
  37. data/app/models/linked_rails/menus/item.rb +10 -13
  38. data/app/models/linked_rails/menus/list.rb +16 -7
  39. data/app/models/linked_rails/ontology/base.rb +3 -1
  40. data/app/models/linked_rails/ontology/class.rb +3 -3
  41. data/app/models/linked_rails/ontology.rb +6 -5
  42. data/app/models/linked_rails/property_query.rb +2 -0
  43. data/app/models/linked_rails/sequence.rb +4 -13
  44. data/app/models/linked_rails/shacl/property_shape.rb +1 -1
  45. data/app/models/linked_rails/web_page.rb +0 -4
  46. data/app/models/linked_rails/web_site.rb +0 -4
  47. data/app/models/linked_rails/widget.rb +4 -11
  48. data/app/policies/linked_rails/actions/object_policy.rb +11 -0
  49. data/app/policies/linked_rails/collection_policy.rb +2 -2
  50. data/app/policies/linked_rails/form_policy.rb +13 -0
  51. data/app/policies/linked_rails/ontology_policy.rb +13 -0
  52. data/app/serializers/linked_rails/actions/item_serializer.rb +5 -5
  53. data/app/serializers/linked_rails/actions/object_serializer.rb +9 -0
  54. data/app/serializers/linked_rails/collection/filter_field_serializer.rb +3 -2
  55. data/app/serializers/linked_rails/collection/filter_option_serializer.rb +1 -1
  56. data/app/serializers/linked_rails/collection/filter_serializer.rb +1 -1
  57. data/app/serializers/linked_rails/collection/sorting_serializer.rb +1 -1
  58. data/app/serializers/linked_rails/collection/view_serializer.rb +3 -3
  59. data/app/serializers/linked_rails/collection_serializer.rb +8 -7
  60. data/app/serializers/linked_rails/condition_serializer.rb +3 -3
  61. data/app/serializers/linked_rails/entry_point_serializer.rb +3 -3
  62. data/app/serializers/linked_rails/enum_value_serializer.rb +1 -0
  63. data/app/serializers/linked_rails/form/field/association_input_serializer.rb +1 -0
  64. data/app/serializers/linked_rails/form/field/file_input_serializer.rb +11 -0
  65. data/app/serializers/linked_rails/form/field_serializer.rb +3 -1
  66. data/app/serializers/linked_rails/form/group_serializer.rb +1 -1
  67. data/app/serializers/linked_rails/form/page_serializer.rb +1 -1
  68. data/app/serializers/linked_rails/menus/item_serializer.rb +3 -3
  69. data/app/serializers/linked_rails/menus/list_serializer.rb +1 -1
  70. data/app/serializers/linked_rails/ontology_serializer.rb +2 -2
  71. data/app/serializers/linked_rails/property_query_serializer.rb +7 -0
  72. data/app/serializers/linked_rails/sequence_serializer.rb +2 -5
  73. data/app/serializers/linked_rails/shacl/node_shape_serializer.rb +1 -1
  74. data/app/serializers/linked_rails/shacl/property_shape_serializer.rb +1 -1
  75. data/app/serializers/linked_rails/shacl/shape_serializer.rb +5 -5
  76. data/app/serializers/linked_rails/web_page_serializer.rb +3 -3
  77. data/app/serializers/linked_rails/web_site_serializer.rb +1 -1
  78. data/app/serializers/linked_rails/widget_serializer.rb +3 -3
  79. data/app/workers/linked_rails/invalidation_stream_worker.rb +16 -0
  80. data/lib/generators/linked_rails/install/install_generator.rb +7 -8
  81. data/lib/generators/linked_rails/install/templates/README +7 -0
  82. data/lib/generators/linked_rails/install/templates/app_menu_list.rb +36 -7
  83. data/lib/generators/linked_rails/install/templates/application_menu_list.rb +40 -1
  84. data/lib/generators/linked_rails/install/templates/initializer.rb +1 -2
  85. data/lib/generators/linked_rails/install/templates/locales.yml +16 -0
  86. data/lib/generators/linked_rails/install/templates/rdf_serializers_initializer.rb +1 -1
  87. data/lib/generators/linked_rails/install/templates/vocab.rb +1 -0
  88. data/lib/generators/linked_rails/install/templates/vocab.yml +2 -2
  89. data/lib/generators/linked_rails/model/model_generator.rb +0 -1
  90. data/lib/generators/linked_rails/model/templates/controller.rb.tt +5 -1
  91. data/lib/generators/linked_rails/model/templates/form.rb.tt +3 -0
  92. data/lib/generators/linked_rails/model/templates/menu_list.rb.tt +15 -0
  93. data/lib/generators/linked_rails/model/templates/policy.rb.tt +13 -0
  94. data/lib/generators/linked_rails/model/templates/serializer.rb.tt +5 -1
  95. data/lib/linked_rails/active_response/controller/collections.rb +1 -1
  96. data/lib/linked_rails/active_response/controller/crud_defaults.rb +4 -4
  97. data/lib/linked_rails/active_response/controller/params.rb +10 -10
  98. data/lib/linked_rails/active_response/controller.rb +8 -18
  99. data/lib/linked_rails/active_response/responders/rdf.rb +19 -10
  100. data/lib/linked_rails/callable_variable.rb +1 -1
  101. data/lib/linked_rails/collection_params_parser.rb +93 -0
  102. data/lib/linked_rails/controller/actionable.rb +121 -0
  103. data/lib/linked_rails/controller/authorization.rb +6 -0
  104. data/lib/linked_rails/controller/default_actions/create.rb +52 -0
  105. data/lib/linked_rails/controller/default_actions/destroy.rb +42 -0
  106. data/lib/linked_rails/controller/default_actions/update.rb +43 -0
  107. data/lib/linked_rails/controller/delta.rb +58 -0
  108. data/lib/linked_rails/controller/error_handling.rb +14 -7
  109. data/lib/linked_rails/controller/rendering.rb +48 -0
  110. data/lib/linked_rails/controller.rb +24 -4
  111. data/lib/linked_rails/enhanceable.rb +1 -1
  112. data/lib/linked_rails/enhancements/creatable/controller.rb +1 -1
  113. data/lib/linked_rails/enhancements/destroyable/controller.rb +1 -1
  114. data/lib/linked_rails/enhancements/updatable/controller.rb +1 -1
  115. data/lib/linked_rails/enhancements.rb +0 -16
  116. data/lib/linked_rails/errors/forbidden.rb +37 -0
  117. data/lib/linked_rails/errors.rb +3 -0
  118. data/lib/linked_rails/helpers/delta_helper.rb +26 -57
  119. data/lib/linked_rails/helpers/ontola_actions_helper.rb +2 -2
  120. data/lib/linked_rails/helpers/resource_helper.rb +11 -2
  121. data/lib/linked_rails/iri_mapper.rb +17 -39
  122. data/lib/linked_rails/middleware/error_handling.rb +51 -0
  123. data/lib/linked_rails/middleware/linked_data_params.rb +30 -151
  124. data/lib/linked_rails/model/actionable.rb +68 -0
  125. data/lib/linked_rails/model/cacheable.rb +45 -0
  126. data/lib/linked_rails/model/collections.rb +201 -39
  127. data/lib/linked_rails/model/dirty.rb +6 -18
  128. data/lib/linked_rails/model/enhancements.rb +1 -6
  129. data/lib/linked_rails/model/filtering.rb +4 -6
  130. data/lib/linked_rails/model/indexable.rb +6 -16
  131. data/lib/linked_rails/model/iri.rb +29 -19
  132. data/lib/linked_rails/model/iri_mapping.rb +37 -8
  133. data/lib/linked_rails/model/menuable.rb +28 -0
  134. data/lib/linked_rails/model/serialization.rb +2 -15
  135. data/lib/linked_rails/model/singularable.rb +57 -0
  136. data/lib/linked_rails/model/sorting.rb +0 -5
  137. data/lib/linked_rails/model/tables.rb +26 -0
  138. data/lib/linked_rails/model.rb +18 -7
  139. data/lib/linked_rails/params_parser.rb +131 -54
  140. data/lib/linked_rails/policy/attribute_conditions.rb +2 -2
  141. data/lib/linked_rails/policy.rb +44 -46
  142. data/lib/linked_rails/railtie.rb +11 -0
  143. data/lib/linked_rails/rdf_error.rb +2 -2
  144. data/lib/linked_rails/renderers.rb +1 -0
  145. data/lib/linked_rails/routes.rb +38 -22
  146. data/lib/linked_rails/serializer/actionable.rb +27 -0
  147. data/lib/linked_rails/serializer/menuable.rb +31 -0
  148. data/lib/linked_rails/serializer/singularable.rb +26 -0
  149. data/lib/linked_rails/serializer.rb +28 -11
  150. data/lib/linked_rails/storage.rb +32 -0
  151. data/lib/linked_rails/test_methods.rb +114 -0
  152. data/lib/linked_rails/translate.rb +31 -9
  153. data/lib/linked_rails/types/iri_type.rb +37 -0
  154. data/lib/linked_rails/uri_template.rb +30 -0
  155. data/lib/linked_rails/url.rb +11 -0
  156. data/lib/linked_rails/version.rb +1 -1
  157. data/lib/linked_rails/vocab.rb +9 -0
  158. data/lib/linked_rails.rb +40 -14
  159. data/lib/rails/welcome_controller.rb +3 -2
  160. data/lib/rdf/list.rb +9 -0
  161. data/lib/rdf/query_fix.rb +15 -0
  162. metadata +80 -33
  163. data/app/models/linked_rails/actions/default_actions/create.rb +0 -60
  164. data/app/models/linked_rails/actions/default_actions/destroy.rb +0 -45
  165. data/app/models/linked_rails/actions/default_actions/update.rb +0 -50
  166. data/app/models/linked_rails/actions/default_actions.rb +0 -17
  167. data/lib/generators/linked_rails/install/templates/application_action_list.rb +0 -3
  168. data/lib/generators/linked_rails/model/templates/action_list.rb.tt +0 -6
  169. data/lib/linked_rails/enhancements/actionable/model.rb +0 -71
  170. data/lib/linked_rails/enhancements/actionable/serializer.rb +0 -25
  171. data/lib/linked_rails/enhancements/creatable/action.rb +0 -15
  172. data/lib/linked_rails/enhancements/destroyable/action.rb +0 -15
  173. data/lib/linked_rails/enhancements/destroyable/routing.rb +0 -19
  174. data/lib/linked_rails/enhancements/menuable/model.rb +0 -36
  175. data/lib/linked_rails/enhancements/menuable/serializer.rb +0 -33
  176. data/lib/linked_rails/enhancements/route_concerns.rb +0 -56
  177. data/lib/linked_rails/enhancements/singularable/controller.rb +0 -43
  178. data/lib/linked_rails/enhancements/singularable/model.rb +0 -47
  179. data/lib/linked_rails/enhancements/singularable/serializer.rb +0 -28
  180. data/lib/linked_rails/enhancements/tableable/model.rb +0 -28
  181. data/lib/linked_rails/enhancements/updatable/action.rb +0 -15
  182. data/lib/linked_rails/enhancements/updatable/routing.rb +0 -20
@@ -16,19 +16,6 @@ module LinkedRails
16
16
  end
17
17
 
18
18
  module ClassMethods
19
- def attributes_from_filters(filters)
20
- filters.each_with_object({}) do |(key, value), hash|
21
- next unless value.count == 1
22
-
23
- attribute = predicate_mapping[key]&.key
24
- hash[attribute] = value.first if attribute
25
- end
26
- end
27
-
28
- def include_in_collection?
29
- false
30
- end
31
-
32
19
  def input_select_property
33
20
  Vocab.schema.name
34
21
  end
@@ -68,7 +55,7 @@ module LinkedRails
68
55
  serializer
69
56
  .attributes_to_serialize
70
57
  .values
71
- .select { |attr| attr.predicate.present? }
58
+ .select { |attr| attr.predicate.present? && !attr.predicate.is_a?(Proc) }
72
59
  .map { |attr| [attr.predicate, attr] }
73
60
  end
74
61
 
@@ -79,7 +66,7 @@ module LinkedRails
79
66
  serializer
80
67
  .relationships_to_serialize
81
68
  .values
82
- .select { |value| value.predicate.present? }
69
+ .select { |value| value.predicate.present? && !value.predicate.is_a?(Proc) }
83
70
  .map { |value| [value.predicate, value] }
84
71
  end
85
72
  end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ module Model
5
+ module Singularable
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ attr_accessor :singular_resource
10
+ end
11
+
12
+ def root_relative_singular_iri(**opts)
13
+ RDF::URI(singular_iri_template.expand(singular_iri_opts.merge(opts)))
14
+ end
15
+
16
+ def root_relative_iri(**opts)
17
+ return super unless singular_iri?
18
+
19
+ root_relative_singular_iri(**opts)
20
+ end
21
+
22
+ def singular_iri(**opts)
23
+ return iri_with_root(root_relative_singular_iri(**opts)) if opts.present?
24
+
25
+ @singular_iri ||= iri_with_root(root_relative_singular_iri)
26
+ end
27
+
28
+ def singular_iri?
29
+ anonymous_iri?
30
+ end
31
+
32
+ def singular_iri_opts
33
+ {}
34
+ end
35
+
36
+ def singular_iri_template
37
+ self.class.singular_iri_template
38
+ end
39
+
40
+ def singular_resource?
41
+ singular_resource || false
42
+ end
43
+
44
+ class_methods do
45
+ delegate :singular_route_key, to: :model_name
46
+
47
+ def requested_singular_resource(_params, _user_context)
48
+ raise(NotImplementedError)
49
+ end
50
+
51
+ def singular_iri_template
52
+ @singular_iri_template ||= LinkedRails::URITemplate.new("{/parent_iri*}/#{singular_route_key}{#fragment}")
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -5,11 +5,6 @@ module LinkedRails
5
5
  module Sorting
6
6
  extend ActiveSupport::Concern
7
7
 
8
- included do
9
- class_attribute :default_sortings, instance_accessor: false, instance_predicate: false
10
- self.default_sortings = [{key: Vocab.schema.dateCreated, direction: :desc}]
11
- end
12
-
13
8
  module ClassMethods
14
9
  def default_sort_column
15
10
  :created_at
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ module Model
5
+ module Tables
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ class_attribute :defined_columns
10
+ end
11
+
12
+ module ClassMethods
13
+ def with_columns(opts)
14
+ initialize_columns
15
+ defined_columns.merge!(opts)
16
+ end
17
+
18
+ def initialize_columns
19
+ return if defined_columns && method(:defined_columns).owner == singleton_class
20
+
21
+ self.defined_columns = superclass.try(:defined_columns)&.dup || {}.with_indifferent_access
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'model/actionable'
4
+ require_relative 'model/cacheable'
3
5
  require_relative 'model/collections'
4
6
  require_relative 'model/dirty'
5
7
  require_relative 'model/enhancements'
@@ -7,33 +9,42 @@ require_relative 'model/filtering'
7
9
  require_relative 'model/indexable'
8
10
  require_relative 'model/iri'
9
11
  require_relative 'model/iri_mapping'
12
+ require_relative 'model/menuable'
10
13
  require_relative 'model/serialization'
14
+ require_relative 'model/singularable'
11
15
  require_relative 'model/sorting'
16
+ require_relative 'model/tables'
12
17
 
13
18
  module LinkedRails
14
19
  module Model
15
20
  extend ActiveSupport::Concern
21
+ include Actionable
16
22
  include Collections
17
23
  include Dirty
18
24
  include Enhancements
19
25
  include Filtering
20
26
  include Indexable
21
- include Iri
22
- include IriMapping
27
+ include IRI
28
+ include IRIMapping
29
+ include Menuable
23
30
  include Serialization
31
+ include Singularable
24
32
  include Sorting
33
+ include Tables
25
34
 
26
35
  def build_child(klass, user_context: nil)
27
36
  klass.build_new(parent: self, user_context: user_context)
28
37
  end
29
38
 
30
- def singular_resource?
31
- false
32
- end
33
-
34
39
  module ClassMethods
35
40
  def build_new(parent: nil, user_context: nil)
36
- new(attributes_for_new(parent: parent, user_context: user_context))
41
+ raise(ActiveRecord::RecordNotFound) if try(:abstract_class?)
42
+
43
+ new(**attributes_for_new(parent: parent, user_context: user_context))
44
+ end
45
+
46
+ def controller_class
47
+ @controller_class ||= "#{to_s.pluralize}Controller".safe_constantize || superclass.try(:controller_class)
37
48
  end
38
49
 
39
50
  def form_class
@@ -1,93 +1,170 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LinkedRails
4
- class ParamsParser
5
- attr_reader :params, :user_context
4
+ class ParamsParser # rubocop:disable Metrics/ClassLength
5
+ include ::Empathy::EmpJson::Helpers::Slices
6
+ include ::Empathy::EmpJson::Helpers::Parsing
6
7
 
7
- def initialize(params)
8
- @user_context = params[:user_context]
9
- @params = params.is_a?(Hash) ? ActionController::Parameters.new(params) : params
8
+ attr_accessor :params, :slice, :user_context
9
+ delegate :filter_params, to: :collection_params_parser
10
+
11
+ def initialize(slice: nil, params: {}, user_context: nil)
12
+ self.slice = slice
13
+ self.params = params
14
+ self.user_context = user_context
10
15
  end
11
16
 
12
- def before_params
13
- return @before_params if instance_variable_defined?(:@before_params)
17
+ def attributes_from_filters(klass)
18
+ ActionController::Parameters.new(
19
+ filter_params.each_with_object({}) do |(predicate, value), hash|
20
+ key_and_value = filter_to_param(klass, predicate, value)
21
+ hash[key_and_value.first] = key_and_value.second if key_and_value
22
+ end
23
+ )
24
+ end
14
25
 
15
- values = permit_params(before: [])[:before]
16
- return @before_params = nil if values.blank?
26
+ def parse_param(klass, predicate, object)
27
+ field_options = serializer_field(klass, predicate) unless predicate == '_id'
28
+ if field_options.is_a?(FastJsonapi::Attribute)
29
+ parse_attribute(klass, field_options, emp_to_primitive(object))
30
+ elsif field_options.is_a?(FastJsonapi::Relationship)
31
+ parse_association(klass, field_options, emp_to_primitive(object))
32
+ end
33
+ end
17
34
 
18
- @before_params = values.map do |f|
19
- key, value = f.split('=')
20
- {key: RDF::URI(CGI.unescape(key)), value: value}
35
+ # Recursively parses a resource from slice
36
+ def parse_resource(subject, klass)
37
+ (record_from_slice(slice, subject) || {}).reduce({}) do |h, (k, v)|
38
+ (v.is_a?(Array) ? v : [v]).each do |value|
39
+ param = parse_param(klass, RDF::URI(k), value)
40
+ param ? add_param(h, param.first, param.second) : h
41
+ end
42
+ h
21
43
  end
22
44
  end
23
45
 
24
- def collection_params
25
- return @collection_params if instance_variable_defined?(:@collection_params)
46
+ private
47
+
48
+ def add_param(hash, key, value) # rubocop:disable Metrics/MethodLength
49
+ case hash[key]
50
+ when nil
51
+ hash[key] = value
52
+ when Hash
53
+ hash[key].merge!(value)
54
+ when Array
55
+ hash[key].append(value)
56
+ else
57
+ hash[key] = [hash[key], value]
58
+ end
59
+ hash
60
+ end
26
61
 
27
- values = permit_params(:display, :page_size, :title, :type)
62
+ def associated_class_from_params(reflection, object)
63
+ return reflection.klass unless reflection.polymorphic?
28
64
 
29
- filter = filter_params
30
- values[:filter] = filter if filter
65
+ type = values_from_slice(slice, object, Vocab.rdfv[:type])
31
66
 
32
- sort = sorting_params
33
- values[:sort] = sort if sort
67
+ raise("No type given for '#{object}' referenced by polymorphic association '#{reflection.name}'") if type.blank?
34
68
 
35
- values[:user_context] = user_context if user_context
69
+ iri_to_class(type)
70
+ end
36
71
 
37
- @collection_params = values
72
+ def collection_params_parser
73
+ @collection_params_parser ||= LinkedRails::CollectionParamsParser.new(params.merge(user_context: user_context))
38
74
  end
39
75
 
40
- def collection_view_params
41
- return @collection_view_params if instance_variable_defined?(:@collection_view_params)
76
+ def filter_to_param(klass, predicate, value)
77
+ options = serializer_field(klass, predicate)
78
+ return unless value.count == 1 && options.present?
42
79
 
43
- values = permit_params(:page)
80
+ parsed_value = value.first.start_with?('http') ? RDF::URI(value.first) : value.first
81
+ parse_param(
82
+ klass,
83
+ options.predicate,
84
+ object_to_value(parsed_value)
85
+ )
86
+ end
44
87
 
45
- before = before_params
46
- values[:before] = before if before
88
+ def foreign_key_for_reflection(reflection)
89
+ if reflection.options[:through]
90
+ reflection.has_one? ? "#{reflection.name}_id" : "#{reflection.name.to_s.singularize}_ids"
91
+ elsif reflection.belongs_to?
92
+ reflection.foreign_key
93
+ end
94
+ end
47
95
 
48
- @collection_view_params = values
96
+ def iri_to_class(iri)
97
+ iri.to_s.split(Vocab.app.to_s).pop&.classify&.safe_constantize ||
98
+ LinkedRails.linked_models.detect { |klass| klass.iri == iri }
49
99
  end
50
100
 
51
- def filter_params # rubocop:disable Metrics/AbcSize
52
- return @filter_params if instance_variable_defined?(:@filter_params)
101
+ def nested_attributes(object, klass, association, collection)
102
+ parsed = parse_nested_resource(object, klass)
103
+ nested_resources = collection ? {rand(1_000_000_000).to_s => parsed} : parsed
104
+
105
+ ["#{association}_attributes", nested_resources]
106
+ end
53
107
 
54
- values = permit_params(filter: [])[:filter] || permit_params(filter: {})[:filter]
55
- return @filter_params = values if values.is_a?(Hash)
56
- return @filter_params = {} if values.blank?
108
+ def parse_association(klass, field_options, object)
109
+ association = field_options.association || field_options.key
110
+ reflection = klass.reflect_on_association(association) || raise("#{association} not found for #{name}")
57
111
 
58
- @filter_params = values.each_with_object({}) do |f, hash|
59
- values = f.split('=')
60
- key = RDF::URI(CGI.unescape(values.first))
61
- hash[key] ||= []
62
- hash[key] << CGI.unescape(values.second)
112
+ if slice&.key?(object.to_s)
113
+ association_klass = associated_class_from_params(reflection, object)
114
+ nested_attributes(object, association_klass, association, reflection.collection?)
115
+ elsif object.iri?
116
+ parse_iri_param(object, reflection)
63
117
  end
64
118
  end
65
119
 
66
- def sorting_params
67
- return @sorting_params if instance_variable_defined?(:@sorting_params)
120
+ def parse_attribute(klass, field_options, value)
121
+ parsed_value = parse_enum_attribute(klass, field_options.key, value) || value
68
122
 
69
- values = permit_params(sort: [])[:sort]
70
- return @sorting_params = nil if values.blank?
123
+ [field_options.key, parsed_value.to_s]
124
+ end
71
125
 
72
- @sorting_params = values.map do |f|
73
- parse_filter_value(f)
74
- end
126
+ def parse_enum_attribute(klass, key, value)
127
+ opts = RDF::Serializers.serializer_for(klass)&.enum_options(key)
128
+ return if opts.blank?
129
+
130
+
131
+ opts.detect { |_k, options| options.iri == value }&.first&.to_s
75
132
  end
76
133
 
77
- private
134
+ def parse_iri_param(iri, reflection)
135
+ key = foreign_key_for_reflection(reflection)
136
+ value = parse_iri_param_value(iri, reflection) if key
137
+
138
+ [key, value.to_s] if value
139
+ end
78
140
 
79
- def parse_filter_value(value)
80
- return value if value.is_a?(Hash)
141
+ def parse_iri_param_value(iri, reflection)
142
+ resource = LinkedRails.iri_mapper.resource_from_iri(iri, user_context)
81
143
 
82
- key, value = value.split('=')
83
- {key: RDF::URI(CGI.unescape(key)), direction: value}
144
+ resource&.try(reflection.association_primary_key)
145
+ end
146
+
147
+ def parse_nested_resource(object, klass)
148
+ resource = parse_resource(object, klass)
149
+ resource[:id] ||= nested_resource_id(object)
150
+ resource
151
+ end
152
+
153
+ def nested_resource_id(object)
154
+ return unless object.iri?
155
+
156
+ opts = LinkedRails.iri_mapper.opts_from_iri(object)
157
+ if opts[:class].method(:requested_single_resource).owner == LinkedRails::Model::IRIMapping::ClassMethods
158
+ opts[:params][:id]
159
+ else
160
+ opts[:class].requested_single_resource(opts[:params], nil)&.id
161
+ end
84
162
  end
85
163
 
86
- def permit_params(*keys)
87
- params
88
- .permit(*keys)
89
- .to_h
90
- .with_indifferent_access
164
+ def serializer_field(klass, predicate)
165
+ field = klass.try(:predicate_mapping).try(:[], predicate)
166
+ Rails.logger.info("#{predicate} not found for #{klass || 'nil'}") if field.blank?
167
+ field
91
168
  end
92
169
  end
93
170
  end
@@ -22,7 +22,7 @@ module LinkedRails
22
22
  module ClassMethods
23
23
  private
24
24
 
25
- def has_properties_shapes(properties) # rubocop:disable Naming/PredicateName
25
+ def has_properties_shapes(properties)
26
26
  properties.map do |key, boolean|
27
27
  SHACL::PropertyShape.new(
28
28
  path: policy_class.predicate_for_key(key),
@@ -32,7 +32,7 @@ module LinkedRails
32
32
  end
33
33
  end
34
34
 
35
- def has_values_shapes(values) # rubocop:disable Naming/PredicateName
35
+ def has_values_shapes(values)
36
36
  values.map do |key, value|
37
37
  enum = RDF::Serializers.serializer_for(policy_class).enum_options(key).try(:[], value)
38
38
  santized_value = enum ? -> { enum.iri } : value
@@ -15,7 +15,7 @@ module LinkedRails
15
15
  enhanceable :policy_class, :Policy
16
16
  class_attribute :_permitted_attributes
17
17
 
18
- attr_reader :message, :user_context, :record
18
+ attr_reader :action_status, :message, :user_context, :record
19
19
 
20
20
  def initialize(user_context, record)
21
21
  @user_context = user_context
@@ -23,12 +23,12 @@ module LinkedRails
23
23
  end
24
24
  end
25
25
 
26
- def create_child?(klass, opts = {})
27
- child_policy(klass, opts).create?
26
+ def create_child?(klass, **opts)
27
+ child_policy(klass, **opts).create?
28
28
  end
29
29
 
30
- def index_children?(klass, opts = {})
31
- child_policy(klass, opts).show?
30
+ def index_children?(klass, **opts)
31
+ child_policy(klass, **opts).show?
32
32
  end
33
33
 
34
34
  def permitted_attributes
@@ -38,6 +38,18 @@ module LinkedRails
38
38
  .flatten
39
39
  end
40
40
 
41
+ def permitted_attributes_from_filters(filters)
42
+ params_parser(filters).attributes_from_filters(policy_class).permit(permitted_attributes)
43
+ end
44
+
45
+ def policy_class
46
+ self.class.policy_class
47
+ end
48
+
49
+ def public_resource?
50
+ false
51
+ end
52
+
41
53
  def show?
42
54
  false
43
55
  end
@@ -64,25 +76,32 @@ module LinkedRails
64
76
  end
65
77
  end
66
78
 
67
- def child_policy(klass, opts = {})
68
- Pundit.policy(user_context, record.build_child(klass, opts.merge(user_context: user_context)))
79
+ def child_policy(klass, **opts)
80
+ Pundit.policy(user_context, record.build_child(klass, **opts.merge(user_context: user_context)))
69
81
  end
70
82
 
71
- def forbid_with_message(message)
83
+ def forbid_with_message(message, status = nil)
84
+ forbid_with_status(status) if status
72
85
  @message = message
73
86
  false
74
87
  end
75
88
 
89
+ def forbid_with_status(status, message = nil)
90
+ forbid_with_message(message) if message
91
+ @action_status = status
92
+ false
93
+ end
94
+
95
+ def params_parser(filters)
96
+ LinkedRails::ParamsParser.new(user_context: user_context, params: {filter: filters})
97
+ end
98
+
76
99
  def parent_policy
77
100
  return if record.try(:parent).blank?
78
101
 
79
102
  @parent_policy ||= Pundit.policy(user_context, record.parent)
80
103
  end
81
104
 
82
- def policy_class
83
- self.class.policy_class
84
- end
85
-
86
105
  def sanitize_array_attribute(attr)
87
106
  [attr, attr => []]
88
107
  end
@@ -118,17 +137,16 @@ module LinkedRails
118
137
  end
119
138
 
120
139
  module ClassMethods
121
- def condition_for(attr, pass, shape_opts = {})
122
- raise("#{attr} not permitted by #{self}") if attribute_options(attr).blank? && pass.permission_required?
123
-
124
- alternatives = node_shapes_for(attr, **shape_opts)
125
- if alternatives.count == 1
126
- Condition.new(shape: alternatives.first, pass: pass)
127
- elsif alternatives.count.positive?
128
- Condition.new(shape: SHACL::NodeShape.new(or: alternatives), pass: pass)
129
- else
130
- pass
131
- end
140
+ def condition_alternatives(attr, permission_required)
141
+ raise("#{attr} not permitted by #{self}") if attribute_options(attr).blank? && permission_required
142
+
143
+ attribute_options(attr)
144
+ .select { |opts| opts[:conditions].present? }
145
+ .map { |opts| property_shapes(opts[:conditions]) }
146
+ end
147
+
148
+ def policy_class
149
+ @policy_class ||= name.sub(/Policy/, '').classify.safe_constantize
132
150
  end
133
151
 
134
152
  def permitted_attributes
@@ -143,44 +161,24 @@ module LinkedRails
143
161
  permitted_attributes.select { |opts| opts[:attributes].include?(attr) }
144
162
  end
145
163
 
146
- def condition_alternatives(attr)
147
- attribute_options(attr)
148
- .select { |opts| opts[:conditions].present? }
149
- .map { |opts| opts[:conditions] }
150
- end
151
-
152
- def node_shapes_for(attr, property: [], sh_not: [])
153
- alternatives = condition_alternatives(attr)
154
- alternatives = [[]] if alternatives.empty? && (property.any? || sh_not.any?)
155
-
156
- alternatives.map do |props|
157
- properties = property_shapes(props) + property
158
- SHACL::NodeShape.new(property: properties, sh_not: sh_not)
159
- end
160
- end
161
-
162
164
  def initialize_permitted_attributes
163
165
  return if _permitted_attributes && method(:_permitted_attributes).owner == singleton_class
164
166
 
165
167
  self._permitted_attributes = superclass.try(:_permitted_attributes)&.dup || []
166
168
  end
167
169
 
168
- def permit_attributes(attrs, conditions = {})
170
+ def permit_attributes(attrs, **conditions)
169
171
  permitted_attributes << {attributes: attrs, conditions: conditions, options: {}}
170
172
  end
171
173
 
172
- def permit_array_attributes(attrs, conditions = {})
174
+ def permit_array_attributes(attrs, **conditions)
173
175
  permitted_attributes << {attributes: attrs, conditions: conditions, options: {array: true}}
174
176
  end
175
177
 
176
- def permit_nested_attributes(attrs, conditions = {})
178
+ def permit_nested_attributes(attrs, **conditions)
177
179
  permitted_attributes << {attributes: attrs, conditions: conditions, options: {nested: true}}
178
180
  end
179
181
 
180
- def policy_class
181
- @policy_class ||= name.sub(/Policy/, '').classify.safe_constantize
182
- end
183
-
184
182
  def property_shapes(conditions)
185
183
  conditions.map { |key, options| send("#{key}_shapes", options) }.flatten.compact
186
184
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ class Railtie < Rails::Railtie
5
+ initializer :add_welcome_page do
6
+ ActiveSupport.on_load(:action_controller_base) do
7
+ require_relative '../rails/welcome_controller'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -4,12 +4,12 @@ module LinkedRails
4
4
  class RDFError
5
5
  include ActiveModel::Model
6
6
 
7
- attr_accessor :error, :iri, :status
8
- delegate :message, to: :error
7
+ attr_accessor :error, :iri, :message, :status
9
8
 
10
9
  def initialize(status, requested_url, original_error)
11
10
  self.status = status
12
11
  self.error = original_error.is_a?(StandardError) ? original_error : original_error.new
12
+ self.message = error.message unless Rails.env.production?
13
13
  self.iri = ::RDF::URI(requested_url)
14
14
  end
15
15
 
@@ -19,6 +19,7 @@ module LinkedRails
19
19
  end
20
20
 
21
21
  register_renderer(%i[hndjson], ['application/hex+x-ndjson'], :hndjson)
22
+ register_renderer(%i[empjson], ['application/empathy+json'], :empjson)
22
23
  rdf_content_types.freeze
23
24
  end
24
25