linked_rails 0.0.1 → 0.0.4.pre.g1faca9643

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +21 -674
  3. data/app/controllers/linked_rails/actions/objects_controller.rb +9 -0
  4. data/app/controllers/linked_rails/bulk_controller.rb +105 -22
  5. data/app/controllers/linked_rails/enum_values_controller.rb +0 -42
  6. data/app/models/linked_rails/actions/item.rb +64 -55
  7. data/app/models/linked_rails/actions/list.rb +6 -31
  8. data/app/models/linked_rails/actions/object.rb +38 -0
  9. data/app/models/linked_rails/collection/configuration.rb +55 -0
  10. data/app/models/linked_rails/collection/filter.rb +1 -1
  11. data/app/models/linked_rails/collection/filter_field.rb +19 -2
  12. data/app/models/linked_rails/collection/filter_option.rb +1 -1
  13. data/app/models/linked_rails/collection/filterable.rb +6 -9
  14. data/app/models/linked_rails/collection/infinite.rb +113 -0
  15. data/app/models/linked_rails/collection/infinite_view.rb +1 -90
  16. data/app/models/linked_rails/collection/iri.rb +47 -43
  17. data/app/models/linked_rails/collection/iri_mapping.rb +15 -7
  18. data/app/models/linked_rails/collection/paginated.rb +46 -0
  19. data/app/models/linked_rails/collection/paginated_view.rb +1 -33
  20. data/app/models/linked_rails/collection/sortable.rb +1 -9
  21. data/app/models/linked_rails/collection/sorting.rb +1 -1
  22. data/app/models/linked_rails/collection/view.rb +51 -14
  23. data/app/models/linked_rails/collection.rb +53 -85
  24. data/app/models/linked_rails/creative_work.rb +1 -1
  25. data/app/models/linked_rails/entry_point.rb +8 -5
  26. data/app/models/linked_rails/enum_value.rb +40 -2
  27. data/app/models/linked_rails/form/field/association_input.rb +7 -1
  28. data/app/models/linked_rails/form/field/file_input.rb +1 -0
  29. data/app/models/linked_rails/form/field/resource_field.rb +2 -0
  30. data/{lib/linked_rails/enhancements/indexable/model.rb → app/models/linked_rails/form/field/url_input.rb} +3 -3
  31. data/app/models/linked_rails/form/field.rb +37 -13
  32. data/app/models/linked_rails/form/field_factory.rb +48 -18
  33. data/app/models/linked_rails/form/group.rb +4 -6
  34. data/app/models/linked_rails/form/page.rb +8 -4
  35. data/app/models/linked_rails/form.rb +17 -21
  36. data/app/models/linked_rails/manifest.rb +102 -22
  37. data/app/models/linked_rails/menus/item.rb +10 -13
  38. data/app/models/linked_rails/menus/list.rb +16 -7
  39. data/app/models/linked_rails/ontology/base.rb +3 -1
  40. data/app/models/linked_rails/ontology/class.rb +3 -3
  41. data/app/models/linked_rails/ontology.rb +6 -5
  42. data/app/models/linked_rails/property_query.rb +2 -0
  43. data/app/models/linked_rails/sequence.rb +4 -13
  44. data/app/models/linked_rails/shacl/property_shape.rb +1 -1
  45. data/app/models/linked_rails/web_page.rb +0 -4
  46. data/app/models/linked_rails/web_site.rb +0 -4
  47. data/app/models/linked_rails/widget.rb +4 -11
  48. data/app/policies/linked_rails/actions/object_policy.rb +11 -0
  49. data/app/policies/linked_rails/collection_policy.rb +2 -2
  50. data/app/policies/linked_rails/form_policy.rb +13 -0
  51. data/app/policies/linked_rails/ontology_policy.rb +13 -0
  52. data/app/serializers/linked_rails/actions/item_serializer.rb +5 -5
  53. data/app/serializers/linked_rails/actions/object_serializer.rb +9 -0
  54. data/app/serializers/linked_rails/collection/filter_field_serializer.rb +3 -2
  55. data/app/serializers/linked_rails/collection/filter_option_serializer.rb +1 -1
  56. data/app/serializers/linked_rails/collection/filter_serializer.rb +1 -1
  57. data/app/serializers/linked_rails/collection/sorting_serializer.rb +1 -1
  58. data/app/serializers/linked_rails/collection/view_serializer.rb +3 -3
  59. data/app/serializers/linked_rails/collection_serializer.rb +8 -7
  60. data/app/serializers/linked_rails/condition_serializer.rb +3 -3
  61. data/app/serializers/linked_rails/entry_point_serializer.rb +3 -3
  62. data/app/serializers/linked_rails/enum_value_serializer.rb +1 -0
  63. data/app/serializers/linked_rails/form/field/association_input_serializer.rb +1 -0
  64. data/app/serializers/linked_rails/form/field/file_input_serializer.rb +11 -0
  65. data/app/serializers/linked_rails/form/field_serializer.rb +3 -1
  66. data/app/serializers/linked_rails/form/group_serializer.rb +1 -1
  67. data/app/serializers/linked_rails/form/page_serializer.rb +1 -1
  68. data/app/serializers/linked_rails/menus/item_serializer.rb +3 -3
  69. data/app/serializers/linked_rails/menus/list_serializer.rb +1 -1
  70. data/app/serializers/linked_rails/ontology_serializer.rb +2 -2
  71. data/app/serializers/linked_rails/property_query_serializer.rb +7 -0
  72. data/app/serializers/linked_rails/sequence_serializer.rb +2 -5
  73. data/app/serializers/linked_rails/shacl/node_shape_serializer.rb +1 -1
  74. data/app/serializers/linked_rails/shacl/property_shape_serializer.rb +1 -1
  75. data/app/serializers/linked_rails/shacl/shape_serializer.rb +5 -5
  76. data/app/serializers/linked_rails/web_page_serializer.rb +3 -3
  77. data/app/serializers/linked_rails/web_site_serializer.rb +1 -1
  78. data/app/serializers/linked_rails/widget_serializer.rb +3 -3
  79. data/app/workers/linked_rails/invalidation_stream_worker.rb +16 -0
  80. data/lib/generators/linked_rails/install/install_generator.rb +7 -8
  81. data/lib/generators/linked_rails/install/templates/README +7 -0
  82. data/lib/generators/linked_rails/install/templates/app_menu_list.rb +36 -7
  83. data/lib/generators/linked_rails/install/templates/application_menu_list.rb +40 -1
  84. data/lib/generators/linked_rails/install/templates/initializer.rb +1 -2
  85. data/lib/generators/linked_rails/install/templates/locales.yml +16 -0
  86. data/lib/generators/linked_rails/install/templates/rdf_serializers_initializer.rb +1 -1
  87. data/lib/generators/linked_rails/install/templates/vocab.rb +1 -0
  88. data/lib/generators/linked_rails/install/templates/vocab.yml +2 -2
  89. data/lib/generators/linked_rails/model/model_generator.rb +0 -1
  90. data/lib/generators/linked_rails/model/templates/controller.rb.tt +5 -1
  91. data/lib/generators/linked_rails/model/templates/form.rb.tt +3 -0
  92. data/lib/generators/linked_rails/model/templates/menu_list.rb.tt +15 -0
  93. data/lib/generators/linked_rails/model/templates/policy.rb.tt +13 -0
  94. data/lib/generators/linked_rails/model/templates/serializer.rb.tt +5 -1
  95. data/lib/linked_rails/active_response/controller/collections.rb +1 -1
  96. data/lib/linked_rails/active_response/controller/crud_defaults.rb +4 -4
  97. data/lib/linked_rails/active_response/controller/params.rb +10 -10
  98. data/lib/linked_rails/active_response/controller.rb +8 -18
  99. data/lib/linked_rails/active_response/responders/rdf.rb +19 -10
  100. data/lib/linked_rails/callable_variable.rb +1 -1
  101. data/lib/linked_rails/collection_params_parser.rb +93 -0
  102. data/lib/linked_rails/controller/actionable.rb +121 -0
  103. data/lib/linked_rails/controller/authorization.rb +6 -0
  104. data/lib/linked_rails/controller/default_actions/create.rb +52 -0
  105. data/lib/linked_rails/controller/default_actions/destroy.rb +42 -0
  106. data/lib/linked_rails/controller/default_actions/update.rb +43 -0
  107. data/lib/linked_rails/controller/delta.rb +58 -0
  108. data/lib/linked_rails/controller/error_handling.rb +14 -7
  109. data/lib/linked_rails/controller/rendering.rb +48 -0
  110. data/lib/linked_rails/controller.rb +24 -4
  111. data/lib/linked_rails/enhanceable.rb +1 -1
  112. data/lib/linked_rails/enhancements/creatable/controller.rb +1 -1
  113. data/lib/linked_rails/enhancements/destroyable/controller.rb +1 -1
  114. data/lib/linked_rails/enhancements/updatable/controller.rb +1 -1
  115. data/lib/linked_rails/enhancements.rb +0 -16
  116. data/lib/linked_rails/errors/forbidden.rb +37 -0
  117. data/lib/linked_rails/errors.rb +3 -0
  118. data/lib/linked_rails/helpers/delta_helper.rb +26 -57
  119. data/lib/linked_rails/helpers/ontola_actions_helper.rb +2 -2
  120. data/lib/linked_rails/helpers/resource_helper.rb +11 -2
  121. data/lib/linked_rails/iri_mapper.rb +17 -39
  122. data/lib/linked_rails/middleware/error_handling.rb +51 -0
  123. data/lib/linked_rails/middleware/linked_data_params.rb +30 -151
  124. data/lib/linked_rails/model/actionable.rb +68 -0
  125. data/lib/linked_rails/model/cacheable.rb +45 -0
  126. data/lib/linked_rails/model/collections.rb +201 -39
  127. data/lib/linked_rails/model/dirty.rb +6 -18
  128. data/lib/linked_rails/model/enhancements.rb +1 -6
  129. data/lib/linked_rails/model/filtering.rb +4 -6
  130. data/lib/linked_rails/model/indexable.rb +6 -16
  131. data/lib/linked_rails/model/iri.rb +29 -19
  132. data/lib/linked_rails/model/iri_mapping.rb +37 -8
  133. data/lib/linked_rails/model/menuable.rb +28 -0
  134. data/lib/linked_rails/model/serialization.rb +2 -15
  135. data/lib/linked_rails/model/singularable.rb +57 -0
  136. data/lib/linked_rails/model/sorting.rb +0 -5
  137. data/lib/linked_rails/model/tables.rb +26 -0
  138. data/lib/linked_rails/model.rb +18 -7
  139. data/lib/linked_rails/params_parser.rb +131 -54
  140. data/lib/linked_rails/policy/attribute_conditions.rb +2 -2
  141. data/lib/linked_rails/policy.rb +44 -46
  142. data/lib/linked_rails/railtie.rb +11 -0
  143. data/lib/linked_rails/rdf_error.rb +2 -2
  144. data/lib/linked_rails/renderers.rb +1 -0
  145. data/lib/linked_rails/routes.rb +38 -22
  146. data/lib/linked_rails/serializer/actionable.rb +27 -0
  147. data/lib/linked_rails/serializer/menuable.rb +31 -0
  148. data/lib/linked_rails/serializer/singularable.rb +26 -0
  149. data/lib/linked_rails/serializer.rb +28 -11
  150. data/lib/linked_rails/storage.rb +32 -0
  151. data/lib/linked_rails/test_methods.rb +114 -0
  152. data/lib/linked_rails/translate.rb +31 -9
  153. data/lib/linked_rails/types/iri_type.rb +37 -0
  154. data/lib/linked_rails/uri_template.rb +30 -0
  155. data/lib/linked_rails/url.rb +11 -0
  156. data/lib/linked_rails/version.rb +1 -1
  157. data/lib/linked_rails/vocab.rb +9 -0
  158. data/lib/linked_rails.rb +40 -14
  159. data/lib/rails/welcome_controller.rb +3 -2
  160. data/lib/rdf/list.rb +9 -0
  161. data/lib/rdf/query_fix.rb +15 -0
  162. metadata +80 -33
  163. data/app/models/linked_rails/actions/default_actions/create.rb +0 -60
  164. data/app/models/linked_rails/actions/default_actions/destroy.rb +0 -45
  165. data/app/models/linked_rails/actions/default_actions/update.rb +0 -50
  166. data/app/models/linked_rails/actions/default_actions.rb +0 -17
  167. data/lib/generators/linked_rails/install/templates/application_action_list.rb +0 -3
  168. data/lib/generators/linked_rails/model/templates/action_list.rb.tt +0 -6
  169. data/lib/linked_rails/enhancements/actionable/model.rb +0 -71
  170. data/lib/linked_rails/enhancements/actionable/serializer.rb +0 -25
  171. data/lib/linked_rails/enhancements/creatable/action.rb +0 -15
  172. data/lib/linked_rails/enhancements/destroyable/action.rb +0 -15
  173. data/lib/linked_rails/enhancements/destroyable/routing.rb +0 -19
  174. data/lib/linked_rails/enhancements/menuable/model.rb +0 -36
  175. data/lib/linked_rails/enhancements/menuable/serializer.rb +0 -33
  176. data/lib/linked_rails/enhancements/route_concerns.rb +0 -56
  177. data/lib/linked_rails/enhancements/singularable/controller.rb +0 -43
  178. data/lib/linked_rails/enhancements/singularable/model.rb +0 -47
  179. data/lib/linked_rails/enhancements/singularable/serializer.rb +0 -28
  180. data/lib/linked_rails/enhancements/tableable/model.rb +0 -28
  181. data/lib/linked_rails/enhancements/updatable/action.rb +0 -15
  182. data/lib/linked_rails/enhancements/updatable/routing.rb +0 -20
