grape 0.13.0 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of grape might be problematic. Click here for more details.

Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/Appraisals +9 -4
  3. data/CHANGELOG.md +28 -0
  4. data/Gemfile +0 -1
  5. data/Gemfile.lock +166 -0
  6. data/README.md +305 -163
  7. data/Rakefile +30 -33
  8. data/UPGRADING.md +31 -0
  9. data/benchmark/simple.rb +27 -0
  10. data/gemfiles/rack_1.5.2.gemfile +13 -0
  11. data/gemfiles/rails_3.gemfile +2 -2
  12. data/gemfiles/rails_4.gemfile +1 -2
  13. data/grape.gemspec +5 -4
  14. data/lib/grape.rb +9 -5
  15. data/lib/grape/dsl/configuration.rb +5 -2
  16. data/lib/grape/dsl/helpers.rb +8 -3
  17. data/lib/grape/dsl/inside_route.rb +67 -44
  18. data/lib/grape/dsl/parameters.rb +21 -12
  19. data/lib/grape/dsl/request_response.rb +1 -1
  20. data/lib/grape/dsl/routing.rb +3 -4
  21. data/lib/grape/endpoint.rb +63 -28
  22. data/lib/grape/error_formatter/base.rb +6 -6
  23. data/lib/grape/exceptions/base.rb +5 -5
  24. data/lib/grape/exceptions/invalid_version_header.rb +10 -0
  25. data/lib/grape/formatter/serializable_hash.rb +3 -2
  26. data/lib/grape/locale/en.yml +4 -1
  27. data/lib/grape/middleware/auth/base.rb +2 -2
  28. data/lib/grape/middleware/auth/dsl.rb +1 -1
  29. data/lib/grape/middleware/auth/strategies.rb +1 -1
  30. data/lib/grape/middleware/base.rb +7 -4
  31. data/lib/grape/middleware/error.rb +3 -2
  32. data/lib/grape/middleware/filter.rb +1 -1
  33. data/lib/grape/middleware/formatter.rb +47 -44
  34. data/lib/grape/middleware/globals.rb +3 -3
  35. data/lib/grape/middleware/versioner/accept_version_header.rb +5 -7
  36. data/lib/grape/middleware/versioner/header.rb +113 -50
  37. data/lib/grape/middleware/versioner/param.rb +5 -8
  38. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +20 -0
  39. data/lib/grape/middleware/versioner/path.rb +3 -6
  40. data/lib/grape/path.rb +3 -3
  41. data/lib/grape/request.rb +40 -0
  42. data/lib/grape/util/content_types.rb +9 -9
  43. data/lib/grape/util/env.rb +22 -0
  44. data/lib/grape/util/strict_hash_configuration.rb +2 -1
  45. data/lib/grape/validations/attributes_iterator.rb +8 -3
  46. data/lib/grape/validations/params_scope.rb +83 -15
  47. data/lib/grape/validations/types.rb +144 -0
  48. data/lib/grape/validations/types/build_coercer.rb +53 -0
  49. data/lib/grape/validations/types/custom_type_coercer.rb +183 -0
  50. data/lib/grape/validations/types/file.rb +28 -0
  51. data/lib/grape/validations/types/json.rb +65 -0
  52. data/lib/grape/validations/types/multiple_type_coercer.rb +76 -0
  53. data/lib/grape/validations/types/variant_collection_coercer.rb +59 -0
  54. data/lib/grape/validations/types/virtus_collection_patch.rb +16 -0
  55. data/lib/grape/validations/validators/all_or_none.rb +1 -1
  56. data/lib/grape/validations/validators/allow_blank.rb +3 -3
  57. data/lib/grape/validations/validators/base.rb +7 -0
  58. data/lib/grape/validations/validators/coerce.rb +31 -42
  59. data/lib/grape/validations/validators/presence.rb +2 -3
  60. data/lib/grape/validations/validators/regexp.rb +2 -4
  61. data/lib/grape/validations/validators/values.rb +3 -3
  62. data/lib/grape/version.rb +1 -1
  63. data/pkg/grape-0.13.0.gem +0 -0
  64. data/spec/grape/api/custom_validations_spec.rb +5 -4
  65. data/spec/grape/api/deeply_included_options_spec.rb +7 -7
  66. data/spec/grape/api/nested_helpers_spec.rb +4 -2
  67. data/spec/grape/api/shared_helpers_spec.rb +8 -8
  68. data/spec/grape/api_spec.rb +88 -54
  69. data/spec/grape/dsl/configuration_spec.rb +13 -0
  70. data/spec/grape/dsl/helpers_spec.rb +16 -2
  71. data/spec/grape/dsl/inside_route_spec.rb +3 -2
  72. data/spec/grape/dsl/parameters_spec.rb +0 -6
  73. data/spec/grape/dsl/routing_spec.rb +1 -1
  74. data/spec/grape/endpoint_spec.rb +61 -20
  75. data/spec/grape/entity_spec.rb +10 -8
  76. data/spec/grape/exceptions/invalid_accept_header_spec.rb +1 -15
  77. data/spec/grape/integration/rack_spec.rb +3 -2
  78. data/spec/grape/middleware/base_spec.rb +7 -5
  79. data/spec/grape/middleware/error_spec.rb +16 -15
  80. data/spec/grape/middleware/exception_spec.rb +45 -43
  81. data/spec/grape/middleware/formatter_spec.rb +34 -0
  82. data/spec/grape/middleware/versioner/header_spec.rb +79 -47
  83. data/spec/grape/path_spec.rb +10 -10
  84. data/spec/grape/presenters/presenter_spec.rb +2 -2
  85. data/spec/grape/request_spec.rb +100 -0
  86. data/spec/grape/validations/params_scope_spec.rb +11 -9
  87. data/spec/grape/validations/types_spec.rb +95 -0
  88. data/spec/grape/validations/validators/coerce_spec.rb +335 -2
  89. data/spec/grape/validations/validators/values_spec.rb +15 -15
  90. data/spec/grape/validations_spec.rb +53 -24
  91. data/spec/shared/versioning_examples.rb +2 -2
  92. data/spec/spec_helper.rb +0 -1
  93. data/spec/support/versioned_helpers.rb +2 -2
  94. metadata +51 -13
  95. data/.gitignore +0 -46
  96. data/.rspec +0 -2
  97. data/.rubocop.yml +0 -7
  98. data/.rubocop_todo.yml +0 -84
  99. data/.travis.yml +0 -20
  100. data/.yardopts +0 -2
  101. data/lib/grape/http/request.rb +0 -35
  102. data/lib/grape/util/parameter_types.rb +0 -58
  103. data/spec/grape/util/parameter_types_spec.rb +0 -54
