linked_rails 0.0.3 → 0.0.4.pre.g0ec6e30ca

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 (180) 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 +107 -22
  5. data/app/controllers/linked_rails/enum_values_controller.rb +0 -42
  6. data/app/models/linked_rails/actions/item.rb +72 -55
  7. data/app/models/linked_rails/actions/list.rb +6 -31
  8. data/app/models/linked_rails/actions/object.rb +40 -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 +53 -15
  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 +5 -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 +49 -25
  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 +11 -14
  38. data/app/models/linked_rails/menus/list.rb +30 -10
  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 +3 -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/collection/filter_field_serializer.rb +3 -2
  54. data/app/serializers/linked_rails/collection/filter_option_serializer.rb +1 -1
  55. data/app/serializers/linked_rails/collection/filter_serializer.rb +1 -1
  56. data/app/serializers/linked_rails/collection/sorting_serializer.rb +1 -1
  57. data/app/serializers/linked_rails/collection/view_serializer.rb +4 -3
  58. data/app/serializers/linked_rails/collection_serializer.rb +8 -7
  59. data/app/serializers/linked_rails/condition_serializer.rb +3 -3
  60. data/app/serializers/linked_rails/entry_point_serializer.rb +3 -3
  61. data/app/serializers/linked_rails/enum_value_serializer.rb +1 -0
  62. data/app/serializers/linked_rails/form/field/association_input_serializer.rb +1 -0
  63. data/app/serializers/linked_rails/form/field/file_input_serializer.rb +11 -0
  64. data/app/serializers/linked_rails/form/field_serializer.rb +3 -1
  65. data/app/serializers/linked_rails/form/group_serializer.rb +1 -1
  66. data/app/serializers/linked_rails/form/page_serializer.rb +1 -1
  67. data/app/serializers/linked_rails/menus/item_serializer.rb +3 -3
  68. data/app/serializers/linked_rails/menus/list_serializer.rb +1 -1
  69. data/app/serializers/linked_rails/ontology_serializer.rb +2 -2
  70. data/app/serializers/linked_rails/property_query_serializer.rb +7 -0
  71. data/app/serializers/linked_rails/sequence_serializer.rb +2 -5
  72. data/app/serializers/linked_rails/shacl/node_shape_serializer.rb +1 -1
  73. data/app/serializers/linked_rails/shacl/property_shape_serializer.rb +1 -1
  74. data/app/serializers/linked_rails/shacl/shape_serializer.rb +5 -5
  75. data/app/serializers/linked_rails/web_page_serializer.rb +3 -3
  76. data/app/serializers/linked_rails/web_site_serializer.rb +1 -1
  77. data/app/serializers/linked_rails/widget_serializer.rb +3 -3
  78. data/app/workers/linked_rails/invalidation_stream_worker.rb +16 -0
  79. data/lib/generators/linked_rails/install/install_generator.rb +5 -8
  80. data/lib/generators/linked_rails/install/templates/README +2 -0
  81. data/lib/generators/linked_rails/install/templates/app_menu_list.rb +36 -7
  82. data/lib/generators/linked_rails/install/templates/application_menu_list.rb +40 -1
  83. data/lib/generators/linked_rails/install/templates/initializer.rb +1 -2
  84. data/lib/generators/linked_rails/install/templates/locales.yml +16 -0
  85. data/lib/generators/linked_rails/install/templates/rdf_serializers_initializer.rb +1 -1
  86. data/lib/generators/linked_rails/install/templates/vocab.rb +1 -0
  87. data/lib/generators/linked_rails/install/templates/vocab.yml +2 -2
  88. data/lib/generators/linked_rails/model/model_generator.rb +0 -1
  89. data/lib/generators/linked_rails/model/templates/controller.rb.tt +5 -1
  90. data/lib/generators/linked_rails/model/templates/form.rb.tt +3 -0
  91. data/lib/generators/linked_rails/model/templates/menu_list.rb.tt +15 -0
  92. data/lib/generators/linked_rails/model/templates/policy.rb.tt +13 -0
  93. data/lib/generators/linked_rails/model/templates/serializer.rb.tt +5 -1
  94. data/lib/linked_rails/active_response/controller/collections.rb +1 -1
  95. data/lib/linked_rails/active_response/controller/crud_defaults.rb +4 -4
  96. data/lib/linked_rails/active_response/controller/params.rb +10 -10
  97. data/lib/linked_rails/active_response/controller.rb +8 -18
  98. data/lib/linked_rails/active_response/responders/rdf.rb +19 -10
  99. data/lib/linked_rails/callable_variable.rb +1 -1
  100. data/lib/linked_rails/collection_params_parser.rb +93 -0
  101. data/lib/linked_rails/controller/actionable.rb +121 -0
  102. data/lib/linked_rails/controller/authorization.rb +6 -0
  103. data/lib/linked_rails/controller/default_actions/create.rb +52 -0
  104. data/lib/linked_rails/controller/default_actions/destroy.rb +42 -0
  105. data/lib/linked_rails/controller/default_actions/update.rb +43 -0
  106. data/lib/linked_rails/controller/delta.rb +58 -0
  107. data/lib/linked_rails/controller/error_handling.rb +12 -7
  108. data/lib/linked_rails/controller/rendering.rb +48 -0
  109. data/lib/linked_rails/controller.rb +24 -4
  110. data/lib/linked_rails/enhancements/creatable/controller.rb +1 -1
  111. data/lib/linked_rails/enhancements/destroyable/controller.rb +1 -1
  112. data/lib/linked_rails/enhancements/updatable/controller.rb +1 -1
  113. data/lib/linked_rails/enhancements.rb +0 -16
  114. data/lib/linked_rails/errors/forbidden.rb +37 -0
  115. data/lib/linked_rails/errors.rb +3 -0
  116. data/lib/linked_rails/helpers/delta_helper.rb +28 -57
  117. data/lib/linked_rails/helpers/ontola_actions_helper.rb +2 -2
  118. data/lib/linked_rails/helpers/resource_helper.rb +14 -2
  119. data/lib/linked_rails/iri_mapper.rb +18 -40
  120. data/lib/linked_rails/middleware/error_handling.rb +51 -0
  121. data/lib/linked_rails/middleware/linked_data_params.rb +30 -151
  122. data/lib/linked_rails/model/actionable.rb +68 -0
  123. data/lib/linked_rails/model/cacheable.rb +45 -0
  124. data/lib/linked_rails/model/collections.rb +203 -39
  125. data/lib/linked_rails/model/dirty.rb +6 -18
  126. data/lib/linked_rails/model/enhancements.rb +1 -6
  127. data/lib/linked_rails/model/filtering.rb +4 -6
  128. data/lib/linked_rails/model/indexable.rb +6 -16
  129. data/lib/linked_rails/model/iri.rb +29 -19
  130. data/lib/linked_rails/model/iri_mapping.rb +37 -8
  131. data/lib/linked_rails/model/menuable.rb +28 -0
  132. data/lib/linked_rails/model/serialization.rb +2 -15
  133. data/lib/linked_rails/model/singularable.rb +57 -0
  134. data/lib/linked_rails/model/sorting.rb +0 -5
  135. data/lib/linked_rails/model/tables.rb +26 -0
  136. data/lib/linked_rails/model.rb +18 -7
  137. data/lib/linked_rails/params_parser.rb +140 -53
  138. data/lib/linked_rails/policy/attribute_conditions.rb +2 -2
  139. data/lib/linked_rails/policy.rb +44 -46
  140. data/lib/linked_rails/railtie.rb +11 -0
  141. data/lib/linked_rails/rdf_error.rb +2 -2
  142. data/lib/linked_rails/renderers.rb +1 -0
  143. data/lib/linked_rails/routes.rb +38 -22
  144. data/lib/linked_rails/serializer/actionable.rb +27 -0
  145. data/lib/linked_rails/serializer/menuable.rb +31 -0
  146. data/lib/linked_rails/serializer/singularable.rb +26 -0
  147. data/lib/linked_rails/serializer.rb +35 -12
  148. data/lib/linked_rails/storage.rb +32 -0
  149. data/lib/linked_rails/test_methods.rb +114 -0
  150. data/lib/linked_rails/translate.rb +31 -9
  151. data/lib/linked_rails/types/iri_type.rb +37 -0
  152. data/lib/linked_rails/uri_template.rb +30 -0
  153. data/lib/linked_rails/url.rb +11 -0
  154. data/lib/linked_rails/version.rb +1 -1
  155. data/lib/linked_rails/vocab.rb +9 -0
  156. data/lib/linked_rails.rb +40 -14
  157. data/lib/rails/welcome_controller.rb +3 -3
  158. data/lib/rdf/list.rb +9 -0
  159. data/lib/rdf/query_fix.rb +15 -0
  160. metadata +78 -33
  161. data/app/models/linked_rails/actions/default_actions/create.rb +0 -60
  162. data/app/models/linked_rails/actions/default_actions/destroy.rb +0 -45
  163. data/app/models/linked_rails/actions/default_actions/update.rb +0 -50
  164. data/app/models/linked_rails/actions/default_actions.rb +0 -17
  165. data/lib/generators/linked_rails/install/templates/application_action_list.rb +0 -3
  166. data/lib/generators/linked_rails/model/templates/action_list.rb.tt +0 -6
  167. data/lib/linked_rails/enhancements/actionable/model.rb +0 -71
  168. data/lib/linked_rails/enhancements/actionable/serializer.rb +0 -25
  169. data/lib/linked_rails/enhancements/creatable/action.rb +0 -15
  170. data/lib/linked_rails/enhancements/destroyable/action.rb +0 -15
  171. data/lib/linked_rails/enhancements/destroyable/routing.rb +0 -19
  172. data/lib/linked_rails/enhancements/menuable/model.rb +0 -36
  173. data/lib/linked_rails/enhancements/menuable/serializer.rb +0 -33
  174. data/lib/linked_rails/enhancements/route_concerns.rb +0 -56
  175. data/lib/linked_rails/enhancements/singularable/controller.rb +0 -43
  176. data/lib/linked_rails/enhancements/singularable/model.rb +0 -47
  177. data/lib/linked_rails/enhancements/singularable/serializer.rb +0 -28
  178. data/lib/linked_rails/enhancements/tableable/model.rb +0 -28
  179. data/lib/linked_rails/enhancements/updatable/action.rb +0 -15
  180. data/lib/linked_rails/enhancements/updatable/routing.rb +0 -20
