rhino_project_core 0.20.0.alpha.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (134) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +28 -0
  4. data/Rakefile +35 -0
  5. data/app/assets/stripe_flow.png +0 -0
  6. data/app/controllers/concerns/rhino/authenticated.rb +18 -0
  7. data/app/controllers/concerns/rhino/error_handling.rb +60 -0
  8. data/app/controllers/concerns/rhino/paper_trail_whodunnit.rb +11 -0
  9. data/app/controllers/concerns/rhino/permit.rb +38 -0
  10. data/app/controllers/concerns/rhino/set_current_user.rb +13 -0
  11. data/app/controllers/rhino/account_controller.rb +34 -0
  12. data/app/controllers/rhino/active_model_extension_controller.rb +52 -0
  13. data/app/controllers/rhino/base_controller.rb +23 -0
  14. data/app/controllers/rhino/crud_controller.rb +57 -0
  15. data/app/controllers/rhino/simple_controller.rb +13 -0
  16. data/app/controllers/rhino/simple_stream_controller.rb +12 -0
  17. data/app/helpers/rhino/omniauth_helper.rb +67 -0
  18. data/app/helpers/rhino/policy_helper.rb +42 -0
  19. data/app/helpers/rhino/segment_helper.rb +62 -0
  20. data/app/models/rhino/account.rb +13 -0
  21. data/app/models/rhino/current.rb +7 -0
  22. data/app/models/rhino/user.rb +44 -0
  23. data/app/overrides/active_record/autosave_association_override.rb +18 -0
  24. data/app/overrides/active_record/delegated_type_override.rb +14 -0
  25. data/app/overrides/activestorage/direct_uploads_controller_override.rb +23 -0
  26. data/app/overrides/activestorage/redirect_controller_override.rb +21 -0
  27. data/app/overrides/activestorage/redirect_representation_controller_override.rb +21 -0
  28. data/app/overrides/devise_token_auth/confirmations_controller_override.rb +14 -0
  29. data/app/overrides/devise_token_auth/omniauth_callbacks_controller_override.rb +45 -0
  30. data/app/overrides/devise_token_auth/passwords_controller_override.rb +9 -0
  31. data/app/overrides/devise_token_auth/registrations_controller_override.rb +20 -0
  32. data/app/overrides/devise_token_auth/sessions_controller_override.rb +26 -0
  33. data/app/overrides/devise_token_auth/token_validations_controller_override.rb +18 -0
  34. data/app/policies/rhino/account_policy.rb +27 -0
  35. data/app/policies/rhino/active_storage_attachment_policy.rb +16 -0
  36. data/app/policies/rhino/admin_policy.rb +20 -0
  37. data/app/policies/rhino/base_policy.rb +72 -0
  38. data/app/policies/rhino/crud_policy.rb +109 -0
  39. data/app/policies/rhino/editor_policy.rb +12 -0
  40. data/app/policies/rhino/global_policy.rb +8 -0
  41. data/app/policies/rhino/resource_info_policy.rb +9 -0
  42. data/app/policies/rhino/user_policy.rb +20 -0
  43. data/app/policies/rhino/viewer_policy.rb +19 -0
  44. data/app/resources/rhino/info_graph.rb +41 -0
  45. data/app/resources/rhino/open_api_info.rb +108 -0
  46. data/config/routes.rb +19 -0
  47. data/db/migrate/20180101000000_devise_token_auth_create_users.rb +54 -0
  48. data/db/migrate/20180622142754_add_allow_change_password_to_users.rb +5 -0
  49. data/db/migrate/20191217010224_add_approved_to_users.rb +7 -0
  50. data/db/migrate/20200503182019_change_tokens_to_json_b.rb +9 -0
  51. data/lib/commands/rhino/module/coverage_command.rb +44 -0
  52. data/lib/commands/rhino/module/dummy_command.rb +43 -0
  53. data/lib/commands/rhino/module/new_command.rb +34 -0
  54. data/lib/commands/rhino/module/rails_command.rb +43 -0
  55. data/lib/commands/rhino/module/test_command.rb +43 -0
  56. data/lib/generators/rhino/dev/setup/setup_generator.rb +199 -0
  57. data/lib/generators/rhino/dev/setup/templates/env.client.tt +4 -0
  58. data/lib/generators/rhino/dev/setup/templates/env.root.tt +1 -0
  59. data/lib/generators/rhino/dev/setup/templates/env.server.tt +35 -0
  60. data/lib/generators/rhino/dev/setup/templates/prepare-commit-msg +17 -0
  61. data/lib/generators/rhino/install/install_generator.rb +24 -0
  62. data/lib/generators/rhino/install/templates/account.rb +4 -0
  63. data/lib/generators/rhino/install/templates/rhino.rb +24 -0
  64. data/lib/generators/rhino/install/templates/user.rb +4 -0
  65. data/lib/generators/rhino/model/model_generator.rb +96 -0
  66. data/lib/generators/rhino/module/USAGE +6 -0
  67. data/lib/generators/rhino/module/module_generator.rb +92 -0
  68. data/lib/generators/rhino/module/templates/%name%.gemspec.tt +24 -0
  69. data/lib/generators/rhino/module/templates/lib/%namespaced_name%/engine.rb.tt +18 -0
  70. data/lib/generators/rhino/module/templates/lib/generators/%namespaced_name%/install/install_generator.rb.tt +12 -0
  71. data/lib/generators/rhino/module/templates/lib/tasks/%namespaced_name%_tasks.rake.tt +13 -0
  72. data/lib/generators/rhino/module/templates/test/dummy/app/models/user.rb +4 -0
  73. data/lib/generators/rhino/module/templates/test/dummy/config/database.yml +25 -0
  74. data/lib/generators/rhino/module/templates/test/dummy/config/initializers/devise.rb +311 -0
  75. data/lib/generators/rhino/module/templates/test/dummy/config/initializers/devise_token_auth.rb +71 -0
  76. data/lib/generators/rhino/module/templates/test/test_helper.rb +54 -0
  77. data/lib/generators/rhino/policy/policy_generator.rb +33 -0
  78. data/lib/generators/rhino/policy/templates/policy.rb.tt +46 -0
  79. data/lib/generators/test_unit/rhino_policy_generator.rb +13 -0
  80. data/lib/generators/test_unit/templates/policy_test.rb.tt +57 -0
  81. data/lib/rhino/engine.rb +166 -0
  82. data/lib/rhino/omniauth/strategies/azure_o_auth2.rb +16 -0
  83. data/lib/rhino/resource/active_model_extension/backing_store/google_sheet.rb +89 -0
  84. data/lib/rhino/resource/active_model_extension/backing_store.rb +33 -0
  85. data/lib/rhino/resource/active_model_extension/describe.rb +38 -0
  86. data/lib/rhino/resource/active_model_extension/params.rb +70 -0
  87. data/lib/rhino/resource/active_model_extension/properties.rb +231 -0
  88. data/lib/rhino/resource/active_model_extension/reference.rb +50 -0
  89. data/lib/rhino/resource/active_model_extension/routing.rb +15 -0
  90. data/lib/rhino/resource/active_model_extension/serialization.rb +16 -0
  91. data/lib/rhino/resource/active_model_extension.rb +38 -0
  92. data/lib/rhino/resource/active_record_extension/describe.rb +44 -0
  93. data/lib/rhino/resource/active_record_extension/params.rb +213 -0
  94. data/lib/rhino/resource/active_record_extension/properties.rb +85 -0
  95. data/lib/rhino/resource/active_record_extension/properties_describe.rb +228 -0
  96. data/lib/rhino/resource/active_record_extension/reference.rb +50 -0
  97. data/lib/rhino/resource/active_record_extension/routing.rb +21 -0
  98. data/lib/rhino/resource/active_record_extension/search.rb +23 -0
  99. data/lib/rhino/resource/active_record_extension/serialization.rb +16 -0
  100. data/lib/rhino/resource/active_record_extension/super_admin.rb +25 -0
  101. data/lib/rhino/resource/active_record_extension.rb +32 -0
  102. data/lib/rhino/resource/active_record_tree.rb +50 -0
  103. data/lib/rhino/resource/active_storage_extension.rb +41 -0
  104. data/lib/rhino/resource/describe.rb +19 -0
  105. data/lib/rhino/resource/owner.rb +172 -0
  106. data/lib/rhino/resource/params.rb +31 -0
  107. data/lib/rhino/resource/properties.rb +192 -0
  108. data/lib/rhino/resource/reference.rb +29 -0
  109. data/lib/rhino/resource/routing.rb +107 -0
  110. data/lib/rhino/resource/serialization.rb +13 -0
  111. data/lib/rhino/resource/sieves.rb +36 -0
  112. data/lib/rhino/resource.rb +55 -0
  113. data/lib/rhino/sieve/filter.rb +149 -0
  114. data/lib/rhino/sieve/geospatial.rb +45 -0
  115. data/lib/rhino/sieve/helpers.rb +11 -0
  116. data/lib/rhino/sieve/limit.rb +20 -0
  117. data/lib/rhino/sieve/offset.rb +16 -0
  118. data/lib/rhino/sieve/order.rb +143 -0
  119. data/lib/rhino/sieve/search.rb +20 -0
  120. data/lib/rhino/sieve.rb +159 -0
  121. data/lib/rhino/test_case/controller.rb +145 -0
  122. data/lib/rhino/test_case/model.rb +86 -0
  123. data/lib/rhino/test_case/override.rb +19 -0
  124. data/lib/rhino/test_case/policy.rb +76 -0
  125. data/lib/rhino/test_case.rb +11 -0
  126. data/lib/rhino/version.rb +17 -0
  127. data/lib/rhino_project_core.rb +131 -0
  128. data/lib/tasks/rhino.rake +24 -0
  129. data/lib/tasks/rhino_dev.rake +17 -0
  130. data/lib/validators/country_validator.rb +11 -0
  131. data/lib/validators/email_validator.rb +8 -0
  132. data/lib/validators/ipv4_validator.rb +10 -0
  133. data/lib/validators/mac_address_validator.rb +9 -0
  134. metadata +531 -0