data/Rakefile CHANGED
@@ -17,52 +17,49 @@ end
17
17
  task :spec
18
18
 
19
19
  require 'rainbow/ext/string' unless String.respond_to?(:color)
20
+
20
21
  require 'rubocop/rake_task'
21
22
  RuboCop::RakeTask.new
22
23
 
23
24
  task default: [:rubocop, :spec]
24
25
 
25
- begin
26
- require 'yard'
27
- DOC_FILES = ['lib/**/*.rb', 'README.md']
26
+ require 'yard'
27
+ DOC_FILES = ['lib/**/*.rb', 'README.md']
28
+
29
+ YARD::Rake::YardocTask.new(:doc) do |t|
30
+ t.files = DOC_FILES
31
+ end
28
32
 
29
- YARD::Rake::YardocTask.new(:doc) do |t|
33
+ namespace :doc do
34
+ YARD::Rake::YardocTask.new(:pages) do |t|
30
35
  t.files = DOC_FILES
36
+ t.options = ['-o', '../grape.doc/docs']
31
37
  end
32
38
 
33
- namespace :doc do
34
- YARD::Rake::YardocTask.new(:pages) do |t|
35
- t.files = DOC_FILES
36
- t.options = ['-o', '../grape.doc/docs']
37
- end
38
-
39
- namespace :pages do
40
- desc 'Check out gh-pages.'
41
- task :checkout do
42
- dir = File.dirname(__FILE__) + '/../grape.doc'
43
- unless Dir.exist?(dir)
44
- Dir.mkdir(dir)
45
- Dir.chdir(dir) do
46
- system('git init')
47
- system('git remote add origin git@github.com:ruby-grape/grape.git')
48
- system('git pull')
49
- system('git checkout gh-pages')
50
- end
39
+ namespace :pages do
40
+ desc 'Check out gh-pages.'
41
+ task :checkout do
42
+ dir = File.dirname(__FILE__) + '/../grape.doc'
43
+ unless Dir.exist?(dir)
44
+ Dir.mkdir(dir)
45
+ Dir.chdir(dir) do
46
+ system('git init')
47
+ system('git remote add origin git@github.com:ruby-grape/grape.git')
48
+ system('git pull')
49
+ system('git checkout gh-pages')
51
50
  end