@@ -5,8 +5,181 @@ module LinkedRails
5
5
  module Collections
6
6
  extend ActiveSupport::Concern
7
7
 
8
+ COLLECTION_CUSTOMIZABLE_OPTIONS = {
9
+ # display [Sym] The default display type.
10
+ # Choose between :grid, :settingsTable, :table, :card, :default
11
+ display: :default,
12
+ # grid_max_columns [Integer] The default amount of columns to use in a grid.
13
+ grid_max_columns: 3,
14
+ # page_size [Integer] The default page size.
15
+ page_size: 20,
16
+ # table_type [Sym] The columns to use in the table.
17
+ table_type: lambda {
18
+ case display&.to_sym
19
+ when :table
20
+ :default
21
+ when :settingsTable
22
+ :settings
23
+ end
24
+ },
25
+ # title String The default title.
26
+ title: -> { title_from_translation },
27
+ # type [Sym] The default pagination type.
28
+ # Choose between :paginated, :infinite.
29
+ type: :paginated
30
+ }.freeze
31
+ COLLECTION_STATIC_OPTIONS = {
32
+ # association [Sym] The association of the collection items.
33
+ association: nil,
34
+ # association_base [Scope, Array] The items of the collection.
35
+ association_base: -> { apply_scope(sorted_association(filtered_association)) },
36
+ # association_class [Class] The class of the collection items.
37
+ association_class: nil,
38
+ # association_scope [Sym] The scope applied to the collection.
39
+ association_scope: nil,
40
+ # call_to_action [String] The label shown as call to action.
41
+ call_to_action: nil,
42
+ # collection_class [Class] The base class of the collection.
43
+ # If you want to use a class other than LinkedRails.collection_class.
44
+ collection_class: nil,
45
+ # collection_class [Hash] The default filters applied to the collection.
46
+ default_filters: {},
47
+ # collection_class [Array<Hash>] The default sortings applied to the collection.
48
+ default_sortings: [{key: Vocab.schema.dateCreated, direction: :desc}],
49
+ # include_members [Boolean] Whether to include the members of this collection.
50
+ include_members: false,
51
+ # iri_template_keys [Array<Sym>] Custom query keys for the iri template
52
+ iri_template_keys: [],
53
+ # joins [Array<Sym>, Sym] The associations to join
54
+ joins: nil,
55
+ # parent [Instance] The default parent of a collection.
56
+ parent: nil,
57
+ # parent_iri [Array<String>] The iri elements of the parent
58
+ parent_iri: -> { parent&.iri_elements },
59
+ # part_of [Instance] The record to serialize as isPartOf
60
+ part_of: -> { parent },
61
+ # policy_scope [Scope] The policy scope class to be used for scoping
62
+ # Set to false to skip scoping
63
+ policy_scope: -> { policy ? policy::Scope : Pundit::PolicyFinder.new(filtered_association).scope! },
64
+ # route_key [Symbol, String] The route key for the association
65
+ route_key: nil,
66
+ # view [IRI] The view to use for rendering the members
67
+ view: nil
68
+ }.freeze
69
+ COLLECTION_OPTIONS = COLLECTION_CUSTOMIZABLE_OPTIONS.merge(COLLECTION_STATIC_OPTIONS)
70
+
71
+ module ClassMethods
72
+ def collection_iri(**opts)
73
+ LinkedRails.iri(path: collection_root_relative_iri(**opts))
74
+ end
75
+
76
+ # Sets the defaults for all collections for this class.
77
+ # Can be overridden by #with_collection, called from associated models,
78
+ # or by passing parameters in an iri.
79
+ # @param [Hash] options
80
+ def collection_options(**options)
81
+ initialize_default_collection_opts
82
+
83
+ options.each do |key, value|
84
+ raise("Invalid key passed to collection_options: #{key}") unless valid_collection_option?(key)
85
+
86
+ _default_collection_opts[key] = value
87
+ end
88
+ _default_collection_opts[:iri_template] = _default_collection_opts[:collection_class].generate_iri_template(
89
+ _default_collection_opts[:iri_template_keys]
90
+ )
91
+ _default_collection_opts
92
+ end
93
+
94
+ def collection_root_relative_iri(**opts)
95
+ opts[:filter] = LinkedRails.collection_class.filter_iri_opts(opts[:filter]) if opts.key?(:filter)
96
+ opts[:route_key] = collection_route_key
97
+ default_collection_option(:iri_template).expand(**opts)
98
+ end
99
+
100
+ def collection_route_key
101
+ default_collection_option(:route_key) || route_key
102
+ end
103
+
104
+ def default_collection_options
105
+ initialize_default_collection_opts
106
+
107
+ _default_collection_opts
108
+ end
109
+
110
+ def default_collection_option(key)
111
+ default_collection_options[key]
112
+ end
113
+
114
+ # Defines a collection to be used in {collection_for}
115
+ # @see Ldable#collection_for
116
+ # @note Adds a instance_method <name>_collection
117
+ # @param [Hash] name as to be used in {collection_for}
118
+ # @param [Hash] options See COLLECTION_OPTIONS
119
+ # @return [Collection]
120
+ def with_collection(name, **options) # rubocop:disable Metrics/AbcSize
121
+ options[:association] ||= name.to_sym
122
+ options[:association_class] ||= name.to_s.classify.constantize
123
+ merged_options = options[:association_class].default_collection_options.merge(options)
124
+ merged_options[:iri_template] = merged_options[:collection_class].generate_iri_template(
125
+ merged_options[:iri_template_keys]
126
+ )
127
+ collections_add(name: name, options: merged_options)
128
+
129
+ define_method "#{name.to_s.singularize}_collection" do |opts = {}|
130
+ collection_for(name, **opts)
131
+ end
132
+ end
133
+
134
+ private
135
+
136
+ def collections_add(opts)
137
+ initialize_collections
138
+ collections.delete_if { |c| c[:name] == opts[:name] }
139
+ opts[:options] = sanitized_collection_options(opts[:options])
140
+ collections.append(opts)
141
+ end
142
+
143
+ def initialize_collections
144
+ return if collections && method(:collections).owner == singleton_class
145
+
146
+ self.collections = superclass.try(:collections)&.dup || []
147
+ end
148
+
149
+ def initialize_default_collection_opts # rubocop:disable Metrics/AbcSize
150
+ return if _default_collection_opts && method(:_default_collection_opts).owner == singleton_class
151
+
152
+ self._default_collection_opts = (superclass.try(:_default_collection_opts) || COLLECTION_OPTIONS).dup
153
+
154
+ _default_collection_opts[:collection_class] ||= LinkedRails.collection_class
155
+ _default_collection_opts[:association_class] = self
156
+ _default_collection_opts[:iri_template] = _default_collection_opts[:collection_class].generate_iri_template(
157
+ _default_collection_opts[:iri_template_keys]
158
+ )
159
+
160
+ _default_collection_opts
161
+ end
162
+
163
+ def sanitized_collection_options(opts)
164
+ opts.each_with_object(HashWithIndifferentAccess.new) do |(key, value), hash|
165
+ raise("Invalid key passed to with_collection: #{key}") unless valid_collection_option?(key.to_sym)
166
+
167
+ hash_key = COLLECTION_CUSTOMIZABLE_OPTIONS.key?(key.to_sym) ? "default_#{key}" : key
168
+
169
+ hash[hash_key] = value
170
+ end
171
+ end
172
+
173
+ def valid_collection_option?(key)
174
+ COLLECTION_OPTIONS.key?(key) || key == :iri_template
175
+ end
176
+ end
177
+
8
178
  included do
