linked_rails 0.0.2 → 0.0.4.pre.gaa82a222d

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 (174) 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 +71 -21
  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 +16 -21
  36. data/app/models/linked_rails/manifest.rb +8 -2
  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 +5 -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/serializers/linked_rails/actions/item_serializer.rb +5 -5
  51. data/app/serializers/linked_rails/actions/object_serializer.rb +9 -0
  52. data/app/serializers/linked_rails/collection/filter_field_serializer.rb +3 -2
  53. data/app/serializers/linked_rails/collection/filter_option_serializer.rb +1 -1
  54. data/app/serializers/linked_rails/collection/filter_serializer.rb +1 -1
  55. data/app/serializers/linked_rails/collection/sorting_serializer.rb +1 -1
  56. data/app/serializers/linked_rails/collection/view_serializer.rb +3 -3
  57. data/app/serializers/linked_rails/collection_serializer.rb +8 -7
  58. data/app/serializers/linked_rails/condition_serializer.rb +3 -3
  59. data/app/serializers/linked_rails/entry_point_serializer.rb +3 -3
  60. data/app/serializers/linked_rails/enum_value_serializer.rb +1 -0
  61. data/app/serializers/linked_rails/form/field/association_input_serializer.rb +1 -0
  62. data/app/serializers/linked_rails/form/field/file_input_serializer.rb +11 -0
  63. data/app/serializers/linked_rails/form/field_serializer.rb +3 -1
  64. data/app/serializers/linked_rails/form/group_serializer.rb +1 -1
  65. data/app/serializers/linked_rails/form/page_serializer.rb +1 -1
  66. data/app/serializers/linked_rails/menus/item_serializer.rb +3 -3
  67. data/app/serializers/linked_rails/menus/list_serializer.rb +1 -1
  68. data/app/serializers/linked_rails/ontology_serializer.rb +2 -2
  69. data/app/serializers/linked_rails/property_query_serializer.rb +7 -0
  70. data/app/serializers/linked_rails/sequence_serializer.rb +2 -5
  71. data/app/serializers/linked_rails/shacl/node_shape_serializer.rb +1 -1
  72. data/app/serializers/linked_rails/shacl/property_shape_serializer.rb +1 -1
  73. data/app/serializers/linked_rails/shacl/shape_serializer.rb +5 -5
  74. data/app/serializers/linked_rails/web_page_serializer.rb +3 -3
  75. data/app/serializers/linked_rails/web_site_serializer.rb +1 -1
  76. data/app/serializers/linked_rails/widget_serializer.rb +3 -3
  77. data/lib/generators/linked_rails/install/install_generator.rb +7 -8
  78. data/lib/generators/linked_rails/install/templates/README +7 -0
  79. data/lib/generators/linked_rails/install/templates/app_menu_list.rb +36 -7
  80. data/lib/generators/linked_rails/install/templates/application_menu_list.rb +40 -1
  81. data/lib/generators/linked_rails/install/templates/initializer.rb +1 -2
  82. data/lib/generators/linked_rails/install/templates/locales.yml +14 -0
  83. data/lib/generators/linked_rails/install/templates/rdf_serializers_initializer.rb +1 -1
  84. data/lib/generators/linked_rails/install/templates/vocab.rb +1 -0
  85. data/lib/generators/linked_rails/install/templates/vocab.yml +2 -2
  86. data/lib/generators/linked_rails/model/model_generator.rb +0 -1
  87. data/lib/generators/linked_rails/model/templates/controller.rb.tt +5 -1
  88. data/lib/generators/linked_rails/model/templates/form.rb.tt +3 -0
  89. data/lib/generators/linked_rails/model/templates/menu_list.rb.tt +15 -0
  90. data/lib/generators/linked_rails/model/templates/policy.rb.tt +13 -0
  91. data/lib/generators/linked_rails/model/templates/serializer.rb.tt +5 -1
  92. data/lib/linked_rails/active_response/controller/collections.rb +1 -1
  93. data/lib/linked_rails/active_response/controller/crud_defaults.rb +4 -4
  94. data/lib/linked_rails/active_response/controller/params.rb +10 -10
  95. data/lib/linked_rails/active_response/controller.rb +8 -18
  96. data/lib/linked_rails/active_response/responders/rdf.rb +19 -10
  97. data/lib/linked_rails/callable_variable.rb +1 -1
  98. data/lib/linked_rails/collection_params_parser.rb +93 -0
  99. data/lib/linked_rails/controller/actionable.rb +121 -0
  100. data/lib/linked_rails/controller/authorization.rb +6 -0
  101. data/lib/linked_rails/controller/default_actions/create.rb +52 -0
  102. data/lib/linked_rails/controller/default_actions/destroy.rb +42 -0
  103. data/lib/linked_rails/controller/default_actions/update.rb +43 -0
  104. data/lib/linked_rails/controller/delta.rb +78 -0
  105. data/lib/linked_rails/controller/error_handling.rb +11 -9
  106. data/lib/linked_rails/controller/rendering.rb +48 -0
  107. data/lib/linked_rails/controller.rb +24 -4
  108. data/lib/linked_rails/enhanceable.rb +1 -1
  109. data/lib/linked_rails/enhancements/creatable/controller.rb +1 -1
  110. data/lib/linked_rails/enhancements/destroyable/controller.rb +1 -1
  111. data/lib/linked_rails/enhancements/updatable/controller.rb +1 -1
  112. data/lib/linked_rails/enhancements.rb +0 -16
  113. data/lib/linked_rails/helpers/delta_helper.rb +26 -57
  114. data/lib/linked_rails/helpers/ontola_actions_helper.rb +2 -2
  115. data/lib/linked_rails/helpers/resource_helper.rb +4 -2
  116. data/lib/linked_rails/iri_mapper.rb +17 -39
  117. data/lib/linked_rails/middleware/error_handling.rb +51 -0
  118. data/lib/linked_rails/middleware/linked_data_params.rb +30 -151
  119. data/lib/linked_rails/model/actionable.rb +68 -0
  120. data/lib/linked_rails/model/collections.rb +201 -39
  121. data/lib/linked_rails/model/dirty.rb +7 -8
  122. data/lib/linked_rails/model/enhancements.rb +1 -6
  123. data/lib/linked_rails/model/filtering.rb +4 -6
  124. data/lib/linked_rails/model/indexable.rb +6 -16
  125. data/lib/linked_rails/model/iri.rb +28 -19
  126. data/lib/linked_rails/model/iri_mapping.rb +37 -8
  127. data/lib/linked_rails/model/menuable.rb +28 -0
  128. data/lib/linked_rails/model/serialization.rb +2 -15
  129. data/lib/linked_rails/model/singularable.rb +57 -0
  130. data/lib/linked_rails/model/sorting.rb +0 -5
  131. data/lib/linked_rails/model/tables.rb +26 -0
  132. data/lib/linked_rails/model.rb +17 -7
  133. data/lib/linked_rails/params_parser.rb +133 -55
  134. data/lib/linked_rails/policy/attribute_conditions.rb +2 -2
  135. data/lib/linked_rails/policy.rb +40 -46
  136. data/lib/linked_rails/railtie.rb +11 -0
  137. data/lib/linked_rails/rdf_error.rb +2 -2
  138. data/lib/linked_rails/renderers.rb +1 -0
  139. data/lib/linked_rails/routes.rb +38 -22
  140. data/lib/linked_rails/serializer/actionable.rb +27 -0
  141. data/lib/linked_rails/serializer/menuable.rb +31 -0
  142. data/lib/linked_rails/serializer/singularable.rb +26 -0
  143. data/lib/linked_rails/serializer.rb +28 -11
  144. data/lib/linked_rails/test_methods.rb +114 -0
  145. data/lib/linked_rails/translate.rb +31 -9
  146. data/lib/linked_rails/types/iri_type.rb +37 -0
  147. data/lib/linked_rails/uri_template.rb +30 -0
  148. data/lib/linked_rails/version.rb +1 -1
  149. data/lib/linked_rails/vocab.rb +9 -0
  150. data/lib/linked_rails.rb +30 -13
  151. data/lib/rails/welcome_controller.rb +3 -2
  152. data/lib/rdf/list.rb +9 -0
  153. data/lib/rdf/query_fix.rb +15 -0
  154. metadata +58 -33
  155. data/app/models/linked_rails/actions/default_actions/create.rb +0 -60
  156. data/app/models/linked_rails/actions/default_actions/destroy.rb +0 -45
  157. data/app/models/linked_rails/actions/default_actions/update.rb +0 -50
  158. data/app/models/linked_rails/actions/default_actions.rb +0 -17
  159. data/lib/generators/linked_rails/install/templates/application_action_list.rb +0 -3
  160. data/lib/generators/linked_rails/model/templates/action_list.rb.tt +0 -6
  161. data/lib/linked_rails/enhancements/actionable/model.rb +0 -71
  162. data/lib/linked_rails/enhancements/actionable/serializer.rb +0 -25
  163. data/lib/linked_rails/enhancements/creatable/action.rb +0 -15
  164. data/lib/linked_rails/enhancements/destroyable/action.rb +0 -15
  165. data/lib/linked_rails/enhancements/destroyable/routing.rb +0 -19
  166. data/lib/linked_rails/enhancements/menuable/model.rb +0 -36
  167. data/lib/linked_rails/enhancements/menuable/serializer.rb +0 -33
  168. data/lib/linked_rails/enhancements/route_concerns.rb +0 -56
  169. data/lib/linked_rails/enhancements/singularable/controller.rb +0 -43
  170. data/lib/linked_rails/enhancements/singularable/model.rb +0 -47
  171. data/lib/linked_rails/enhancements/singularable/serializer.rb +0 -28
  172. data/lib/linked_rails/enhancements/tableable/model.rb +0 -28
  173. data/lib/linked_rails/enhancements/updatable/action.rb +0 -15
  174. 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,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'model/actionable'