@@ -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(property)
186
+ raise NotImplementedError, "#describe_property is not implemented for #{property}"
187
+ end
188
+ end
189
+ # rubocop:enable Style/RedundantSelf
190
+ end
191
+ end
192
+ end
@@ -0,0 +1,29 @@
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
+ self._references = references
21
+ end
22
+
23
+ def references
24
+ self._references # rubocop:disable Style/RedundantSelf
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rhino
4
+ module Resource
5
+ module Routing
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ class_attribute :_route_key, default: nil
10
+ class_attribute :_route_path, default: nil
11
+ class_attribute :_route_singular, default: false
12
+
13
+ class_attribute :_rhino_routes, default: nil
14
+ class_attribute :_rhino_routes_except, default: []
15
+
16
+ class_attribute :controller_name, default: 'rhino/crud'
17
+
18
+ delegate :routes, to: :class
19
+ end
20
+
21
+ # rubocop:disable Style/RedundantSelf, Metrics/BlockLength
22
+ class_methods do
23
+ def route_key
24
+ self._route_key ||= if route_singular?
25
+ name.demodulize.underscore
26
+ else
27
+ name.demodulize.underscore.pluralize
28
+ end
29
+ end
30
+
31
+ def route_path
32
+ self._route_path ||= route_key
33
+ end
34
+
35
+ def route_singular?
36
+ self._route_singular
37
+ end
38
+
39
+ def route_path_frontend
40
+ route_path.camelize(:lower)
41
+ end
42
+
43
+ def route_frontend
44
+ "/#{route_path_frontend}"
45
+ end
46
+
47
+ def route_api
48
+ "/#{Rhino.namespace}/#{route_path}"
49
+ end
50
+
51
+ def routes
52
+ unless self._rhino_routes
53
+ if global_owner? # rubocop:disable Style/ConditionalAssignment
54
+ self._rhino_routes = %i[index show]
55
+ else
56
+ self._rhino_routes = %i[index create show update destroy]
57
+ end
58
+ end
59
+ self._rhino_routes - self._rhino_routes_except
60
+ end
61
+
62
+ def rhino_routing(**options)
63
+ self._route_key = options.delete(:key) if options.key?(:key)
64
+ self._route_path = options.delete(:path) if options.key?(:path)
65
+ self._route_singular = options.delete(:singular) if options.key?(:singular)
66
+
67
+ self._rhino_routes = options.delete(:only) if options.key?(:only)
68
+ self._rhino_routes_except = options.delete(:except) if options.key?(:except)
69
+ end
70
+
71
+ def rhino_controller(controller)
72
+ self.controller_name = "rhino/#{controller}"
73
+ end
74
+ end
75
+ # rubocop:enable Style/RedundantSelf, Metrics/BlockLength
76
+ end
77
+
78
+ def route_frontend # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
79
+ base_owner_pk = "#{Rhino.base_owner.table_name}.#{Rhino.base_owner.primary_key}"
80
+
81
+ joins = joins_for_base_owner
82
+ base_owner_id = if joins.empty?
83
+ # if this is Model is the base owner, we don't to include it in frontend url
84
+ nil
85
+ else
86
+ base_owner_ids = self.class.joins(joins).where(id:).pluck(base_owner_pk)
87
+ if base_owner_ids.length == 1
88
+ base_owner_ids.first
89
+ else
90
+ # if this Model doesn't have a clear single path to the base owner Model,
91
+ # we shouldn't include it in the frontend url
92
+ nil
93
+ end
94
+ end
95
+
96
+ if base_owner_id.nil?
97
+ "#{self.class.route_frontend}/#{id}"
98
+ else
99
+ "/#{base_owner_id}#{self.class.route_frontend}/#{id}"
100
+ end
101
+ end
102
+
103
+ def route_api
104
+ "#{self.class.route_api}/#{id}"
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rhino
4
+ module Resource
5
+ module Serialization
6
+ extend ActiveSupport::Concern
7
+
8
+ def to_caching_json
9
+ raise NotImplementedError, '#to_caching_json is not implemented'
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rhino
4
+ module Resource
5
+ module Sieves
6
+ extend ActiveSupport::Concern
7
+ include ArelHelpers::ArelTable
8
+
9
+ included do
10
+ class_attribute :_sieves
11
+ class_attribute :_built_sieves
12
+
13
+ delegate :sieves, to: :class
14
+ end
15
+
16
+ # rubocop:disable Style/RedundantSelf
17
+ class_methods do
18
+ def rhino_sieves
19
+ self._sieves = Rhino.sieves.dup unless self._sieves
20
+ self._sieves
21
+ end
22
+
23
+ def sieves
24
+ self._built_sieves = rhino_sieves.build(self) unless self._built_sieves
25
+
26
+ self._built_sieves
27
+ end
28
+
29
+ def resolve(scope, _params)
30
+ scope
31
+ end
32
+ end
33
+ # rubocop:enable Style/RedundantSelf
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'resource/owner'
4
+ require_relative 'resource/properties'
5
+ require_relative 'resource/reference'
6
+ require_relative 'resource/describe'
7
+ require_relative 'resource/routing'
8
+ require_relative 'resource/params'
9
+ require_relative 'resource/serialization'
10
+ require_relative 'resource/sieves'
11
+
12
+ require_relative '../../app/policies/rhino/crud_policy'
13
+
14
+ module Rhino
15
+ module Resource
16
+ extend ActiveSupport::Concern
17
+
18
+ include Rhino::Resource::Owner
19
+ include Rhino::Resource::Properties
20
+ include Rhino::Resource::Reference
21
+ include Rhino::Resource::Describe
22
+ include Rhino::Resource::Routing
23
+ include Rhino::Resource::Params
24
+ include Rhino::Resource::Serialization
25
+ include Rhino::Resource::Sieves
26
+
27
+ included do
28
+ class_attribute :_policy_class, default: Rhino::CrudPolicy
29
+
30
+ def owner
31
+ send self.class.resource_owned_by
32
+ end
33
+
34
+ def display_name
35
+ return name if respond_to? :name
36
+ return title if respond_to? :title
37
+
38
+ nil
39
+ end
40
+ end
41
+
42
+ class_methods do
43
+ def policy_class
44
+ _policy_class
45
+ end
46
+
47
+ def rhino_policy(policy)
48
+ # Look for local policy first, then look in the rhino namespace
49
+ self._policy_class = "#{policy}_policy".classify.safe_constantize || "rhino/#{policy}_policy".classify.safe_constantize
50
+
51
+ raise "Policy #{policy} not found for #{name}" unless _policy_class
52
+ end
53
+ end
54
+ end
55
+ end