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.
- checksums.yaml +7 -0
- data/README.md +28 -0
- data/Rakefile +34 -0
- data/app/assets/stripe_flow.png +0 -0
- data/app/controllers/concerns/rhino/authenticated.rb +18 -0
- data/app/controllers/concerns/rhino/error_handling.rb +58 -0
- data/app/controllers/concerns/rhino/paper_trail_whodunnit.rb +11 -0
- data/app/controllers/concerns/rhino/permit.rb +38 -0
- data/app/controllers/concerns/rhino/set_current_user.rb +13 -0
- data/app/controllers/rhino/account_controller.rb +34 -0
- data/app/controllers/rhino/active_model_extension_controller.rb +52 -0
- data/app/controllers/rhino/base_controller.rb +23 -0
- data/app/controllers/rhino/crud_controller.rb +57 -0
- data/app/controllers/rhino/simple_controller.rb +11 -0
- data/app/controllers/rhino/simple_stream_controller.rb +12 -0
- data/app/helpers/rhino/omniauth_helper.rb +67 -0
- data/app/helpers/rhino/policy_helper.rb +42 -0
- data/app/helpers/rhino/segment_helper.rb +62 -0
- data/app/models/rhino/account.rb +13 -0
- data/app/models/rhino/current.rb +7 -0
- data/app/models/rhino/user.rb +44 -0
- data/app/overrides/active_record/autosave_association_override.rb +18 -0
- data/app/overrides/active_record/delegated_type_override.rb +14 -0
- data/app/overrides/activestorage/direct_uploads_controller_override.rb +23 -0
- data/app/overrides/activestorage/redirect_controller_override.rb +21 -0
- data/app/overrides/activestorage/redirect_representation_controller_override.rb +21 -0
- data/app/overrides/devise_token_auth/confirmations_controller_override.rb +14 -0
- data/app/overrides/devise_token_auth/omniauth_callbacks_controller_override.rb +45 -0
- data/app/overrides/devise_token_auth/passwords_controller_override.rb +9 -0
- data/app/overrides/devise_token_auth/registrations_controller_override.rb +20 -0
- data/app/overrides/devise_token_auth/sessions_controller_override.rb +26 -0
- data/app/overrides/devise_token_auth/token_validations_controller_override.rb +18 -0
- data/app/policies/rhino/account_policy.rb +27 -0
- data/app/policies/rhino/active_storage_attachment_policy.rb +16 -0
- data/app/policies/rhino/admin_policy.rb +20 -0
- data/app/policies/rhino/base_policy.rb +72 -0
- data/app/policies/rhino/crud_policy.rb +109 -0
- data/app/policies/rhino/editor_policy.rb +12 -0
- data/app/policies/rhino/global_policy.rb +8 -0
- data/app/policies/rhino/resource_info_policy.rb +9 -0
- data/app/policies/rhino/user_policy.rb +20 -0
- data/app/policies/rhino/viewer_policy.rb +19 -0
- data/app/resources/rhino/info_graph.rb +41 -0
- data/app/resources/rhino/open_api_info.rb +108 -0
- data/config/routes.rb +19 -0
- data/db/migrate/20180101000000_devise_token_auth_create_users.rb +54 -0
- data/db/migrate/20180622142754_add_allow_change_password_to_users.rb +5 -0
- data/db/migrate/20191217010224_add_approved_to_users.rb +7 -0
- data/db/migrate/20200503182019_change_tokens_to_json_b.rb +9 -0
- data/lib/commands/rhino_command.rb +59 -0
- data/lib/generators/rhino/dev/setup/setup_generator.rb +175 -0
- data/lib/generators/rhino/dev/setup/templates/env.client.tt +4 -0
- data/lib/generators/rhino/dev/setup/templates/env.server.tt +35 -0
- data/lib/generators/rhino/dev/setup/templates/prepare-commit-msg +17 -0
- data/lib/generators/rhino/install/install_generator.rb +24 -0
- data/lib/generators/rhino/install/templates/account.rb +4 -0
- data/lib/generators/rhino/install/templates/rhino.rb +24 -0
- data/lib/generators/rhino/install/templates/user.rb +4 -0
- data/lib/generators/rhino/module/module_generator.rb +93 -0
- data/lib/generators/rhino/module/templates/engine.rb.tt +19 -0
- data/lib/generators/rhino/module/templates/install_generator.rb.tt +12 -0
- data/lib/generators/rhino/module/templates/module_tasks.rake.tt +17 -0
- data/lib/generators/rhino/policy/policy_generator.rb +33 -0
- data/lib/generators/rhino/policy/templates/policy.rb.tt +46 -0
- data/lib/generators/test_unit/rhino_policy_generator.rb +13 -0
- data/lib/generators/test_unit/templates/policy_test.rb.tt +57 -0
- data/lib/rhino/engine.rb +140 -0
- data/lib/rhino/omniauth/strategies/azure_o_auth2.rb +16 -0
- data/lib/rhino/resource/active_model_extension/backing_store/google_sheet.rb +89 -0
- data/lib/rhino/resource/active_model_extension/backing_store.rb +33 -0
- data/lib/rhino/resource/active_model_extension/describe.rb +38 -0
- data/lib/rhino/resource/active_model_extension/params.rb +70 -0
- data/lib/rhino/resource/active_model_extension/properties.rb +229 -0
- data/lib/rhino/resource/active_model_extension/reference.rb +50 -0
- data/lib/rhino/resource/active_model_extension/routing.rb +15 -0
- data/lib/rhino/resource/active_model_extension/serialization.rb +16 -0
- data/lib/rhino/resource/active_model_extension.rb +38 -0
- data/lib/rhino/resource/active_record_extension/describe.rb +44 -0
- data/lib/rhino/resource/active_record_extension/params.rb +213 -0
- data/lib/rhino/resource/active_record_extension/properties.rb +85 -0
- data/lib/rhino/resource/active_record_extension/properties_describe.rb +226 -0
- data/lib/rhino/resource/active_record_extension/reference.rb +50 -0
- data/lib/rhino/resource/active_record_extension/routing.rb +21 -0
- data/lib/rhino/resource/active_record_extension/search.rb +23 -0
- data/lib/rhino/resource/active_record_extension/serialization.rb +16 -0
- data/lib/rhino/resource/active_record_extension.rb +30 -0
- data/lib/rhino/resource/active_record_tree.rb +50 -0
- data/lib/rhino/resource/active_storage_extension.rb +41 -0
- data/lib/rhino/resource/describe.rb +19 -0
- data/lib/rhino/resource/owner.rb +172 -0
- data/lib/rhino/resource/params.rb +31 -0
- data/lib/rhino/resource/properties.rb +192 -0
- data/lib/rhino/resource/reference.rb +31 -0
- data/lib/rhino/resource/routing.rb +107 -0
- data/lib/rhino/resource/serialization.rb +13 -0
- data/lib/rhino/resource/sieves.rb +36 -0
- data/lib/rhino/resource.rb +54 -0
- data/lib/rhino/sieve/filter.rb +149 -0
- data/lib/rhino/sieve/helpers.rb +11 -0
- data/lib/rhino/sieve/limit.rb +20 -0
- data/lib/rhino/sieve/offset.rb +16 -0
- data/lib/rhino/sieve/order.rb +143 -0
- data/lib/rhino/sieve/search.rb +20 -0
- data/lib/rhino/sieve.rb +158 -0
- data/lib/rhino/test_case/controller.rb +134 -0
- data/lib/rhino/test_case/override.rb +19 -0
- data/lib/rhino/test_case/policy.rb +76 -0
- data/lib/rhino/test_case.rb +10 -0
- data/lib/rhino/version.rb +17 -0
- data/lib/rhino.rb +129 -0
- data/lib/tasks/rhino.rake +38 -0
- data/lib/tasks/rhino_dev.rake +17 -0
- data/lib/validators/country_validator.rb +11 -0
- data/lib/validators/email_validator.rb +8 -0
- data/lib/validators/ipv4_validator.rb +10 -0
- data/lib/validators/mac_address_validator.rb +9 -0
- 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
|