linked_rails 0.0.3 → 0.0.4

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 (131) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/linked_rails/bulk_controller.rb +26 -6
  3. data/app/controllers/linked_rails/enum_values_controller.rb +0 -42
  4. data/app/models/linked_rails/actions/item.rb +46 -30
  5. data/app/models/linked_rails/actions/list.rb +6 -25
  6. data/app/models/linked_rails/collection/configuration.rb +55 -0
  7. data/app/models/linked_rails/collection/filter.rb +1 -1
  8. data/app/models/linked_rails/collection/filter_field.rb +5 -1
  9. data/app/models/linked_rails/collection/filter_option.rb +1 -1
  10. data/app/models/linked_rails/collection/filterable.rb +3 -8
  11. data/app/models/linked_rails/collection/infinite.rb +102 -0
  12. data/app/models/linked_rails/collection/infinite_view.rb +1 -90
  13. data/app/models/linked_rails/collection/iri.rb +33 -52
  14. data/app/models/linked_rails/collection/iri_mapping.rb +14 -6
  15. data/app/models/linked_rails/collection/paginated.rb +45 -0
  16. data/app/models/linked_rails/collection/paginated_view.rb +1 -33
  17. data/app/models/linked_rails/collection/sortable.rb +1 -9
  18. data/app/models/linked_rails/collection/sorting.rb +1 -1
  19. data/app/models/linked_rails/collection/view.rb +15 -5
  20. data/app/models/linked_rails/collection.rb +33 -67
  21. data/app/models/linked_rails/creative_work.rb +1 -1
  22. data/app/models/linked_rails/entry_point.rb +8 -5
  23. data/app/models/linked_rails/enum_value.rb +39 -1
  24. data/app/models/linked_rails/form/field/resource_field.rb +2 -0
  25. data/app/models/linked_rails/form/field.rb +6 -8
  26. data/app/models/linked_rails/form/field_factory.rb +21 -10
  27. data/app/models/linked_rails/form/group.rb +2 -2
  28. data/app/models/linked_rails/form/page.rb +4 -4
  29. data/app/models/linked_rails/form.rb +13 -15
  30. data/app/models/linked_rails/manifest.rb +8 -2
  31. data/app/models/linked_rails/menus/item.rb +1 -1
  32. data/app/models/linked_rails/menus/list.rb +3 -2
  33. data/app/models/linked_rails/ontology/base.rb +1 -1
  34. data/app/models/linked_rails/ontology/class.rb +3 -3
  35. data/app/models/linked_rails/ontology.rb +5 -5
  36. data/app/models/linked_rails/sequence.rb +2 -2
  37. data/app/models/linked_rails/shacl/property_shape.rb +1 -1
  38. data/app/models/linked_rails/widget.rb +1 -1
  39. data/app/serializers/linked_rails/actions/item_serializer.rb +2 -1
  40. data/app/serializers/linked_rails/collection_serializer.rb +7 -2
  41. data/app/serializers/linked_rails/entry_point_serializer.rb +1 -1
  42. data/app/serializers/linked_rails/ontology_serializer.rb +2 -2
  43. data/lib/generators/linked_rails/install/install_generator.rb +5 -8
  44. data/lib/generators/linked_rails/install/templates/README +2 -0
  45. data/lib/generators/linked_rails/install/templates/app_menu_list.rb +36 -7
  46. data/lib/generators/linked_rails/install/templates/application_menu_list.rb +40 -1
  47. data/lib/generators/linked_rails/install/templates/initializer.rb +1 -2
  48. data/lib/generators/linked_rails/install/templates/locales.yml +12 -0
  49. data/lib/generators/linked_rails/install/templates/vocab.rb +1 -0
  50. data/lib/generators/linked_rails/install/templates/vocab.yml +2 -2
  51. data/lib/generators/linked_rails/model/model_generator.rb +0 -1
  52. data/lib/generators/linked_rails/model/templates/controller.rb.tt +5 -1
  53. data/lib/generators/linked_rails/model/templates/form.rb.tt +3 -0
  54. data/lib/generators/linked_rails/model/templates/menu_list.rb.tt +15 -0
  55. data/lib/generators/linked_rails/model/templates/policy.rb.tt +13 -0
  56. data/lib/generators/linked_rails/model/templates/serializer.rb.tt +1 -1
  57. data/lib/linked_rails/active_response/controller/collections.rb +1 -1
  58. data/lib/linked_rails/active_response/controller/crud_defaults.rb +3 -3
  59. data/lib/linked_rails/active_response/controller/params.rb +5 -5
  60. data/lib/linked_rails/active_response/controller.rb +11 -0
  61. data/lib/linked_rails/active_response/responders/rdf.rb +19 -10
  62. data/lib/linked_rails/callable_variable.rb +1 -1
  63. data/lib/linked_rails/collection_params_parser.rb +93 -0
  64. data/lib/linked_rails/controller/actionable.rb +118 -0
  65. data/lib/linked_rails/controller/authorization.rb +6 -0
  66. data/lib/linked_rails/controller/default_actions/create.rb +52 -0
  67. data/lib/linked_rails/controller/default_actions/destroy.rb +42 -0
  68. data/lib/linked_rails/controller/default_actions/update.rb +43 -0
  69. data/lib/linked_rails/controller.rb +20 -3
  70. data/lib/linked_rails/enhancements/creatable/controller.rb +1 -1
  71. data/lib/linked_rails/enhancements/destroyable/controller.rb +1 -1
  72. data/lib/linked_rails/enhancements/updatable/controller.rb +1 -1
  73. data/lib/linked_rails/enhancements.rb +0 -16
  74. data/lib/linked_rails/helpers/delta_helper.rb +26 -3
  75. data/lib/linked_rails/helpers/ontola_actions_helper.rb +2 -2
  76. data/lib/linked_rails/helpers/resource_helper.rb +1 -1
  77. data/lib/linked_rails/iri_mapper.rb +17 -39
  78. data/lib/linked_rails/middleware/linked_data_params.rb +7 -127
  79. data/lib/linked_rails/model/actionable.rb +69 -0
  80. data/lib/linked_rails/model/collections.rb +195 -39
  81. data/lib/linked_rails/model/dirty.rb +1 -1
  82. data/lib/linked_rails/model/enhancements.rb +1 -6
  83. data/lib/linked_rails/model/filtering.rb +2 -4
  84. data/lib/linked_rails/model/indexable.rb +5 -15
  85. data/lib/linked_rails/model/iri.rb +15 -17
  86. data/lib/linked_rails/model/iri_mapping.rb +35 -6
  87. data/lib/linked_rails/model/menuable.rb +34 -0
  88. data/lib/linked_rails/model/serialization.rb +0 -9
  89. data/lib/linked_rails/model/singularable.rb +55 -0
  90. data/lib/linked_rails/model/sorting.rb +0 -5
  91. data/lib/linked_rails/model/tables.rb +26 -0
  92. data/lib/linked_rails/model.rb +13 -5
  93. data/lib/linked_rails/params_parser.rb +131 -55
  94. data/lib/linked_rails/policy/attribute_conditions.rb +2 -2
  95. data/lib/linked_rails/policy.rb +24 -17
  96. data/lib/linked_rails/rdf_error.rb +2 -2
  97. data/lib/linked_rails/routes.rb +37 -22
  98. data/lib/linked_rails/serializer/actionable.rb +27 -0
  99. data/lib/linked_rails/serializer/menuable.rb +31 -0
  100. data/lib/linked_rails/serializer/singularable.rb +26 -0
  101. data/lib/linked_rails/serializer.rb +23 -10
  102. data/lib/linked_rails/test_methods.rb +114 -0
  103. data/lib/linked_rails/translate.rb +19 -9
  104. data/lib/linked_rails/uri_template.rb +30 -0
  105. data/lib/linked_rails/version.rb +1 -1
  106. data/lib/linked_rails/vocab.rb +8 -0
  107. data/lib/linked_rails.rb +25 -12
  108. data/lib/rails/welcome_controller.rb +3 -3
  109. data/lib/rdf/query_fix.rb +15 -0
  110. metadata +22 -25
  111. data/app/models/linked_rails/actions/default_actions/create.rb +0 -60
  112. data/app/models/linked_rails/actions/default_actions/destroy.rb +0 -45
  113. data/app/models/linked_rails/actions/default_actions/update.rb +0 -50
  114. data/app/models/linked_rails/actions/default_actions.rb +0 -17
  115. data/lib/generators/linked_rails/install/templates/application_action_list.rb +0 -3
  116. data/lib/generators/linked_rails/model/templates/action_list.rb.tt +0 -6
  117. data/lib/linked_rails/enhancements/actionable/model.rb +0 -71
  118. data/lib/linked_rails/enhancements/actionable/serializer.rb +0 -25
  119. data/lib/linked_rails/enhancements/creatable/action.rb +0 -15
  120. data/lib/linked_rails/enhancements/destroyable/action.rb +0 -15
  121. data/lib/linked_rails/enhancements/destroyable/routing.rb +0 -19
  122. data/lib/linked_rails/enhancements/indexable/model.rb +0 -10
  123. data/lib/linked_rails/enhancements/menuable/model.rb +0 -36
  124. data/lib/linked_rails/enhancements/menuable/serializer.rb +0 -33
  125. data/lib/linked_rails/enhancements/route_concerns.rb +0 -56
  126. data/lib/linked_rails/enhancements/singularable/controller.rb +0 -43
  127. data/lib/linked_rails/enhancements/singularable/model.rb +0 -47
  128. data/lib/linked_rails/enhancements/singularable/serializer.rb +0 -28
  129. data/lib/linked_rails/enhancements/tableable/model.rb +0 -28
  130. data/lib/linked_rails/enhancements/updatable/action.rb +0 -15
  131. data/lib/linked_rails/enhancements/updatable/routing.rb +0 -20
