grape 1.1.0 → 1.2.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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/Appraisals +3 -7
  3. data/CHANGELOG.md +21 -1
  4. data/Gemfile.lock +26 -26
  5. data/README.md +109 -29
  6. data/UPGRADING.md +45 -0
  7. data/gemfiles/rack_1.5.2.gemfile.lock +232 -0
  8. data/gemfiles/rails_3.gemfile +1 -1
  9. data/gemfiles/rails_3.gemfile.lock +288 -0
  10. data/gemfiles/rails_4.gemfile +1 -1
  11. data/gemfiles/rails_4.gemfile.lock +280 -0
  12. data/gemfiles/rails_5.gemfile +1 -1
  13. data/gemfiles/rails_5.gemfile.lock +312 -0
  14. data/lib/grape.rb +1 -0
  15. data/lib/grape/api.rb +74 -195
  16. data/lib/grape/api/instance.rb +242 -0
  17. data/lib/grape/dsl/desc.rb +17 -1
  18. data/lib/grape/dsl/middleware.rb +7 -0
  19. data/lib/grape/dsl/parameters.rb +9 -4
  20. data/lib/grape/dsl/routing.rb +5 -1
  21. data/lib/grape/endpoint.rb +1 -1
  22. data/lib/grape/exceptions/base.rb +9 -1
  23. data/lib/grape/exceptions/invalid_response.rb +9 -0
  24. data/lib/grape/locale/en.yml +1 -0
  25. data/lib/grape/middleware/error.rb +8 -2
  26. data/lib/grape/middleware/versioner/header.rb +2 -2
  27. data/lib/grape/validations/params_scope.rb +1 -0
  28. data/lib/grape/validations/validators/multiple_params_base.rb +1 -1
  29. data/lib/grape/version.rb +1 -1
  30. data/pkg/grape-1.2.0.gem +0 -0
  31. data/spec/grape/api/routes_with_requirements_spec.rb +59 -0
  32. data/spec/grape/api_remount_spec.rb +85 -0
  33. data/spec/grape/api_spec.rb +70 -1
  34. data/spec/grape/dsl/desc_spec.rb +17 -1
  35. data/spec/grape/dsl/middleware_spec.rb +8 -0
  36. data/spec/grape/dsl/routing_spec.rb +10 -0
  37. data/spec/grape/exceptions/base_spec.rb +61 -0
  38. data/spec/grape/exceptions/invalid_response_spec.rb +11 -0
  39. data/spec/grape/middleware/auth/dsl_spec.rb +3 -3
  40. data/spec/grape/middleware/exception_spec.rb +1 -1
  41. data/spec/grape/middleware/versioner/header_spec.rb +6 -0
  42. data/spec/grape/validations/params_scope_spec.rb +133 -0
  43. data/spec/spec_helper.rb +3 -1
  44. metadata +99 -87
  45. data/gemfiles/rack_1.5.2.gemfile +0 -35
  46. data/pkg/grape-0.17.0.gem +0 -0
  47. data/pkg/grape-0.19.0.gem +0 -0
@@ -75,6 +75,7 @@ module Grape
75
75
  autoload :InvalidAcceptHeader
76
76
  autoload :InvalidVersionHeader
77
77
  autoload :MethodNotAllowed
78
+ autoload :InvalidResponse
78
79
  end
79
80
 
80
81
  module Extensions
@@ -1,233 +1,112 @@
1
1
  require 'grape/router'
2
+ require 'grape/api/instance'
2
3
 
3
4
  module Grape
4
5
  # The API class is the primary entry point for creating Grape APIs. Users
5
6
  # should subclass this class in order to build an API.
6
7
  class API
7
- include Grape::DSL::API
8
+ # Class methods that we want to call on the API rather than on the API object
9
+ NON_OVERRIDABLE = %I[define_singleton_method instance_variable_set inspect class is_a? ! kind_of?
10
+ respond_to? respond_to_missing? const_defined? const_missing parent
11
+ parent_name name equal? to_s parents anonymous?].freeze
8
12
 
9
13
  class << self
