linked_rails 0.0.3 → 0.0.4.pre.g14b377f91

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 (173) 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 +104 -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 +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 +5 -8
  78. data/lib/generators/linked_rails/install/templates/README +2 -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 +58 -0
  105. data/lib/linked_rails/controller/error_handling.rb +10 -10
  106. data/lib/linked_rails/controller/rendering.rb +48 -0
  107. data/lib/linked_rails/controller.rb +24 -4
  108. data/lib/linked_rails/enhancements/creatable/controller.rb +1 -1
  109. data/lib/linked_rails/enhancements/destroyable/controller.rb +1 -1
  110. data/lib/linked_rails/enhancements/updatable/controller.rb +1 -1
  111. data/lib/linked_rails/enhancements.rb +0 -16
  112. data/lib/linked_rails/helpers/delta_helper.rb +26 -57
  113. data/lib/linked_rails/helpers/ontola_actions_helper.rb +2 -2
  114. data/lib/linked_rails/helpers/resource_helper.rb +4 -2
  115. data/lib/linked_rails/iri_mapper.rb +17 -39
  116. data/lib/linked_rails/middleware/error_handling.rb +51 -0
  117. data/lib/linked_rails/middleware/linked_data_params.rb +30 -151
  118. data/lib/linked_rails/model/actionable.rb +68 -0
  119. data/lib/linked_rails/model/collections.rb +201 -39
  120. data/lib/linked_rails/model/dirty.rb +6 -18
  121. data/lib/linked_rails/model/enhancements.rb +1 -6
  122. data/lib/linked_rails/model/filtering.rb +4 -6
  123. data/lib/linked_rails/model/indexable.rb +6 -16
  124. data/lib/linked_rails/model/iri.rb +28 -19
  125. data/lib/linked_rails/model/iri_mapping.rb +37 -8
  126. data/lib/linked_rails/model/menuable.rb +28 -0
  127. data/lib/linked_rails/model/serialization.rb +2 -15
  128. data/lib/linked_rails/model/singularable.rb +57 -0
  129. data/lib/linked_rails/model/sorting.rb +0 -5
  130. data/lib/linked_rails/model/tables.rb +26 -0
  131. data/lib/linked_rails/model.rb +17 -7
  132. data/lib/linked_rails/params_parser.rb +131 -54
  133. data/lib/linked_rails/policy/attribute_conditions.rb +2 -2
  134. data/lib/linked_rails/policy.rb +40 -46
  135. data/lib/linked_rails/railtie.rb +11 -0
  136. data/lib/linked_rails/rdf_error.rb +2 -2
  137. data/lib/linked_rails/renderers.rb +1 -0
  138. data/lib/linked_rails/routes.rb +38 -22
  139. data/lib/linked_rails/serializer/actionable.rb +27 -0
  140. data/lib/linked_rails/serializer/menuable.rb +31 -0
  141. data/lib/linked_rails/serializer/singularable.rb +26 -0
  142. data/lib/linked_rails/serializer.rb +28 -11
  143. data/lib/linked_rails/test_methods.rb +114 -0
  144. data/lib/linked_rails/translate.rb +31 -9
  145. data/lib/linked_rails/types/iri_type.rb +37 -0
  146. data/lib/linked_rails/uri_template.rb +30 -0
  147. data/lib/linked_rails/version.rb +1 -1
  148. data/lib/linked_rails/vocab.rb +9 -0
  149. data/lib/linked_rails.rb +30 -13
  150. data/lib/rails/welcome_controller.rb +3 -3
  151. data/lib/rdf/list.rb +9 -0
  152. data/lib/rdf/query_fix.rb +15 -0
  153. metadata +71 -33
  154. data/app/models/linked_rails/actions/default_actions/create.rb +0 -60
  155. data/app/models/linked_rails/actions/default_actions/destroy.rb +0 -45
  156. data/app/models/linked_rails/actions/default_actions/update.rb +0 -50
  157. data/app/models/linked_rails/actions/default_actions.rb +0 -17
  158. data/lib/generators/linked_rails/install/templates/application_action_list.rb +0 -3
  159. data/lib/generators/linked_rails/model/templates/action_list.rb.tt +0 -6
  160. data/lib/linked_rails/enhancements/actionable/model.rb +0 -71
  161. data/lib/linked_rails/enhancements/actionable/serializer.rb +0 -25
  162. data/lib/linked_rails/enhancements/creatable/action.rb +0 -15
  163. data/lib/linked_rails/enhancements/destroyable/action.rb +0 -15
  164. data/lib/linked_rails/enhancements/destroyable/routing.rb +0 -19
  165. data/lib/linked_rails/enhancements/menuable/model.rb +0 -36
  166. data/lib/linked_rails/enhancements/menuable/serializer.rb +0 -33
  167. data/lib/linked_rails/enhancements/route_concerns.rb +0 -56
  168. data/lib/linked_rails/enhancements/singularable/controller.rb +0 -43
  169. data/lib/linked_rails/enhancements/singularable/model.rb +0 -47
  170. data/lib/linked_rails/enhancements/singularable/serializer.rb +0 -28
  171. data/lib/linked_rails/enhancements/tableable/model.rb +0 -28
  172. data/lib/linked_rails/enhancements/updatable/action.rb +0 -15
  173. data/lib/linked_rails/enhancements/updatable/routing.rb +0 -20