3
4
  require_relative 'model/collections'
4
5
  require_relative 'model/dirty'
5
6
  require_relative 'model/enhancements'
@@ -7,33 +8,42 @@ require_relative 'model/filtering'
7
8
  require_relative 'model/indexable'
8
9
  require_relative 'model/iri'
9
10
  require_relative 'model/iri_mapping'
11
+ require_relative 'model/menuable'
10
12
  require_relative 'model/serialization'
13
+ require_relative 'model/singularable'
11
14
  require_relative 'model/sorting'
15
+ require_relative 'model/tables'
12
16
 
13
17
  module LinkedRails
14
18
  module Model
15
19
  extend ActiveSupport::Concern
20
+ include Actionable
16
21
  include Collections
17
22
  include Dirty
18
23
  include Enhancements
19
24
  include Filtering
20
25
  include Indexable
21
- include Iri
22
- include IriMapping
26
+ include IRI
27
+ include IRIMapping
28
+ include Menuable
23
29
  include Serialization
30
+ include Singularable
24
31
  include Sorting
32
+ include Tables
25
33
 
26
34
  def build_child(klass, user_context: nil)
27
35
  klass.build_new(parent: self, user_context: user_context)
28
36
  end
29
37
 
30
- def singular_resource?
31
- false
32
- end
33
-
34
38
  module ClassMethods