@@ -1,93 +1,169 @@
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
+ attr_accessor :params, :graph, :user_context
6
6
 
7
- def initialize(params)
8
- @user_context = params[:user_context]
9
- @params = params.is_a?(Hash) ? ActionController::Parameters.new(params) : params
7
+ def initialize(graph: nil, params: {}, user_context: nil)
8
+ self.graph = graph
9
+ self.params = params
10
+ self.user_context = user_context
10
11
  end
11
12
 
12
- def before_params
13
- return @before_params if instance_variable_defined?(:@before_params)
14
-
15
- values = permit_params(before: [])[:before]
16
- return @before_params = nil if values.blank?
13
+ def attributes_from_filters(klass)
14
+ ActionController::Parameters.new(
15
+ collection_params_parser.filter_params.each_with_object({}) do |(predicate, value), hash|
16
+ key_and_value = filter_to_param(klass, predicate, value)
17
+ hash[key_and_value.first] = key_and_value.second if key_and_value
18
+ end
19
+ )
20
+ end
17
21
 
18
- @before_params = values.map do |f|
19
- key, value = f.split('=')
20
- {key: RDF::URI(CGI.unescape(key)), value: value}
22
+ def parse_param(klass, predicate, object)
23
+ field_options = serializer_field(klass, predicate)
24
+ if field_options.is_a?(FastJsonapi::Attribute)
25
+ parse_attribute(klass, field_options, object.value)
26
+ elsif field_options.is_a?(FastJsonapi::Relationship)
27
+ parse_association(klass, field_options, object)
21
28
  end
