rhino_project_core 0.20.0.beta.18

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +28 -0
  3. data/Rakefile +34 -0
  4. data/app/assets/stripe_flow.png +0 -0
  5. data/app/controllers/concerns/rhino/authenticated.rb +18 -0
  6. data/app/controllers/concerns/rhino/error_handling.rb +58 -0
  7. data/app/controllers/concerns/rhino/paper_trail_whodunnit.rb +11 -0
  8. data/app/controllers/concerns/rhino/permit.rb +38 -0
  9. data/app/controllers/concerns/rhino/set_current_user.rb +13 -0
  10. data/app/controllers/rhino/account_controller.rb +34 -0
  11. data/app/controllers/rhino/active_model_extension_controller.rb +52 -0
  12. data/app/controllers/rhino/base_controller.rb +23 -0
  13. data/app/controllers/rhino/crud_controller.rb +57 -0
  14. data/app/controllers/rhino/simple_controller.rb +11 -0
  15. data/app/controllers/rhino/simple_stream_controller.rb +12 -0
  16. data/app/helpers/rhino/omniauth_helper.rb +67 -0
  17. data/app/helpers/rhino/policy_helper.rb +42 -0
  18. data/app/helpers/rhino/segment_helper.rb +62 -0
  19. data/app/models/rhino/account.rb +13 -0
  20. data/app/models/rhino/current.rb +7 -0
  21. data/app/models/rhino/user.rb +44 -0
  22. data/app/overrides/active_record/autosave_association_override.rb +18 -0
  23. data/app/overrides/active_record/delegated_type_override.rb +14 -0
  24. data/app/overrides/activestorage/direct_uploads_controller_override.rb +23 -0
  25. data/app/overrides/activestorage/redirect_controller_override.rb +21 -0
  26. data/app/overrides/activestorage/redirect_representation_controller_override.rb +21 -0
  27. data/app/overrides/devise_token_auth/confirmations_controller_override.rb +14 -0
  28. data/app/overrides/devise_token_auth/omniauth_callbacks_controller_override.rb +45 -0
  29. data/app/overrides/devise_token_auth/passwords_controller_override.rb +9 -0
  30. data/app/overrides/devise_token_auth/registrations_controller_override.rb +20 -0
  31. data/app/overrides/devise_token_auth/sessions_controller_override.rb +26 -0
  32. data/app/overrides/devise_token_auth/token_validations_controller_override.rb +18 -0
  33. data/app/policies/rhino/account_policy.rb +27 -0
  34. data/app/policies/rhino/active_storage_attachment_policy.rb +16 -0
  35. data/app/policies/rhino/admin_policy.rb +20 -0
  36. data/app/policies/rhino/base_policy.rb +72 -0
  37. data/app/policies/rhino/crud_policy.rb +109 -0
  38. data/app/policies/rhino/editor_policy.rb +12 -0
  39. data/app/policies/rhino/global_policy.rb +8 -0
  40. data/app/policies/rhino/resource_info_policy.rb +9 -0
  41. data/app/policies/rhino/user_policy.rb +20 -0
  42. data/app/policies/rhino/viewer_policy.rb +19 -0
  43. data/app/resources/rhino/info_graph.rb +41 -0
  44. data/app/resources/rhino/open_api_info.rb +108 -0
  45. data/config/routes.rb +19 -0
  46. data/db/migrate/20180101000000_devise_token_auth_create_users.rb +54 -0
  47. data/db/migrate/20180622142754_add_allow_change_password_to_users.rb +5 -0
  48. data/db/migrate/20191217010224_add_approved_to_users.rb +7 -0
  49. data/db/migrate/20200503182019_change_tokens_to_json_b.rb +9 -0
  50. data/lib/commands/rhino_command.rb +59 -0
  51. data/lib/generators/rhino/dev/setup/setup_generator.rb +175 -0
  52. data/lib/generators/rhino/dev/setup/templates/env.client.tt +4 -0
  53. data/lib/generators/rhino/dev/setup/templates/env.server.tt +35 -0
  54. data/lib/generators/rhino/dev/setup/templates/prepare-commit-msg +17 -0
  55. data/lib/generators/rhino/install/install_generator.rb +24 -0
  56. data/lib/generators/rhino/install/templates/account.rb +4 -0
  57. data/lib/generators/rhino/install/templates/rhino.rb +24 -0
  58. data/lib/generators/rhino/install/templates/user.rb +4 -0
  59. data/lib/generators/rhino/module/module_generator.rb +93 -0
  60. data/lib/generators/rhino/module/templates/engine.rb.tt +19 -0
  61. data/lib/generators/rhino/module/templates/install_generator.rb.tt +12 -0
  62. data/lib/generators/rhino/module/templates/module_tasks.rake.tt +17 -0
  63. data/lib/generators/rhino/policy/policy_generator.rb +33 -0
  64. data/lib/generators/rhino/policy/templates/policy.rb.tt +46 -0
  65. data/lib/generators/test_unit/rhino_policy_generator.rb +13 -0
  66. data/lib/generators/test_unit/templates/policy_test.rb.tt +57 -0
  67. data/lib/rhino/engine.rb +140 -0
  68. data/lib/rhino/omniauth/strategies/azure_o_auth2.rb +16 -0
  69. data/lib/rhino/resource/active_model_extension/backing_store/google_sheet.rb +89 -0
  70. data/lib/rhino/resource/active_model_extension/backing_store.rb +33 -0
  71. data/lib/rhino/resource/active_model_extension/describe.rb +38 -0
  72. data/lib/rhino/resource/active_model_extension/params.rb +70 -0
  73. data/lib/rhino/resource/active_model_extension/properties.rb +229 -0
  74. data/lib/rhino/resource/active_model_extension/reference.rb +50 -0
  75. data/lib/rhino/resource/active_model_extension/routing.rb +15 -0
  76. data/lib/rhino/resource/active_model_extension/serialization.rb +16 -0
  77. data/lib/rhino/resource/active_model_extension.rb +38 -0
  78. data/lib/rhino/resource/active_record_extension/describe.rb +44 -0
  79. data/lib/rhino/resource/active_record_extension/params.rb +213 -0
  80. data/lib/rhino/resource/active_record_extension/properties.rb +85 -0
  81. data/lib/rhino/resource/active_record_extension/properties_describe.rb +226 -0
  82. data/lib/rhino/resource/active_record_extension/reference.rb +50 -0
  83. data/lib/rhino/resource/active_record_extension/routing.rb +21 -0
  84. data/lib/rhino/resource/active_record_extension/search.rb +23 -0
  85. data/lib/rhino/resource/active_record_extension/serialization.rb +16 -0
  86. data/lib/rhino/resource/active_record_extension.rb +30 -0
  87. data/lib/rhino/resource/active_record_tree.rb +50 -0
  88. data/lib/rhino/resource/active_storage_extension.rb +41 -0
  89. data/lib/rhino/resource/describe.rb +19 -0
  90. data/lib/rhino/resource/owner.rb +172 -0
  91. data/lib/rhino/resource/params.rb +31 -0
  92. data/lib/rhino/resource/properties.rb +192 -0
  93. data/lib/rhino/resource/reference.rb +31 -0
  94. data/lib/rhino/resource/routing.rb +107 -0
  95. data/lib/rhino/resource/serialization.rb +13 -0
  96. data/lib/rhino/resource/sieves.rb +36 -0
  97. data/lib/rhino/resource.rb +54 -0
  98. data/lib/rhino/sieve/filter.rb +149 -0
  99. data/lib/rhino/sieve/helpers.rb +11 -0
  100. data/lib/rhino/sieve/limit.rb +20 -0
  101. data/lib/rhino/sieve/offset.rb +16 -0
  102. data/lib/rhino/sieve/order.rb +143 -0
  103. data/lib/rhino/sieve/search.rb +20 -0
  104. data/lib/rhino/sieve.rb +158 -0
  105. data/lib/rhino/test_case/controller.rb +134 -0
  106. data/lib/rhino/test_case/override.rb +19 -0
  107. data/lib/rhino/test_case/policy.rb +76 -0
  108. data/lib/rhino/test_case.rb +10 -0
  109. data/lib/rhino/version.rb +17 -0
  110. data/lib/rhino.rb +129 -0
  111. data/lib/tasks/rhino.rake +38 -0
  112. data/lib/tasks/rhino_dev.rake +17 -0
  113. data/lib/validators/country_validator.rb +11 -0
  114. data/lib/validators/email_validator.rb +8 -0
  115. data/lib/validators/ipv4_validator.rb +10 -0
  116. data/lib/validators/mac_address_validator.rb +9 -0
  117. metadata +178 -0
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rhino
4
+ module Resource
5
+ module ActiveRecordExtension
6
+ module Reference
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ scope :eager_load_refs, -> { includes(klass.references) } if defined?(scope)
11
+
12
+ def references_for_serialization
13
+ serialize_references(references)
14
+ end
15
+
16
+ private
17
+ # FIXME: Duplicated in params.rb
18
+ def reference_to_sym(reference)
19
+ reference.is_a?(Hash) ? reference.keys.first : reference
20
+ end
21
+
22
+ # FIXME: Duplicated in params.rb
23
+ def reference_from_sym(sym)
24
+ ref = try(sym)
25
+ return unless ref
26
+
27
+ # This is mostly how serializable_hash does it
28
+ # Get the first object
29
+ return ref.first if ref.respond_to?(:to_ary)
30
+
31
+ ref
32
+ end
33
+
34
+ def serialize_references(references)
35
+ hash = {}
36
+ references.each do |ref_item|
37
+ sym = reference_to_sym(ref_item)
38
+
39
+ hash[sym] = {}
40
+ hash[sym][:methods] = :display_name
41
+ hash[sym][:include] = serialize_references(ref_item[sym]) if ref_item.is_a?(Hash)
42
+ end.flatten.compact
43
+
44
+ hash
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rhino
4
+ module Resource
5
+ module ActiveRecordExtension
6
+ module Routing
7
+ extend ActiveSupport::Concern
8
+
9
+ class_methods do
10
+ def route_key
11
+ if route_singular?
12
+ model_name.singular_route_key
13
+ else
14
+ model_name.route_key
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rhino
4
+ module Resource
5
+ module ActiveRecordExtension
6
+ module Search
7
+ extend ActiveSupport::Concern
8
+
9
+ class_methods do
10
+ def rhino_search(fields, associated_fields = {})
11
+ include PgSearch::Model
12
+ # stores the searchable fields so it is possible to retrieve them
13
+ # in the open API model description
14
+ @rhino_is_searchable = fields.present? || associated_fields.present?
15
+ # FIXME: We can't autodiscover text fields because it causes a very
16
+ # slow reload in the development server with bigger apps
17
+ pg_search_scope :search_text_fields, against: fields, associated_against: associated_fields, using: { tsearch: { prefix: true } }
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rhino
4
+ module Resource
5
+ module ActiveRecordExtension
6
+ module Serialization
7
+ extend ActiveSupport::Concern
8
+
9
+ def to_caching_json
10
+ serializable_hash(methods: :display_name, include: references_for_serialization)
11
+ # JSON.generate(hash)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../resource'
4
+ require_relative 'active_record_extension/properties'
5
+ require_relative 'active_record_extension/reference'
6
+ require_relative 'active_record_extension/describe'
7
+ require_relative 'active_record_extension/routing'
8
+ require_relative 'active_record_extension/params'
9
+ require_relative 'active_record_extension/serialization'
10
+ require_relative 'active_record_extension/search'
11
+
12
+ module Rhino
13
+ module Resource
14
+ module ActiveRecordExtension
15
+ extend ActiveSupport::Concern
16
+
17
+ # Base
18
+ include Rhino::Resource
19
+
20
+ # Active Record implementations
21
+ include Rhino::Resource::ActiveRecordExtension::Properties
22
+ include Rhino::Resource::ActiveRecordExtension::Reference
23
+ include Rhino::Resource::ActiveRecordExtension::Describe
24
+ include Rhino::Resource::ActiveRecordExtension::Routing
25
+ include Rhino::Resource::ActiveRecordExtension::Params
26
+ include Rhino::Resource::ActiveRecordExtension::Serialization
27
+ include Rhino::Resource::ActiveRecordExtension::Search
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rhino
4
+ module Resource
5
+ module ActiveRecordTree
6
+ extend ActiveSupport::Concern
7
+
8
+ # Base
9
+ include Rhino::Resource::ActiveRecordExtension unless Rhino.auto_include_active_record
10
+
11
+ included do
12
+ has_ancestry
13
+ end
14
+
15
+ class_methods do
16
+ def readable_properties
17
+ super + ["children"]
18
+ end
19
+
20
+ def describe_property(property)
21
+ return super unless property == "children"
22
+
23
+ super.deep_merge({ type: :array, items: ref_descriptor([model_name.element]) })
24
+ end
25
+
26
+ # FIXME: Need to recurse to a MAX DEPTH
27
+ def readable_params(type, refs = references)
28
+ # Remove the base children property and replace with hash
29
+ base_show = super - ["children"]
30
+ base_show + [children: [base_show]]
31
+ end
32
+
33
+ # FIXME: Need writeable params override
34
+ end
35
+
36
+ def serializable_hash(options = nil)
37
+ super(options).merge(children: arrange_serializable_with_options(options))
38
+ end
39
+
40
+ private
41
+ # We do this to carry things like methods all the way through the
42
+ # serialization
43
+ def arrange_serializable_with_options(options = nil)
44
+ children.arrange_serializable do |parent, _children|
45
+ parent.serializable_hash(options)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rhino
4
+ module Resource
5
+ module ActiveStorageExtension
6
+ extend ActiveSupport::Concern
7
+
8
+ # Base
9
+ include Rhino::Resource::ActiveRecordExtension unless Rhino.auto_include_active_record
10
+
11
+ included do
12
+ attribute :url
13
+ attribute :url_attachment
14
+
15
+ rhino_policy :active_storage_attachment
16
+
17
+ def url
18
+ Rails.application.routes.url_helpers.rails_blob_url(self, only_path: false)
19
+ end
20
+
21
+ def url_attachment
22
+ Rails.application.routes.url_helpers.rails_blob_url(self, only_path: false, disposition: :attachment)
23
+ end
24
+
25
+ def display_name
26
+ filename.to_s
27
+ end
28
+ end
29
+
30
+ class_methods do
31
+ def readable_properties
32
+ super + ["signed_id"]
33
+ end
34
+ end
35
+
36
+ def serializable_hash(options = nil)
37
+ super(options).merge("signed_id" => signed_id)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rhino
4
+ module Resource
5
+ module Describe
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ delegate :describe, to: :class
10
+ end
11
+
12
+ class_methods do
13
+ def describe
14
+ raise NotImplementedError, '#describe is not implemented'
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,172 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rhino
4
+ module Resource
5
+ module Owner
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ class_attribute :resource_owned_by, default: nil
10
+
11
+ delegate :auth_owner?, :base_owner?, :global_owner?, to: :class
12
+ delegate :joins_for, :joins_for_auth_owner, :joins_for_base_owner, to: :class
13
+ end
14
+
15
+ def base_owner_ids
16
+ owner_ids(:joins_for_base_owner)
17
+ end
18
+
19
+ def auth_owner_ids
20
+ owner_ids(:joins_for_auth_owner)
21
+ end
22
+
23
+ # The self is actually required to work with class_attribute properly
24
+ # rubocop:disable Style/RedundantSelf
25
+ class_methods do # rubocop:disable Metrics/BlockLength
26
+ # Test if rhino_owner[rdoc-ref:rhino_owner] is the auth owner
27
+ # Also available on the instance
28
+ def auth_owner?
29
+ self == Rhino.auth_owner
30
+ end
31
+
32
+ # Test if rhino_owner[rdoc-ref:rhino_owner] is the base owner
33
+ # Also available on the instance
34
+ def base_owner?
35
+ self == Rhino.base_owner
36
+ end
37
+
38
+ # Test if rhino_owner[rdoc-ref:rhino_owner] is the base owner
39
+ # Also available on the instance
40
+ def global_owner?
41
+ self.resource_owned_by == :global
42
+ end
43
+
44
+ # Test if rhino_owner[rdoc-ref:rhino_owner] is the base owner
45
+ # Also available on the instance
46
+ def global_owned?
47
+ chained_scope = self
48
+ while !chained_scope.auth_owner? && !chained_scope.base_owner? && !chained_scope.global_owner?
49
+ chained_scope = chained_scope.resource_owned_by.to_s.classify.safe_constantize
50
+ end
51
+
52
+ chained_scope.global_owner?
53
+ end
54
+
55
+ # In Rhino the owner specifies which reference is next in the hierarchy.
56
+ # A resource can only have one owned. This owner will either be global
57
+ # for resources that are not owned by the base owner or a belongs to
58
+ # reference that will eventually lead to the base owner. The resource
59
+ # must respond to send(:owner_name) on the instance unless it is global.
60
+ # Such as:
61
+ #
62
+ # rhino_owner :user
63
+ #
64
+ # Will indicate the resource has a has_one reference to user
65
+ #
66
+ # === Examples
67
+ #
68
+ # # Blog is owned by user
69
+ # class Blog < ApplicationRecord
70
+ # include Rhino::Resource::ActiveRecord
71
+ #
72
+ # rhino_owner :user
73
+ # end
74
+ #
75
+ # # BlogPost is owned by Blog
76
+ # class Blog < ApplicationRecord
77
+ # include Rhino::Resource::ActiveRecord
78
+ #
79
+ # rhino_owner :blog
80
+ # end
81
+ #
82
+ # # Category is global
83
+ # class Category < ApplicationRecord
84
+ # include Rhino::Resource::ActiveRecord
85
+ #
86
+ # # Can also use rhino_owner_global as a short cut
87
+ # rhino_owner :global
88
+ # end
89
+ #
90
+ def rhino_owner(name, **_options)
91
+ self.resource_owned_by = name
92
+ rhino_policy :global if global_owned?
93
+ end
94
+
95
+ # Sets rhino_owner[rdoc-ref:rhino_owner] to be the base owner
96
+ def rhino_owner_base(...)
97
+ rhino_owner(Rhino.base_owner.model_name.i18n_key, ...)
98
+ end
99
+
100
+ # Sets rhino_owner[rdoc-ref:rhino_owner] to be global
101
+ def rhino_owner_global(...)
102
+ rhino_owner(:global, ...)
103
+ end
104
+
105
+ def joins_for_auth_owner
106
+ return {} if auth_owner?
107
+
108
+ # Find the reflection to the auth owner
109
+ return Rhino.base_to_auth if base_owner?
110
+
111
+ joins = simple_joins_for_base_owner
112
+
113
+ # Only chain extra to auth if its not the same
114
+ joins.inject(Rhino.same_owner? ? {} : Rhino.base_to_auth) { |a, n| { n => a } }
115
+ end
116
+
117
+ def joins_for_base_owner
118
+ return {} if base_owner?
119
+
120
+ return Rhino.auth_to_base if auth_owner?
121
+
122
+ joins = simple_joins_for_base_owner
123
+
124
+ joins.inject({}) { |a, n| { n => a } }
125
+ end
126
+
127
+ def joins_for(parent)
128
+ return {} if parent.to_s.classify == self.to_s.classify
129
+
130
+ joins = simple_joins_for(parent)
131
+
132
+ joins.inject({}) { |a, n| { n => a } }
133
+ end
134
+
135
+ private
136
+ def simple_joins_for(parent)
137
+ # FIXME: There is probably a more rubyish way to do this
138
+ chained_scope = self
139
+ joins = []
140
+
141
+ # The ownership could be a many, so we classify first
142
+ while chained_scope.resource_owned_by.to_s.classify != parent.to_s.classify
143
+ joins << chained_scope.resource_owned_by
144
+ chained_scope = chained_scope.resource_owned_by.to_s.classify.constantize
145
+ end
146
+ joins << chained_scope.resource_owned_by
147
+
148
+ joins.reverse
149
+ end
150
+
151
+ def simple_joins_for_base_owner
152
+ simple_joins_for(Rhino.base_owner)
153
+ end
154
+ end
155
+ # rubocop:enable Style/RedundantSelf
156
+ end
157
+
158
+ private
159
+ # We use the parent as the starting point because if the record is not
160
+ # persisted yet, we won't be able to find it
161
+ def owner_ids(joins)
162
+ bo = Rhino.base_owner
163
+
164
+ return [] unless owner
165
+
166
+ parent_klass = owner.class
167
+ pk = parent_klass.primary_key
168
+
169
+ parent_klass.joins(parent_klass.send(joins)).where("#{pk}": owner[pk]).pluck("#{bo.table_name}.#{bo.primary_key}")
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rhino
4
+ module Resource
5
+ module Params
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ delegate :create_params, :show_params, :update_params, :transform_params, to: :class
10
+ end
11
+
12
+ class_methods do
13
+ def create_params
14
+ raise NotImplementedError, "#create_params is not implemented"
15
+ end
16
+
17
+ def show_params
18
+ raise NotImplementedError, "#show_params is not implemented"
19
+ end
20
+
21
+ def update_params
22
+ raise NotImplementedError, "#update_params is not implemented"
23
+ end
24
+
25
+ def transform_params(params)
26
+ params
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,192 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rhino
4
+ module Resource
5
+ # == Rhino \Resource \Properties
6
+ #
7
+ # Provides a way to control what properties are exposed on resources as well
8
+ # as an interface for resource implementations to implement and existence
9
+ # utilities.
10
+ #
11
+ # Read, create and update properties can be constrained with only and except
12
+ # directives:
13
+ #
14
+ # class User
15
+ # include ActiveModel::AttributeMethods
16
+ #
17
+ # rhino_properties_read except: :password
18
+ # rhino_properties_create only: [:email]
19
+ # rhino_properties_update only: [:name]
20
+ # end
21
+ #
22
+ # rdoc-ref:rhino_properties_write constrains both create and update.
23
+ #
24
+ # Resource implementations need to implement the following methods:
25
+ # identifier_property
26
+ # readable_properties
27
+ # creatable_properties
28
+ # updatable_properties
29
+ module Properties # rubocop:todo Metrics/ModuleLength
30
+ extend ActiveSupport::Concern
31
+
32
+ included do
33
+ class_attribute :_read_properties, default: nil
34
+ class_attribute :_read_properties_only, default: nil
35
+ class_attribute :_read_properties_except, default: []
36
+
37
+ class_attribute :_create_properties, default: nil
38
+ class_attribute :_create_properties_only, default: nil
39
+ class_attribute :_create_properties_except, default: []
40
+
41
+ class_attribute :_update_properties, default: nil
42
+ class_attribute :_update_properties_only, default: nil
43
+ class_attribute :_update_properties_except, default: []
44
+
45
+ class_attribute :_all_properties, default: nil
46
+
47
+ class_attribute :_properties_overrides, default: ActiveSupport::HashWithIndifferentAccess.new
48
+ class_attribute :_properties_array, default: {}
49
+
50
+ delegate :identifier_property, to: :class
51
+ delegate :read_properties, :create_properties, :update_properties, to: :class
52
+ delegate :all_properties, to: :class
53
+
54
+ delegate :describe_property, to: :class
55
+ end
56
+
57
+ # rubocop:disable Style/RedundantSelf
58
+ class_methods do # rubocop:disable Metrics/BlockLength
59
+ def rhino_properties_read(**options)
60
+ self._read_properties_only = Array.wrap(options[:only]).map(&:to_s) if options.key?(:only)
61
+ self._read_properties_except = Array.wrap(options[:except]).map(&:to_s) if options.key?(:except)
62
+ end
63
+
64
+ def rhino_properties_create(**options)
65
+ self._create_properties_only = Array.wrap(options[:only]).map(&:to_s) if options.key?(:only)
66
+ self._create_properties_except = Array.wrap(options[:except]).map(&:to_s) if options.key?(:except)
67
+ end
68
+
69
+ def rhino_properties_update(**options)
70
+ self._update_properties_only = Array.wrap(options[:only]).map(&:to_s) if options.key?(:only)
71
+ self._update_properties_except = Array.wrap(options[:except]).map(&:to_s) if options.key?(:except)
72
+ end
73
+
74
+ # Constrain create and update properties
75
+ # Accepts only: and except: as either a single property or an array of
76
+ # properties
77
+ def rhino_properties_write(...)
78
+ rhino_properties_create(...)
79
+ rhino_properties_update(...)
80
+ end
81
+
82
+ def rhino_properties_format(formats)
83
+ formats.each do |property, format|
84
+ self._properties_overrides = self._properties_overrides.deep_merge(property => { format: })
85
+ end
86
+ end
87
+
88
+ def rhino_properties_readable_name(readable_names)
89
+ readable_names.each do |property, readable_name|
90
+ self._properties_overrides = self._properties_overrides.deep_merge(property => { "x-rhino-attribute": { readableName: readable_name } })
91
+ end
92
+ end
93
+
94
+ def rhino_properties_array(options)
95
+ self._properties_array = options
96
+ end
97
+
98
+ def identifier_property
99
+ raise NotImplementedError, '#identifier_property is not implemented'
100
+ end
101
+
102
+ def readable_properties
103
+ raise NotImplementedError, '#readable_properties is not implemented'
104
+ end
105
+
106
+ def creatable_properties
107
+ raise NotImplementedError, '#creatable_properties is not implemented'
108
+ end
109
+
110
+ def updatable_properties
111
+ raise NotImplementedError, '#updatable_properties is not implemented'
112
+ end
113
+
114
+ # Return list of read properties for the resource (show and index)
115
+ def read_properties
116
+ # If :only was not set explicitly, select only the default includables
117
+ # and extended models, leaving out the excepted models
118
+ unless self._read_properties
119
+ # If only was set, use that
120
+ return self._read_properties_only if self._read_properties_only
121
+
122
+ self._read_properties = readable_properties.select do |property|
123
+ # And its not excepted
124
+ self._read_properties_except.exclude?(property)
125
+ end
126
+ end
127
+
128
+ self._read_properties
129
+ end
130
+
131
+ # Check if read property exists
132
+ def read_property?(property)
133
+ read_properties.include?(property)
134
+ end
135
+
136
+ # Return list of create properties for the resource
137
+ def create_properties
138
+ unless self._create_properties
139
+ # If create only was set, use that
140
+ return self._create_properties_only if self._create_properties_only
141
+
142
+ self._create_properties = creatable_properties.select do |property|
143
+ self._create_properties_except.exclude?(property)
144
+ end
145
+ end
146
+
147
+ self._create_properties
148
+ end
149
+
150
+ # Check if create property exists
151
+ def create_property?(property)
152
+ create_properties.include?(property)
153
+ end
154
+
155
+ # Return list of update properties for the resource
156
+ def update_properties
157
+ unless self._update_properties
158
+ # If update only was set, use that
159
+ return self._update_properties_only if self._update_properties_only
160
+
161
+ self._update_properties = updatable_properties.select do |property|
162
+ self._update_properties_except.exclude?(property)
163
+ end
164
+ end
165
+
166
+ self._update_properties
167
+ end
168
+
169
+ # Check if update property exists
170
+ def update_property?(property)
171
+ update_properties.include?(property)
172
+ end
173
+
174
+ def all_properties
175
+ self._all_properties = (read_properties + create_properties + update_properties).uniq.map(&:to_s) unless self._all_properties
176
+
177
+ self._all_properties
178
+ end
179
+
180
+ # Check if property exists
181
+ def property?(property)
182
+ all_properties.include?(property)
183
+ end
184
+
185
+ def describe_property
186
+ raise NotImplementedError, '#describe_property is not implemented'
187
+ end
188
+ end
189
+ # rubocop:enable Style/RedundantSelf
190
+ end
191
+ end
192
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rhino
4
+ module Resource
5
+ module Reference
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ class_attribute :_references, default: []
10
+
11
+ delegate :references, to: :class
12
+
13
+ def references_for_serialization
14
+ raise NotImplementedError, '#references_for_serialization is not implemented'
15
+ end
16
+ end
17
+
18
+ class_methods do
19
+ def rhino_references(references)
20
+ raise "rhino_references called multiple times for #{model_name}" if self._references.present? # rubocop:disable Style/RedundantSelf
21
+
22
+ self._references = references
23
+ end
24
+
25
+ def references
26
+ self._references # rubocop:disable Style/RedundantSelf
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end