35
39
  def build_new(parent: nil, user_context: nil)
36
- new(attributes_for_new(parent: parent, user_context: user_context))
40
+ raise(ActiveRecord::RecordNotFound) if try(:abstract_class?)
41
+
42
+ new(**attributes_for_new(parent: parent, user_context: user_context))
43
+ end
44
+
45
+ def controller_class
46
+ @controller_class ||= "#{to_s.pluralize}Controller".safe_constantize || superclass.try(:controller_class)
37
47
  end
38
48
 
39
49
  def form_class
@@ -1,93 +1,171 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LinkedRails
4
- class ParamsParser
5
- attr_reader :params, :user_context
6
-
7
- def initialize(params)
8
- @user_context = params[:user_context]
9
- @params = params.is_a?(Hash) ? ActionController::Parameters.new(params) : params
4
+ class ParamsParser # rubocop:disable Metrics/ClassLength
5
+ include ::Empathy::EmpJson::Helpers::Slices
6
+ include ::Empathy::EmpJson::Helpers::Primitives
7
+ include ::Empathy::EmpJson::Helpers::Values
8
+
9
+ attr_accessor :params, :slice, :user_context
10
+ delegate :filter_params, to: :collection_params_parser
11
+
12
+ def initialize(slice: nil, params: {}, user_context: nil)
13
+ self.slice = slice
14
+ self.params = params
15
+ self.user_context = user_context
10
16
  end