22
29
  end
23
30
 
24
- def collection_params
25
- return @collection_params if instance_variable_defined?(:@collection_params)
31
+ # Recursively parses a resource from graph
32
+ def parse_resource(subject, klass)
33
+ graph
34
+ .query([subject])
35
+ .map { |statement| parse_statement(statement, klass) }
36
+ .compact
37
+ .reduce({}) { |h, (k, v)| add_param(h, k, v) }
38
+ end
26
39
 
27
- values = permit_params(:display, :page_size, :title, :type)
40
+ private
41
+
42
+ def add_param(hash, key, value) # rubocop:disable Metrics/MethodLength
43
+ case hash[key]
44
+ when nil
45
+ hash[key] = value
46
+ when Hash
47
+ hash[key].merge!(value)
48
+ when Array
49
+ hash[key].append(value)
50
+ else
51
+ hash[key] = [hash[key], value]
52
+ end
53
+ hash
54
+ end
28
55
 
29
- filter = filter_params
30
- values[:filter] = filter if filter
56
+ def associated_class_from_params(reflection, object)
57
+ return reflection.klass unless reflection.polymorphic?
31
58
 
32
- sort = sorting_params
33
- values[:sort] = sort if sort
59
+ query = graph.query(subject: object, predicate: Vocab.rdfv[:type])
34
60
 