10
- attr_reader :instance
11
-
12
- # A class-level lock to ensure the API is not compiled by multiple
13
- # threads simultaneously within the same process.
14
- LOCK = Mutex.new
15
-
16
- # Clears all defined routes, endpoints, etc., on this API.
17
- def reset!
18
- reset_endpoints!
19
- reset_routes!
20
- reset_validations!
21
- end
22
-
23
- # Parses the API's definition and compiles it into an instance of
24
- # Grape::API.
25
- def compile
26
- @instance ||= new
27
- end
28
-
29
- # Wipe the compiled API so we can recompile after changes were made.
30
- def change!
31
- @instance = nil
14
+ attr_accessor :base_instance, :instances
15
+ # When inherited, will create a list of all instances (times the API was mounted)
16
+ # It will listen to the setup required to mount that endpoint, and replicate it on any new instance
17
+ def inherited(api, base_instance_parent = Grape::API::Instance)
18
+ api.initial_setup(base_instance_parent)
19
+ api.override_all_methods!
20
+ make_inheritable(api)
32
21
  end
33
22
 
34
- # This is the interface point between Rack and Grape; it accepts a request
35
- # from Rack and ultimately returns an array of three values: the status,
36
- # the headers, and the body. See [the rack specification]
37
- # (http://www.rubydoc.info/github/rack/rack/master/file/SPEC) for more.
38
- def call(env)
39
- LOCK.synchronize { compile } unless instance
40
- call!(env)
23
+ # Initialize the instance variables on the remountable class, and the base_instance
24
+ # an instance that will be used to create the set up but will not be mounted
25
+ def initial_setup(base_instance_parent)
26
+ @instances = []
27
+ @setup = []
28
+ @base_parent = base_instance_parent
29
+ @base_instance = mount_instance
41
30
  end
42
31
 
43
- # A non-synchronized version of ::call.
44
- def call!(env)
45
- instance.call(env)
46
- end
47
-
48
- # (see #cascade?)
49
- def cascade(value = nil)
50
- if value.nil?
51
- inheritable_setting.namespace_inheritable.keys.include?(:cascade) ? !namespace_inheritable(:cascade).nil? : true
52
- else
53
- namespace_inheritable(:cascade, value)
32
+ # Redefines all methods so that are forwarded to add_setup and be recorded
33
+ def override_all_methods!
34
+ (base_instance.methods - NON_OVERRIDABLE).each do |method_override|
35
+ define_singleton_method(method_override) do |*args, &block|
36
+ add_setup(method_override, *args, &block)
37
+ end
54
38
  end
55
39
  end
56
40
 
57
- # see Grape::Router#recognize_path
58
- def recognize_path(path)
59
- LOCK.synchronize { compile } unless instance
60
- instance.router.recognize_path(path)
61
- end
62
-
63
- protected
64
-
65
- def prepare_routes
66
- endpoints.map(&:routes).flatten
41
+ # Allows an API to itself be inheritable:
42
+ def make_inheritable(api)
43
+ # When a child API inherits from a parent API.
44
+ def api.inherited(child_api)
45
+ # The instances of the child API inherit from the instances of the parent API
46
+ Grape::API.inherited(child_api, base_instance)
47
+ end
67
48
  end
68
49
 
69
- # Execute first the provided block, then each of the
70
- # block passed in. Allows for simple 'before' setups
71
- # of settings stack pushes.
72
- def nest(*blocks, &block)
73
- blocks.reject!(&:nil?)
74
- if blocks.any?
75
- instance_eval(&block) if block_given?
76
- blocks.each { |b| instance_eval(&b) }
77
- reset_validations!
50
+ # Alleviates problems with autoloading by tring to search for the constant
51
+ def const_missing(*args)
52
+ if base_instance.const_defined?(*args)
53
+ base_instance.const_get(*args)
54
+ elsif parent && parent.const_defined?(*args)
55
+ parent.const_get(*args)
78
56
  else
79
- instance_eval(&block)
57
+ super
80
58
  end
81
59
  end
82
60
 
83
- def inherited(subclass)
84
- subclass.reset!
85
- subclass.logger = logger.clone
61
+ # The remountable class can have a configuration hash to provide some dynamic class-level variables.
62
+ # For instance, a descripcion could be done using: `desc configuration[:description]` if it may vary
63
+ # depending on where the endpoint is mounted. Use with care, if you find yourself using configuration
64
+ # too much, you may actually want to provide a new API rather than remount it.
65
+ def mount_instance(opts = {})
66
+ instance = Class.new(@base_parent)
67
+ instance.configuration = opts[:configuration] || {}
68
+ instance.base = self
69
+ replay_setup_on(instance)
70
+ instance
86
71
  end