@@ -4,7 +4,7 @@ require 'linked_rails/constraints/whitelist'
4
4
 
5
5
  module LinkedRails
6
6
  module Routes
7
- def use_linked_rails(opts = {}) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
7
+ def use_linked_rails(**opts) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
8
8
  constraints(LinkedRails::Constraints::Whitelist) do
9
9
  post 'spi/bulk', to: "#{opts.fetch(:bulk, 'linked_rails/bulk')}#show"
10
10
  end
@@ -22,24 +22,29 @@ module LinkedRails
22
22
 
23
23
  get '(*parent_iri)/actions', to: "#{opts.fetch(:actions, 'linked_rails/actions')}/items#index"
24
24
  get '(*parent_iri)/actions/:id', to: "#{opts.fetch(:actions, 'linked_rails/actions')}/items#show"
25
- get '(*parent_iri)/new', to: "#{opts.fetch(:actions, 'linked_rails/actions')}/items#show", id: :create
26
- get '(*parent_iri)/edit', to: "#{opts.fetch(:actions, 'linked_rails/actions')}/items#show", id: :update
27
- get '(*parent_iri)/delete', to: "#{opts.fetch(:actions, 'linked_rails/actions')}/items#show", id: :destroy
28
- get '(*parent_iri)/trash', to: "#{opts.fetch(:actions, 'linked_rails/actions')}/items#show", id: :trash
29
- get '(*parent_iri)/untrash', to: "#{opts.fetch(:actions, 'linked_rails/actions')}/items#show", id: :untrash
25
+ get '(*parent_iri)/action_object', to: "#{opts.fetch(:actions, 'linked_rails/actions')}/objects#show"
30
26
  end
