linked_rails 0.0.1

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 (207) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +674 -0
  3. data/README.md +65 -0
  4. data/Rakefile +34 -0
  5. data/app/controllers/linked_rails/actions/items_controller.rb +9 -0
  6. data/app/controllers/linked_rails/bulk_controller.rb +195 -0
  7. data/app/controllers/linked_rails/current_user_controller.rb +13 -0
  8. data/app/controllers/linked_rails/enum_values_controller.rb +49 -0
  9. data/app/controllers/linked_rails/forms_controller.rb +13 -0
  10. data/app/controllers/linked_rails/manifests_controller.rb +21 -0
  11. data/app/controllers/linked_rails/menus/items_controller.rb +9 -0
  12. data/app/controllers/linked_rails/menus/lists_controller.rb +9 -0
  13. data/app/controllers/linked_rails/not_found_controller.rb +15 -0
  14. data/app/controllers/linked_rails/ontologies_controller.rb +7 -0
  15. data/app/models/linked_rails/actions/default_actions/create.rb +60 -0
  16. data/app/models/linked_rails/actions/default_actions/destroy.rb +45 -0
  17. data/app/models/linked_rails/actions/default_actions/update.rb +50 -0
  18. data/app/models/linked_rails/actions/default_actions.rb +17 -0
  19. data/app/models/linked_rails/actions/item.rb +234 -0
  20. data/app/models/linked_rails/actions/list.rb +113 -0
  21. data/app/models/linked_rails/collection/filter.rb +16 -0
  22. data/app/models/linked_rails/collection/filter_field.rb +30 -0
  23. data/app/models/linked_rails/collection/filter_option.rb +17 -0
  24. data/app/models/linked_rails/collection/filterable.rb +92 -0
  25. data/app/models/linked_rails/collection/infinite_view.rb +98 -0
  26. data/app/models/linked_rails/collection/iri.rb +74 -0
  27. data/app/models/linked_rails/collection/iri_mapping.rb +33 -0
  28. data/app/models/linked_rails/collection/paginated_view.rb +41 -0
  29. data/app/models/linked_rails/collection/sortable.rb +60 -0
  30. data/app/models/linked_rails/collection/sorting.rb +72 -0
  31. data/app/models/linked_rails/collection/view.rb +101 -0
  32. data/app/models/linked_rails/collection.rb +220 -0
  33. data/app/models/linked_rails/condition.rb +7 -0
  34. data/app/models/linked_rails/creative_work.rb +21 -0
  35. data/app/models/linked_rails/current_user.rb +28 -0
  36. data/app/models/linked_rails/entry_point.rb +53 -0
  37. data/app/models/linked_rails/enum_value.rb +33 -0
  38. data/app/models/linked_rails/form/field/association_input.rb +23 -0
  39. data/app/models/linked_rails/form/field/checkbox_group.rb +10 -0
  40. data/app/models/linked_rails/form/field/checkbox_input.rb +10 -0
  41. data/app/models/linked_rails/form/field/color_input.rb +17 -0
  42. data/app/models/linked_rails/form/field/date_input.rb +10 -0
  43. data/app/models/linked_rails/form/field/date_time_input.rb +10 -0
  44. data/app/models/linked_rails/form/field/email_input.rb +10 -0
  45. data/app/models/linked_rails/form/field/file_input.rb +10 -0
  46. data/app/models/linked_rails/form/field/location_input.rb +11 -0
  47. data/app/models/linked_rails/form/field/markdown_input.rb +10 -0
  48. data/app/models/linked_rails/form/field/number_input.rb +10 -0
  49. data/app/models/linked_rails/form/field/password_input.rb +10 -0
  50. data/app/models/linked_rails/form/field/postal_range_input.rb +10 -0
  51. data/app/models/linked_rails/form/field/radio_group.rb +10 -0
  52. data/app/models/linked_rails/form/field/resource_field.rb +23 -0
  53. data/app/models/linked_rails/form/field/select_input.rb +11 -0
  54. data/app/models/linked_rails/form/field/slider_input.rb +10 -0
  55. data/app/models/linked_rails/form/field/text_area_input.rb +10 -0
  56. data/app/models/linked_rails/form/field/text_input.rb +10 -0
  57. data/app/models/linked_rails/form/field/toggle_button_group.rb +10 -0
  58. data/app/models/linked_rails/form/field.rb +117 -0
  59. data/app/models/linked_rails/form/field_factory.rb +219 -0
  60. data/app/models/linked_rails/form/group.rb +39 -0
  61. data/app/models/linked_rails/form/page.rb +31 -0
  62. data/app/models/linked_rails/form.rb +156 -0
  63. data/app/models/linked_rails/manifest.rb +102 -0
  64. data/app/models/linked_rails/media_object.rb +31 -0
  65. data/app/models/linked_rails/menus/item.rb +92 -0
  66. data/app/models/linked_rails/menus/list.rb +138 -0
  67. data/app/models/linked_rails/ontology/base.rb +50 -0
  68. data/app/models/linked_rails/ontology/class.rb +43 -0
  69. data/app/models/linked_rails/ontology/property.rb +19 -0
  70. data/app/models/linked_rails/ontology.rb +34 -0
  71. data/app/models/linked_rails/property_query.rb +11 -0
  72. data/app/models/linked_rails/resource.rb +17 -0
  73. data/app/models/linked_rails/sequence.rb +64 -0
  74. data/app/models/linked_rails/shacl/node_shape.rb +21 -0
  75. data/app/models/linked_rails/shacl/property_shape.rb +53 -0
  76. data/app/models/linked_rails/shacl/shape.rb +33 -0
  77. data/app/models/linked_rails/web_page.rb +22 -0
  78. data/app/models/linked_rails/web_site.rb +17 -0
  79. data/app/models/linked_rails/widget.rb +55 -0
  80. data/app/policies/linked_rails/actions/item_policy.rb +11 -0
  81. data/app/policies/linked_rails/actions/list_policy.rb +11 -0
  82. data/app/policies/linked_rails/collection/view_policy.rb +13 -0
  83. data/app/policies/linked_rails/collection_policy.rb +41 -0
  84. data/app/policies/linked_rails/enum_value_policy.rb +32 -0
  85. data/app/policies/linked_rails/menus/item_policy.rb +11 -0
  86. data/app/policies/linked_rails/menus/list_policy.rb +11 -0
  87. data/app/policies/linked_rails/sequence_policy.rb +9 -0
  88. data/app/serializers/linked_rails/actions/item_serializer.rb +28 -0
  89. data/app/serializers/linked_rails/collection/filter_field_serializer.rb +12 -0
  90. data/app/serializers/linked_rails/collection/filter_option_serializer.rb +12 -0
  91. data/app/serializers/linked_rails/collection/filter_serializer.rb +13 -0
  92. data/app/serializers/linked_rails/collection/sorting_serializer.rb +13 -0
  93. data/app/serializers/linked_rails/collection/view_serializer.rb +22 -0
  94. data/app/serializers/linked_rails/collection_serializer.rb +44 -0
  95. data/app/serializers/linked_rails/condition_serializer.rb +9 -0
  96. data/app/serializers/linked_rails/creative_work_serializer.rb +10 -0
  97. data/app/serializers/linked_rails/current_user_serializer.rb +7 -0
  98. data/app/serializers/linked_rails/entry_point_serializer.rb +20 -0
  99. data/app/serializers/linked_rails/enum_value_serializer.rb +12 -0
  100. data/app/serializers/linked_rails/form/field/association_input_serializer.rb +13 -0
  101. data/app/serializers/linked_rails/form/field/resource_field_serializer.rb +11 -0
  102. data/app/serializers/linked_rails/form/field/select_input_serializer.rb +11 -0
  103. data/app/serializers/linked_rails/form/field_serializer.rb +39 -0
  104. data/app/serializers/linked_rails/form/group_serializer.rb +14 -0
  105. data/app/serializers/linked_rails/form/page_serializer.rb +13 -0
  106. data/app/serializers/linked_rails/form_serializer.rb +9 -0
  107. data/app/serializers/linked_rails/media_object_serializer.rb +17 -0
  108. data/app/serializers/linked_rails/menus/item_serializer.rb +35 -0
  109. data/app/serializers/linked_rails/menus/list_serializer.rb +13 -0
  110. data/app/serializers/linked_rails/ontology/class_serializer.rb +19 -0
  111. data/app/serializers/linked_rails/ontology/property_serializer.rb +16 -0
  112. data/app/serializers/linked_rails/ontology_serializer.rb +8 -0
  113. data/app/serializers/linked_rails/rdf_error_serializer.rb +8 -0
  114. data/app/serializers/linked_rails/sequence_serializer.rb +14 -0
  115. data/app/serializers/linked_rails/shacl/node_shape_serializer.rb +12 -0
  116. data/app/serializers/linked_rails/shacl/property_shape_serializer.rb +38 -0
  117. data/app/serializers/linked_rails/shacl/shape_serializer.rb +25 -0
  118. data/app/serializers/linked_rails/web_page_serializer.rb +10 -0
  119. data/app/serializers/linked_rails/web_site_serializer.rb +11 -0
  120. data/app/serializers/linked_rails/widget_serializer.rb +15 -0
  121. data/config/initializers/inflections.rb +5 -0
  122. data/lib/generators/linked_rails/install/install_generator.rb +65 -0
  123. data/lib/generators/linked_rails/install/templates/app_menu_list.rb +41 -0
  124. data/lib/generators/linked_rails/install/templates/application_action_list.rb +3 -0
  125. data/lib/generators/linked_rails/install/templates/application_form.rb +3 -0
  126. data/lib/generators/linked_rails/install/templates/application_menu_list.rb +3 -0
  127. data/lib/generators/linked_rails/install/templates/application_policy.rb +18 -0
  128. data/lib/generators/linked_rails/install/templates/application_serializer.rb +5 -0
  129. data/lib/generators/linked_rails/install/templates/initializer.rb +9 -0
  130. data/lib/generators/linked_rails/install/templates/locales.yml +12 -0
  131. data/lib/generators/linked_rails/install/templates/rdf_responder.rb +5 -0
  132. data/lib/generators/linked_rails/install/templates/rdf_serializers_initializer.rb +5 -0
  133. data/lib/generators/linked_rails/install/templates/vocab.rb +5 -0
  134. data/lib/generators/linked_rails/install/templates/vocab.yml +26 -0
  135. data/lib/generators/linked_rails/model/model_generator.rb +58 -0
  136. data/lib/generators/linked_rails/model/templates/action_list.rb.tt +6 -0
  137. data/lib/generators/linked_rails/model/templates/controller.rb.tt +7 -0
  138. data/lib/generators/linked_rails/model/templates/create_table_migration.rb.tt +24 -0
  139. data/lib/generators/linked_rails/model/templates/form.rb.tt +6 -0
  140. data/lib/generators/linked_rails/model/templates/menu_list.rb.tt +6 -0
  141. data/lib/generators/linked_rails/model/templates/model.rb.tt +15 -0
  142. data/lib/generators/linked_rails/model/templates/module.rb.tt +7 -0
  143. data/lib/generators/linked_rails/model/templates/policy.rb.tt +6 -0
  144. data/lib/generators/linked_rails/model/templates/serializer.rb.tt +9 -0
  145. data/lib/linked_rails/active_response/controller/collections.rb +43 -0
  146. data/lib/linked_rails/active_response/controller/crud_defaults.rb +92 -0
  147. data/lib/linked_rails/active_response/controller/params.rb +51 -0
  148. data/lib/linked_rails/active_response/controller.rb +37 -0
  149. data/lib/linked_rails/active_response/responders/rdf.rb +158 -0
  150. data/lib/linked_rails/cache.rb +35 -0
  151. data/lib/linked_rails/callable_variable.rb +25 -0
  152. data/lib/linked_rails/constraints/whitelist.rb +36 -0
  153. data/lib/linked_rails/controller/authorization.rb +15 -0
  154. data/lib/linked_rails/controller/error_handling.rb +76 -0
  155. data/lib/linked_rails/controller.rb +43 -0
  156. data/lib/linked_rails/engine.rb +7 -0
  157. data/lib/linked_rails/enhanceable.rb +21 -0
  158. data/lib/linked_rails/enhancements/actionable/model.rb +71 -0
  159. data/lib/linked_rails/enhancements/actionable/serializer.rb +25 -0
  160. data/lib/linked_rails/enhancements/creatable/action.rb +15 -0
  161. data/lib/linked_rails/enhancements/creatable/controller.rb +15 -0
  162. data/lib/linked_rails/enhancements/destroyable/action.rb +15 -0
  163. data/lib/linked_rails/enhancements/destroyable/controller.rb +15 -0
  164. data/lib/linked_rails/enhancements/destroyable/routing.rb +19 -0
  165. data/lib/linked_rails/enhancements/indexable/model.rb +10 -0
  166. data/lib/linked_rails/enhancements/menuable/model.rb +36 -0
  167. data/lib/linked_rails/enhancements/menuable/serializer.rb +33 -0
  168. data/lib/linked_rails/enhancements/route_concerns.rb +56 -0
  169. data/lib/linked_rails/enhancements/singularable/controller.rb +43 -0
  170. data/lib/linked_rails/enhancements/singularable/model.rb +47 -0
  171. data/lib/linked_rails/enhancements/singularable/serializer.rb +28 -0
  172. data/lib/linked_rails/enhancements/tableable/model.rb +28 -0
  173. data/lib/linked_rails/enhancements/updatable/action.rb +15 -0
  174. data/lib/linked_rails/enhancements/updatable/controller.rb +15 -0
  175. data/lib/linked_rails/enhancements/updatable/routing.rb +20 -0
  176. data/lib/linked_rails/enhancements/updatable/serializer.rb +17 -0
  177. data/lib/linked_rails/enhancements.rb +22 -0
  178. data/lib/linked_rails/helpers/delta_helper.rb +86 -0
  179. data/lib/linked_rails/helpers/ontola_actions_helper.rb +32 -0
  180. data/lib/linked_rails/helpers/resource_helper.rb +70 -0
  181. data/lib/linked_rails/iri_mapper.rb +125 -0
  182. data/lib/linked_rails/middleware/linked_data_params.rb +224 -0
  183. data/lib/linked_rails/model/collections.rb +82 -0
  184. data/lib/linked_rails/model/dirty.rb +82 -0
  185. data/lib/linked_rails/model/enhancements.rb +61 -0
  186. data/lib/linked_rails/model/filtering.rb +89 -0
  187. data/lib/linked_rails/model/indexable.rb +51 -0
  188. data/lib/linked_rails/model/iri.rb +121 -0
  189. data/lib/linked_rails/model/iri_mapping.rb +69 -0
  190. data/lib/linked_rails/model/serialization.rb +88 -0
  191. data/lib/linked_rails/model/sorting.rb +20 -0
  192. data/lib/linked_rails/model.rb +68 -0
  193. data/lib/linked_rails/params_parser.rb +93 -0
  194. data/lib/linked_rails/policy/attribute_conditions.rb +53 -0
  195. data/lib/linked_rails/policy.rb +189 -0
  196. data/lib/linked_rails/rdf_error.rb +36 -0
  197. data/lib/linked_rails/renderers.rb +46 -0
  198. data/lib/linked_rails/routes.rb +108 -0
  199. data/lib/linked_rails/serializer.rb +137 -0
  200. data/lib/linked_rails/translate.rb +176 -0
  201. data/lib/linked_rails/version.rb +5 -0
  202. data/lib/linked_rails/vocab.rb +81 -0
  203. data/lib/linked_rails.rb +86 -0
  204. data/lib/nill_class_renderer.rb +3 -0
  205. data/lib/rails/welcome_controller.rb +45 -0
  206. data/lib/tasks/linked_rails_tasks.rake +6 -0
  207. metadata +416 -0
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ class Collection
5
+ class Filter < RDF::Node
6
+ include ActiveModel::Model
7
+ include LinkedRails::Model
8
+
9
+ attr_accessor :default_filter, :key, :value, :collection
10
+
11
+ def iri(_opts = {})
12
+ self
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ class Collection
5
+ class FilterField < RDF::Node
6
+ include ActiveModel::Serialization
7
+ include ActiveModel::Model
8
+ include LinkedRails::Model
9
+ include LinkedRails::CallableVariable
10
+
11
+ attr_accessor :key, :klass, :collection
12
+ attr_writer :options_array, :options_in
13
+ callable_variable(:options_array)
14
+ callable_variable(:options_in)
15
+
16
+ def iri(_opts = {})
17
+ self
18
+ end
19
+
20
+ def options
21
+ @options ||= options_array&.map(&method(:filter_option)) || []
22
+ end
23
+
24
+ def filter_option(option)
25
+ attrs = option.is_a?(Hash) ? option : {value: option}
26
+ Collection::FilterOption.new(attrs.merge(collection: collection, key: key))
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ class Collection
5
+ class FilterOption < RDF::Node
6
+ include ActiveModel::Serialization
7
+ include ActiveModel::Model
8
+ include LinkedRails::Model
9
+
10
+ attr_accessor :collection, :count, :value, :key
11
+
12
+ def iri(_opts = {})
13
+ self
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ class Collection
5
+ module Filterable
6
+ include RDF::Serializers::DataTypeHelper
7
+
8
+ attr_writer :default_filters, :filter, :unfiltered_collection
9
+
10
+ def default_filters
11
+ opts = @default_filters || association_class.try(:default_filters) || {}
12
+ opts.respond_to?(:call) ? opts.call(parent) : opts
13
+ end
14
+
15
+ def filter
16
+ default_filters.merge(@filter || {})
17
+ end
18
+
19
+ delegate :filter_options, to: :association_class
20
+
21
+ def filter_fields
22
+ @filter_fields ||= filter_options&.map { |key, options| filter_field(key, options) }
23
+ end
24
+
25
+ def filtered?
26
+ filter.present?
27
+ end
28
+
29
+ def filters
30
+ @filters ||= filter.map do |key, values|
31
+ predicate = key.is_a?(RDF::URI) ? key : association_class.predicate_for_key(key)
32
+
33
+ LinkedRails.collection_filter_class.new(
34
+ collection: self,
35
+ default_filter: !@filter&.key?(predicate),
36
+ key: predicate,
37
+ value: values.map { |val| sanitized_filter_value(predicate, val) }
38
+ )
39
+ end
40
+ end
41
+
42
+ def unfiltered
43
+ filtered? ? unfiltered_collection : self
44
+ end
45
+
46
+ def unfiltered_collection
47
+ @unfiltered_collection ||= new_child(filter: {})
48
+ end
49
+
50
+ private
51
+
52
+ def apply_filters(scope)
53
+ (filters || []).reduce(scope) do |s, filter|
54
+ apply_filter(s, filter.key, filter.value)
55
+ end
56
+ end
57
+
58
+ def apply_filter(scope, key, values)
59
+ filter = filter_options.fetch(key).try(:[], :filter)
60
+
61
+ return filter.call(scope, values) if filter
62
+
63
+ scope.where(association_class.predicate_mapping[key].key => values)
64
+ end
65
+
66
+ def filtered_association # rubocop:disable Metrics/AbcSize
67
+ scope = association && parent&.send(association) || association_class.all
68
+ scope = scope.send(association_scope) if association_scope
69
+ scope = scope.joins(joins) if joins
70
+ scope = apply_filters(scope) if filtered?
71
+ scope
72
+ end
73
+
74
+ def filter_field(key, options)
75
+ Collection::FilterField.new(
76
+ collection: self,
77
+ klass: association_class,
78
+ key: key,
79
+ options_in: options[:values_in],
80
+ options_array: options[:values]
81
+ )
82
+ end
83
+
84
+ def sanitized_filter_value(key, value)
85
+ mapping = association_class.predicate_mapping[key]
86
+ datatype = mapping.is_a?(FastJsonapi::Relationship) ? RDF.xsd.anyURI : mapping.datatype
87
+ val = xsd_to_rdf(datatype, value)
88
+ val.literal? ? val.object : val
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ class Collection
5
+ class InfiniteView < LinkedRails.collection_view_class
6
+ attr_accessor :before
7
+
8
+ def initialize(orignial = {})
9
+ attrs = orignial.with_indifferent_access
10
+ attrs[:before] = attrs[:before]&.map { |val| val.with_indifferent_access }
11
+ super(attrs)
12
+ end
13
+
14
+ def next
15
+ return if before.blank? || members.blank?
16
+
17
+ current_opts = {
18
+ collection: collection,
19
+ filter: filter
20
+ }
21
+ next_view = collection.view_with_opts(current_opts.merge(before: next_before_values))
22
+
23
+ next_view.iri if next_view.count.positive?
24
+ end
25
+
26
+ def prev; end
27
+
28
+ def type
29
+ :infinite
30
+ end
31
+
32
+ private
33
+
34
+ def additional_statements(arel, index)
35
+ (preconditions(index) + [arel]).reduce { |or_acc, or_value| or_acc.and(or_value) }
36
+ end
37
+
38
+ def before_iri_opts
39
+ before&.map { |hash| "#{CGI.escape(hash[:key])}=#{hash[:value]}" }
40
+ end
41
+
42
+ def before_query
43
+ before_values.each_with_index.reduce(nil) do |acc, (value, index)|
44
+ arel = arel_table[value[:attribute]].send(value[:direction], value[:value])
45
+ acc.nil? ? arel : acc.or(additional_statements(arel, index))
46
+ end
47
+ end
48
+
49
+ def before_values
50
+ @before_values ||= before&.map do |value|
51
+ sorting = collection.sortings.detect { |s| s.key == value[:key] }
52
+ {
53
+ attribute: sorting.attribute_name,
54
+ direction: sorting.sort_direction,
55
+ key: value[:key],
56
+ value: value[:value]
57
+ }
58
+ end
59
+ end
60
+
61
+ def iri_opts
62
+ {
63
+ 'before%5B%5D': before_iri_opts
64
+ }.merge(collection.iri_opts)
65
+ end
66
+
67
+ def members_query
68
+ prepare_members(association_base)
69
+ .where(before_query)
70
+ .limit(page_size)
71
+ end
72
+
73
+ def next_before_values
74
+ last_record = members.last
75
+ before_values.map do |val|
76
+ value = last_record.send(val[:attribute])
77
+ value = value.utc.iso8601(6) if value.is_a?(Time)
78
+ {
79
+ key: val[:key],
80
+ value: value
81
+ }
82
+ end
83
+ end
84
+
85
+ def preconditions(index)
86
+ before_values[0...index].map do |v|
87
+ condition = arel_table[v[:attribute]].eq(v[:value])
88
+ condition = condition.or(arel_table[v[:attribute]].eq(nil)) if v[:direction] == :gt
89
+ condition
90
+ end
91
+ end
92
+
93
+ def sort_column
94
+ @sort_column ||= collection.sortings.last.sort_value.keys.first
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ class Collection
5
+ module Iri
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ attr_writer :iri_template
10
+ end
11
+
12
+ def iri_opts
13
+ opts = {}
14
+ iri_opts_add(opts, :display, display) if @display
15
+ iri_opts_add(opts, :title, title) if @title
16
+ iri_opts_add(opts, :type, type) if @type
17
+ iri_opts_add(opts, :page_size, page_size) if @page_size
18
+ iri_opts_add(opts, :'filter%5B%5D', filter_iri_opts)
19
+ iri_opts_add(opts, :'sort%5B%5D', sort_iri_opts)
20
+ opts
21
+ end
22
+
23
+ def iri_template
24
+ @iri_template ||=
25
+ URITemplate.new(
26
+ [
27
+ LinkedRails.iri(
28
+ path: [parent&.root_relative_iri&.to_s&.split('?')&.first, association_class.route_key].join('/')
29
+ ),
30
+ "{?#{self.class.iri_template_parsed_keys}}"
31
+ ].join('')
32
+ )
33
+ end
34
+
35
+ def iri_template_opts
36
+ opts = iri_opts.with_indifferent_access.slice(*self.class.iri_template_keys)
37
+ Hash[opts.keys.map { |key| [CGI.unescape(key).sub('[]', ''), opts[key]] }].to_param
38
+ end
39
+
40
+ def filter_iri_opts
41
+ @filter&.map do |key, values|
42
+ predicate = key.is_a?(RDF::URI) ? key : association_class.predicate_for_key(key)
43
+
44
+ values.map { |value| "#{CGI.escape(predicate.to_s)}=#{CGI.escape(value.to_s)}" }
45
+ end&.flatten
46
+ end
47
+
48
+ def iri_opts_add(opts, key, value)
49
+ opts[key] = value if value.present?
50
+ end
51
+
52
+ def sort_iri_opts
53
+ sort&.map { |s| "#{CGI.escape(s[:key].to_s)}=#{s[:direction]}" }
54
+ end
55
+
56
+ class_methods do
57
+ def iri
58
+ [super, Vocab.as.Collection]
59
+ end
60
+
61
+ def iri_template_keys
62
+ @iri_template_keys ||= %i[before%5B%5D display filter%5B%5D page page_size sort%5B%5D title type]
63
+ end
64
+
65
+ def iri_template_parsed_keys
66
+ @iri_template_parsed_keys ||=
67
+ iri_template_keys
68
+ .map { |k| k.to_s.ends_with?('%5B%5D') ? "#{k}*" : k.to_s }
69
+ .join(',')
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ class Collection
5
+ module IriMapping
6
+ extend ActiveSupport::Concern
7
+
8
+ class_methods do
9
+ def collection_or_view(options, params)
10
+ parser = params_parser_class.new(params)
11
+ collection = new(options.merge(collection_params(parser)))
12
+ view_params = collection_view_params(parser)
13
+
14
+ view_params.present? ? collection&.view_with_opts(view_params) : collection
15
+ end
16
+
17
+ private
18
+
19
+ def collection_params(parser)
20
+ parser.collection_params
21
+ end
22
+
23
+ def collection_view_params(parser)
24
+ parser.collection_view_params
25
+ end
26
+
27
+ def params_parser_class
28
+ LinkedRails::ParamsParser
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ class Collection
5
+ class PaginatedView < LinkedRails.collection_view_class
6
+ attr_accessor :page
7
+
8
+ def next
9
+ return if page.nil? || page.to_i >= (total_page_count || 0)
10
+
11
+ iri_with_root(root_relative_iri(page: page.to_i + 1))
12
+ end
13
+
14
+ def prev
15
+ return if page.nil? || page.to_i <= 1
16
+
17
+ iri_with_root(root_relative_iri(page: page.to_i - 1))
18
+ end
19
+
20
+ private
21
+
22
+ def iri_opts
23
+ {
24
+ page: page
25
+ }.merge(collection.iri_opts)
26
+ end
27
+
28
+ def members_query
29
+ prepare_members(association_base)
30
+ .page(page)
31
+ .per(page_size)
32
+ end
33
+
34
+ def prepare_members(scope)
35
+ return super unless scope.is_a?(Array) && !scope.is_a?(Kaminari::PaginatableArray)
36
+
37
+ Kaminari.paginate_array(super)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ class Collection
5
+ module Sortable
6
+ attr_accessor :sort
7
+ attr_writer :default_sortings, :sort_options
8
+
9
+ def default_before_value
10
+ sortings.map do |sorting|
11
+ {
12
+ key: sorting.key,
13
+ value: sorting.default_value
14
+ }
15
+ end
16
+ end
17
+
18
+ def default_sortings
19
+ opts =
20
+ @default_sortings ||
21
+ association_class.try(:default_sortings) ||
22
+ [{key: Vocab.schema.dateCreated, direction: :desc}]
23
+ opts.respond_to?(:call) ? opts.call(parent) : opts
24
+ end
25
+
26
+ def parsed_sort_values
27
+ sortings.map(&:sort_value)
28
+ end
29
+
30
+ def primary_key_sorting
31
+ [
32
+ {
33
+ key: Vocab.ontola[:primaryKey],
34
+ direction: :asc
35
+ }
36
+ ]
37
+ end
38
+
39
+ def sort_options
40
+ @sort_options || association_class.try(:sort_options, self)
41
+ end
42
+
43
+ def sorted_association(scope)
44
+ scope.respond_to?(:reorder) ? scope.reorder(parsed_sort_values) : scope
45
+ end
46
+
47
+ def sorted?
48
+ sort.present?
49
+ end
50
+
51
+ def sortings
52
+ @sortings ||= LinkedRails.collection_sorting_class.from_array(
53
+ association_class,
54
+ (sort || default_sortings) + primary_key_sorting,
55
+ self
56
+ )
57
+ end
58
+ end
59
+ end
60
+ end