wallaby-core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ # Resolver to provide support for cell and partial
5
+ # @since 5.2.0
6
+ class CellResolver < ActionView::OptimizedFileSystemResolver
7
+ # for Rails 5.2 and below
8
+ begin
9
+ # @note this method is only applicable to Rails 5.2 and below
10
+ # A cell query looks like:
11
+ #
12
+ # ```
13
+ # app/views/wallaby/resources/index/integer{_en,}{_html,}.rb
14
+ # ```
15
+ #
16
+ # Wallaby adds it to the front of the whole query as below:
17
+ #
18
+ # ```
19
+ # {app/views/wallaby/resources/index/integer{_en,}{_html,}.rb,
20
+ # app/views/wallaby/resources/index/_integer{.en,}{.html,}{.erb,}}
21
+ # ```
22
+ # @param path [String]
23
+ # @param details [Hash]
24
+ # see {https://api.rubyonrails.org/classes/ActionView/LookupContext/ViewPaths.html#method-i-detail_args_for
25
+ # Detials from ViewPaths}
26
+ # @return [String] a path query
27
+ def build_query(path, details)
28
+ # NOTE: super is impacted by {#escape_entry}
29
+ origin = super
30
+ file_name = origin[%r{(?<=/\{,_\})[^/\{]+}]
31
+ return origin unless file_name
32
+
33
+ base_dir = origin.gsub(%r{/[^/]*$}, '')
34
+ locales = convert details[:locale]
35
+ formats = convert details[:formats]
36
+ cell = "#{base_dir}/#{file_name}{#{locales}}{#{formats}}.rb"
37
+ "{#{cell},#{origin}}"
38
+ end
39
+ end
40
+
41
+ # for Rails 6 and above
42
+ begin
43
+ # @note this method is only applicable to Rails 6 and above
44
+ # This is to extend the origin functionality to enable to return cell file.
45
+ # at highest precedence.
46
+ # @param path [String]
47
+ # @param details [Hash]
48
+ # @return [ActionView::Template] found template
49
+ def find_template_paths_from_details(path, details)
50
+ details[:handlers].unshift(:rb) if details[:handlers].try(:first) != :rb
51
+ super
52
+ end
53
+
54
+ # @note this method is only applicable to Rails 6 and above
55
+ # This is to extend the origin functionality to enable to query cell files.
56
+ # @param path [String]
57
+ # @param details [Hash]
58
+ # @return [Regexp]
59
+ def build_regex(path, details)
60
+ origin = super.source
61
+ Regexp.new(
62
+ origin
63
+ .gsub(%r{/\{,_\}([^/]+)\z}, '/_?\\1')
64
+ .gsub('\\.', '[_\\.]')
65
+ .gsub('raw|', 'rb|raw|')
66
+ )
67
+ end
68
+ end
69
+
70
+ # This is to extend the origin funcationality to enable the query
71
+ # to look for cell files
72
+ # @example extend the query
73
+ # escape_entry('integer') # => '/{,_}integer'
74
+ # @param entry [String]
75
+ # @return [String] an escaped and extended query
76
+ def escape_entry(entry)
77
+ super.gsub(%r{/_([^/]+)\z}, '/{,_}\1')
78
+ end
79
+
80
+ private
81
+
82
+ # @example concat a list of values into a string
83
+ # convert(['html', 'csv']) # => '_html,_cvs,'
84
+ # @param values [Array<String>]
85
+ def convert(values)
86
+ (values.map { |v| "_#{v}" } << '').join ','
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ # A custom lookup context that uses {Wallaby::CellResolver} to find cell/partial
5
+ class CustomLookupContext < ::ActionView::LookupContext
6
+ def self.normalize(lookup, details: nil, prefixes: nil)
7
+ return lookup if lookup.is_a? self
8
+
9
+ CustomLookupContext.new(
10
+ lookup.view_paths,
11
+ details || lookup.instance_variable_get('@details'),
12
+ prefixes || lookup.prefixes
13
+ )
14
+ end
15
+
16
+ # @note for Rails 6 and above
17
+ # It overrides the origin method to convert paths to {Wallaby::CellResolver}
18
+ # @param paths [Array]
19
+ # @return [ActionView::PathSet]
20
+ def build_view_paths(paths)
21
+ ActionView::PathSet.new Array(paths).map(&method(:convert))
22
+ end
23
+
24
+ # @note for Rails 5.2 and below
25
+ # It overrides the origin method to convert paths to {Wallaby::CellResolver}
26
+ # @param paths [Array]
27
+ # @return [ActionView::PathSet]
28
+ def view_paths=(paths)
29
+ @view_paths = build_view_paths paths
30
+ end
31
+
32
+ # It overrides the oirgin method to call the origin `find_template` and cache the result during a request.
33
+ # @param name [String]
34
+ # @param prefixes [Array<String>]
35
+ # @param partial [Boolean]
36
+ # @param keys [Array<String>] keys of local variables
37
+ # @param options [Hash]
38
+ def find_template(name, prefixes = [], partial = false, keys = [], options = {})
39
+ prefixes = [] if partial && name.include?(SLASH) # reset the prefixes if `/` is detected
40
+ key = [name, prefixes, partial, keys, options].map(&:inspect).join(SLASH)
41
+ cached_lookup[key] ||= super
42
+ end
43
+
44
+ protected
45
+
46
+ # @!attribute [r] cached_lookup
47
+ # Cached lookup result
48
+ def cached_lookup
49
+ @cached_lookup ||= {}
50
+ end
51
+
52
+ # Convert path to {Wallaby::CellResolver}
53
+ # @param path [Object]
54
+ # @return [Wallaby::CellResolver]
55
+ def convert(path)
56
+ case path
57
+ when ActionView::OptimizedFileSystemResolver, Pathname, String
58
+ CellResolver.new path.to_s
59
+ else
60
+ path
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ # Custom partial renderer to provide support for cell rendering
5
+ class CustomPartialRenderer < ::ActionView::PartialRenderer
6
+ # When a type partial is found, it works as usual.
7
+ #
8
+ # But when a cell is found, there is an exception {Wallaby::CellHandling} raised. This error will be captured,
9
+ # and the cell will be rendered.
10
+ # @param context [ActionView::Context]
11
+ # @param options [Hash]
12
+ # @param block [Proc]
13
+ # @return [String] HTML output
14
+ def render(context, options, block)
15
+ super.try do |rendered|
16
+ ModuleUtils.try_to(rendered, :body) || # Rails 6 and above
17
+ rendered # Rails 5.2 and below
18
+ end
19
+ rescue CellHandling => e
20
+ CellUtils.render context, e.message, options[:locals], &block
21
+ end
22
+
23
+ # Override origin method to stop rendering when a cell is found.
24
+ # @return [ActionView::Template] partial template
25
+ # @raise [Wallaby:::CellHandling] when a cell is found
26
+ def find_partial(*)
27
+ super.tap do |partial|
28
+ cell = CellUtils.find_cell(partial.identifier, partial.inspect)
29
+ raise CellHandling, cell if cell
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ # Custom view renderer to provide support for cell rendering
5
+ class CustomRenderer < ::ActionView::Renderer
6
+ def initialize(lookup_context)
7
+ super CustomLookupContext.normalize(lookup_context)
8
+ end
9
+
10
+ # @return [String] HTML output
11
+ # @see Wallaby::CustomPartialRenderer
12
+ def render_partial(context, options, &block) #:nodoc:
13
+ CustomPartialRenderer.new(lookup_context).render(context, options, block)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ # Easter egg: simple responder for JSON API
5
+ class JsonApiResponder < ResourcesResponder
6
+ delegate :params, :headers, to: :request
7
+
8
+ # @see #to_json
9
+ def to_html
10
+ to_json
11
+ end
12
+
13
+ # @return [String] JSON
14
+ def to_json(*)
15
+ json_options = { content_type: 'application/vnd.api+json', status: options[:status] }
16
+ if exception?
17
+ render json_options.merge(json: exception_details)
18
+ else
19
+ render json_options.merge(action_options)
20
+ end
21
+ end
22
+
23
+ protected
24
+
25
+ def action_options
26
+ if !get? && has_errors?
27
+ { json: resource_errors, status: :unprocessable_entity }
28
+ elsif index?
29
+ { json: collection_data }
30
+ else
31
+ { json: resource_data }
32
+ end
33
+ end
34
+
35
+ def single(resource)
36
+ {
37
+ id: resource.id,
38
+ type: params[:resources],
39
+ attributes: attributes_of(resource)
40
+ }
41
+ end
42
+
43
+ def collection_data
44
+ {
45
+ data: resource.map(&method(:single)),
46
+ links: {
47
+ self: controller.url_for(resources: params[:resources], action: 'index')
48
+ }
49
+ }
50
+ end
51
+
52
+ def resource_data
53
+ {
54
+ data: single(resource),
55
+ links: {
56
+ self: controller.url_for(resources: params[:resources], action: 'show', id: resource.id)
57
+ }
58
+ }
59
+ end
60
+
61
+ def resource_errors
62
+ decorated = controller.decorate resource
63
+ {
64
+ errors: decorated.errors.each_with_object([]) do |(field, message), json|
65
+ json.push(
66
+ status: 422,
67
+ source: { pointer: "/data/attributes/#{field}" },
68
+ detail: message
69
+ )
70
+ end
71
+ }
72
+ end
73
+
74
+ def attributes_of(resource)
75
+ decorated = controller.decorate resource
76
+ field_names = index? ? decorated.index_field_names : decorated.show_field_names
77
+ field_names.each_with_object({}) do |name, attributes|
78
+ attributes[name] = decorated.public_send name
79
+ end
80
+ end
81
+
82
+ def exception_details
83
+ {
84
+ errors: [
85
+ {
86
+ status: options[:status],
87
+ detail: resource.try(:message)
88
+ }
89
+ ]
90
+ }
91
+ end
92
+
93
+ def index?
94
+ params[:action] == 'index'
95
+ end
96
+
97
+ def exception?
98
+ (resource.nil? || resource.is_a?(Exception)) && options[:template] == ERROR_PATH
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ # Resources responder
5
+ class ResourcesResponder < ActionController::Responder
6
+ include ::Responders::FlashResponder
7
+
8
+ # Render CSV with a file name that contains export timestamp.
9
+ def to_csv
10
+ controller.headers['Content-Disposition'] = "attachment; filename=\"#{file_name_to_export}\""
11
+ default_render
12
+ end
13
+
14
+ protected
15
+
16
+ # @return [String] file name with export timestamp
17
+ def file_name_to_export
18
+ timestamp = Time.zone.now.to_s(:number)
19
+ "#{request.params[:resources]}-exported-#{timestamp}.#{format}"
20
+ end
21
+
22
+ # @return [Boolean] true if there is exception or resource has errors
23
+ # @return [Boolean] false otherwise
24
+ def has_errors? # rubocop:disable Naming/PredicateName
25
+ resource.nil? || resource.is_a?(Exception) || controller.decorate(resource).errors.present?
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ # This is the core of wallaby that dynamically dispatches request to appropriate controller and action.
5
+ class ResourcesRouter
6
+ # @see http://edgeguides.rubyonrails.org/routing.html#routing-to-rack-applications
7
+ # It tries to find out the controller that has the same model class from converted resources name.
8
+ # Otherwise, it falls back to base resources controller which will come from the following sources:
9
+ #
10
+ # 1. `:resources_controller` parameter
11
+ # 2. resources_controller mapping configuration,
12
+ # e.g. `Admin::ApplicationController` if defined or `Wallaby::ResourcesController`
13
+ # @param env [Hash] @see http://www.rubydoc.info/github/rack/rack/master/file/SPEC
14
+ def call(env)
15
+ params = env[ActionDispatch::Http::Parameters::PARAMETERS_KEY]
16
+ controller = find_controller_by params
17
+ controller.action(params[:action]).call env
18
+ rescue ::AbstractController::ActionNotFound, ModelNotFound => e
19
+ set_message_for e, env
20
+ default_controller(params).action(:not_found).call env
21
+ rescue UnprocessableEntity => e
22
+ set_message_for e, env
23
+ default_controller(params).action(:unprocessable_entity).call env
24
+ end
25
+
26
+ private
27
+
28
+ # Find controller class
29
+ # @param params [Hash]
30
+ # @return [Class] controller class
31
+ def find_controller_by(params)
32
+ model_class = find_model_class_by params
33
+ Map.controller_map(model_class, params[:resources_controller]) || default_controller(params)
34
+ end
35
+
36
+ # Default controller class sources from:
37
+ #
38
+ # 1. `:resources_controller` parameter
39
+ # 2. resources_controller mapping configuration,
40
+ # @param params [Hash]
41
+ # @return [Class] controller class
42
+ def default_controller(params)
43
+ params[:resources_controller] || Wallaby.configuration.mapping.resources_controller
44
+ end
45
+
46
+ # Find out the model class
47
+ # @param params [Hash]
48
+ # @return [Class]
49
+ # @raise [Wallaby::ModelNotFound] when model class is not found
50
+ # @raise [Wallaby::UnprocessableEntity] when there is no corresponding mode found for model class
51
+ def find_model_class_by(params)
52
+ model_class = Map.model_class_map params[:resources]
53
+ return model_class unless MODEL_ACTIONS.include? params[:action].to_sym
54
+ raise ModelNotFound, params[:resources] unless model_class
55
+ unless Map.mode_map[model_class]
56
+ raise UnprocessableEntity, I18n.t('errors.unprocessable_entity.model', model: model_class)
57
+ end
58
+
59
+ model_class
60
+ end
61
+
62
+ # Set flash error message
63
+ # @param exception [Exception]
64
+ # @param env [Hash] @see http://www.rubydoc.info/github/rack/rack/master/file/SPEC
65
+ def set_message_for(exception, env)
66
+ session = env[ActionDispatch::Request::Session::ENV_SESSION_KEY] || {}
67
+ env[ActionDispatch::Flash::KEY] ||= ActionDispatch::Flash::FlashHash.from_session_value session['flash']
68
+ flash = env[ActionDispatch::Flash::KEY]
69
+ flash[:alert] = exception.message
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,154 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ # Model servicer contains resourceful operations for Rails resourceful actions.
5
+ class ModelServicer
6
+ extend Baseable::ClassMethods
7
+
8
+ class << self
9
+ # @!attribute [w] model_class
10
+ attr_writer :model_class
11
+
12
+ # @!attribute [r] model_class
13
+ # Return associated model class, e.g. return **Product** for **ProductServicer**.
14
+ #
15
+ # If Wallaby can't recognise the model class for Servicer, it's required to be configured as below example:
16
+ # @example To configure model class
17
+ # class Admin::ProductServicer < Admin::ApplicationServicer
18
+ # self.model_class = Product
19
+ # end
20
+ # @example To configure model class for version below 5.2.0
21
+ # class Admin::ProductServicer < Admin::ApplicationServicer
22
+ # def self.model_class
23
+ # Product
24
+ # end
25
+ # end
26
+ # @return [Class] assoicated model class
27
+ # @return [nil] if current class is marked as base class
28
+ # @return [nil] if current class is the same as the value of {Wallaby::Configuration::Mapping#model_servicer}
29
+ # @return [nil] if current class is {Wallaby::ModelServicer}
30
+ # @return [nil] if assoicated model class is not found
31
+ def model_class
32
+ return unless self < ModelServicer
33
+ return if base_class? || self == Wallaby.configuration.mapping.model_servicer
34
+
35
+ @model_class ||= Map.model_class_map(name.gsub(/(^#{namespace}::)|(Servicer$)/, EMPTY_STRING))
36
+ end
37
+
38
+ # @!attribute provider_class
39
+ # @return [Class] service provider class
40
+ # @since 5.2.0
41
+ attr_accessor :provider_class
42
+ end
43
+
44
+ # @!attribute [r] model_class
45
+ # @return [Class]
46
+ attr_reader :model_class
47
+
48
+ # @!attribute [r] model_decorator
49
+ # @return [Wallaby::ModelDecorator]
50
+ # @since 5.2.0
51
+ attr_reader :model_decorator
52
+
53
+ # @!attribute [r] authorizer
54
+ # @return [Wallaby::ModelAuthorizer]
55
+ # @since 5.2.0
56
+ attr_reader :authorizer
57
+
58
+ # @!attribute [r] provider
59
+ # @return [Wallaby::ModelServiceProvider]
60
+ # @since 5.2.0
61
+ attr_reader :provider
62
+
63
+ # @!method user
64
+ # @return [Object]
65
+ # @since 5.2.0
66
+ delegate :user, to: :authorizer
67
+
68
+ # During initialization, Wallaby will assign a service provider for this servicer
69
+ # to carry out the actual execution.
70
+ #
71
+ # Therefore, all its actions can be completely replaced by user's own implemnetation.
72
+ # @param model_class [Class]
73
+ # @param authorizer [Wallaby::ModelAuthorizer]
74
+ # @param model_decorator [Wallaby::ModelDecorator]
75
+ # @raise [ArgumentError] if param model_class is blank
76
+ def initialize(model_class, authorizer, model_decorator = nil)
77
+ @model_class = model_class || self.class.model_class
78
+ raise ArgumentError, I18n.t('errors.required', subject: 'model_class') unless @model_class
79
+
80
+ @model_decorator = model_decorator || Map.model_decorator_map(model_class)
81
+ @authorizer = authorizer
82
+ provider_class = self.class.provider_class || Map.service_provider_map(@model_class)
83
+ @provider = provider_class.new(@model_class, @model_decorator)
84
+ end
85
+
86
+ # @note This is a template method that can be overridden by subclasses.
87
+ # Whitelist parameters for mass assignment.
88
+ # @param params [ActionController::Parameters, Hash]
89
+ # @param action [String, Symbol]
90
+ # @return [ActionController::Parameters] permitted params
91
+ def permit(params, action = nil)
92
+ provider.permit params, action, authorizer
93
+ end
94
+
95
+ # @note This is a template method that can be overridden by subclasses.
96
+ # Return a collection by querying the datasource (e.g. database, REST API).
97
+ # @param params [ActionController::Parameters, Hash]
98
+ # @return [Enumerable] list of records
99
+ def collection(params)
100
+ provider.collection params, authorizer
101
+ end
102
+
103
+ # @note This is a template method that can be overridden by subclasses.
104
+ # Paginate given {#collection}.
105
+ # @param query [Enumerable]
106
+ # @param params [ActionController::Parameters]
107
+ # @return [Enumerable] list of records
108
+ delegate :paginate, to: :provider
109
+
110
+ # @note This is a template method that can be overridden by subclasses.
111
+ # Initialize an instance of the model class.
112
+ # @param params [ActionController::Parameters]
113
+ # @return [Object] initialized object
114
+ def new(params)
115
+ provider.new params, authorizer
116
+ end
117
+
118
+ # @note This is a template method that can be overridden by subclasses.
119
+ # To find a record.
120
+ # @param id [Object]
121
+ # @param params [ActionController::Parameters]
122
+ # @return [Object] resource object
123
+ def find(id, params)
124
+ provider.find id, params, authorizer
125
+ end
126
+
127
+ # @note This is a template method that can be overridden by subclasses.
128
+ # To create a record.
129
+ # @param resource [Object]
130
+ # @param params [ActionController::Parameters]
131
+ # @return [Object] resource object
132
+ def create(resource, params)
133
+ provider.create resource, params, authorizer
134
+ end
135
+
136
+ # @note This is a template method that can be overridden by subclasses.
137
+ # To update a record.
138
+ # @param resource [Object]
139
+ # @param params [ActionController::Parameters]
140
+ # @return [Object] resource object
141
+ def update(resource, params)
142
+ provider.update resource, params, authorizer
143
+ end
144
+
145
+ # @note This is a template method that can be overridden by subclasses.
146
+ # To delete a record.
147
+ # @param resource [Object]
148
+ # @param params [ActionController::Parameters]
149
+ # @return [Object] resource object
150
+ def destroy(resource, params)
151
+ provider.destroy resource, params, authorizer
152
+ end
153
+ end
154
+ end