9
179
  class_attribute :collections
180
+ class_attribute :_default_collection_opts,
181
+ instance_accessor: false,
182
+ instance_predicate: false
10
183
  end
11
184
 
12
185
  # Initialises a {Collection} for one of the collections defined by {has_collection}
@@ -18,19 +191,42 @@ module LinkedRails
18
191
  # @param [ApplicationRecord] part_of
19
192
  # @param [Hash] opts Additional options to be passed to the collection.
20
193
  # @return [Collection]
21
- def collection_for(name, instance_opts = {})
22
- collection_opts = collections.detect { |c| c[:name] == name }.try(:[], :options).dup
194
+ def collection_for(name, **instance_opts)
195
+ collection_opts = collection_options_for(name).dup
23
196
  return if collection_opts.blank?
24
197
 
25
198
  collection_opts[:name] = name
26
199
  collection_opts[:parent] = self
27
- collection_opts[:part_of] = collection_opts.key?(:part_of) ? send(collection_opts[:part_of]) : self
28
- collection_class = collection_opts.delete(:collection_class) || LinkedRails.collection_class
200
+ collection_class =
201
+ collection_opts.delete(:collection_class) ||
202
+ collection_opts[:association_class].default_collection_option(:collection_class) ||
203
+ LinkedRails.collection_class
29
204
  collection_class.collection_or_view(collection_opts, instance_opts)
