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,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ # Servicer related attributes
5
+ module Servicable
6
+ # Configurable attribute
7
+ module ClassMethods
8
+ # @!attribute [w] model_servicer
9
+ def model_servicer=(model_servicer)
10
+ ModuleUtils.inheritance_check model_servicer, application_servicer
11
+ @model_servicer = model_servicer
12
+ end
13
+
14
+ # @!attribute [r] model_servicer
15
+ # If Wallaby doesn't get it right, please specify the **model_servicer**.
16
+ # @example To set model servicer
17
+ # class Admin::ProductionsController < Admin::ApplicationController
18
+ # self.model_servicer = ProductServicer
19
+ # end
20
+ # @return [Class] model servicer
21
+ # @raise [ArgumentError] when **model_servicer** doesn't inherit from **application_servicer**
22
+ # @see Wallaby::ModelServicer
23
+ # @since 5.2.0
24
+ attr_reader :model_servicer
25
+
26
+ # @!attribute [w] application_servicer
27
+ def application_servicer=(application_servicer)
28
+ ModuleUtils.inheritance_check model_servicer, application_servicer
29
+ @application_servicer = application_servicer
30
+ end
31
+
32
+ # @!attribute [r] application_servicer
33
+ # The **application_servicer** is as the base class of {#model_servicer}.
34
+ # @example To set application decorator:
35
+ # class Admin::ApplicationController < Wallaby::ResourcesController
36
+ # self.application_servicer = AnotherApplicationServicer
37
+ # end
38
+ # @return [Class] application decorator
39
+ # @raise [ArgumentError] when **model_servicer** doesn't inherit from **application_servicer**
40
+ # @see Wallaby::ModelServicer
41
+ # @since 5.2.0
42
+ def application_servicer
43
+ @application_servicer ||= ModuleUtils.try_to superclass, :application_servicer
44
+ end
45
+ end
46
+
47
+ # Model servicer for current modal class. It comes from:
48
+ #
49
+ # - controller configuration {Wallaby::Servicable::ClassMethods#model_servicer .model_servicer}
50
+ # - a generic servicer based on {Wallaby::Servicable::ClassMethods#application_servicer .application_servicer}
51
+ # @return [Wallaby::ModelServicer] model servicer
52
+ # @since 5.2.0
53
+ def current_servicer
54
+ @current_servicer ||=
55
+ (controller_to_get(:model_servicer) \
56
+ || Map.servicer_map(current_model_class, controller_to_get(:application_servicer))).try do |klass|
57
+ Rails.logger.info %( - Current servicer: #{klass})
58
+ klass.new current_model_class, current_authorizer, current_model_decorator
59
+ end
60
+ end
61
+
62
+ # @deprecated Use {#current_servicer} instead. It will be removed from 5.3.*
63
+ def current_model_service
64
+ Utils.deprecate 'deprecation.current_model_service', caller: caller
65
+ current_servicer
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ # Shared helpers
5
+ module SharedHelpers
6
+ protected
7
+
8
+ # Fetch value for given attribute.
9
+ #
10
+ # If it's used in controller, it will fetch it from class attribute.
11
+ # If it's used in view, it will fetch it from controller.
12
+ # @param attribute_name [String, Symbol] instance attribute name
13
+ # @param class_attribute_name [String, Symbol] class attribute name
14
+ # @return [Object] the value
15
+ def controller_to_get(attribute_name, class_attribute_name = nil)
16
+ class_attribute_name ||= attribute_name
17
+ return ModuleUtils.try_to self.class, class_attribute_name if is_a? ::ActionController::Base # controller?
18
+
19
+ ModuleUtils.try_to controller, attribute_name # view?
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ # Theme related methods
5
+ module Themeable
6
+ # Configurable attributes
7
+ module ClassMethods
8
+ # @!attribute [w] theme_name
9
+ def theme_name=(theme_name)
10
+ layout theme_name
11
+ @theme_name = theme_name
12
+ end
13
+
14
+ # @!attribute [r] theme_name
15
+ # The theme name is used to apply a set of frontend (html/css/javascript) implementation.
16
+ #
17
+ # When theme name is set to e.g. `custom_theme`, the following changes will be made:
18
+ #
19
+ # - layout will be set to the same name `custom_theme`
20
+ # - it will be added to the partial lookup prefixes right on top of `wallaby/resources` prefix.
21
+ #
22
+ # Once theme name is set, all its controller subclasses will inherit the same theme name
23
+ # @example To set an theme name:
24
+ # class Admin::ApplicationController < Wallaby::ResourcesController
25
+ # self.theme_name = 'admin_theme'
26
+ # end
27
+ # @return [String, Symbol, nil] theme name
28
+ # @since 5.2.0
29
+ def theme_name
30
+ @theme_name ||= ModuleUtils.try_to superclass, :theme_name
31
+ end
32
+ end
33
+
34
+ # @return [String, Symbol, nil] theme name
35
+ # @since 5.2.0
36
+ def current_theme_name
37
+ controller_to_get __callee__, :theme_name
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,189 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ # Resource Decorator base class, designed for decorator pattern.
5
+ # @see Wallaby::ModelDecorator
6
+ class ResourceDecorator
7
+ extend Baseable::ClassMethods
8
+
9
+ DELEGATE_METHODS =
10
+ ModelDecorator.public_instance_methods(false) + Fieldable.public_instance_methods(false) - %i(model_class)
11
+
12
+ class << self
13
+ delegate(*DELEGATE_METHODS, to: :model_decorator, allow_nil: true)
14
+
15
+ # @!attribute [w] model_class
16
+ attr_writer :model_class
17
+
18
+ # @!attribute [r] model_class
19
+ # Return associated model class, e.g. return **Product** for **ProductDecorator**.
20
+ #
21
+ # If Wallaby can't recognise the model class for Decorator, it's required to be configured as below example:
22
+ # @example To configure model class
23
+ # class Admin::ProductDecorator < Admin::ApplicationDecorator
24
+ # self.model_class = Product
25
+ # end
26
+ # @example To configure model class for version below 5.2.0
27
+ # class Admin::ProductDecorator < Admin::ApplicationDecorator
28
+ # def self.model_class
29
+ # Product
30
+ # end
31
+ # end
32
+ # @return [Class] assoicated model class
33
+ # @return [nil] if current class is marked as base class
34
+ # @return [nil] if current class is the same as the value of {Wallaby::Configuration::Mapping#resource_decorator}
35
+ # @return [nil] if current class is {Wallaby::ResourceDecorator}
36
+ # @return [nil] if assoicated model class is not found
37
+ def model_class
38
+ return unless self < ResourceDecorator
39
+ return if base_class? || self == Wallaby.configuration.mapping.resource_decorator
40
+
41
+ @model_class ||= Map.model_class_map(name.gsub(/(^#{namespace}::)|(Decorator$)/, EMPTY_STRING))
42
+ end
43
+
44
+ # @!attribute [w] application_decorator
45
+ def application_decorator=(application_decorator)
46
+ ModuleUtils.inheritance_check self, application_decorator
47
+ @application_decorator = application_decorator
48
+ end
49
+
50
+ # @note This attribute have to be the same as the one defined in the controller in order to make things working.
51
+ # see {Wallaby::Decoratable::ClassMethods#application_decorator}
52
+ # @!attribute [r] application_decorator
53
+ # Assoicated base class.
54
+ #
55
+ # Wallaby will try to get the application decorator class from current class's ancestors chain.
56
+ # For instance, if current class is **ProductDecorator**, and it inherits from **CoreDecorator**,
57
+ # then Wallaby will return application decorator class **CoreDecorator**.
58
+ #
59
+ # However, there is a chance that Wallaby doesn't get it right.
60
+ # For instance, if **CoreDecorator** in the above example inherits from **Admin::ApplicationDecorator**, and
61
+ # the controller that needs **ProductDecorator** has set its **application_decorator** to
62
+ # **Admin::ApplicationDecorator**, then it's needed to configure **application_decorator** as the example
63
+ # describes
64
+ # @example To set application decorator class
65
+ # class ProductController < Admin::ApplicationController
66
+ # self.application_decorator = Admin::ApplicationDecorator
67
+ # end
68
+ #
69
+ # class CoreDecorator < Admin::ApplicationDecorator
70
+ # base_class!
71
+ # end
72
+ #
73
+ # class ProductDecorator < CoreDecorator
74
+ # self.application_decorator = Admin::ApplicationDecorator
75
+ # end
76
+ # @return [Class] assoicated base class.
77
+ # @return [nil] if assoicated base class is not found from its ancestors chain
78
+ # @raise [ArgumentError] when current class doesn't inherit from given value
79
+ # @since 5.2.0
80
+ def application_decorator
81
+ @application_decorator ||= ancestors.find { |parent| parent < ResourceDecorator && !parent.model_class }
82
+ end
83
+
84
+ # Return associated model decorator.
85
+ #
86
+ # Fetch model decorator instance from cached map using keys {.model_class} and {.application_decorator}
87
+ # so that model decorator can be used in its sub classes declaration/scope.
88
+ # @param model_class [Class]
89
+ # @return [Wallaby::ModelDecorator]
90
+ def model_decorator(model_class = self.model_class)
91
+ return unless self < ResourceDecorator || model_class
92
+
93
+ Map.model_decorator_map model_class, application_decorator
94
+ end
95
+
96
+ # @!attribute [w] h
97
+ attr_writer :h
98
+
99
+ # @!attribute [r] h
100
+ # @return [ActionView::Base]
101
+ # {Wallaby::Configuration::Mapping#resources_controller resources controller}'s helpers
102
+ def h
103
+ @h ||= Wallaby.configuration.mapping.resources_controller.helpers
104
+ end
105
+ end
106
+
107
+ # @!attribute [r] resource
108
+ # @return [Object]
109
+ attr_reader :resource
110
+
111
+ # @!attribute [r] model_decorator
112
+ # @return [Wallaby::ModelDecorator]
113
+ attr_reader :model_decorator
114
+
115
+ # @return [ActionView::Base]
116
+ # {Wallaby::Configuration::Mapping#resources_controller resources controller}'s helpers
117
+ # @see .h
118
+ def h
119
+ self.class.h
120
+ end
121
+
122
+ delegate(*DELEGATE_METHODS, to: :model_decorator)
123
+ # NOTE: this delegation is to make url helper method working properly with resource decorator instance
124
+ delegate :to_s, :to_param, to: :resource
125
+
126
+ # @param resource [Object]
127
+ def initialize(resource)
128
+ @resource = resource
129
+ @model_decorator = self.class.model_decorator(model_class)
130
+ end
131
+
132
+ # @return [Class] resource's class
133
+ def model_class
134
+ resource.class
135
+ end
136
+
137
+ # @param field_name [String, Symbol]
138
+ # @return [Object] value of given field name
139
+ def value_of(field_name)
140
+ return unless field_name
141
+
142
+ resource.try field_name
143
+ end
144
+
145
+ # Guess the title for given resource.
146
+ #
147
+ # It falls back to primary key value when no text field is found.
148
+ # @return [String] a label
149
+ def to_label
150
+ # NOTE: `.to_s` at the end is to ensure String is returned that won't cause any
151
+ # issue when `#to_label` is used in a link_to block. Coz integer is ignored.
152
+ (model_decorator.guess_title(resource) || primary_key_value).to_s
153
+ end
154
+
155
+ # @return [Hash, Array] validation/result errors
156
+ def errors
157
+ model_decorator.form_active_errors(resource)
158
+ end
159
+
160
+ # @return [Object] primary key value
161
+ def primary_key_value
162
+ resource.try primary_key
163
+ end
164
+
165
+ # @return [ActiveModel::Name]
166
+ def model_name
167
+ ModuleUtils.try_to(resource, :model_name) || ActiveModel::Name.new(model_class)
168
+ end
169
+
170
+ # @return [nil] if no primary key
171
+ # @return [Array<String>] primary key
172
+ def to_key
173
+ key = ModuleUtils.try_to(resource, primary_key)
174
+ key ? [key] : nil
175
+ end
176
+
177
+ # Delegate missing method to {#resource}
178
+ def method_missing(method_id, *args, &block)
179
+ return super unless resource.respond_to? method_id
180
+
181
+ resource.try method_id, *args, &block
182
+ end
183
+
184
+ # Delegate missing method check to context
185
+ def respond_to_missing?(method_id, _include_private)
186
+ resource.respond_to?(method_id) || super
187
+ end
188
+ end
189
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ class CellHandling < ::StandardError
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ class Forbidden < GeneralError
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ class GeneralError < ::StandardError
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ class InvalidError < GeneralError
5
+ end
6
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ # Model not found error
5
+ class ModelNotFound < NotFound
6
+ # @return [String] not found error message
7
+ def message
8
+ I18n.t 'errors.not_found.model', model: super
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ class NotAuthenticated < GeneralError
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ class NotFound < GeneralError
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ class NotImplemented < GeneralError
5
+ end
6
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ # Resource not found error
5
+ class ResourceNotFound < NotFound
6
+ # @return [String] resource not found error message
7
+ def message
8
+ I18n.t 'errors.not_found.resource', resource: super
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ class UnprocessableEntity < GeneralError
5
+ end
6
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ # Custom form builder to add more helper functions
5
+ class FormBuilder < ::ActionView::Helpers::FormBuilder
6
+ # Return error class if there is error
7
+ # @param field_name [String/Symbol]
8
+ # @return [String]
9
+ def error_class(field_name)
10
+ 'has-error' if object.errors[field_name].present?
11
+ end
12
+
13
+ # Build up the HTML for displaying error messages
14
+ # @param field_name [String/Symbol]
15
+ # @return [String] HTML
16
+ def error_messages(field_name)
17
+ errors = Array object.errors[field_name]
18
+ return if errors.blank?
19
+
20
+ content_tag :ul, class: 'errors' do
21
+ errors.each do |message|
22
+ concat content_tag :li, content_tag(
23
+ :small, raw(message) # rubocop:disable Rails/OutputSafety
24
+ )
25
+ end
26
+ end
27
+ end
28
+
29
+ # Extend label to accept proc type `text` argument
30
+ # @see ActionView::Helpers::FormBuilder#label
31
+ def label(method, text = nil, options = {}, &block)
32
+ text = instance_exec(&text) if text.is_a?(Proc)
33
+ super
34
+ end
35
+
36
+ # Extend select to accept proc type `choices` argument
37
+ # @see ActionView::Helpers::FormBuilder#select
38
+ def select(method, choices = nil, options = {}, html_options = {}, &block)
39
+ choices = instance_exec(&choices) if choices.is_a?(Proc)
40
+ super
41
+ end
42
+
43
+ protected
44
+
45
+ # Delegate missing method to `@template`
46
+ def method_missing(method, *args, &block)
47
+ return super unless @template.respond_to? method
48
+
49
+ # Delegate the method so that we don't come in here the next time
50
+ # when same method is called
51
+ self.class.delegate method, to: :@template
52
+ @template.public_send method, *args, &block
53
+ end
54
+
55
+ # Delegate missing method check to `@template`
56
+ def respond_to_missing?(method, _include_private)
57
+ @template.respond_to?(method) || super
58
+ end
59
+ end
60
+ end