35
- values[:user_context] = user_context if user_context
61
+ raise("No type given for '#{object}' referenced by polymorphic association '#{reflection.name}'") if query.empty?
36
62
 
37
- @collection_params = values
63
+ iri_to_class(query.first.object)
38
64
  end
39
65
 
40
- def collection_view_params
41
- return @collection_view_params if instance_variable_defined?(:@collection_view_params)
66
+ def collection_params_parser
67
+ @collection_params_parser ||= LinkedRails::CollectionParamsParser.new(params.merge(user_context: user_context))
68
+ end
69
+
70
+ def filter_to_param(klass, predicate, value)
71
+ options = serializer_field(klass, predicate)
72
+ return unless value.count == 1 && options.present?
73
+
74
+ parsed_value = value.first.start_with?('http') ? RDF::URI(value.first) : RDF::Literal(value.first)
75
+ parse_param(
76
+ klass,
77
+ options.predicate,
78
+ parsed_value
79
+ )
80
+ end
42
81
 
43
- values = permit_params(:page)
82
+ def foreign_key_for_reflection(reflection)
83
+ if reflection.options[:through]
84
+ reflection.has_one? ? "#{reflection.name}_id" : "#{reflection.name.to_s.singularize}_ids"
85
+ elsif reflection.belongs_to?
86
+ reflection.foreign_key
87
+ end
88
+ end
44
89
 
45
- before = before_params
46
- values[:before] = before if before
90
+ def iri_to_class(iri)
91
+ iri.to_s.split(Vocab.app.to_s).pop&.classify&.safe_constantize ||
92
+ LinkedRails.linked_models.detect { |klass| klass.iri == iri }
93
+ end
47
94
 
48
- @collection_view_params = values
95
+ def nested_attributes(object, klass, association, collection)
96
+ nested_resources =
97
+ if graph.query([object, Vocab.rdfv[:first], nil]).present?
98
+ nested_attributes_from_list(object, klass)
99
+ else
100
+ parsed = parse_nested_resource(object, klass)
101
+ collection ? {rand(1_000_000_000).to_s => parsed} : parsed
102
+ end
103
+ ["#{association}_attributes", nested_resources]
49
104
  end
50
105
 
51
- def filter_params # rubocop:disable Metrics/AbcSize
52
- return @filter_params if instance_variable_defined?(:@filter_params)
106
+ def nested_attributes_from_list(object, klass)
107
+ Hash[
108
+ RDF::List.new(subject: object, graph: graph)
109
+ .map { |nested| [rand(1_000_000_000).to_s, parse_nested_resource(nested, klass)] }
110
+ ]
111
+ end
53
112
 
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?
113
+ def parse_association(klass, field_options, object)
114
+ association = field_options.association || field_options.key
115
+ reflection = klass.reflect_on_association(association) || raise("#{association} not found for #{name}")
57
116
 
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)
117
+ if graph&.has_subject?(object)
118
+ association_klass = associated_class_from_params(reflection, object)
119
+ nested_attributes(object, association_klass, association, reflection.collection?)
120
+ elsif object.iri?
121
+ parse_iri_param(object, reflection)
63
122
  end
64
123
  end
65
124
 
66
- def sorting_params
67
- return @sorting_params if instance_variable_defined?(:@sorting_params)
125
+ def parse_attribute(klass, field_options, value)
126
+ parsed_value = parse_blob_attribute(value) || parse_enum_attribute(klass, field_options.key, value) || value
68
127
 
69
- values = permit_params(sort: [])[:sort]
70
- return @sorting_params = nil if values.blank?
128
+ [field_options.key, parsed_value]
129
+ end
71
130
 
72
- @sorting_params = values.map do |f|
73
- parse_filter_value(f)
74
- end
131
+ def parse_blob_attribute(value)
132
+ params["<#{value}>"] if value.starts_with?(Vocab.ll['blobs/'])
75
133
  end
76
134
 
77
- private
135
+ def parse_enum_attribute(klass, key, value)
136
+ opts = RDF::Serializers.serializer_for(klass)&.enum_options(key)
137
+ return if opts.blank?
138
+
139
+ opts.detect { |_k, options| options.iri == value }&.first&.to_s
140
+ end
78
141
 