30
205
  end
31
206
 
207
+ def collection_iri(collection, **opts)
208
+ LinkedRails.iri(path: collection_root_relative_iri(collection, **opts))
209
+ end
210
+
211
+ def collection_options_for(name)
212
+ opts = collections.detect { |c| c[:name] == name.to_sym }
213
+ raise("Collection #{name} not found for #{self}") unless opts
214
+
215
+ opts[:options] || {}
216
+ end
217
+
218
+ def collection_root_relative_iri(collection, **opts)
219
+ collection_opts = collection_options_for(collection).dup
220
+ template = collection_opts[:iri_template]
221
+ klass = collection_opts[:association_class]
222
+ opts[:route_key] = collection_opts[:route_key] || klass.collection_route_key
223
+ opts[:parent_iri] = iri_elements
224
+
225
+ template.expand(**opts).to_s
226
+ end
227
+
32
228
  def parent_collections(user_context)
33
- return [] if try(:parent).try(:collections).blank?
229
+ return [self.class.root_collection(user_context: user_context)] if try(:parent).try(:collections).blank?
34
230
 
35
231
  parent_collections_for(parent, user_context)
36
232
  end
@@ -43,40 +239,6 @@ module LinkedRails
43
239
  .select { |collection| is_a?(collection[:options][:association_class]) }
