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,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ # Model finder interface
5
+ class ModelFinder
6
+ # Need to implement this method to get all the available model for a mode
7
+ # @return [Array<Class>] a list of model class
8
+ def all
9
+ raise NotImplemented
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ # Model pagination provider interface
5
+ class ModelPaginationProvider
6
+ # @param collection [#to_a]
7
+ # @param params [ActionController::Parameters]
8
+ # @param options [Hash] options
9
+ # @param model_decorator [Wallaby::ModelDecorator, nil]
10
+ def initialize(collection, params, options: {}, model_decorator: nil)
11
+ @collection = collection
12
+ @params = params
13
+ @options = options
14
+ @model_decorator = model_decorator
15
+ end
16
+
17
+ # If a collection has pagination feature
18
+ # @return [Boolean]
19
+ def paginatable?
20
+ raise NotImplemented
21
+ end
22
+
23
+ # Check and see if it's the first page
24
+ # @return [Boolean]
25
+ def first_page?
26
+ page_number > first_page_number
27
+ end
28
+
29
+ # Check and see if it's the previous page
30
+ # @return [Boolean]
31
+ def prev_page?
32
+ page_number > first_page_number
33
+ end
34
+
35
+ # Check and see if it's the last page
36
+ # @return [Boolean]
37
+ def last_page?
38
+ page_number < last_page_number
39
+ end
40
+
41
+ # Check and see if it's the next page
42
+ # @return [Boolean]
43
+ def next_page?
44
+ page_number < last_page_number
45
+ end
46
+
47
+ # Find out the offset `from`
48
+ # @return [Integer]
49
+ def from
50
+ total.zero? ? total : (page_number - 1) * page_size + 1
51
+ end
52
+
53
+ # Find out the offset `to`
54
+ # @return [Integer]
55
+ def to
56
+ [page_number * page_size, total].min
57
+ end
58
+
59
+ # Find out the total count of current query
60
+ # @return [Integer]
61
+ def total
62
+ raise NotImplemented
63
+ end
64
+
65
+ # Find out the current page size
66
+ # @return [Integer]
67
+ def page_size
68
+ raise NotImplemented
69
+ end
70
+
71
+ # Find out the current page number
72
+ # @return [Integer]
73
+ def page_number
74
+ raise NotImplemented
75
+ end
76
+
77
+ # Page number of first page
78
+ # @return [Integer]
79
+ def first_page_number
80
+ 1
81
+ end
82
+
83
+ # Page number of last page
84
+ # @return [Integer]
85
+ def last_page_number
86
+ number_of_pages
87
+ end
88
+
89
+ # Page number of previous page
90
+ # @return [Integer]
91
+ def prev_page_number
92
+ [page_number - 1, first_page_number].max
93
+ end
94
+
95
+ # Page number of next page
96
+ # @return [Integer]
97
+ def next_page_number
98
+ [page_number + 1, last_page_number].min
99
+ end
100
+
101
+ # Total number of pages
102
+ # @return [Integer]
103
+ def number_of_pages
104
+ (total / page_size.to_f).ceil
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ # Model service provider interface
5
+ class ModelServiceProvider
6
+ # @param model_class [Class]
7
+ # @param model_decorator [Wallaby::ModelDecorator, nil] model decorator
8
+ def initialize(model_class, model_decorator)
9
+ raise ::ArgumentError, 'model class required' unless model_class
10
+
11
+ @model_class = model_class
12
+ @model_decorator = model_decorator
13
+ end
14
+
15
+ # To whitelist params for a model class
16
+ # @param _params [ActionController::Parameters]
17
+ # @param _action [String, Symbol]
18
+ # @param _authorizer [Wallaby::ModelAuthorizer]
19
+ # @return [ActionController::Parameters] whitelisted params
20
+ def permit(_params, _action, _authorizer)
21
+ raise NotImplemented
22
+ end
23
+
24
+ # Fetch collection by params
25
+ # @param _params [ActionController::Parameters]
26
+ # @param _authorizer
27
+ # @return [#to_a]
28
+ def collection(_params, _authorizer)
29
+ raise NotImplemented
30
+ end
31
+
32
+ # Paginate the resources
33
+ # @param _query
34
+ # @param _params [ActionController::Parameters]
35
+ # @return [#to_a]
36
+ def paginate(_query, _params)
37
+ raise NotImplemented
38
+ end
39
+
40
+ # Initialize the model class using params
41
+ # @param _params [ActionController::Parameters]
42
+ # @param _authorizer
43
+ # @return a resource object
44
+ def new(_params, _authorizer)
45
+ raise NotImplemented
46
+ end
47
+
48
+ # Find a resource using id
49
+ # @param _id [Object]
50
+ # @param _params [ActionController::Parameters]
51
+ # @param _authorizer
52
+ # @return a resource object
53
+ def find(_id, _params, _authorizer)
54
+ raise NotImplemented
55
+ end
56
+
57
+ # Save the newly initialized resource
58
+ # @param _resource [Object]
59
+ # @param _params [ActionController::Parameters]
60
+ # @param _authorizer
61
+ # @return a resource object
62
+ def create(_resource, _params, _authorizer)
63
+ raise NotImplemented
64
+ end
65
+
66
+ # Update the persisted resource
67
+ # @param _resource [Object]
68
+ # @param _params [ActionController::Parameters]
69
+ # @param _authorizer
70
+ # @return a resource object
71
+ def update(_resource, _params, _authorizer)
72
+ raise NotImplemented
73
+ end
74
+
75
+ # Destroy the given resource
76
+ # @param _resource [Object]
77
+ # @param _params [ActionController::Parameters]
78
+ # @param _authorizer
79
+ # @return a resource object
80
+ def destroy(_resource, _params, _authorizer)
81
+ raise NotImplemented
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ # Model paginator to provide support for pagination on index page
5
+ class ModelPaginator
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 **ProductPaginator**.
14
+ #
15
+ # If Wallaby can't recognise the model class for Paginator, it's required to be configured as below example:
16
+ # @example To configure model class
17
+ # class Admin::ProductPaginator < Admin::ApplicationPaginator
18
+ # self.model_class = Product
19
+ # end
20
+ # @example To configure model class for version below 5.2.0
21
+ # class Admin::ProductPaginator < Admin::ApplicationPaginator
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_paginator}
29
+ # @return [nil] if current class is {Wallaby::ModelPaginator}
30
+ # @return [nil] if assoicated model class is not found
31
+ def model_class
32
+ return unless self < ModelPaginator
33
+ return if base_class? || self == Wallaby.configuration.mapping.model_paginator
34
+
35
+ @model_class ||= Map.model_class_map(name.gsub(/(^#{namespace}::)|(Paginator$)/, EMPTY_STRING))
36
+ end
37
+
38
+ # @!attribute provider_class
39
+ # @return [Class] pagination 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] provider
49
+ # @return [Wallaby::ModelServiceProvider]
50
+ # @since 5.2.0
51
+ attr_reader :provider
52
+
53
+ # During initialization, Wallaby will assign a pagination provider for this paginator
54
+ # to carry out the actual execution.
55
+ #
56
+ # Therefore, all its actions can be completely replaced by user's own implemnetation.
57
+ # @param model_class [Class]
58
+ # @param collection [#to_a] a collection of the resources
59
+ # @param params [ActionController::Parameters]
60
+ def initialize(model_class, collection, params)
61
+ @model_class = self.class.model_class || model_class
62
+ raise ArgumentError, I18n.t('errors.required', subject: 'model_class') unless @model_class
63
+
64
+ @collection = collection
65
+ @params = params
66
+ @provider = Map.pagination_provider_map(@model_class).new(@collection, @params)
67
+ end
68
+
69
+ delegate(*ModelPaginationProvider.instance_methods(false), to: :provider)
70
+ # @!method paginatable?
71
+ # (see Wallaby::ModelPaginationProvider#paginatable?)
72
+
73
+ # @!method first_page?
74
+ # (see Wallaby::ModelPaginationProvider#first_page?)
75
+
76
+ # @!method prev_page?
77
+ # (see Wallaby::ModelPaginationProvider#prev_page?)
78
+
79
+ # @!method last_page?
80
+ # (see Wallaby::ModelPaginationProvider#last_page?)
81
+
82
+ # @!method next_page?
83
+ # (see Wallaby::ModelPaginationProvider#next_page?)
84
+
85
+ # @!method from
86
+ # (see Wallaby::ModelPaginationProvider#from)
87
+
88
+ # @!method to
89
+ # (see Wallaby::ModelPaginationProvider#to)
90
+
91
+ # @!method total
92
+ # (see Wallaby::ModelPaginationProvider#total)
93
+
94
+ # @!method page_size
95
+ # (see Wallaby::ModelPaginationProvider#page_size)
96
+
97
+ # @!method page_number
98
+ # (see Wallaby::ModelPaginationProvider#page_number)
99
+
100
+ # @!method first_page_number
101
+ # (see Wallaby::ModelPaginationProvider#first_page_number)
102
+
103
+ # @!method last_page_number
104
+ # (see Wallaby::ModelPaginationProvider#last_page_number)
105
+
106
+ # @!method prev_page_number
107
+ # (see Wallaby::ModelPaginationProvider#prev_page_number)
108
+
109
+ # @!method next_page_number
110
+ # (see Wallaby::ModelPaginationProvider#next_page_number)
111
+
112
+ # @!method number_of_pages
113
+ # (see Wallaby::ModelPaginationProvider#number_of_pages)
114
+ end
115
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ # Resource paginator
5
+ class ResourcePaginator < ModelPaginator
6
+ base_class!
7
+
8
+ def self.inherited(_sub_class)
9
+ Utils.deprecate 'deprecation.resource_paginator_inheirtance', caller: caller
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ # a parser to handle colon query
5
+ class Parser < Parslet::Parser
6
+ root(:statement)
7
+ rule(:statement) { expression >> (space >> expression).repeat }
8
+ rule(:expression) { colon_query | general_keyword }
9
+ rule(:colon_query) do
10
+ name.as(:left) >> operator.as(:op) >> keywords.as(:right)
11
+ end
12
+ rule(:name) { (space.absent? >> colon.absent? >> any).repeat(1) }
13
+ rule(:operator) { colon >> match('[^\s\'\"\:\,0-9a-zA-Z]').repeat(0, 3) }
14
+ rule(:keywords) { general_keyword >> (comma >> general_keyword).repeat }
15
+ rule(:general_keyword) { quoted_keyword | keyword }
16
+
17
+ # basic elements
18
+ rule(:quoted_keyword) do
19
+ open_quote >>
20
+ (close_quote.absent? >> any).repeat.as(:keyword) >>
21
+ close_quote
22
+ end
23
+ rule(:keyword) { ((space | comma).absent? >> any).repeat.as(:keyword) }
24
+
25
+ # atomic entities
26
+ rule(:comma) { str(',') }
27
+ rule(:space) { match('\s').repeat(1) }
28
+ rule(:colon) { str(':') }
29
+
30
+ # open-close elements
31
+ rule(:open_quote) { match('[\'\"]').capture(:quote) }
32
+ rule(:close_quote) { dynamic { |_src, ctx| str(ctx.captures[:quote]) } }
33
+ end
34
+ end
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ # In order to improve the rendering performance, cell is designed as simple partial component.
5
+ # @since 5.2.0
6
+ class Cell
7
+ # @!attribute [r] context
8
+ # @return [Object] view context
9
+ attr_reader :context
10
+
11
+ # @!attribute [r] local_assigns
12
+ # @return [Hash] a list of local_assigns containing {#object}, {#field_name}, {#value}, {#metadata} and {#form}
13
+ attr_reader :local_assigns
14
+
15
+ # @!attribute [r] buffer
16
+ # @return [String] output string buffer
17
+ attr_reader :buffer
18
+
19
+ delegate(*ERB::Util.singleton_methods, to: ERB::Util)
20
+
21
+ # @param context [ActionView::Context] view context
22
+ # @param local_assigns [Hash] local variables
23
+ def initialize(context, local_assigns)
24
+ @context = context
25
+ @local_assigns = local_assigns
26
+ end
27
+
28
+ # @!attribute [r] object
29
+ # @return [Object] object
30
+ def object
31
+ local_assigns[:object]
32
+ end
33
+
34
+ # @!attribute [w] object
35
+ def object=(object)
36
+ local_assigns[:object] = object
37
+ end
38
+
39
+ # @!attribute [r] field_name
40
+ # @return [String] field name
41
+ def field_name
42
+ local_assigns[:field_name]
43
+ end
44
+
45
+ # @!attribute [w] field_name
46
+ def field_name=(field_name)
47
+ local_assigns[:field_name] = field_name
48
+ end
49
+
50
+ # @!attribute [r] value
51
+ # @return [String] value
52
+ def value
53
+ local_assigns[:value]
54
+ end
55
+
56
+ # @!attribute [w] value
57
+ def value=(value)
58
+ local_assigns[:value] = value
59
+ end
60
+
61
+ # @!attribute [r] metadata
62
+ # @return [String] metadata
63
+ def metadata
64
+ local_assigns[:metadata]
65
+ end
66
+
67
+ # @!attribute [w] metadata
68
+ def metadata=(metadata)
69
+ local_assigns[:metadata] = metadata
70
+ end
71
+
72
+ # @!attribute [r] form
73
+ # @return [ActionView::Helpers::FormBuilder] form object
74
+ def form
75
+ local_assigns[:form]
76
+ end
77
+
78
+ # @!attribute [w] form
79
+ def form=(form)
80
+ local_assigns[:form] = form
81
+ end
82
+
83
+ # @note this is a template method that can be overridden by subclasses
84
+ # Produce output for this cell component.
85
+ #
86
+ # Please note that the output doesn't include the buffer produced by {#concat}.
87
+ # Therefore, use {#render_complete} method instead when the cell is rendered.
88
+ def render; end
89
+
90
+ # This method produces the complete rendered string including the buffer produced by {#concat}.
91
+ # @return [String] output of the cell
92
+ def render_complete(&block)
93
+ @buffer = EMPTY_STRING.dup # reset buffer before rendering
94
+ last_part = render(&block)
95
+ @buffer << last_part.to_s
96
+ end
97
+
98
+ # Append string to output buffer
99
+ # @param string [String] string to concat
100
+ def concat(string)
101
+ (@buffer ||= EMPTY_STRING.dup) << string
102
+ end
103
+
104
+ # @overload at(name)
105
+ # Get view instance variable value
106
+ # @example To get view instance variable value
107
+ # at('name') # => get value of `@name` from the view
108
+ # @param name [String, Symbol] view instance variable name without `@`
109
+ # @overload at(name, value)
110
+ # Set view instance variable value
111
+ # @example To set view instance variable value
112
+ # at('name', value) # => set value of `@name` in the view
113
+ # @param name [String, Symbol] view instance variable name without `@`
114
+ # @param value [object] value
115
+ # @return [object] view instance variable value
116
+ def at(*args)
117
+ raise ArgumentError unless args.length.in? [1, 2]
118
+ return context.instance_variable_get :"@#{args.first}" if args.length == 1
119
+
120
+ context.instance_variable_set :"@#{args.first}", args.last
121
+ end
122
+
123
+ private
124
+
125
+ # Delegate missing method to {#context}
126
+ def method_missing(method_id, *args, &block)
127
+ return super unless context.respond_to? method_id
128
+
129
+ context.public_send method_id, *args, &block
130
+ end
131
+
132
+ # Delegate missing method check to {#context}
133
+ def respond_to_missing?(method_id, _include_private)
134
+ context.respond_to?(method_id) || super
135
+ end
136
+ end
137
+ end