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