31
27
 
32
- def linked_resource(klass, controller: nil, nested: false, &block)
28
+ def linked_resource(klass, controller: nil, collection: true, nested: false, resource: true, &block) # rubocop:disable Metrics/MethodLength, Metrics/ParameterLists
33
29
  options = route_options(klass, controller, nested, klass.route_key)
34
30
 
31
+ if collection
32
+ get(options[:parentable_path], to: "#{options[:controller]}#index")
33
+ route_block(
34
+ klass,
35
+ :collection,
36
+ controller: options[:controller],
37
+ prefix: options[:parentable_path]
38
+ ).call
39
+ end
40
+
41
+ return unless resource
42
+
35
43
  resources(
36
44
  options[:route_name],
37
45
  active_controller_opts(options),
38
- &route_block(klass, &block)
46
+ &route_block(klass, :resource, &block)
39
47
  )
40
-
41
- post(options[:parentable_path], to: "#{options[:controller]}#create") if options[:creatable]
42
- get(options[:parentable_path], to: "#{options[:controller]}#index")
43
48
  end
44
49
 
45
50
  def singular_linked_resource(klass, controller: nil, nested: true, &block)
@@ -48,10 +53,8 @@ module LinkedRails
48
53
  resource(
49
54
  options[:route_name],
50
55
  active_controller_opts(options).merge(singular_route: true),
51
- &route_block(klass, &block)
56
+ &route_block(klass, :singular, &block)
52
57
  )