87
72
 
88
- def inherit_settings(other_settings)
89
- top_level_setting.inherit_from other_settings.point_in_time_copy
90
-
91
- # Propagate any inherited params down to our endpoints, and reset any
92
- # compiled routes.
93
- endpoints.each do |e|
94
- e.inherit_settings(top_level_setting.namespace_stackable)
95
- e.reset_routes!
73
+ # Replays the set up to produce an API as defined in this class, can be called
74
+ # on classes that inherit from Grape::API
75
+ def replay_setup_on(instance)
76
+ @setup.each do |setup_stage|
77
+ instance.send(setup_stage[:method], *setup_stage[:args], &setup_stage[:block])
96
78
  end
97
-
98
- reset_routes!
99
79
  end
100
- end
101
-
102
- attr_reader :router
103
80
 
104
- # Builds the routes from the defined endpoints, effectively compiling
105
- # this API into a usable form.
106
- def initialize
107
- @router = Router.new
108
- add_head_not_allowed_methods_and_options_methods
109
- self.class.endpoints.each do |endpoint|
110
- endpoint.mount_in(@router)
81
+ def respond_to?(method, include_private = false)
82
+ super(method, include_private) || base_instance.respond_to?(method, include_private)
111
83
  end
112
84
 
113
- @router.compile!
114
- @router.freeze
115
- end
116
-
117
- # Handle a request. See Rack documentation for what `env` is.
118
- def call(env)
119
- result = @router.call(env)
120
- result[1].delete(Grape::Http::Headers::X_CASCADE) unless cascade?
121
- result
122
- end
123
-
124
- # Some requests may return a HTTP 404 error if grape cannot find a matching
125
- # route. In this case, Grape::Router adds a X-Cascade header to the response
126
- # and sets it to 'pass', indicating to grape's parents they should keep
127
- # looking for a matching route on other resources.
128
- #
129
- # In some applications (e.g. mounting grape on rails), one might need to trap
130
- # errors from reaching upstream. This is effectivelly done by unsetting
131
- # X-Cascade. Default :cascade is true.
132
- def cascade?
133
- return self.class.namespace_inheritable(:cascade) if self.class.inheritable_setting.namespace_inheritable.keys.include?(:cascade)
134
- return self.class.namespace_inheritable(:version_options)[:cascade] if self.class.namespace_inheritable(:version_options) && self.class.namespace_inheritable(:version_options).key?(:cascade)
135
- true
136
- end
137
-
138
- reset!
139
-
140
- private
141
-
142
- # For every resource add a 'OPTIONS' route that returns an HTTP 204 response
143
- # with a list of HTTP methods that can be called. Also add a route that
144
- # will return an HTTP 405 response for any HTTP method that the resource
145
- # cannot handle.
146
- def add_head_not_allowed_methods_and_options_methods
147
- routes_map = {}
148
-
149
- self.class.endpoints.each do |endpoint|
150
- routes = endpoint.routes
151
- routes.each do |route|
152
- # using the :any shorthand produces [nil] for route methods, substitute all manually
153
- route_key = route.pattern.to_regexp
154
- routes_map[route_key] ||= {}
155
- route_settings = routes_map[route_key]
156
- route_settings[:pattern] = route.pattern
157
- route_settings[:requirements] = route.requirements
158
- route_settings[:path] = route.origin
159
- route_settings[:methods] ||= []
160
- route_settings[:methods] << route.request_method
161
- route_settings[:endpoint] = route.app
85
+ def respond_to_missing?(method, include_private = false)
86
+ base_instance.respond_to?(method, include_private)
87
+ end
162
88
 
163
- # using the :any shorthand produces [nil] for route methods, substitute all manually
164
- route_settings[:methods] = %w[GET PUT POST DELETE PATCH HEAD OPTIONS] if route_settings[:methods].include?('*')
89
+ def method_missing(method, *args, &block)
90
+ # If there's a missing method, it may be defined on the base_instance instead.
91
+ if respond_to_missing?(method)
92
+ base_instance.send(method, *args, &block)
93
+ else
94
+ super
165
95
  end
166
96
  end
167
97
 