79
- def parse_filter_value(value)
80
- return value if value.is_a?(Hash)
142
+ def parse_iri_param(iri, reflection)
143
+ resource = LinkedRails.iri_mapper.resource_from_iri(iri, user_context)
144
+ return unless resource
145
+
146
+ key = foreign_key_for_reflection(reflection)
147
+
148
+ value = resource&.try(reflection.association_primary_key) if key
149
+
150
+ [key, value] if value
151
+ end
152
+
153
+ def parse_nested_resource(object, klass)
154
+ resource = parse_resource(object, klass)
155
+ resource[:id] ||= LinkedRails.iri_mapper.opts_from_iri(object)[:params][:id] if object.iri?
156
+ resource
157
+ end
81
158
 
82
- key, value = value.split('=')
83
- {key: RDF::URI(CGI.unescape(key)), direction: value}
159
+ def parse_statement(statement, klass)
160
+ parse_param(klass, statement.predicate, statement.object)
84
161
  end
85
162
 
86
- def permit_params(*keys)
87
- params
88
- .permit(*keys)
89
- .to_h
90
- .with_indifferent_access
163
+ def serializer_field(klass, predicate)
164
+ field = klass.try(:predicate_mapping).try(:[], predicate)
165
+ Rails.logger.info("#{predicate} not found for #{klass || 'nil'}") if field.blank?
166
+ field
91
167
  end
92
168
  end
93
169
  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,11 +23,11 @@ module LinkedRails
23
23
  end
24
24
  end
25
25
 
26
- def create_child?(klass, opts = {})
26
+ def create_child?(klass, **opts)
27
27
  child_policy(klass, opts).create?
28
28
  end
29
29
 
30
- def index_children?(klass, opts = {})
30
+ def index_children?(klass, **opts)
31
31
  child_policy(klass, opts).show?
32
32
  end
33
33
 
@@ -38,6 +38,10 @@ module LinkedRails
38
38
  .flatten
39
39
  end
40
40
 
41
+ def policy_class
42
+ self.class.policy_class
43
+ end
44
+
41
45
  def show?
42
46
  false
43
47
  end
@@ -64,25 +68,28 @@ module LinkedRails
64
68
  end
65
69
  end
66
70
 
67
- def child_policy(klass, opts = {})
71
+ def child_policy(klass, **opts)
68
72
  Pundit.policy(user_context, record.build_child(klass, opts.merge(user_context: user_context)))
69
73
  end
70
74
 
71
- def forbid_with_message(message)
75
+ def forbid_with_message(message, status = nil)
76
+ forbid_with_status(status) if status
72
77
  @message = message
73
78
  false
74
79
  end
75
80
 
81
+ def forbid_with_status(status, message = nil)
82
+ forbid_with_message(message) if message
83
+ @action_status = status
84
+ false
85
+ end
86
+
76
87
  def parent_policy
77
88
  return if record.try(:parent).blank?
78
89
 
79
90
  @parent_policy ||= Pundit.policy(user_context, record.parent)
80
91
  end
81
92
 
82
- def policy_class
83
- self.class.policy_class
84
- end
85
-
86
93
  def sanitize_array_attribute(attr)
87
94
  [attr, attr => []]
88
95
  end
@@ -118,7 +125,7 @@ module LinkedRails
118
125
  end
119
126
 
120
127
  module ClassMethods
121
- def condition_for(attr, pass, shape_opts = {})
128
+ def condition_for(attr, pass, **shape_opts)
122
129
  raise("#{attr} not permitted by #{self}") if attribute_options(attr).blank? && pass.permission_required?
123
130
 
124
131
  alternatives = node_shapes_for(attr, **shape_opts)
@@ -131,6 +138,10 @@ module LinkedRails
131
138
  end
132
139
  end
133
140
 
141
+ def policy_class
142
+ @policy_class ||= name.sub(/Policy/, '').classify.safe_constantize
143
+ end
144
+
134
145
  def permitted_attributes
135
146
  initialize_permitted_attributes
136
147
 
@@ -165,22 +176,18 @@ module LinkedRails
165
176
  self._permitted_attributes = superclass.try(:_permitted_attributes)&.dup || []