53
-
54
- post(options[:parentable_path], to: "#{options[:controller]}#create", singular_route: true) if options[:creatable]
55
58
  end
56
59
 
57
60
  def find_tenant_route
@@ -60,6 +63,16 @@ module LinkedRails
60
63
 
61
64
  private
62
65
 
66
+ def action_routes(controller, prefix, key, value)
67
+ path = value.fetch(:action_path, key)
68
+ action = value.fetch(:action_name)
69
+ get([prefix, path].compact.join('/'), action: action, action_key: key, controller: controller)
70
+ return if value[:target_path].nil?
71
+
72
+ method = value.fetch(:http_method).downcase
73
+ send(method, [prefix, value.fetch(:target_path)].compact.join('/'), action: key, controller: controller)
74
+ end
75
+
63
76
  def active_controller_opts(route_options)
64
77
  {
65
78
  controller: route_options[:controller],
@@ -68,22 +81,20 @@ module LinkedRails
68
81
  }
69
82
  end
70
83
 
71
- def creatable(klass)
72
- klass.enhanced_with?(LinkedRails::Enhancements::Creatable, :Routing)
73
- end
74
-
75
- def route_block(klass)
84
+ def route_block(klass, action_type, controller: nil, prefix: nil)
76
85
  lambda do
77
- include_route_concerns(klass: klass)
78
-
86
+ klass.action_list.defined_actions[action_type].each do |key, value|
87
+ action_routes(controller, prefix, key, value)
88
+ end
79
89
  yield if block_given?
80
90
  end
81
91
  end
82
92
 
83
93
  def route_options(klass, controller, nested, path)
94
+ touch_controller(klass)
95
+
84
96
  {
85
97
  controller: controller || klass.name.tableize,
86
- creatable: creatable(klass),
87
98
  nested: nested,
88
99
  only: %i[show],
89
100
  parentable_path: "(*parent_iri)/#{path}",
@@ -91,6 +102,11 @@ module LinkedRails
91
102
  route_name: klass.name.demodulize.tableize
92
103
  }.with_indifferent_access
93
104
  end
105
+
106
+ # Make sure all actions are initialized before generating the routes
107
+ def touch_controller(klass)
108
+ klass.controller_class
109
+ end
94
110
  end
95
111
  end