@@ -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,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
 
@@ -4,7 +4,7 @@ require 'linked_rails/constraints/whitelist'
4
4
 
5
5
  module LinkedRails
6
6
  module Routes
7
- def use_linked_rails(opts = {}) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
7
+ def use_linked_rails(**opts) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
8
8
  constraints(LinkedRails::Constraints::Whitelist) do
9
9
  post 'spi/bulk', to: "#{opts.fetch(:bulk, 'linked_rails/bulk')}#show"
10
10
  end
@@ -22,24 +22,29 @@ module LinkedRails
22
22
 
23
23
  get '(*parent_iri)/actions', to: "#{opts.fetch(:actions, 'linked_rails/actions')}/items#index"
24
24
  get '(*parent_iri)/actions/:id', to: "#{opts.fetch(:actions, 'linked_rails/actions')}/items#show"
25
- get '(*parent_iri)/new', to: "#{opts.fetch(:actions, 'linked_rails/actions')}/items#show", id: :create
26
- get '(*parent_iri)/edit', to: "#{opts.fetch(:actions, 'linked_rails/actions')}/items#show", id: :update
27
- get '(*parent_iri)/delete', to: "#{opts.fetch(:actions, 'linked_rails/actions')}/items#show", id: :destroy
28
- get '(*parent_iri)/trash', to: "#{opts.fetch(:actions, 'linked_rails/actions')}/items#show", id: :trash
29
- get '(*parent_iri)/untrash', to: "#{opts.fetch(:actions, 'linked_rails/actions')}/items#show", id: :untrash
25
+ get '(*parent_iri)/action_object', to: "#{opts.fetch(:actions, 'linked_rails/actions')}/objects#show"
30
26
  end
31
27
 
32
- def linked_resource(klass, controller: nil, nested: false, &block)
28
+ def linked_resource(klass, controller: nil, collection: true, nested: false, resource: true, &block) # rubocop:disable Metrics/MethodLength, Metrics/ParameterLists
33
29
  options = route_options(klass, controller, nested, klass.route_key)
34
30
 
31
+ if collection
32
+ get(options[:parentable_path], to: "#{options[:controller]}#index")
33
+ route_block(
34
+ klass,
35
+ :collection,
36
+ controller: options[:controller],
37
+ prefix: options[:parentable_path]
38
+ ).call
39
+ end
40
+
41
+ return unless resource
42
+
35
43
  resources(
36
44
  options[:route_name],
37
45
  active_controller_opts(options),
38
- &route_block(klass, &block)
46
+ &route_block(klass, :resource, &block)
39
47
  )
40
-
41
- post(options[:parentable_path], to: "#{options[:controller]}#create") if options[:creatable]
42
- get(options[:parentable_path], to: "#{options[:controller]}#index")
43
48
  end
44
49
 
45
50
  def singular_linked_resource(klass, controller: nil, nested: true, &block)