168
- # The paths we collected are prepared (cf. Path#prepare), so they
169
- # contain already versioning information when using path versioning.
170
- # Disable versioning so adding a route won't prepend versioning
171
- # informations again.
172
- without_root_prefix do
173
- without_versioning do
174
- routes_map.each do |_, config|
175
- methods = config[:methods]
176
- allowed_methods = methods.dup
177
-
178
- unless self.class.namespace_inheritable(:do_not_route_head)
179
- allowed_methods |= [Grape::Http::Headers::HEAD] if allowed_methods.include?(Grape::Http::Headers::GET)
180
- end
98
+ private
181
99
 
182
- allow_header = (self.class.namespace_inheritable(:do_not_route_options) ? allowed_methods : [Grape::Http::Headers::OPTIONS] | allowed_methods).join(', ')
183
-
184
- unless self.class.namespace_inheritable(:do_not_route_options) || allowed_methods.include?(Grape::Http::Headers::OPTIONS)
185
- config[:endpoint].options[:options_route_enabled] = true
186
- end
187
-
188
- attributes = config.merge(allowed_methods: allowed_methods, allow_header: allow_header)
189
- generate_not_allowed_method(config[:pattern], attributes)
190
- end
100
+ # Adds a new stage to the set up require to get a Grape::API up and running
101
+ def add_setup(method, *args, &block)
102
+ setup_stage = { method: method, args: args, block: block }
103
+ @setup << setup_stage
104
+ last_response = nil
105
+ @instances.each do |instance|
106
+ last_response = instance.send(setup_stage[:method], *setup_stage[:args], &setup_stage[:block])
191
107
  end
108
+ last_response
192
109
  end
193
110
  end
194
-
195
- # Generate a route that returns an HTTP 405 response for a user defined
196
- # path on methods not specified
197
- def generate_not_allowed_method(pattern, allowed_methods: [], **attributes)
198
- not_allowed_methods = %w[GET PUT POST DELETE PATCH HEAD] - allowed_methods
199
- not_allowed_methods << Grape::Http::Headers::OPTIONS if self.class.namespace_inheritable(:do_not_route_options)
200
-
201
- return if not_allowed_methods.empty?
202
-
203
- @router.associate_routes(pattern, not_allowed_methods: not_allowed_methods, **attributes)
204
- end
205
-
206
- # Allows definition of endpoints that ignore the versioning configuration
207
- # used by the rest of your API.
208
- def without_versioning(&_block)
209
- old_version = self.class.namespace_inheritable(:version)
210
- old_version_options = self.class.namespace_inheritable(:version_options)
211
-
212
- self.class.namespace_inheritable_to_nil(:version)
213
- self.class.namespace_inheritable_to_nil(:version_options)
214
-
215
- yield
216
-
217
- self.class.namespace_inheritable(:version, old_version)
218
- self.class.namespace_inheritable(:version_options, old_version_options)
219
- end
220
-
221
- # Allows definition of endpoints that ignore the root prefix used by the
222
- # rest of your API.
223
- def without_root_prefix(&_block)
224
- old_prefix = self.class.namespace_inheritable(:root_prefix)
225
-
226
- self.class.namespace_inheritable_to_nil(:root_prefix)
227
-
228
- yield
229
-
230
- self.class.namespace_inheritable(:root_prefix, old_prefix)
231
- end
232
111
  end
233
112
  end