96
112
 
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ module Serializer
5
+ module Actionable
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ has_many :favorite_actions,
10
+ predicate: Vocab.ontola[:favoriteAction] do |object|
11
+ object.try(:favorite_actions)
12
+ end
13
+ statements :action_triples
14
+ end
15
+
16
+ module ClassMethods
17
+ def action_triples(object, _params)
18
+ if named_object?(object) || object.try(:singular_resource?)
19
+ object.try(:action_triples) || []
20
+ else
21
+ []
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ module Serializer
5
+ module Menuable
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ include_menus
10
+ end
11
+
12
+ module ClassMethods
13
+ def inherited(target)
14
+ super
15
+ target.include_menus
16
+ end
17
+
18
+ def include_menus
19
+ serializable_class.try(:menu_class)&.defined_menus&.keys&.each do |menu|
20
+ has_one "#{menu}_menu",
21
+ predicate: Vocab.ontola["#{menu.to_s.camelize(:lower)}Menu"],
22
+ if: method(:named_object?),
23
+ polymorphic: true do |object, opts|
24
+ object.menu(menu, opts[:scope])
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ module Serializer
5
+ module Singularable
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ statements :same_as_statement
10
+
11
+ def self.same_as_statement(object, _params)
12
+ return [] unless object.try(:singular_resource?) && object.singular_iri != object.iri
13
+
14
+ [
15
+ RDF::Statement.new(
16
+ object.singular_iri,
17
+ Vocab.owl.sameAs,
18
+ object.iri,
19
+ graph_name: Vocab.ld[:supplant]
20
+ )
21
+ ]
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -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
+ include Empathy::EmpJson::Serializer
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,28 +94,42 @@ 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
94
105
 
95
106
  def serialize_image(obj)
96
- if obj.is_a?(String) || obj.is_a?(Symbol)
107
+ if (obj.is_a?(String) || obj.is_a?(Symbol)) && !obj.to_s.include?('http')
97
108
  RDF::URI(obj.to_s.gsub(/^fa-/, 'http://fontawesome.io/icon/'))
98
109
  else
99
110
  obj.presence
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
- opts[:if] ||= method(:named_object?)
110
118
 
111
- has_one collection_name, opts do |object, params|
112
- object.send(collection_name, user_context: params[:scope], display: display, page_size: page_size)
119
+ action_object = opts.delete(:action_object)
120
+ opts[:if] ||= -> (object) {
121
+ return action_object if object.iri.try(:path)&.end_with?('/action_object')
122
+
123
+ named_object?(object)
124
+ }
125
+
126
+ collection_opts = {}
127
+ collection_opts[:page_size] = opts.delete(:page_size) if opts.key?(:page_size)
128
+ collection_opts[:display] = opts.delete(:display) if opts.key?(:display)
129
+ collection_opts[:table_type] = opts.delete(:table_type) if opts.key?(:table_type)
130
+
131
+ has_one collection_name, **opts do |object, params|
132
+ object.send(collection_name, **collection_opts.merge(user_context: params[:scope]))
113
133
  end
114
134
  end
115
135
 
@@ -135,3 +155,6 @@ module LinkedRails
135
155
  end
136
156
  end
137
157
  end