11
17
 
12
- def before_params
13
- return @before_params if instance_variable_defined?(:@before_params)
18
+ def attributes_from_filters(klass)
19
+ ActionController::Parameters.new(
20
+ filter_params.each_with_object({}) do |(predicate, value), hash|
21
+ key_and_value = filter_to_param(klass, predicate, value)
22
+ hash[key_and_value.first] = key_and_value.second if key_and_value
23
+ end
24
+ )
25
+ end
14
26
 
15
- values = permit_params(before: [])[:before]
16
- return @before_params = nil if values.blank?
27
+ def parse_param(klass, predicate, object)
28
+ field_options = serializer_field(klass, predicate) unless predicate == '_id'
29
+ if field_options.is_a?(FastJsonapi::Attribute)
30
+ parse_attribute(klass, field_options, emp_to_primitive(object))
31
+ elsif field_options.is_a?(FastJsonapi::Relationship)
32
+ parse_association(klass, field_options, emp_to_primitive(object))
33
+ end
34
+ end
17
35
 
18
- @before_params = values.map do |f|
19
- key, value = f.split('=')
20
- {key: RDF::URI(CGI.unescape(key)), value: value}
36
+ # Recursively parses a resource from slice
37
+ def parse_resource(subject, klass)
38
+ (record_from_slice(slice, subject) || {}).reduce({}) do |h, (k, v)|
39
+ (v.is_a?(Array) ? v : [v]).each do |value|
40
+ param = parse_param(klass, RDF::URI(k), value)
41
+ param ? add_param(h, param.first, param.second) : h
42
+ end
43
+ h
21
44
  end
22
45
  end
23
46
 
24
- def collection_params
25
- return @collection_params if instance_variable_defined?(:@collection_params)
47
+ private
48
+
49
+ def add_param(hash, key, value) # rubocop:disable Metrics/MethodLength
50
+ case hash[key]
51
+ when nil
52
+ hash[key] = value
53
+ when Hash
54
+ hash[key].merge!(value)
55
+ when Array
56
+ hash[key].append(value)
57
+ else
58
+ hash[key] = [hash[key], value]
59
+ end
60
+ hash
61
+ end
26
62
 
27
- values = permit_params(:display, :page_size, :title, :type)
63
+ def associated_class_from_params(reflection, object)
64
+ return reflection.klass unless reflection.polymorphic?
28
65
 
29
- filter = filter_params
30
- values[:filter] = filter if filter
66
+ query = slice.query(subject: object, predicate: Vocab.rdfv[:type])
31
67
 
32
- sort = sorting_params
33
- values[:sort] = sort if sort
68
+ raise("No type given for '#{object}' referenced by polymorphic association '#{reflection.name}'") if query.empty?
34
69
 
35
- values[:user_context] = user_context if user_context
70
+ iri_to_class(query.first.object)
71
+ end
36
72
 
37
- @collection_params = values
73
+ def collection_params_parser
74
+ @collection_params_parser ||= LinkedRails::CollectionParamsParser.new(params.merge(user_context: user_context))
38
75
  end
39
76
 
40
- def collection_view_params
41
- return @collection_view_params if instance_variable_defined?(:@collection_view_params)
77
+ def filter_to_param(klass, predicate, value)
78
+ options = serializer_field(klass, predicate)
79
+ return unless value.count == 1 && options.present?
42
80
 