44
240
  .map { |collection| parent.collection_for(collection[:name], user_context: user_context) }
45
241
  end
46
-
47
- module ClassMethods
48
- def collections_add(opts)
49
- initialize_collections
50
- collections.delete_if { |c| c[:name] == opts[:name] }
51
- collections.append(opts)
52
- end
53
-
54
- def initialize_collections
55
- return if collections && method(:collections).owner == singleton_class
56
-
57
- self.collections = superclass.try(:collections)&.dup || []
58
- end
59
-
60
- # Defines a collection to be used in {collection_for}
61
- # @see Ldable#collection_for
62
- # @note Adds a instance_method <name>_collection
63
- # @param [Hash] name as to be used in {collection_for}
64
- # @param [Hash] options
65
- # @option options [Sym] association the name of the association
66
- # @option options [Class] association_class the class of the association
67
- # @option options [Sym] joins the associations to join
68
- # @return [Collection]
69
- def with_collection(name, options = {})
70
- options[:association] ||= name.to_sym
71
- options[:association_class] ||= name.to_s.classify.constantize
72
-
73
- collections_add(name: name, options: options)
74
-
75
- define_method "#{name.to_s.singularize}_collection" do |opts = {}|
76
- collection_for(name, opts)
77
- end
78
- end
79
- end
80
242
  end
81
243
  end
82
244
  end
@@ -22,40 +22,28 @@ module LinkedRails
22
22
  end