@@ -0,0 +1,242 @@
1
+ require 'grape/router'
2
+
3
+ module Grape
4
+ class API
5
+ # The API Instance class, is the engine behind Grape::API. Each class that inherits
6
+ # from this will represent a different API instance
7
+ class Instance
8
+ include Grape::DSL::API
9
+
10
+ class << self
11
+ attr_reader :instance
12
+ attr_reader :base
13
+ attr_accessor :configuration
14
+
15
+ def base=(grape_api)
16
+ @base = grape_api
17
+ grape_api.instances << self
18
+ end
19
+
20
+ # A class-level lock to ensure the API is not compiled by multiple
21
+ # threads simultaneously within the same process.
22
+ LOCK = Mutex.new
23
+
24
+ # Clears all defined routes, endpoints, etc., on this API.
25
+ def reset!
26
+ reset_endpoints!
27
+ reset_routes!
28
+ reset_validations!
29
+ end
30
+
31
+ # Parses the API's definition and compiles it into an instance of
32
+ # Grape::API.
33
+ def compile
34
+ @instance ||= new
35
+ end
36
+
37
+ # Wipe the compiled API so we can recompile after changes were made.
38
+ def change!
39
+ @instance = nil
40
+ end
41
+
42
+ # This is the interface point between Rack and Grape; it accepts a request
43
+ # from Rack and ultimately returns an array of three values: the status,
44
+ # the headers, and the body. See [the rack specification]
45
+ # (http://www.rubydoc.info/github/rack/rack/master/file/SPEC) for more.
46
+ def call(env)
47
+ LOCK.synchronize { compile } unless instance
48
+ call!(env)
49
+ end
50
+
51
+ # A non-synchronized version of ::call.
52
+ def call!(env)
53
+ instance.call(env)
54
+ end
55
+
56
+ # (see #cascade?)
57
+ def cascade(value = nil)
58
+ if value.nil?
59
+ inheritable_setting.namespace_inheritable.keys.include?(:cascade) ? !namespace_inheritable(:cascade).nil? : true
60
+ else
61
+ namespace_inheritable(:cascade, value)
62
+ end
63
+ end
64
+
65
+ # see Grape::Router#recognize_path
66
+ def recognize_path(path)
67
+ LOCK.synchronize { compile } unless instance
68
+ instance.router.recognize_path(path)
69
+ end
70
+
71
+ protected
72
+
73
+ def prepare_routes
74
+ endpoints.map(&:routes).flatten
75
+ end
76
+
77
+ # Execute first the provided block, then each of the
78
+ # block passed in. Allows for simple 'before' setups
79
+ # of settings stack pushes.
80
+ def nest(*blocks, &block)
81
+ blocks.reject!(&:nil?)
82
+ if blocks.any?
83
+ instance_eval(&block) if block_given?
84
+ blocks.each { |b| instance_eval(&b) }
85
+ reset_validations!
86
+ else
87
+ instance_eval(&block)
88
+ end
89
+ end
90
+
91
+ def inherited(subclass)
92
+ subclass.reset!
93
+ subclass.logger = logger.clone
94
+ end
95
+
96
+ def inherit_settings(other_settings)
97
+ top_level_setting.inherit_from other_settings.point_in_time_copy
98
+
99
+ # Propagate any inherited params down to our endpoints, and reset any
100
+ # compiled routes.
101
+ endpoints.each do |e|
102
+ e.inherit_settings(top_level_setting.namespace_stackable)
103
+ e.reset_routes!
104
+ end
105
+
106
+ reset_routes!
107
+ end
108
+ end
109
+
110
+ attr_reader :router
111
+
112
+ # Builds the routes from the defined endpoints, effectively compiling
113
+ # this API into a usable form.
114
+ def initialize
115
+ @router = Router.new
116
+ add_head_not_allowed_methods_and_options_methods
117
+ self.class.endpoints.each do |endpoint|
118
+ endpoint.mount_in(@router)
119
+ end
120
+
121
+ @router.compile!
122
+ @router.freeze
123
+ end
124
+
125
+ # Handle a request. See Rack documentation for what `env` is.
126
+ def call(env)
127
+ result = @router.call(env)
128
+ result[1].delete(Grape::Http::Headers::X_CASCADE) unless cascade?
129
+ result
130
+ end
131
+
132
+ # Some requests may return a HTTP 404 error if grape cannot find a matching
133
+ # route. In this case, Grape::Router adds a X-Cascade header to the response
134
+ # and sets it to 'pass', indicating to grape's parents they should keep
135
+ # looking for a matching route on other resources.
136
+ #
137
+ # In some applications (e.g. mounting grape on rails), one might need to trap
138
+ # errors from reaching upstream. This is effectivelly done by unsetting
139
+ # X-Cascade. Default :cascade is true.
140
+ def cascade?
141
+ return self.class.namespace_inheritable(:cascade) if self.class.inheritable_setting.namespace_inheritable.keys.include?(:cascade)
142
+ return self.class.namespace_inheritable(:version_options)[:cascade] if self.class.namespace_inheritable(:version_options) && self.class.namespace_inheritable(:version_options).key?(:cascade)
143
+ true
144
+ end
145
+
146
+ reset!
147
+
148
+ private
149
+
150
+ # For every resource add a 'OPTIONS' route that returns an HTTP 204 response
151
+ # with a list of HTTP methods that can be called. Also add a route that
152
+ # will return an HTTP 405 response for any HTTP method that the resource
153
+ # cannot handle.
154
+ def add_head_not_allowed_methods_and_options_methods
155
+ routes_map = {}
156
+
157
+ self.class.endpoints.each do |endpoint|
158
+ routes = endpoint.routes
159
+ routes.each do |route|
160
+ # using the :any shorthand produces [nil] for route methods, substitute all manually
161
+ route_key = route.pattern.to_regexp
162
+ routes_map[route_key] ||= {}
163
+ route_settings = routes_map[route_key]
164
+ route_settings[:pattern] = route.pattern
165
+ route_settings[:requirements] = route.requirements
166
+ route_settings[:path] = route.origin
167
+ route_settings[:methods] ||= []
168
+ route_settings[:methods] << route.request_method
169
+ route_settings[:endpoint] = route.app
170
+
171
+ # using the :any shorthand produces [nil] for route methods, substitute all manually
172
+ route_settings[:methods] = %w[GET PUT POST DELETE PATCH HEAD OPTIONS] if route_settings[:methods].include?('*')
173
+ end
174
+ end
175
+
176
+ # The paths we collected are prepared (cf. Path#prepare), so they
177
+ # contain already versioning information when using path versioning.
178
+ # Disable versioning so adding a route won't prepend versioning
179
+ # informations again.
180
+ without_root_prefix do
181
+ without_versioning do
182
+ routes_map.each do |_, config|
183
+ methods = config[:methods]
184
+ allowed_methods = methods.dup
185
+
186
+ unless self.class.namespace_inheritable(:do_not_route_head)
187
+ allowed_methods |= [Grape::Http::Headers::HEAD] if allowed_methods.include?(Grape::Http::Headers::GET)
188
+ end
189
+
190
+ allow_header = (self.class.namespace_inheritable(:do_not_route_options) ? allowed_methods : [Grape::Http::Headers::OPTIONS] | allowed_methods).join(', ')
191
+
192
+ unless self.class.namespace_inheritable(:do_not_route_options) || allowed_methods.include?(Grape::Http::Headers::OPTIONS)
193
+ config[:endpoint].options[:options_route_enabled] = true
194
+ end
195
+
196
+ attributes = config.merge(allowed_methods: allowed_methods, allow_header: allow_header)
197
+ generate_not_allowed_method(config[:pattern], attributes)
198
+ end
199
+ end
200
+ end
201
+ end
202
+
203
+ # Generate a route that returns an HTTP 405 response for a user defined
204
+ # path on methods not specified
205
+ def generate_not_allowed_method(pattern, allowed_methods: [], **attributes)
206
+ not_allowed_methods = %w[GET PUT POST DELETE PATCH HEAD] - allowed_methods
207
+ not_allowed_methods << Grape::Http::Headers::OPTIONS if self.class.namespace_inheritable(:do_not_route_options)
208
+
209
+ return if not_allowed_methods.empty?
210
+
211
+ @router.associate_routes(pattern, not_allowed_methods: not_allowed_methods, **attributes)
212
+ end
213
+
214
+ # Allows definition of endpoints that ignore the versioning configuration
215
+ # used by the rest of your API.
216
+ def without_versioning(&_block)
217
+ old_version = self.class.namespace_inheritable(:version)
218
+ old_version_options = self.class.namespace_inheritable(:version_options)
219
+
220
+ self.class.namespace_inheritable_to_nil(:version)
221
+ self.class.namespace_inheritable_to_nil(:version_options)
222
+
223
+ yield
224
+
225
+ self.class.namespace_inheritable(:version, old_version)
226
+ self.class.namespace_inheritable(:version_options, old_version_options)
227
+ end
228
+
229
+ # Allows definition of endpoints that ignore the root prefix used by the
230
+ # rest of your API.
231
+ def without_root_prefix(&_block)
232
+ old_prefix = self.class.namespace_inheritable(:root_prefix)
233
+
234
+ self.class.namespace_inheritable_to_nil(:root_prefix)
235
+
236
+ yield
237
+
238
+ self.class.namespace_inheritable(:root_prefix, old_prefix)
239
+ end
240
+ end
241
+ end
242
+ end