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,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