158
+
159
+ RDF::Serializers::NilSerializer.include(Empathy::EmpJson::Serializer)
160
+ RDF::Serializers::ListSerializer.include(Empathy::EmpJson::Serializer)
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'redis'
4
+
5
+ module LinkedRails
6
+ class Storage
7
+ REDIS_DB = {
8
+ cache: LinkedRails.cache_redis_database,
9
+ persistent: LinkedRails.persistent_redis_database,
10
+ stream: LinkedRails.stream_redis_database
11
+ }.freeze
12
+ KEYS = {
13
+ manifest: 'cache:Manifest',
14
+ redirect_exact: 'cache:Redirect:Exact',
15
+ redirect_prefix: 'cache:Redirect:Prefix'
16
+ }.freeze
17
+
18
+ class << self
19
+ %i[xadd].each do |method|
20
+ define_method(method) do |db, *args|
21
+ Redis.new(db: REDIS_DB.fetch(db)).send(method, *args)
22
+ end
23
+ end
24
+
25
+ %i[hset hdel hget].each do |method|
26
+ define_method(method) do |db, key, *args|
27
+ Redis.new(db: REDIS_DB.fetch(db)).send(method, KEYS.fetch(key), *args)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ module TestMethods
5
+ def expect_disabled_form(iri: requested_iri, error: 'This action is currently not available')
6
+ assert_response 200
7
+ expect_triple(iri, NS.schema.actionStatus, NS.ontola[:DisabledActionStatus])
8
+ expect_triple(iri, NS.schema.error, error) if error
9
+ end
10
+
11
+ def expect_enabled_form(iri: requested_iri)
12
+ assert_response 200
13
+ expect_triple(iri, NS.schema.actionStatus, NS.schema.PotentialActionStatus)
14
+ end
15
+
16
+ def expect_errors(iri, errors)
17
+ error_response = expect_triple(iri, NS.ll[:errorResponse], nil).first.object
18
+ assert_equal expect_triple(error_response, nil, nil).count, errors.count + 2
19
+ errors.each do |key, value|
20
+ expect_triple(error_response, key, value)
21
+ end
22
+ end
23
+
24
+ def expect_header(key, value)
25
+ expect(response.headers[key]).to(include(value))
26
+ end
27
+
28
+ def expect_ontola_action(redirect: nil, snackbar: nil, reload: nil)
29
+ if redirect
30
+ expect_header('Exec-Action', "actions/redirect?#{{location: redirect, reload: reload}.compact.to_param}")
31
+ end
32
+ expect_header('Exec-Action', "actions/snackbar?#{{text: snackbar}.to_param}") if snackbar
33
+
34
+ expect_ontola_action_count([redirect, snackbar].compact.size)
35
+ end
36
+
37
+ def expect_ontola_action_count(count)
38
+ if count.zero?
39
+ assert_nil response.headers['Exec-Action']
40
+ else
41
+ assert_equal count, response.headers['Exec-Action'].count("\n"), response.headers['Exec-Action']
42
+ end
43
+ end
44
+
45
+ # Tests if the property {path} starting at {start} resolves to {value}.
46
+ # If multiple solutions are present only one has to match to pass.
47
+ #
48
+ # @param [IRI] start The node to start at
49
+ # @param [Array] path The property path to traverse
50
+ # @param [IRI|IRI[]] value The value to match at the end, pass an array to
51
+ # match multiple values.
52
+ def expect_path(start, path, value) # rubocop:disable Metrics/AbcSize
53
+ match = path.each_with_index.reduce(start) do |node, (path_seg, i)|
54
+ obj = [*node].map { |cur_node| rdf_body.query([cur_node, path_seg, nil, nil]).map(&:object) }.flatten
55
+ break (obj & [*value]).present? if path.length - 1 == i
56
+
57
+ nodes = obj.filter { |o| o.is_a?(RDF::Resource) }
58
+ break false if nodes.blank?
59
+
60
+ nodes
61
+ end
62
+
63
+ segs = path.map(&:to_s).join(', ')
64
+ assert match,
65
+ "Expected to find '#{value}' from '#{start}' through '[#{segs}]' in\n#{response.body}"
66
+ end
67
+
68
+ def expect_resource_type(type, iri: requested_iri)
69
+ expect_triple(iri, RDF[:type], type)
70
+ end
71
+
72
+ def expect_sequence(subject, predicate)
73
+ expect_triple(subject, predicate, nil).first.object
74
+ end
75
+
76
+ def expect_sequence_member(subject, index, object)
77
+ expect_triple(subject, RDF[:"_#{index}"], object)
78
+ object
79
+ end
80
+
81
+ def expect_sequence_size(subject, expected_count)
82
+ count =
83
+ expect_triple(subject, nil, nil)
84
+ .select { |s| s.predicate.to_s.starts_with?('http://www.w3.org/1999/02/22-rdf-syntax-ns#_') }
85
+ .count
86
+ assert_equal expected_count, count
87
+ end
88
+
89
+ def expect_triple(subject, predicate, object, graph = NS.ll[:supplant])
90
+ statement = RDF::Statement(subject, predicate, object, graph_name: graph)
91
+ match = rdf_body.query(statement)
92
+ assert match.present?, "Expected to find #{statement} in\n#{response.body}"
93
+ match
94
+ end
95
+
96
+ def refute_triple(subject, predicate, object, graph = nil)
97
+ statement = RDF::Statement(subject, predicate, object, graph_name: graph)
98
+ assert_not rdf_body.query([subject, predicate, object, graph]).present?,
99
+ "Expected not to find #{statement} in\n#{response.body}"
100
+ end
101
+
102
+ def requested_iri
103
+ RDF::URI(request.original_url.sub(".#{request.format.symbol}", ''))
104
+ end
105
+
106
+ def rdf_body
107
+ @rdf_body ||= RDF::Graph.new << RDF::Reader
108
+ .for(content_type: response.headers['Content-Type'])
109
+ .new(response.body)
110
+ end
111
+ end
112
+ end
113
+
114
+ ActiveSupport::TestCase.include(LinkedRails::TestMethods)
@@ -62,28 +62,38 @@ module LinkedRails # rubocop:disable Metrics/ModuleLength
62
62
  end