43
- values = permit_params(:page)
81
+ parsed_value = value.first.start_with?('http') ? RDF::URI(value.first) : RDF::Literal(value.first)
82
+ parse_param(
83
+ klass,
84
+ options.predicate,
85
+ parsed_value
86
+ )
87
+ end
44
88
 
45
- before = before_params
46
- values[:before] = before if before
89
+ def foreign_key_for_reflection(reflection)
90
+ if reflection.options[:through]
91
+ reflection.has_one? ? "#{reflection.name}_id" : "#{reflection.name.to_s.singularize}_ids"
92
+ elsif reflection.belongs_to?
93
+ reflection.foreign_key
94
+ end
95
+ end
47
96
 
48
- @collection_view_params = values
97
+ def iri_to_class(iri)
98
+ iri.to_s.split(Vocab.app.to_s).pop&.classify&.safe_constantize ||
99
+ LinkedRails.linked_models.detect { |klass| klass.iri == iri }
49
100
  end
50
101
 
51
- def filter_params # rubocop:disable Metrics/AbcSize
52
- return @filter_params if instance_variable_defined?(:@filter_params)
102
+ def nested_attributes(object, klass, association, collection)
103
+ parsed = parse_nested_resource(object, klass)
104
+ nested_resources = collection ? {rand(1_000_000_000).to_s => parsed} : parsed
105
+
106
+ ["#{association}_attributes", nested_resources]
107
+ end
53
108
 
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?
109
+ def parse_association(klass, field_options, object)
110
+ association = field_options.association || field_options.key
111
+ reflection = klass.reflect_on_association(association) || raise("#{association} not found for #{name}")
57
112
 
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)
113
+ if slice&.key?(object.to_s)
114
+ association_klass = associated_class_from_params(reflection, object)
115
+ nested_attributes(object, association_klass, association, reflection.collection?)
116
+ elsif object.iri?
117
+ parse_iri_param(object, reflection)
63
118
  end
64
119
  end
65
120
 
66
- def sorting_params
67
- return @sorting_params if instance_variable_defined?(:@sorting_params)
121
+ def parse_attribute(klass, field_options, value)
122
+ parsed_value = parse_enum_attribute(klass, field_options.key, value) || value
68
123
 
69
- values = permit_params(sort: [])[:sort]
70
- return @sorting_params = nil if values.blank?
124
+ [field_options.key, parsed_value.to_s]
125
+ end
71
126
 
72
- @sorting_params = values.map do |f|
73
- parse_filter_value(f)
74
- end
127
+ def parse_enum_attribute(klass, key, value)
128
+ opts = RDF::Serializers.serializer_for(klass)&.enum_options(key)
129
+ return if opts.blank?
130
+
131
+
132
+ opts.detect { |_k, options| options.iri == value }&.first&.to_s
75
133
  end
76
134
 
77
- private
135
+ def parse_iri_param(iri, reflection)
136
+ key = foreign_key_for_reflection(reflection)
137
+ value = parse_iri_param_value(iri, reflection) if key
138
+
139
+ [key, value.to_s] if value
140
+ end
78
141
 
79
- def parse_filter_value(value)
80
- return value if value.is_a?(Hash)
142
+ def parse_iri_param_value(iri, reflection)
143
+ resource = LinkedRails.iri_mapper.resource_from_iri(iri, user_context)
81
144
 
82
- key, value = value.split('=')
83
- {key: RDF::URI(CGI.unescape(key)), direction: value}
145
+ resource&.try(reflection.association_primary_key)
146
+ end
147
+
148
+ def parse_nested_resource(object, klass)
149
+ resource = parse_resource(object, klass)
150
+ resource[:id] ||= nested_resource_id(object)
151
+ resource
152
+ end
153
+
154
+ def nested_resource_id(object)
155
+ return unless object.iri?
156
+
157
+ opts = LinkedRails.iri_mapper.opts_from_iri(object)
158
+ if opts[:class].method(:requested_single_resource).owner == LinkedRails::Model::IRIMapping::ClassMethods
159
+ opts[:params][:id]
160
+ else
161
+ opts[:class].requested_single_resource(opts[:params], nil)&.id
162
+ end
84
163
  end
