wallaby-core 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +31 -0
  4. data/app/controllers/wallaby/application_controller.rb +84 -0
  5. data/app/controllers/wallaby/resources_controller.rb +381 -0
  6. data/app/controllers/wallaby/secure_controller.rb +81 -0
  7. data/app/security/ability.rb +13 -0
  8. data/config/locales/wallaby.en.yml +140 -0
  9. data/config/locales/wallaby_class.en.yml +30 -0
  10. data/config/routes.rb +39 -0
  11. data/lib/adaptors/wallaby/custom.rb +7 -0
  12. data/lib/adaptors/wallaby/custom/default_provider.rb +9 -0
  13. data/lib/adaptors/wallaby/custom/model_decorator.rb +71 -0
  14. data/lib/adaptors/wallaby/custom/model_finder.rb +13 -0
  15. data/lib/adaptors/wallaby/custom/model_pagination_provider.rb +14 -0
  16. data/lib/adaptors/wallaby/custom/model_service_provider.rb +48 -0
  17. data/lib/authorizers/wallaby/cancancan_authorization_provider.rb +72 -0
  18. data/lib/authorizers/wallaby/default_authorization_provider.rb +58 -0
  19. data/lib/authorizers/wallaby/model_authorizer.rb +100 -0
  20. data/lib/authorizers/wallaby/pundit_authorization_provider.rb +89 -0
  21. data/lib/concerns/wallaby/authorizable.rb +103 -0
  22. data/lib/concerns/wallaby/baseable.rb +36 -0
  23. data/lib/concerns/wallaby/decoratable.rb +101 -0
  24. data/lib/concerns/wallaby/defaultable.rb +38 -0
  25. data/lib/concerns/wallaby/engineable.rb +61 -0
  26. data/lib/concerns/wallaby/fieldable.rb +78 -0
  27. data/lib/concerns/wallaby/paginatable.rb +72 -0
  28. data/lib/concerns/wallaby/rails_overridden_methods.rb +42 -0
  29. data/lib/concerns/wallaby/resourcable.rb +149 -0
  30. data/lib/concerns/wallaby/servicable.rb +68 -0
  31. data/lib/concerns/wallaby/shared_helpers.rb +22 -0
  32. data/lib/concerns/wallaby/themeable.rb +40 -0
  33. data/lib/decorators/wallaby/resource_decorator.rb +189 -0
  34. data/lib/errors/wallaby/cell_handling.rb +6 -0
  35. data/lib/errors/wallaby/forbidden.rb +6 -0
  36. data/lib/errors/wallaby/general_error.rb +6 -0
  37. data/lib/errors/wallaby/invalid_error.rb +6 -0
  38. data/lib/errors/wallaby/model_not_found.rb +11 -0
  39. data/lib/errors/wallaby/not_authenticated.rb +6 -0
  40. data/lib/errors/wallaby/not_found.rb +6 -0
  41. data/lib/errors/wallaby/not_implemented.rb +6 -0
  42. data/lib/errors/wallaby/resource_not_found.rb +11 -0
  43. data/lib/errors/wallaby/unprocessable_entity.rb +6 -0
  44. data/lib/forms/wallaby/form_builder.rb +60 -0
  45. data/lib/helpers/wallaby/application_helper.rb +79 -0
  46. data/lib/helpers/wallaby/base_helper.rb +65 -0
  47. data/lib/helpers/wallaby/configuration_helper.rb +18 -0
  48. data/lib/helpers/wallaby/form_helper.rb +62 -0
  49. data/lib/helpers/wallaby/index_helper.rb +84 -0
  50. data/lib/helpers/wallaby/links_helper.rb +213 -0
  51. data/lib/helpers/wallaby/resources_helper.rb +52 -0
  52. data/lib/helpers/wallaby/secure_helper.rb +54 -0
  53. data/lib/helpers/wallaby/styling_helper.rb +82 -0
  54. data/lib/interfaces/wallaby/mode.rb +72 -0
  55. data/lib/interfaces/wallaby/model_authorization_provider.rb +99 -0
  56. data/lib/interfaces/wallaby/model_decorator.rb +168 -0
  57. data/lib/interfaces/wallaby/model_finder.rb +12 -0
  58. data/lib/interfaces/wallaby/model_pagination_provider.rb +107 -0
  59. data/lib/interfaces/wallaby/model_service_provider.rb +84 -0
  60. data/lib/paginators/wallaby/model_paginator.rb +115 -0
  61. data/lib/paginators/wallaby/resource_paginator.rb +12 -0
  62. data/lib/parsers/wallaby/parser.rb +34 -0
  63. data/lib/renderers/wallaby/cell.rb +137 -0
  64. data/lib/renderers/wallaby/cell_resolver.rb +89 -0
  65. data/lib/renderers/wallaby/custom_lookup_context.rb +64 -0
  66. data/lib/renderers/wallaby/custom_partial_renderer.rb +33 -0
  67. data/lib/renderers/wallaby/custom_renderer.rb +16 -0
  68. data/lib/responders/wallaby/json_api_responder.rb +101 -0
  69. data/lib/responders/wallaby/resources_responder.rb +28 -0
  70. data/lib/routes/wallaby/resources_router.rb +72 -0
  71. data/lib/servicers/wallaby/model_servicer.rb +154 -0
  72. data/lib/services/wallaby/engine_name_finder.rb +22 -0
  73. data/lib/services/wallaby/engine_url_for.rb +46 -0
  74. data/lib/services/wallaby/link_options_normalizer.rb +19 -0
  75. data/lib/services/wallaby/map/mode_mapper.rb +27 -0
  76. data/lib/services/wallaby/map/model_class_collector.rb +49 -0
  77. data/lib/services/wallaby/map/model_class_mapper.rb +38 -0
  78. data/lib/services/wallaby/prefixes_builder.rb +66 -0
  79. data/lib/services/wallaby/sorting/hash_builder.rb +19 -0
  80. data/lib/services/wallaby/sorting/link_builder.rb +69 -0
  81. data/lib/services/wallaby/sorting/next_builder.rb +63 -0
  82. data/lib/services/wallaby/sorting/single_builder.rb +20 -0
  83. data/lib/services/wallaby/type_renderer.rb +50 -0
  84. data/lib/support/action_dispatch/routing/mapper.rb +75 -0
  85. data/lib/tree/wallaby/node.rb +25 -0
  86. data/lib/utils/wallaby/cell_utils.rb +34 -0
  87. data/lib/utils/wallaby/field_utils.rb +43 -0
  88. data/lib/utils/wallaby/filter_utils.rb +20 -0
  89. data/lib/utils/wallaby/model_utils.rb +51 -0
  90. data/lib/utils/wallaby/module_utils.rb +46 -0
  91. data/lib/utils/wallaby/params_utils.rb +14 -0
  92. data/lib/utils/wallaby/preload_utils.rb +44 -0
  93. data/lib/utils/wallaby/test_utils.rb +34 -0
  94. data/lib/utils/wallaby/utils.rb +27 -0
  95. data/lib/wallaby/configuration.rb +103 -0
  96. data/lib/wallaby/configuration/features.rb +24 -0
  97. data/lib/wallaby/configuration/mapping.rb +140 -0
  98. data/lib/wallaby/configuration/metadata.rb +23 -0
  99. data/lib/wallaby/configuration/models.rb +46 -0
  100. data/lib/wallaby/configuration/pagination.rb +30 -0
  101. data/lib/wallaby/configuration/security.rb +98 -0
  102. data/lib/wallaby/configuration/sorting.rb +28 -0
  103. data/lib/wallaby/constants.rb +45 -0
  104. data/lib/wallaby/core.rb +117 -0
  105. data/lib/wallaby/core/version.rb +7 -0
  106. data/lib/wallaby/engine.rb +43 -0
  107. data/lib/wallaby/map.rb +170 -0
  108. metadata +222 -0
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ # Cancancan base authorization provider
5
+ class CancancanAuthorizationProvider < ModelAuthorizationProvider
6
+ # Detect and see if Cancancan is in use.
7
+ # @param context [ActionController::Base]
8
+ # @return [true] if Cancancan is in use.
9
+ # @return [false] if Cancancan is not in use.
10
+ def self.available?(context)
11
+ defined?(CanCanCan) && defined?(Ability) && context.respond_to?(:current_ability)
12
+ end
13
+
14
+ # This will pull out the args required for contruction from context
15
+ # @param context [ActionController::Base]
16
+ # @return [Hash] args for initialize
17
+ def self.args_from(context)
18
+ { ability: context.current_ability, user: ModuleUtils.try_to(context, :current_user) }
19
+ end
20
+
21
+ # @!attribute [r] ability
22
+ # @return [Ability]
23
+ attr_reader :ability
24
+
25
+ def initialize(ability:, user: nil)
26
+ @ability = ability
27
+ @user = user
28
+ end
29
+
30
+ # Check user's permission for an action on given subject.
31
+ # This method will be used in controller.
32
+ # @param action [Symbol, String]
33
+ # @param subject [Object, Class]
34
+ # @raise [Wallaby::Forbidden] when user is not authorized to perform the action.
35
+ def authorize(action, subject)
36
+ ability.authorize! action, subject
37
+ rescue ::CanCan::AccessDenied
38
+ Rails.logger.info I18n.t('errors.unauthorized', user: user, action: action, subject: subject)
39
+ raise Forbidden
40
+ end
41
+
42
+ # Check and see if user is allowed to perform an action on given subject.
43
+ # @param action [Symbol, String]
44
+ # @param subject [Object, Class]
45
+ # @return [Boolean]
46
+ def authorized?(action, subject)
47
+ ability.can? action, subject
48
+ end
49
+
50
+ # Restrict user to access certain scope.
51
+ # @param action [Symbol, String]
52
+ # @param scope [Object]
53
+ # @return [Object]
54
+ def accessible_for(action, scope)
55
+ ModuleUtils.try_to(scope, :accessible_by, ability, action) || scope
56
+ end
57
+
58
+ # Restrict user to assign certain values.
59
+ # @param action [Symbol, String]
60
+ # @param subject [Object]
61
+ # @return nil
62
+ delegate :attributes_for, to: :ability
63
+
64
+ # Just return nil
65
+ # @param action [Symbol, String]
66
+ # @param subject [Object]
67
+ # @return [nil]
68
+ def permit_params(action, subject)
69
+ # Do nothing
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ # Default authorization provider
5
+ class DefaultAuthorizationProvider < ModelAuthorizationProvider
6
+ # Always available.
7
+ # @param _context [ActionController::Base]
8
+ # @return [true]
9
+ def self.available?(_context)
10
+ true
11
+ end
12
+
13
+ # This will pull out the args required for contruction from context
14
+ # @param _context [ActionController::Base]
15
+ # @return [Hash] args for initialize
16
+ def self.args_from(_context)
17
+ {}
18
+ end
19
+
20
+ # Do nothing
21
+ # @param _action [Symbol, String]
22
+ # @param subject [Object, Class]
23
+ def authorize(_action, subject)
24
+ subject
25
+ end
26
+
27
+ # Always return true
28
+ # @param _action [Symbol, String]
29
+ # @param _subject [Object, Class]
30
+ # @return [true]
31
+ def authorized?(_action, _subject)
32
+ true
33
+ end
34
+
35
+ # Do nothing
36
+ # @param _action [Symbol, String]
37
+ # @param scope [Object]
38
+ def accessible_for(_action, scope)
39
+ scope
40
+ end
41
+
42
+ # Return empty attributes
43
+ # @param _action [Symbol, String]
44
+ # @param _subject [Object]
45
+ # @return [Hash] empty hash
46
+ def attributes_for(_action, _subject)
47
+ {}
48
+ end
49
+
50
+ # @note Please make sure to return nil when the authorization doesn't support this feature.
51
+ # @param _action [Symbol, String]
52
+ # @param _subject [Object]
53
+ # @return [nil]
54
+ def permit_params(_action, _subject)
55
+ # Do nothing
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ # Model Authorizer to provide authorization functions
5
+ # @since 5.2.0
6
+ class ModelAuthorizer
7
+ extend Baseable::ClassMethods
8
+
9
+ class << self
10
+ # @!attribute [w] model_class
11
+ attr_writer :model_class
12
+
13
+ # @!attribute [r] model_class
14
+ # Return associated model class, e.g. return **Product** for **ProductAuthorizer**.
15
+ #
16
+ # If Wallaby can't recognise the model class for Authorizer, it's required to be configured as below example:
17
+ # @example To configure model class
18
+ # class Admin::ProductAuthorizer < Admin::ApplicationAuthorizer
19
+ # self.model_class = Product
20
+ # end
21
+ # @example To configure model class for version below 5.2.0
22
+ # class Admin::ProductAuthorizer < Admin::ApplicationAuthorizer
23
+ # def self.model_class
24
+ # Product
25
+ # end
26
+ # end
27
+ # @return [Class] assoicated model class
28
+ # @return [nil] if current class is marked as base class
29
+ # @return [nil] if current class is the same as the value of {Wallaby::Configuration::Mapping#model_authorizer}
30
+ # @return [nil] if current class is {Wallaby::ModelAuthorizer}
31
+ # @return [nil] if assoicated model class is not found
32
+ def model_class
33
+ return unless self < ModelAuthorizer
34
+ return if base_class? || self == Wallaby.configuration.mapping.model_authorizer
35
+
36
+ @model_class ||= Map.model_class_map(name.gsub(/(^#{namespace}::)|(Authorizer$)/, EMPTY_STRING))
37
+ end
38
+
39
+ # @!attribute [w] provider_name
40
+ attr_writer :provider_name
41
+
42
+ # @!attribute [r] provider_name
43
+ # @return [String, Symbol] provider name of the authorization framework used
44
+ def provider_name
45
+ @provider_name ||= ModuleUtils.try_to superclass, :provider_name
46
+ end
47
+
48
+ # Factory method to create the model authorizer
49
+ # @param context [ActionController::Base]
50
+ # @param model_class [Class]
51
+ # @return [Wallaby::ModelAuthorizer]
52
+ def create(context, model_class)
53
+ model_class ||= self.model_class
54
+ provider_class = guess_provider_class context, model_class
55
+ new model_class, provider_class, provider_class.args_from(context)
56
+ end
57
+
58
+ private
59
+
60
+ # @param context [ActionController::Base]
61
+ # @param model_class [Class]
62
+ # @return [Class] provider class
63
+ def guess_provider_class(context, model_class)
64
+ providers = Map.authorizer_provider_map model_class
65
+ providers[provider_name] || providers.values.find { |klass| klass.available? context }
66
+ end
67
+ end
68
+
69
+ delegate(*ModelAuthorizationProvider.instance_methods(false), to: :@provider)
70
+
71
+ # @!attribute [r] model_class
72
+ # @return [Class]
73
+ attr_reader :model_class
74
+
75
+ # @!attribute [r] provider
76
+ # @return [Wallaby::ModelAuthorizationProvider]
77
+ # @since 5.2.0
78
+ attr_reader :provider
79
+
80
+ # @param model_class [Class]
81
+ # @param provider_name_or_class [String, Symbol, Class]
82
+ # @param options [Hash]
83
+ def initialize(model_class, provider_name_or_class, options = {})
84
+ @model_class = model_class || self.class.model_class
85
+ @provider = init_provider provider_name_or_class, options
86
+ end
87
+
88
+ protected
89
+
90
+ # Go through provider list and detect which provider is used.
91
+ # @param provider_name_or_class [String, Symbol, Class]
92
+ # @param options [Hash]
93
+ # @return [Wallaby::Authorizer]
94
+ def init_provider(provider_name_or_class, options)
95
+ providers = Map.authorizer_provider_map model_class
96
+ provider_class = provider_name_or_class.is_a?(Class) ? provider_name_or_class : providers[provider_name_or_class]
97
+ provider_class.new(**options)
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ # Pundit base authorization provider
5
+ class PunditAuthorizationProvider < ModelAuthorizationProvider
6
+ # Detect and see if Pundit is in use.
7
+ # @param context [ActionController::Base]
8
+ # @return [true] if Pundit is in use.
9
+ # @return [false] if Pundit is not in use.
10
+ def self.available?(context)
11
+ defined?(Pundit) && context.respond_to?(:pundit_user)
12
+ end
13
+
14
+ # This will pull out the args required for contruction from context
15
+ # @param context [ActionController::Base]
16
+ # @return [Hash] args for initialize
17
+ def self.args_from(context)
18
+ { user: context.pundit_user }
19
+ end
20
+
21
+ # @param user [Object]
22
+ def initialize(user:)
23
+ @user = user
24
+ end
25
+
26
+ # Check user's permission for an action on given subject.
27
+ #
28
+ # This method will be used in controller.
29
+ # @param action [Symbol, String]
30
+ # @param subject [Object, Class]
31
+ # @raise [Wallaby::Forbidden] when user is not authorized to perform the action.
32
+ def authorize(action, subject)
33
+ Pundit.authorize(user, subject, normalize(action)) && subject
34
+ rescue ::Pundit::NotAuthorizedError
35
+ Rails.logger.info I18n.t('errors.unauthorized', user: user, action: action, subject: subject)
36
+ raise Forbidden
37
+ end
38
+
39
+ # Check and see if user is allowed to perform an action on given subject
40
+ # @param action [Symbol, String]
41
+ # @param subject [Object, Class]
42
+ # @return [Boolean]
43
+ def authorized?(action, subject)
44
+ policy = Pundit.policy! user, subject
45
+ ModuleUtils.try_to policy, normalize(action)
46
+ end
47
+
48
+ # Restrict user to assign certain values.
49
+ #
50
+ # It will do a lookup in policy's methods and pick the first available method:
51
+ #
52
+ # - attributes\_for\_#\{ action \}
53
+ # - attributes\_for
54
+ # @param action [Symbol, String]
55
+ # @param subject [Object]
56
+ # @return [Hash] field value paired hash that user's allowed to assign
57
+ def attributes_for(action, subject)
58
+ policy = Pundit.policy! user, subject
59
+ value = ModuleUtils.try_to(policy, "attributes_for_#{action}") || ModuleUtils.try_to(policy, 'attributes_for')
60
+ Rails.logger.warn I18n.t('error.pundit.not_found.attributes_for', subject: subject) unless value
61
+ value || {}
62
+ end
63
+
64
+ # Restrict user for mass assignment.
65
+ #
66
+ # It will do a lookup in policy's methods and pick the first available method:
67
+ #
68
+ # - permitted\_attributes\_for\_#\{ action \}
69
+ # - permitted\_attributes
70
+ # @param action [Symbol, String]
71
+ # @param subject [Object]
72
+ # @return [Array] field list that user's allowed to change.
73
+ def permit_params(action, subject)
74
+ policy = Pundit.policy! user, subject
75
+ # @see https://github.com/varvet/pundit/blob/master/lib/pundit.rb#L258
76
+ ModuleUtils.try_to(policy, "permitted_attributes_for_#{action}") \
77
+ || ModuleUtils.try_to(policy, 'permitted_attributes')
78
+ end
79
+
80
+ private
81
+
82
+ # Convert action to pundit method name
83
+ # @param action [Symbol, String]
84
+ # @return [String] e.g. `create?`
85
+ def normalize(action)
86
+ "#{action}?".tr('??', '?')
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ # Authorizer related attributes
5
+ module Authorizable
6
+ # Configurable attribute for authorizer related
7
+ module ClassMethods
8
+ # @!attribute [w] model_authorizer
9
+ def model_authorizer=(model_authorizer)
10
+ ModuleUtils.inheritance_check model_authorizer, application_authorizer
11
+ @model_authorizer = model_authorizer
12
+ end
13
+
14
+ # @!attribute [r] model_authorizer
15
+ # If Wallaby doesn't get it right, please specify the **model_authorizer**.
16
+ # @example To set model authorizer
17
+ # class Admin::ProductionsController < Admin::ApplicationController
18
+ # self.model_authorizer = ProductAuthorizer
19
+ # end
20
+ # @return [Class] model authorizer
21
+ # @raise [ArgumentError] when **model_authorizer** doesn't inherit from **application_authorizer**
22
+ # @see Wallaby::ModelAuthorizer
23
+ # @since 5.2.0
24
+ attr_reader :model_authorizer
25
+
26
+ # @!attribute [w] application_authorizer
27
+ def application_authorizer=(application_authorizer)
28
+ ModuleUtils.inheritance_check model_authorizer, application_authorizer
29
+ @application_authorizer = application_authorizer
30
+ end
31
+
32
+ # @!attribute [r] application_authorizer
33
+ # The **application_authorizer** is as the base class of {#model_authorizer}.
34
+ # @example To set application decorator:
35
+ # class Admin::ApplicationController < Wallaby::ResourcesController
36
+ # self.application_authorizer = AnotherApplicationAuthorizer
37
+ # end
38
+ # @return [Class] application decorator
39
+ # @raise [ArgumentError] when **model_authorizer** doesn't inherit from **application_authorizer**
40
+ # @see Wallaby::ModelAuthorizer
41
+ # @since 5.2.0
42
+ def application_authorizer
43
+ @application_authorizer ||= ModuleUtils.try_to superclass, :application_authorizer
44
+ end
45
+ end
46
+
47
+ # Model authorizer for current modal class.
48
+ #
49
+ # It can be configured in following class attributes:
50
+ #
51
+ # - controller configuration {Wallaby::Authorizable::ClassMethods#model_authorizer .model_authorizer}
52
+ # - a generic authorizer based on
53
+ # {Wallaby::Authorizable::ClassMethods#application_authorizer .application_authorizer}
54
+ # @return [Wallaby::ModelAuthorizer] model authorizer
55
+ # @since 5.2.0
56
+ def current_authorizer
57
+ @current_authorizer ||=
58
+ authorizer_of(current_model_class, controller_to_get(:model_authorizer)).tap do |authorizer|
59
+ Rails.logger.info %( - Current authorizer: #{authorizer.try(:class)})
60
+ end
61
+ end
62
+
63
+ # Check if user is allowed to perform action on given subject
64
+ # @param action [Symbol, String]
65
+ # @param subject [Object, Class]
66
+ # @return [true] if allowed
67
+ # @return [false] if not allowed
68
+ # @since 5.2.0
69
+ def authorized?(action, subject)
70
+ return false unless subject
71
+
72
+ klass = subject.is_a?(Class) ? subject : subject.class
73
+ authorizer_of(klass).authorized? action, subject
74
+ end
75
+
76
+ # Check if user is allowed to perform action on given subject
77
+ # @param action [Symbol, String]
78
+ # @param subject [Object, Class]
79
+ # @return [true] if not allowed
80
+ # @return [false] if allowed
81
+ # @since 5.2.0
82
+ def unauthorized?(action, subject)
83
+ !authorized? action, subject
84
+ end
85
+
86
+ # @deprecated Use {#current_authorizer} instead. It will be removed from 5.3.*
87
+ def authorizer
88
+ Utils.deprecate 'deprecation.authorizer', caller: caller
89
+ current_authorizer
90
+ end
91
+
92
+ protected
93
+
94
+ # @param model_class [Class]
95
+ # @param authorizer_class [Class, nil]
96
+ # @return [Wallaby::ModelAuthorizer] model authorizer for given model
97
+ # @since 5.2.0
98
+ def authorizer_of(model_class, authorizer_class = nil)
99
+ authorizer_class ||= Map.authorizer_map(model_class, controller_to_get(:application_authorizer))
100
+ authorizer_class.try :create, self, model_class
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ # Abstract related class methods
5
+ module Baseable
6
+ # Configurable attributes and class methods for marking a class the base class
7
+ # and skipping {Wallaby::Map Wallaby mapping}
8
+ module ClassMethods
9
+ # @return [true] if class is a base class
10
+ # @return [false] if class is not a base class
11
+ def base_class?
12
+ @base_class ||= false
13
+ end
14
+
15
+ # Mark class a base class
16
+ # @return [true]
17
+ def base_class!
18
+ @base_class = true
19
+ end
20
+
21
+ # @!attribute [w] namespace
22
+ # Used by `model_class`
23
+ # @since 5.2.0
24
+ attr_writer :namespace
25
+
26
+ # @!attribute [r] namespace
27
+ # @return [String] namespace
28
+ # @since 5.2.0
29
+ def namespace
30
+ @namespace ||=
31
+ ModuleUtils.try_to(superclass, :namespace) \
32
+ || name.deconstantize.gsub(/Wallaby(::)?/, EMPTY_STRING).presence
33
+ end
34
+ end
35
+ end
36
+ end