52
51
  end
52
+ end
53
53
 
54
- desc 'Generate and publish YARD docs to GitHub pages.'
55
- task publish: ['doc:pages:checkout', 'doc:pages'] do
56
- Dir.chdir(File.dirname(__FILE__) + '/../grape.doc') do
57
- system('git checkout gh-pages')
58
- system('git add .')
59
- system('git add -u')
60
- system("git commit -m 'Generating docs for version #{Grape::VERSION}.'")
61
- system('git push origin gh-pages')
62
- end
54
+ desc 'Generate and publish YARD docs to GitHub pages.'
55
+ task publish: ['doc:pages:checkout', 'doc:pages'] do
56
+ Dir.chdir(File.dirname(__FILE__) + '/../grape.doc') do
57
+ system('git checkout gh-pages')
58
+ system('git add .')
59
+ system('git add -u')
60
+ system("git commit -m 'Generating docs for version #{Grape::VERSION}.'")
61
+ system('git push origin gh-pages')
63
62
  end
64
63
  end
65
64
  end
66
- rescue LoadError
67
- puts 'You need to install YARD.'
68
65
  end
@@ -1,6 +1,37 @@
1
1
  Upgrading Grape
2
2
  ===============
3
3
 
4
+ ### Upgrading to >= 0.14.0
5
+
6
+ #### Changes to availability of DSL methods in filters
7
+
8
+ The `#declared` method of the route DSL is no longer available in the `before` filter. Using `declared` in a `before` filter will now raise `Grape::DSL::InsideRoute::MethodNotYetAvailable`.
9
+
10
+ See [#1074](https://github.com/ruby-grape/grape/issues/1074) for discussion of the issue.
11
+
12
+ #### Changes to header versioning and invalid header version handling
13
+
14
+ Identical endpoints with different versions now work correctly. A regression introduced in Grape 0.11.0 caused all but the first-mounted version for such an endpoint to wrongly throw an `InvalidAcceptHeader`. As a side effect, requests with a correct vendor but invalid version can no longer be rescued from a `rescue_from` block.
15
+
16
+ See [#1114](https://github.com/ruby-grape/grape/pull/1114) for more information.
17
+
18
+ #### Bypasses formatters when status code indicates no content
19
+
20
+ To be consistent with rack and it's handling of standard responses
21
+ associated with no content, both default and custom formatters will now
22
+ be bypassed when processing responses for status codes defined [by rack](https://github.com/rack/rack/blob/master/lib/rack/utils.rb#L567)
23
+
24
+ See [#1190](https://github.com/ruby-grape/grape/pull/1190) for more information.
25
+
26
+ #### Redirects respond as plain text with message
27
+
28
+ `#redirect` now uses `text/plain` regardless of whether that format has
29
+ been enabled. This prevents formatters from attempting to serialize the
30
+ message body and allows for a descriptive message body to be provided - and
31
+ optionally overridden - that better fulfills the theme of the HTTP spec.
32
+
33
+ See [#1194](https://github.com/ruby-grape/grape/pull/1194) for more information.
34
+
4
35
  ### Upgrading to >= 0.12.0
5
36
 
6
37
  #### Changes in middleware
@@ -0,0 +1,27 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ require 'grape'
3
+ require 'benchmark/ips'
4
+
5
+ class API < Grape::API
6
+ prefix :api
7
+ version 'v1', using: :path
8
+ get '/' do
9
+ 'hello'
10
+ end
11
+ end
12
+
13
+ options = {
14
+ method: 'GET'
15
+ }
16
+
17
+ env = Rack::MockRequest.env_for('/api/v1', options)
18
+
19
+ 10.times do |i|
20
+ env["HTTP_HEADER#{i}"] = '123'
21
+ end
22
+
23
+ Benchmark.ips do |ips|
24
+ ips.report('simple') do
25
+ API.call env
26
+ end
27
+ end
@@ -0,0 +1,13 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gem 'rack', '1.5.2'
6
+
7
+ group :development, :test do
8
+ gem 'guard'
9
+ gem 'guard-rspec'
10
+ gem 'guard-rubocop'
11
+ end
12
+
13
+ gemspec path: '../'
@@ -3,12 +3,12 @@
3
3
  source 'https://rubygems.org'
4
4
 
5
5
  gem 'rails', '3.2.19'
6
+ gem 'rack-cache', '<= 1.2'
6
7
 
7
8
  group :development, :test do
8
- gem 'rubocop', '~> 0.31.0'
9
9
  gem 'guard'
10
10
  gem 'guard-rspec'
11
11
  gem 'guard-rubocop'
12
12
  end
13
13
 
14
- gemspec :path => '../'
14
+ gemspec path: '../'
@@ -5,10 +5,9 @@ source 'https://rubygems.org'
5
5
  gem 'rails', '4.1.6'
6
6
 
7
7
  group :development, :test do
8
- gem 'rubocop', '~> 0.31.0'
9
8
  gem 'guard'
10
9
  gem 'guard-rspec'
11
10
  gem 'guard-rubocop'
12
11
  end
13
12
 
14
- gemspec :path => '../'
13
+ gemspec path: '../'
@@ -31,11 +31,12 @@ Gem::Specification.new do |s|
31
31
  s.add_development_dependency 'bundler'
32
32
  s.add_development_dependency 'cookiejar'
33
33
  s.add_development_dependency 'rack-contrib'
34
- s.add_development_dependency 'mime-types'
34
+ s.add_development_dependency 'mime-types', '< 3.0'
35
35
  s.add_development_dependency 'appraisal'
36
+ s.add_development_dependency 'benchmark-ips'
37
+ s.add_development_dependency 'rubocop', '0.35.1'
36
38
 
37
- s.files = `git ls-files`.split("\n")
38
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
39
- s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
39
+ s.files = Dir['**/*'].keep_if { |file| File.file?(file) }
40
+ s.test_files = Dir['spec/**/*']
40
41
  s.require_paths = ['lib']
41
42
  end
@@ -9,19 +9,21 @@ require 'hashie'
9
9
  require 'set'
10
10
  require 'active_support/version'
11
11
  require 'active_support/core_ext/hash/indifferent_access'
12
- require 'active_support/ordered_hash'
13
- require 'active_support/core_ext/object/conversions'
12
+ require 'active_support/core_ext/object/blank'
14
13
  require 'active_support/core_ext/array/extract_options'
14
+ require 'active_support/core_ext/array/wrap'
15
15
  require 'active_support/core_ext/hash/deep_merge'
16
+ require 'active_support/core_ext/hash/reverse_merge'
16
17
  require 'active_support/core_ext/hash/except'
17
18
  require 'active_support/dependencies/autoload'
18
19
  require 'active_support/notifications'
19
20
  require 'multi_json'
20
21
  require 'multi_xml'
21
- require 'virtus'
22
22
  require 'i18n'
23
23
  require 'thread'
24
24
 
25
+ require 'virtus'
26
+
25
27
  I18n.load_path << File.expand_path('../grape/locale/en.yml', __FILE__)
26
28
 
27
29
  module Grape
@@ -38,7 +40,8 @@ module Grape
38
40
 
39
41
  autoload :Cookies
40
42
  autoload :Validations
41
- autoload :Request, 'grape/http/request'
43
+ autoload :Request
44
+ autoload :Env, 'grape/util/env'
42
45
  end
43
46
 
44
47
  module Http
@@ -67,6 +70,7 @@ module Grape
67
70
  autoload :UnsupportedGroupTypeError, 'grape/exceptions/unsupported_group_type'
68
71
  autoload :InvalidMessageBody
69
72
  autoload :InvalidAcceptHeader
73
+ autoload :InvalidVersionHeader
70
74
  end
71
75
 
72
76
  module ErrorFormatter
@@ -156,7 +160,6 @@ module Grape
156
160
  end
157
161
 
158
162
  require 'grape/util/content_types'
159
- require 'grape/util/parameter_types'
160
163
 
161
164
  require 'grape/validations/validators/base'
162
165
  require 'grape/validations/attributes_iterator'
@@ -171,5 +174,6 @@ require 'grape/validations/validators/regexp'
171
174
  require 'grape/validations/validators/values'
172
175
  require 'grape/validations/params_scope'
173
176
  require 'grape/validations/validators/all_or_none'
177
+ require 'grape/validations/types'
174
178
 
175
179
  require 'grape/version'
@@ -64,6 +64,9 @@ module Grape
64
64
  end
65
65
 
66
66
  config_class.configure(&config_block)
67
+ unless options.empty?
68
+ warn '[DEPRECATION] Passing a options hash and a block to `desc` is deprecated. Move all hash options to block.'
69
+ end
67
70
  options = config_class.settings
68
71
  else
69
72
  options = options.merge(description: description)
@@ -94,8 +97,8 @@ module Grape
94
97
 
95
98
  # Merge multiple layers of settings into one hash.
96
99
  def stacked_hash_to_hash(settings)
97
- return nil if settings.nil? || settings.blank?
98
- settings.each_with_object(ActiveSupport::OrderedHash.new) { |value, result| result.deep_merge!(value) }
100
+ return if settings.blank?
101
+ settings.each_with_object({}) { |value, result| result.deep_merge!(value) }
99
102
  end
100
103
 
101
104
  # Returns an object which configures itself via an instance-context DSL.
@@ -29,6 +29,7 @@ module Grape
29
29
  def helpers(new_mod = nil, &block)
30
30
  if block_given? || new_mod
31
31
  mod = new_mod || Module.new
32
+ define_boolean_in_mod(mod)
32
33
  if new_mod
33
34
  inject_api_helpers_to_mod(new_mod) if new_mod.is_a?(BaseHelper)
34
35
  end
@@ -51,6 +52,11 @@ module Grape
51
52
 
52
53
  protected
53
54
 
55
+ def define_boolean_in_mod(mod)
56
+ return if defined? mod::Boolean
57
+ mod.const_set('Boolean', Virtus::Attribute::Boolean)
58
+ end
59
+
54
60
  def inject_api_helpers_to_mod(mod, &_block)
55
61
  mod.extend(BaseHelper)
56
62
  yield if block_given?
@@ -75,9 +81,8 @@ module Grape
75
81
  protected
76
82
 
77
83
  def process_named_params
78
- if @named_params && @named_params.any?
79
- api.namespace_stackable(:named_params, @named_params)
80
- end
84
+ return unless @named_params && @named_params.any?
85
+ api.namespace_stackable(:named_params, @named_params)
81
86
  end
82
87
  end
83
88
  end
@@ -6,57 +6,74 @@ module Grape
6
6
  extend ActiveSupport::Concern
7
7
  include Grape::DSL::Settings
8
8
 
9
- # A filtering method that will return a hash
10
- # consisting only of keys that have been declared by a
11
- # `params` statement against the current/target endpoint or parent
12
- # namespaces.
13
- #
14
- # @param params [Hash] The initial hash to filter. Usually this will just be `params`
15
- # @param options [Hash] Can pass `:include_missing`, `:stringify` and `:include_parent_namespaces`
16
- # options. `:include_parent_namespaces` defaults to true, hence must be set to false if
17
- # you want only to return params declared against the current/target endpoint.
18
- def declared(params, options = {}, declared_params = nil)
19
- options[:include_missing] = true unless options.key?(:include_missing)
20
- options[:include_parent_namespaces] = true unless options.key?(:include_parent_namespaces)
21
-
22
- if declared_params.nil?
23
- declared_params = (!options[:include_parent_namespaces] ? route_setting(:declared_params) :
24
- (route_setting(:saved_declared_params) || [])).flatten(1) || []
25
- end
9
+ # Denotes a situation where a DSL method has been invoked in a
10
+ # filter which it should not yet be available in
11
+ class MethodNotYetAvailable < StandardError; end
26
12
 
27
- unless declared_params
28
- fail ArgumentError, 'Tried to filter for declared parameters but none exist.'
29
- end
13
+ # @param type [Symbol] The type of filter for which evaluation has been
14
+ # completed
15
+ # @return [Module] A module containing method overrides suitable for the
16
+ # position in the filter evaluation sequence denoted by +type+. This
17
+ # defaults to an empty module if no overrides are defined for the given
18
+ # filter +type+.
19
+ def self.post_filter_methods(type)
20
+ @post_filter_modules ||= { before: PostBeforeFilter }
21
+ @post_filter_modules[type]
22
+ end
30
23
 
31
- if params.is_a? Array
32
- params.map do |param|
33
- declared(param || {}, options, declared_params)
34
- end
35
- else
36
- declared_params.inject(Hashie::Mash.new) do |hash, key|
37
- key = { key => nil } unless key.is_a? Hash
24
+ # Methods which should not be available in filters until the before filter
25
+ # has completed
26
+ module PostBeforeFilter
27
+ def declared(params, options = {}, declared_params = nil)
28
+ options = options.reverse_merge(include_missing: true, include_parent_namespaces: true)
38
29
 
39
- key.each_pair do |parent, children|
40
- output_key = options[:stringify] ? parent.to_s : parent.to_sym
30
+ declared_params ||= (!options[:include_parent_namespaces] ? route_setting(:declared_params) : (route_setting(:saved_declared_params) || [])).flatten(1) || []
41
31
 
42
- next unless options[:include_missing] || params.key?(parent)
32
+ fail ArgumentError, 'Tried to filter for declared parameters but none exist.' unless declared_params
43
33
 
44
- hash[output_key] = if children
45
- children_params = params[parent] || (children.is_a?(Array) ? [] : {})
46
- declared(children_params, options, Array(children))
47
- else
48
- params[parent]
49
- end
34
+ if params.is_a? Array
35
+ params.map do |param|
36
+ declared(param || {}, options, declared_params)
50
37
  end
38
+ else
39
+ declared_params.each_with_object(Hashie::Mash.new) do |key, hash|
40
+ key = { key => nil } unless key.is_a? Hash
51
41
 
52
- hash
42
+ key.each_pair do |parent, children|
43
+ output_key = options[:stringify] ? parent.to_s : parent.to_sym
44
+
45
+ next unless options[:include_missing] || params.key?(parent)
46
+
47
+ hash[output_key] = if children
48
+ children_params = params[parent] || (children.is_a?(Array) ? [] : {})
49
+ declared(children_params, options, Array(children))
50
+ else
51
+ params[parent]
52
+ end
53
+ end
54
+ end
53
55
  end
54
56
  end
55
57
  end
56
58
 
59
+ # A filtering method that will return a hash
60
+ # consisting only of keys that have been declared by a
61
+ # `params` statement against the current/target endpoint or parent
62
+ # namespaces.
63
+ #
64
+ # @see +PostBeforeFilter#declared+
65
+ #
66
+ # @param params [Hash] The initial hash to filter. Usually this will just be `params`
67
+ # @param options [Hash] Can pass `:include_missing`, `:stringify` and `:include_parent_namespaces`
68
+ # options. `:include_parent_namespaces` defaults to true, hence must be set to false if
69
+ # you want only to return params declared against the current/target endpoint.
70
+ def declared(*)
71
+ fail MethodNotYetAvailable, '#declared is not available prior to parameter validation.'
72
+ end
73
+
57
74
  # The API version as specified in the URL.
58
75
  def version
59
- env['api.version']
76
+ env[Grape::Env::API_VERSION]
60
77
  end
61
78
 
62
79
  # End the request and display an error to the
@@ -74,19 +91,25 @@ module Grape
74
91
  # @param url [String] The url to be redirect.
75
92
  # @param options [Hash] The options used when redirect.
76
93
  # :permanent, default false.
94
+ # :body, default a short message including the URL.
77
95
  def redirect(url, options = {})
78
- merged_options = { permanent: false }.merge(options)
79
- if merged_options[:permanent]
96
+ permanent = options.fetch(:permanent, false)
97
+ body_message = options.fetch(:body, nil)
98
+ if permanent
80
99
  status 301
100
+ body_message ||= "This resource has been moved permanently to #{url}."
81
101
  else
82
102
  if env[Grape::Http::Headers::HTTP_VERSION] == 'HTTP/1.1' && request.request_method.to_s.upcase != Grape::Http::Headers::GET
83
103
  status 303
104
+ body_message ||= "An alternate resource is located at #{url}."
84
105
  else
85
106
  status 302
107
+ body_message ||= "This resource has been moved temporarily to #{url}."
86
108
  end
87
109
  end
88
110
  header 'Location', url
89
- body ''
111
+ content_type 'text/plain'
112
+ body body_message
90
113
  end
91
114
 
92
115
  # Set or retrieve the HTTP status code.
@@ -241,7 +264,7 @@ module Grape
241
264
  if key
242
265
  representation = (@body || {}).merge(key => representation)
243
266
  elsif entity_class.present? && @body
244
- fail ArgumentError, "Representation of type #{representation.class} cannot be merged." unless representation.respond_to?('merge')
267
+ fail ArgumentError, "Representation of type #{representation.class} cannot be merged." unless representation.respond_to?(:merge)
245
268
  representation = @body.merge(representation)
246
269
  end
247
270
 
@@ -257,7 +280,7 @@ module Grape
257
280
  # route.route_description
258
281
  # end
259
282
  def route
260
- env['rack.routing_args'][:route_info]
283
+ env[Grape::Env::RACK_ROUTING_ARGS][:route_info]
261
284
  end
262
285
 
263
286
  # Attempt to locate the Entity class for a given object, if not given
@@ -293,7 +316,7 @@ module Grape
293
316
  # the given entity_class.
294
317
  def entity_representation_for(entity_class, object, options)
295
318
  embeds = { env: env }
296
- embeds[:version] = env['api.version'] if env['api.version']
319
+ embeds[:version] = env[Grape::Env::API_VERSION] if env[Grape::Env::API_VERSION]
297
320
  entity_class.represent(object, embeds.merge(options))
298
321
  end
299
322
  end