166
177
  end
167
178
 
168
- def permit_attributes(attrs, conditions = {})
179
+ def permit_attributes(attrs, **conditions)
169
180
  permitted_attributes << {attributes: attrs, conditions: conditions, options: {}}
170
181
  end
171
182
 
172
- def permit_array_attributes(attrs, conditions = {})
183
+ def permit_array_attributes(attrs, **conditions)
173
184
  permitted_attributes << {attributes: attrs, conditions: conditions, options: {array: true}}
174
185
  end
175
186
 
176
- def permit_nested_attributes(attrs, conditions = {})
187
+ def permit_nested_attributes(attrs, **conditions)
177
188
  permitted_attributes << {attributes: attrs, conditions: conditions, options: {nested: true}}
178
189
  end
179
190
 
180
- def policy_class
181
- @policy_class ||= name.sub(/Policy/, '').classify.safe_constantize
182
- end
183
-
184
191
  def property_shapes(conditions)
185
192
  conditions.map { |key, options| send("#{key}_shapes", options) }.flatten.compact
186
193
  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
 
@@ -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,28 @@ 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
30
25
  end
31
26
 
32
- def linked_resource(klass, controller: nil, nested: false, &block)
27
+ def linked_resource(klass, controller: nil, collection: true, nested: false, resource: true, &block) # rubocop:disable Metrics/MethodLength, Metrics/ParameterLists
33
28
  options = route_options(klass, controller, nested, klass.route_key)
34
29
 
30
+ if collection
31
+ get(options[:parentable_path], to: "#{options[:controller]}#index")
32
+ route_block(
33
+ klass,
34
+ :collection,
35
+ controller: options[:controller],
36
+ prefix: options[:parentable_path]
37
+ ).call
38
+ end
39
+
40
+ return unless resource
41
+
35
42
  resources(
36
43
  options[:route_name],
37
44
  active_controller_opts(options),
38
- &route_block(klass, &block)
45
+ &route_block(klass, :resource, &block)
39
46
  )
40
-
41
- post(options[:parentable_path], to: "#{options[:controller]}#create") if options[:creatable]
42
- get(options[:parentable_path], to: "#{options[:controller]}#index")
43
47
  end
44
48
 
45
49
  def singular_linked_resource(klass, controller: nil, nested: true, &block)
@@ -48,10 +52,8 @@ module LinkedRails
48
52
  resource(
49
53
  options[:route_name],
50
54
  active_controller_opts(options).merge(singular_route: true),
51
- &route_block(klass, &block)
55
+ &route_block(klass, :singular, &block)
52
56
  )
53
-
54
- post(options[:parentable_path], to: "#{options[:controller]}#create", singular_route: true) if options[:creatable]
55
57
  end
56
58
 
57
59
  def find_tenant_route
@@ -60,6 +62,16 @@ module LinkedRails
60
62
 
61
63
  private
62
64
 
65
+ def action_routes(controller, prefix, key, value)
66
+ path = value.fetch(:action_path, key)
67
+ action = value.fetch(:action_name)
68
+ get([prefix, path].compact.join('/'), action: action, action_key: key, controller: controller)
69
+ return if value[:target_path].nil?
70
+
71
+ method = value.fetch(:http_method).downcase
72
+ send(method, [prefix, value.fetch(:target_path)].compact.join('/'), action: key, controller: controller)
73
+ end
74
+
63
75
  def active_controller_opts(route_options)
64
76
  {
65
77
  controller: route_options[:controller],
@@ -68,22 +80,20 @@ module LinkedRails
68
80
  }
69
81
  end
70
82
 
71
- def creatable(klass)
72
- klass.enhanced_with?(LinkedRails::Enhancements::Creatable, :Routing)
73
- end
74
-
75
- def route_block(klass)
83
+ def route_block(klass, action_type, controller: nil, prefix: nil)
76
84
  lambda do
77
- include_route_concerns(klass: klass)
78
-
85
+ klass.action_list.defined_actions[action_type].each do |key, value|
86
+ action_routes(controller, prefix, key, value)
87
+ end
79
88
  yield if block_given?
80
89
  end
81
90
  end
82
91
 
83
92
  def route_options(klass, controller, nested, path)