63
63
 
64
64
  Translate.translations_for(:action, :description) do |object, fallback|
65
+ result_class = object.list.send(:result_class)
66
+ klass_iri = result_class.iri.is_a?(Array) ? result_class.iri.first : result_class.iri
67
+ type = LinkedRails.translate(:class, :label, klass_iri)&.downcase
68
+
65
69
  I18n.t(
66
- "actions.#{Translate.translation_key(object)}.#{object.tag}.description",
70
+ "actions.#{Translate.translation_key(object.resource)}.#{object.tag}.description",
67
71
  default: [
68
72
  :"actions.default.#{object.tag}.description",
69
73
  fallback ? object.tag.to_s.humanize : ''
70
- ]
74
+ ],
75
+ type: type
71
76
  )
72
77
  end
73
78
 
74
79
  Translate.translations_for(:action, :label) do |object, fallback|
80
+ result_class = object.list.send(:result_class)
81
+ klass_iri = result_class.iri.is_a?(Array) ? result_class.iri.first : result_class.iri
82
+ type = LinkedRails.translate(:class, :label, klass_iri)&.downcase
83
+
75
84
  I18n.t(
76
- "actions.#{Translate.translation_key(object)}.#{object.tag}.label",
85
+ "actions.#{Translate.translation_key(object.resource)}.#{object.tag}.label",
77
86
  default: [
78
87
  :"actions.default.#{object.tag}.label",
79
88
  fallback ? object.tag.to_s.humanize : ''
80
- ]
89
+ ],
90
+ type: type
81
91
  )
82
92
  end
83
93
 
84
94
  Translate.translations_for(:action, :submit) do |object, fallback|