23
23
  end
24
24
 
25
- def previous_changes_by_predicate
26
- serializer_class = RDF::Serializers.serializer_for(self)
27
- return {} unless respond_to?(:previous_changes) && serializer_class
28
-
29
- Hash[
30
- previous_changes
31
- .map { |k, v| [serializer_class.attributes_to_serialize[k.to_sym]&.predicate, v] }
32
- .select { |k, _v| k.present? }
33
- ]
34
- end
35
-
36
- def previously_changed_relations
25
+ def previously_changed_relations(inverted = nil)
37
26
  serializer_class = RDF::Serializers.serializer_for(self)
38
27
  return {} unless serializer_class.try(:relationships_to_serialize)
39
28
 
40
29
  serializer_class.relationships_to_serialize.select do |key, _value|
41
30
  if respond_to?(key)
42
31
  association_key = key.to_s.ends_with?('_collection') ? send(key).association : key
43
- association_has_destructed?(association_key) || association_changed?(association_key)
32
+ association_has_destructed?(association_key) || association_changed?(association_key, inverted)
44
33
  end
45
34
  end.with_indifferent_access
46
35
  end
47
36
 
48
37
  private
49
38
 
50
- def association_changed?(association) # rubocop:disable Metrics/AbcSize
39
+ def association_changed?(association, inverted) # rubocop:disable Metrics/AbcSize
51
40
  ids_method = "#{association.to_s.singularize}_ids"
52
41
  return true if previous_changes.include?("#{association}_id") || previous_changes.include?(ids_method)
53
42
  return false unless try(:association_cached?, association)
43
+ records = self.class.reflect_on_association(association).collection? ? send(association) : [send(association)]
54
44
 
55
- if self.class.reflect_on_association(association).collection?
56
- send(association).any? { |a| a.previous_changes.present? }
57
- else
58
- send(association)&.previous_changes&.present?
45
+ records.reject { |a| a == inverted }.any? do |a|
46
+ a&.previous_changes.present? || a&.previously_changed_relations(self).present?
59
47
  end
60
48
  end
61
49
 
@@ -16,14 +16,13 @@ module LinkedRails
16
16
 
17
17
  module ClassMethods
18
18
  # Adds an enhancement to a model and includes the Model module.
19
- def enhance(enhancement, opts = {})
19
+ def enhance(enhancement, **opts)
20
20
  initialize_enhancements
21
21
  already_included = enhanced_with?(enhancement)
22
22
 
23
23
  self.enhancements[enhancement] = opts
24
24
  return if already_included
25
25
 
26
- enhance_routing(enhancement) if enhancement.const_defined?(:Routing) && enhanced_with?(enhancement, :Routing)
27
26
  include enhancement::Model if enhancement.const_defined?(:Model) && enhanced_with?(enhancement, :Model)
28
27
  end
29
28
 
@@ -46,10 +45,6 @@ module LinkedRails
46
45
 
47
46
  private
48
47
 
49
- def enhance_routing(enhancement)
50
- LinkedRails::Enhancements::RouteConcerns.add_concern(enhancement)
51
- end
52
-
53
48
  def initialize_enhancements
54
49
  return if enhancements && method(:enhancements).owner == singleton_class
55
50
 
@@ -6,9 +6,7 @@ module LinkedRails
6
6
  extend ActiveSupport::Concern
7
7
 
8
8
  included do
9
- class_attribute :default_filters, instance_accessor: false, instance_predicate: false
10
9
  class_attribute :filter_options
11
- self.default_filters = {}
12
10
  end
13
11
 
14
12
  module ClassMethods
@@ -16,17 +14,17 @@ module LinkedRails
16
14
  class_variables.include?(:@@filter_options) ? super : {}
17
15
  end
18
16
 
19
- def filterable(options = {})
17
+ def filterable(**options)
20
18
  initialize_filter_options
21
19
 
22
- self.filter_options = HashWithIndifferentAccess.new(options).merge(filter_options)
20
+ self.filter_options = filter_options.merge(options)
23
21
 
24
22
  options.map { |k, filter| define_filter_method(k, filter) }
25
23
  end
26
24
 
27
25
  private
28
26
 