85
164
 
86
- def permit_params(*keys)
87
- params
88
- .permit(*keys)
89
- .to_h
90
- .with_indifferent_access
165
+ def serializer_field(klass, predicate)
166
+ field = klass.try(:predicate_mapping).try(:[], predicate)
167
+ Rails.logger.info("#{predicate} not found for #{klass || 'nil'}") if field.blank?
168
+ field
91
169
  end
92
170
  end
93
171
  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,14 @@ 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
+
41
49
  def show?
42
50
  false
43
51
  end
@@ -64,25 +72,32 @@ module LinkedRails
64
72
  end
65
73
  end
66
74
 
67
- def child_policy(klass, opts = {})
68
- Pundit.policy(user_context, record.build_child(klass, opts.merge(user_context: user_context)))
75
+ def child_policy(klass, **opts)
76
+ Pundit.policy(user_context, record.build_child(klass, **opts.merge(user_context: user_context)))
69
77
  end
70
78
 
71
- def forbid_with_message(message)
79
+ def forbid_with_message(message, status = nil)
80
+ forbid_with_status(status) if status
72
81
  @message = message
73
82
  false
74
83
  end
75
84
 
85
+ def forbid_with_status(status, message = nil)
86
+ forbid_with_message(message) if message
87
+ @action_status = status
88
+ false
89
+ end
90
+
91
+ def params_parser(filters)
92
+ LinkedRails::ParamsParser.new(user_context: user_context, params: {filter: filters})
93
+ end
94
+
76
95
  def parent_policy
77
96
  return if record.try(:parent).blank?
78
97
 
79
98
  @parent_policy ||= Pundit.policy(user_context, record.parent)
80
99
  end
81
100
 
82
- def policy_class
83
- self.class.policy_class
84
- end
85
-
86
101
  def sanitize_array_attribute(attr)
87
102
  [attr, attr => []]
88
103
  end
@@ -118,17 +133,16 @@ module LinkedRails
118
133
  end
119
134
 
120
135
  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
136
+ def condition_alternatives(attr, permission_required)
137
+ raise("#{attr} not permitted by #{self}") if attribute_options(attr).blank? && permission_required
138
+
139
+ attribute_options(attr)
140
+ .select { |opts| opts[:conditions].present? }
141
+ .map { |opts| property_shapes(opts[:conditions]) }
142
+ end
143
+
144
+ def policy_class
145
+ @policy_class ||= name.sub(/Policy/, '').classify.safe_constantize
132
146
  end
133
147
 
134
148
  def permitted_attributes
@@ -143,44 +157,24 @@ module LinkedRails
143
157
  permitted_attributes.select { |opts| opts[:attributes].include?(attr) }
144
158
  end
145
159
 
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
160
  def initialize_permitted_attributes
163
161
  return if _permitted_attributes && method(:_permitted_attributes).owner == singleton_class
164
162
 
165
163
  self._permitted_attributes = superclass.try(:_permitted_attributes)&.dup || []
166
164
  end
167
165
 
168
- def permit_attributes(attrs, conditions = {})
166
+ def permit_attributes(attrs, **conditions)
169
167
  permitted_attributes << {attributes: attrs, conditions: conditions, options: {}}
170
168
  end
171
169
 
172
- def permit_array_attributes(attrs, conditions = {})
170
+ def permit_array_attributes(attrs, **conditions)
173
171
  permitted_attributes << {attributes: attrs, conditions: conditions, options: {array: true}}
174
172
  end
175
173
 
176
- def permit_nested_attributes(attrs, conditions = {})
174
+ def permit_nested_attributes(attrs, **conditions)
177
175
  permitted_attributes << {attributes: attrs, conditions: conditions, options: {nested: true}}
178
176
  end
179
177
 
180
- def policy_class
181
- @policy_class ||= name.sub(/Policy/, '').classify.safe_constantize
182
- end
183
-
184
178
  def property_shapes(conditions)
185
179
  conditions.map { |key, options| send("#{key}_shapes", options) }.flatten.compact
186
180
  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