85
95
  I18n.t(
86
- "actions.#{Translate.translation_key(object)}.#{object.tag}.submit",
96
+ "actions.#{Translate.translation_key(object.resource)}.#{object.tag}.submit",
87
97
  default: [
88
98
  :"actions.default.#{object.tag}.submit",
89
99
  fallback ? object.tag.to_s.humanize : ''
@@ -93,7 +103,7 @@ module LinkedRails # rubocop:disable Metrics/ModuleLength
93
103
 
94
104
  Translate.translations_for(:enum, :label) do |object|
95
105
  I18n.t(
96
- "enums.#{Translate.translation_key(object)}.#{object.attr}.#{object.key}",
106
+ "enums.#{Translate.translation_key(object.klass)}.#{object.attr}.#{object.key}",
97
107
  default: [
98
108
  :"enums.#{object.attr}.#{object.key}",
99
109
  object.key.to_s.humanize
@@ -125,6 +135,18 @@ module LinkedRails # rubocop:disable Metrics/ModuleLength
125
135
  end
126
136
  end
127
137
 
138
+ Translate.translations_for(:field, :placeholder) do |object, fallback|
139
+ if object.model_attribute.present?
140
+ I18n.t(
141
+ "forms.#{Translate.translation_key(object.model_class)}.#{object.model_attribute}.placeholder",
142
+ default: [
143
+ :"forms.default.#{object.model_attribute}.placeholder",
144
+ fallback ? object.model_attribute.to_s.humanize : ''
145
+ ]
146
+ )
147
+ end
148
+ end
149
+
128
150
  Translate.translations_for(:field, :label) do |object, fallback|
129
151
  if object.model_attribute.present?
130
152
  I18n.t(
@@ -148,14 +170,14 @@ module LinkedRails # rubocop:disable Metrics/ModuleLength
148
170
  Translate.translations_for(:class, :label) do |object|
149
171
  I18n.t(
150
172
  Translate.key_for_iri(object, :label),
151
- default: (object.label || Translate.tag_for_iri(object)).to_s.underscore.humanize
173
+ default: (object.try(:label) || Translate.tag_for_iri(object)).to_s.underscore.humanize
152
174
  )
153
175
  end
154
176
 
155
177
  Translate.translations_for(:class, :plural_label) do |object|
156
178
  I18n.t(
157
179
  Translate.key_for_iri(object, :plural_label),
158
- default: (object.label || Translate.tag_for_iri(object)).to_s.tableize.humanize
180
+ default: (object.try(:label) || Translate.tag_for_iri(object)).to_s.tableize.humanize
159
181
  )
160
182
  end
161
183
 
@@ -170,7 +192,7 @@ module LinkedRails # rubocop:disable Metrics/ModuleLength
170
192
  Translate.translations_for(:property, :label) do |object|
171
193
  I18n.t(
172
194
  Translate.key_for_iri(object, :label),
173
- default: (object.label || Translate.tag_for_iri(object)).to_s.underscore.humanize
195
+ default: (object.try(:label) || Translate.tag_for_iri(object)).to_s.underscore.humanize
174
196
  )
175
197
  end
176
198
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ module Types
5
+ class IRI < ActiveRecord::Type::Value
6
+ def type
7
+ :string
8
+ end
9
+
10
+ def cast(value)
11
+ RDF::URI(value) if value.present?
12
+ end
13
+
14
+ def serialize(value)
15
+ value.to_s
16
+ end
17
+ end
18
+ end
19
+
20
+ module QuoteIRI
21
+ def quote(value)
22
+ return super unless value.is_a?(RDF::URI) || value.is_a?(URI)
23
+
24
+ super(value.to_s)
25
+ end
26
+
27
+ def type_cast(value)
28
+ return super unless value.is_a?(RDF::URI) || value.is_a?(URI)
29
+
30
+ super(value.to_s)
31
+ end
32
+ end
33
+ end
34
+
35
+ if ActiveRecord::ConnectionAdapters.const_defined?(:PostgreSQLAdapter)
36
+ ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(LinkedRails::QuoteIRI)
37
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uri_template'
4
+
5
+ module LinkedRails
6
+ class URITemplate < URITemplate::RFC6570
7
+ ARRAY_SUFFIX = '%5B%5D'
8
+
9
+ private
10
+
11
+ def normalize_variables(vars)
12
+ variables
13
+ .select { |var| var.ends_with?(ARRAY_SUFFIX) }
14
+ .each_with_object(super.with_indifferent_access) do |var, hash|
15
+ key = var.sub(ARRAY_SUFFIX, '')
16
+ hash[var] = normalize_array_value(hash[key]) if hash.key?(key)
17
+ end
18
+ end
19
+
20
+ def normalize_array_value(original_value)
21
+ return original_value unless original_value.is_a?(Hash)
22
+
23
+ original_value.map do |key, values|
24
+ (values.is_a?(Array) ? values : [values]).map do |value|
25
+ "#{CGI.escape(key.to_s)}=#{value}"
26
+ end
27
+ end.flatten
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,11 @@
1
+ module LinkedRails
2
+ class URL
3
+ class << self
4
+ def as_href(url)
5
+ uri = URI(url)
6
+ uri.path = uri.path.presence || '/'
7
+ uri.to_s
8
+ end
9
+ end
10
+ end
11
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LinkedRails
4
- VERSION = '0.0.3'
4
+ VERSION = '0.0.4'
5
5
  end
@@ -19,6 +19,14 @@ module LinkedRails
19
19
  end
20
20
 
21
21
  class << self
22
+ def app_vocabulary(key)
23
+ vocabulary = send(key)
24
+
25
+ LinkedRails::Vocab.define_singleton_method :app do
26
+ vocabulary
27
+ end
28
+ end
29
+
22
30
  def define_shortcut(key)
23
31
  define_singleton_method(key) do
24
32
  options = RDF::Vocabulary.vocab_map.fetch(key)
@@ -74,6 +82,7 @@ module LinkedRails
74
82
  register(:fhir, 'http://hl7.org/fhir/')
75
83
  register(:form, 'https://ns.ontola.io/form#')
76
84
  register(:libro, 'https://ns.ontola.io/libro/')
85
+ register(:ld, 'http://purl.org/linked-delta/')
77
86
  register(:ll, 'http://purl.org/link-lib/')
78
87
  register(:ontola, 'https://ns.ontola.io/core#')
79
88
  register(:sp, 'http://spinrdf.org/sp#')