29
- def boolean_filter(true_filter, false_filter, options = {})
27
+ def boolean_filter(true_filter, false_filter, **options)
30
28
  {
31
29
  filter: resolve_boolean_filter(true_filter, false_filter),
32
30
  values: [true, false]
@@ -36,7 +34,7 @@ module LinkedRails
36
34
  def initialize_filter_options
37
35
  return if filter_options && method(:filter_options).owner == singleton_class
38
36
 
39
- self.filter_options = superclass.try(:filter_options)&.dup || {}
37
+ self.filter_options = superclass.try(:filter_options)&.dup || HashWithIndifferentAccess.new
40
38
  end
41
39
 
42
40
  def define_filter_method(key, filter)
@@ -15,33 +15,23 @@ module LinkedRails
15
15
  parent.send(collection_name, params) if collection_name
16
16
  end
17
17
 
18
- def root_collection(params = {})
18
+ def root_collection(**params)
19
19
  return unless root_collection?
20
20
 
21
- root_collection_class.collection_or_view(root_collection_opts, params)
22
- end
23
-
24
- def root_collection_class
25
- LinkedRails.collection_class
21
+ default_collection_option(:collection_class).collection_or_view(
22
+ default_collection_options,
23
+ params
24
+ )
26
25
  end
27
26
 
28
27
  private
29
28
 
30
29
  def collection_from_parent_name(parent, _params)
31
- collection_name = "#{name.underscore}_collection"
30
+ collection_name = "#{name.demodulize.underscore}_collection"
32
31
 
33
32
  collection_name if parent.respond_to?(collection_name, true)
34
33
  end
35
34
 
36
- def grid_max_columns; end
37
-
38
- def root_collection_opts
39
- {
40
- association_class: self,
41
- grid_max_columns: grid_max_columns
42
- }
43
- end
44
-
45
35
  def root_collection?
46
36
  true
47
37
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module LinkedRails
4
4
  module Model
5
- module Iri
5
+ module IRI
6
6
  extend ActiveSupport::Concern
7
7
 
8
8
  def anonymous_iri
@@ -14,13 +14,17 @@ module LinkedRails
14
14
  end
15
15
 
16
16
  # @return [RDF::URI].
17
- def iri(opts = {})
17
+ def iri(**opts)
18
18
  return anonymous_iri if anonymous_iri?
19
- return iri_with_root(root_relative_iri(opts)) if opts.present?
19
+ return iri_with_root(root_relative_iri(**opts)) if opts.present?
20
20
 
21
21
  @iri ||= iri_with_root(root_relative_iri)
22
22
  end
23
23
 
24
+ def iri_elements
25
+ root_relative_iri.to_s&.split('?')&.first&.split('/')&.map(&:presence)&.compact&.presence
26
+ end
27
+
24
28
  # @return [Hash]
25
29
  def iri_opts
26
30
  @iri_opts ||= {
@@ -33,14 +37,14 @@ module LinkedRails
33
37
  self.class.iri
34
38
  end
35
39
 
36
- def reload(_opts = {})
40
+ def reload(**_opts)
37
41
  @iri = nil
38
42
  super
39
43
  end
40
44
 
41
45
  # @return [RDF::URI]
42
- def root_relative_iri(opts = {})
43
- RDF::URI(expand_iri_template(iri_opts.merge(opts)))
46
+ def root_relative_iri(**opts)
47
+ RDF::URI(expand_iri_template(**iri_opts.merge(opts)))
44
48
  end
45
49
 
46
50
  # @return [String, Symbol]
@@ -49,7 +53,7 @@ module LinkedRails
49
53
  private
50
54
 
51
55
  # @return [String]
52
- def expand_iri_template(args = {})
56
+ def expand_iri_template(**args)
53
57
  iri_template.expand(args)
54
58
  end
55
59
 
@@ -58,6 +62,7 @@ module LinkedRails
58
62
  iri = root_relative_iri.dup
59
63
  iri.scheme = LinkedRails.scheme
60
64
  iri.host = LinkedRails.host
65
+ iri.path = iri.path.presence || '/'
61
66
  iri
62
67
  end
63
68
 
@@ -68,7 +73,7 @@ module LinkedRails
68
73
 
69
74
  # @return [URITemplate]
70
75
  def iri_template_expand_path(template_base, path)
71
- tokens = template_base.tokens
76
+ tokens = (template_base.is_a?(String) ? LinkedRails::URITemplate.new(template_base) : template_base).tokens
72
77
 
73
78
  ind = tokens.find_index do |t|
74
79
  t.is_a?(URITemplate::RFC6570::Expression::FormQuery) || t.is_a?(URITemplate::RFC6570::Expression::Fragment)
@@ -76,18 +81,12 @@ module LinkedRails
76
81
 
77
82
  prefix = ind ? tokens[0...ind] : tokens
78
83
  suffix = ind ? tokens[ind..-1] : []
79
- URITemplate.new([prefix, path, suffix].flatten.join)
84
+ LinkedRails::URITemplate.new([prefix, path, suffix].flatten.join)
80
85
  end
81
86
 
82
87
  # @return [URITemplate]
83
88
  def iri_template_with_fragment(template_base, fragment)
84
- URITemplate.new("#{template_base.to_s.sub(/{#[\w]+}/, '').split('#').first}##{fragment}")
85
- end
86
-
87
- def singular_iri; end
88
-
89
- def singular_iri_opts
90
- {}
89
+ LinkedRails::URITemplate.new("#{template_base.to_s.sub(/{#[\w]+}/, '').split('#').first}##{fragment}")
91
90
  end
92
91
 
93
92
  module ClassMethods
@@ -99,7 +98,7 @@ module LinkedRails
99
98
  return if self == ApplicationRecord
100
99
 
101
100
  superclass.try(:iri_namespace) ||
102
- (linked_rails_module? ? Vocab.ontola : LinkedRails.app_vocab)
101
+ (linked_rails_module? ? Vocab.ontola : Vocab.app)
103
102
  end
104
103
 
105
104
  def iri_value
@@ -107,14 +106,25 @@ module LinkedRails
107
106
  end
108
107
 
109
108
  def iri_template
110
- @iri_template ||= URITemplate.new("/#{route_key}{/id}{#fragment}")
109
+ @iri_template ||= LinkedRails::URITemplate.new("/#{route_key}{/id}{#fragment}")
110
+ end
111
+
112
+ def iris_from_scope(scope)
113
+ ids = ids_for_iris(scope).uniq
114
+ ids.map { |id| LinkedRails.iri(path: iri_template.expand(id: id)) }
115
+ end
116
+
117
+ def ids_for_iris(scope)
118
+ scope.pluck(:id)
111
119
  end
112
120
 
113
121
  def linked_rails_module?
114
122
  (Rails.version < '6' ? parents : module_parents).include?(LinkedRails)
115
123
  end
116
124
 
117
- delegate :route_key, to: :model_name
125
+ def route_key
126
+ try(:model_name)&.route_key || to_s.tableize
127
+ end
118
128
  end
119
129
  end
120
130
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module LinkedRails
4
4
  module Model
5
- module IriMapping
5
+ module IRIMapping
6
6
  extend ActiveSupport::Concern
7
7
 
8
8
  module ClassMethods
@@ -10,11 +10,24 @@ module LinkedRails
10
10
  LinkedRails.iri_mapper.parent_from_params(params, user_context)
11
11
  end
12
12
 
13
+ def parent_from_params!(params, user_context)
14
+ parent_from_params(params, user_context) || raise(ActiveRecord::RecordNotFound)
15
+ end
16
+
17
+ def requested_action(opts, user_context)
18
+ action_key = opts[:params][:action_key]
19
+ resource = LinkedRails.iri_mapper.resource_from_iri(
20
+ iri_without_action(opts[:iri]),
21
+ user_context
22
+ )
23
+ resource&.action(action_key, user_context)
24
+ end
25
+
13
26
  def requested_index_resource(params, user_context)
14
27
  if params.key?(:parent_iri)
15
28
  collection_from_parent(index_collection_params(params, user_context))
16
29
  else
17
- root_collection(index_collection_params(params, user_context))
30
+ root_collection(**index_collection_params(params, user_context))
18
31
  end
19
32
  end
20
33
 
@@ -25,10 +38,10 @@ module LinkedRails
25
38
  def requested_resource(opts, user_context)
26
39
  if collection_action?(opts)
27
40
  requested_index_resource(opts[:params], user_context)
41
+ elsif action_item_action?(opts)
42
+ requested_action(opts, user_context)
28
43
  elsif singular_action?(opts)
29
- resource = requested_singular_resource(opts[:params], user_context)
30
- resource&.singular_resource = true
31
- resource
44
+ singular_resource(requested_singular_resource(opts[:params], user_context))
32
45
  else
33
46
  requested_single_resource(opts[:params], user_context)
34
47
  end
@@ -38,7 +51,7 @@ module LinkedRails
38
51
  if self < ActiveRecord::Base
39
52
  find_by(primary_key => params[:id]) if params.key?(:id)
40
53
  else
41
- new(params)
54
+ new(params.except(:format))
42
55
  end
43
56
  end
44
57
 
@@ -46,10 +59,20 @@ module LinkedRails
46
59
  requested_single_resource(params, user_context) || raise(ActiveRecord::RecordNotFound)
47
60
  end
48
61
 
62
+ def singular_resource(resource)
63
+ resource&.singular_resource = true
64
+ resource
65
+ end
66
+
49
67
  private
50
68
 
69
+ def action_item_action?(opts)
70
+ opts[:params][:action_key].present?
71
+ end
72
+
51
73
  def collection_action?(opts)
52
- %w[index create].include?(opts[:action]) && !opts[:params][:singular_route]
74
+ opts[:action] == 'index' ||
75
+ opts[:class].action_list.defined_actions[:collection].key?(opts[:action].to_sym)
53
76
  end
54
77
 
55
78
  def index_collection_params(params, user_context)
@@ -57,7 +80,13 @@ module LinkedRails
57
80
 
58
81
  {
59
82
  user_context: user_context
60
- }.merge(params_hash).with_indifferent_access
83
+ }.merge(params_hash).symbolize_keys
84
+ end
85
+
86
+ def iri_without_action(url)
87
+ iri = RDF::URI(url)
88
+ iri.path = iri.path.split('/')[0...-1].join('/') if iri.path
89
+ iri.to_s
61
90
  end
62
91
 
63
92
  def singular_action?(opts)
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ module Model
5
+ module Menuable
6
+ extend ActiveSupport::Concern
7
+
8
+ def menus(user_context = nil)
9
+ menu_list(user_context).menus
10
+ end
11
+
12
+ def menu(tag, user_context = nil)
13
+ menu_list(user_context).menu(tag)
14
+ end
15
+
16
+ def menu_list(user_context = nil)
17
+ @menu_list ||= {}
18
+ @menu_list[user_context] ||= self.class.menu_class.new(resource: self, user_context: user_context)
19
+ end
20
+
21
+ module ClassMethods
22
+ def menu_class
23
+ @menu_class ||= "#{name}MenuList".safe_constantize || "#{superclass.name}MenuList".safe_constantize
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end