@@ -48,10 +53,8 @@ module LinkedRails
48
53
  resource(
49
54
  options[:route_name],
50
55
  active_controller_opts(options).merge(singular_route: true),
51
- &route_block(klass, &block)
56
+ &route_block(klass, :singular, &block)
52
57
  )
53
-
54
- post(options[:parentable_path], to: "#{options[:controller]}#create", singular_route: true) if options[:creatable]
55
58
  end
56
59
 
57
60
  def find_tenant_route
@@ -60,6 +63,16 @@ module LinkedRails
60
63
 
61
64
  private
62
65
 
66
+ def action_routes(controller, prefix, key, value)
67
+ path = value.fetch(:action_path, key)
68
+ action = value.fetch(:action_name)
69
+ get([prefix, path].compact.join('/'), action: action, action_key: key, controller: controller)
70
+ return if value[:target_path].nil?
71
+
72
+ method = value.fetch(:http_method).downcase
73
+ send(method, [prefix, value.fetch(:target_path)].compact.join('/'), action: key, controller: controller)
74
+ end
75
+
63
76
  def active_controller_opts(route_options)
64
77
  {
65
78
  controller: route_options[:controller],
@@ -68,22 +81,20 @@ module LinkedRails
68
81
  }
69
82
  end
70
83
 
71
- def creatable(klass)
72
- klass.enhanced_with?(LinkedRails::Enhancements::Creatable, :Routing)
73
- end
74
-
75
- def route_block(klass)
84
+ def route_block(klass, action_type, controller: nil, prefix: nil)
76
85
  lambda do
77
- include_route_concerns(klass: klass)
78
-
86
+ klass.action_list.defined_actions[action_type].each do |key, value|
87
+ action_routes(controller, prefix, key, value)
88
+ end
79
89
  yield if block_given?
80
90
  end
81
91
  end
82
92
 
83
93
  def route_options(klass, controller, nested, path)
94
+ touch_controller(klass)
95
+
84
96
  {
85
97
  controller: controller || klass.name.tableize,
86
- creatable: creatable(klass),
87
98
  nested: nested,
88
99
  only: %i[show],
89
100
  parentable_path: "(*parent_iri)/#{path}",
@@ -91,6 +102,11 @@ module LinkedRails
91
102
  route_name: klass.name.demodulize.tableize
92
103
  }.with_indifferent_access
93
104
  end
105
+
106
+ # Make sure all actions are initialized before generating the routes
107
+ def touch_controller(klass)
108
+ klass.controller_class
109
+ end
94
110
  end
95
111
  end
96
112
 
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ module Serializer
5
+ module Actionable
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ has_many :favorite_actions,
10
+ predicate: Vocab.ontola[:favoriteAction] do |object|
11
+ object.try(:favorite_actions)
12
+ end
13
+ statements :action_triples
14
+ end
15
+
16
+ module ClassMethods
17
+ def action_triples(object, _params)
18
+ if object.iri.anonymous? && !object.try(:singular_resource?)
19
+ []
20
+ else
21
+ object.try(:action_triples) || []
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ module Serializer
5
+ module Menuable
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ include_menus
10
+ end
11
+
12
+ module ClassMethods
13
+ def inherited(target)
14
+ super
15
+ target.include_menus
16
+ end
17
+
18
+ def include_menus
19
+ serializable_class.try(:menu_class)&.defined_menus&.keys&.each do |menu|
20
+ has_one "#{menu}_menu",
21
+ predicate: Vocab.ontola["#{menu.to_s.camelize(:lower)}Menu"],
22
+ if: method(:named_object?),
23
+ polymorphic: true do |object, opts|
24
+ object.menu(menu, opts[:scope])
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ module Serializer
5
+ module Singularable
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ statements :same_as_statement
10
+
11
+ def self.same_as_statement(object, _params)
12
+ return [] unless object.try(:singular_resource?) && object.singular_iri != object.iri
13
+
14
+ [
15
+ RDF::Statement.new(
16
+ object.singular_iri,
17
+ Vocab.owl.sameAs,
18
+ object.iri,
19
+ graph_name: Vocab.ld[:supplant]
20
+ )
21
+ ]
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end