93
+ touch_controller(klass)
94
+
84
95
  {
85
96
  controller: controller || klass.name.tableize,
86
- creatable: creatable(klass),
87
97
  nested: nested,
88
98
  only: %i[show],
89
99
  parentable_path: "(*parent_iri)/#{path}",
@@ -91,6 +101,11 @@ module LinkedRails
91
101
  route_name: klass.name.demodulize.tableize
92
102
  }.with_indifferent_access
93
103
  end
104
+
105
+ # Make sure all actions are initialized before generating the routes
106
+ def touch_controller(klass)
107
+ klass.controller_class
108
+ end
94
109
  end
95
110
  end
96
111
 
@@ -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.ll[:supplant]
20
+ )
21
+ ]
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -1,11 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'serializer/actionable'
4
+ require_relative 'serializer/menuable'
5
+ require_relative 'serializer/singularable'
6
+
3
7
  module LinkedRails
4
8
  module Serializer
5
9
  extend ActiveSupport::Concern
6
10
 
7
11
  included do
8
12
  include RDF::Serializers::ObjectSerializer
13
+ include Serializer::Actionable
14
+ include Serializer::Menuable
15
+ include Serializer::Singularable
16
+
9
17
  extend Enhanceable
10
18
 
11
19
  enhanceable :serializable_class, :Serializer
@@ -15,7 +23,7 @@ module LinkedRails
15
23
  set_id :iri
16
24
 
17
25
  attribute :rdf_type, predicate: Vocab.rdfv.type, datatype: Vocab.xsd.anyURI
18
- attribute :created_at, predicate: Vocab.schema[:dateCreated] do |object|
26
+ attribute :created_at, predicate: Vocab.schema.dateCreated do |object|
19
27
  object.try(:created_at)
20
28
  end
21
29
  end
@@ -58,8 +66,7 @@ module LinkedRails
58
66
  ]
59
67
  end
60
68
 
61
- # rubocop:disable Naming/PredicateName
62
- def has_one(key, opts = {})
69
+ def has_one(key, **opts)
63
70
  opts[:id_method_name] = :iri
64
71
 
65
72
  return super if block_given?
@@ -69,7 +76,7 @@ module LinkedRails
69
76
  end
70
77
  end
71
78
 
72
- def has_many(key, opts = {})
79
+ def has_many(key, **opts)
73
80
  opts[:id_method_name] = :iri
74
81
 
75
82
  return super if block_given?
@@ -78,7 +85,6 @@ module LinkedRails
78
85
  object.send(key)
79
86
  end
80
87
  end
81
- # rubocop:enable Naming/PredicateName
82
88
 
83
89
  def named_object?(object)
84
90
  !object.iri.anonymous?
@@ -88,6 +94,11 @@ module LinkedRails
88
94
  false
89
95
  end
90
96
 
97
+ def secret_attribute(key, **opts)
98
+ opts[:if] = method(:never)
99
+ attribute(key, **opts)
100
+ end
101
+
91
102
  def serializable_class
92
103
  @serializable_class ||= name.gsub('Serializer', '').safe_constantize
93
104
  end
@@ -100,16 +111,18 @@ module LinkedRails
100
111
  end
101
112
  end
102
113
 
103
- def with_collection(name, opts = {})
114
+ def with_collection(name, **opts) # rubocop:disable Metrics/AbcSize
104
115
  collection_name = "#{name.to_s.singularize}_collection"
105
- page_size = opts.delete(:page_size)
106
- display = opts.delete(:display)
107
116
  opts[:association] ||= name
108
117
  opts[:polymorphic] ||= true
109
118
  opts[:if] ||= method(:named_object?)
110
119
 
111
- has_one collection_name, opts do |object, params|
112
- object.send(collection_name, user_context: params[:scope], display: display, page_size: page_size)
120
+ collection_opts = {}
121
+ collection_opts[:page_size] = opts.delete(:page_size) if opts.key?(:page_size)
122
+ collection_opts[:display] = opts.delete(:display) if opts.key?(:display)
123
+
124
+ has_one collection_name, **opts do |object, params|
125
+ object.send(collection_name, **collection_opts.merge(user_context: params[:scope]))
113
126
  end
114
127
  end
115
128