grape 0.6.1 → 0.7.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 (79) hide show
  1. checksums.yaml +9 -9
  2. data/.gitignore +7 -0
  3. data/.rubocop.yml +5 -0
  4. data/.travis.yml +3 -3
  5. data/CHANGELOG.md +42 -3
  6. data/CONTRIBUTING.md +118 -0
  7. data/Gemfile +4 -4
  8. data/README.md +312 -52
  9. data/Rakefile +6 -1
  10. data/UPGRADING.md +124 -0
  11. data/lib/grape.rb +2 -0
  12. data/lib/grape/api.rb +95 -44
  13. data/lib/grape/cookies.rb +0 -2
  14. data/lib/grape/endpoint.rb +63 -39
  15. data/lib/grape/error_formatter/base.rb +0 -3
  16. data/lib/grape/error_formatter/json.rb +0 -2
  17. data/lib/grape/error_formatter/txt.rb +0 -2
  18. data/lib/grape/error_formatter/xml.rb +0 -2
  19. data/lib/grape/exceptions/base.rb +0 -2
  20. data/lib/grape/exceptions/incompatible_option_values.rb +0 -3
  21. data/lib/grape/exceptions/invalid_formatter.rb +0 -3
  22. data/lib/grape/exceptions/invalid_versioner_option.rb +0 -4
  23. data/lib/grape/exceptions/invalid_with_option_for_represent.rb +0 -2
  24. data/lib/grape/exceptions/missing_mime_type.rb +0 -4
  25. data/lib/grape/exceptions/missing_option.rb +0 -3
  26. data/lib/grape/exceptions/missing_vendor_option.rb +0 -3
  27. data/lib/grape/exceptions/unknown_options.rb +0 -4
  28. data/lib/grape/exceptions/unknown_validator.rb +0 -2
  29. data/lib/grape/exceptions/validation_errors.rb +6 -5
  30. data/lib/grape/formatter/base.rb +0 -3
  31. data/lib/grape/formatter/json.rb +0 -2
  32. data/lib/grape/formatter/serializable_hash.rb +15 -16
  33. data/lib/grape/formatter/txt.rb +0 -2
  34. data/lib/grape/formatter/xml.rb +0 -2
  35. data/lib/grape/http/request.rb +2 -4
  36. data/lib/grape/locale/en.yml +1 -1
  37. data/lib/grape/middleware/auth/oauth2.rb +15 -6
  38. data/lib/grape/middleware/base.rb +7 -7
  39. data/lib/grape/middleware/error.rb +11 -6
  40. data/lib/grape/middleware/formatter.rb +80 -78
  41. data/lib/grape/middleware/globals.rb +13 -0
  42. data/lib/grape/middleware/versioner/accept_version_header.rb +0 -2
  43. data/lib/grape/middleware/versioner/header.rb +5 -3
  44. data/lib/grape/middleware/versioner/param.rb +2 -4
  45. data/lib/grape/middleware/versioner/path.rb +3 -4
  46. data/lib/grape/namespace.rb +0 -1
  47. data/lib/grape/parser/base.rb +0 -3
  48. data/lib/grape/parser/json.rb +0 -2
  49. data/lib/grape/parser/xml.rb +0 -2
  50. data/lib/grape/path.rb +1 -3
  51. data/lib/grape/route.rb +0 -3
  52. data/lib/grape/util/hash_stack.rb +1 -1
  53. data/lib/grape/validations.rb +72 -22
  54. data/lib/grape/validations/coerce.rb +5 -4
  55. data/lib/grape/validations/default.rb +5 -3
  56. data/lib/grape/validations/presence.rb +1 -1
  57. data/lib/grape/validations/regexp.rb +0 -2
  58. data/lib/grape/validations/values.rb +2 -1
  59. data/lib/grape/version.rb +1 -1
  60. data/spec/grape/api_spec.rb +385 -96
  61. data/spec/grape/endpoint_spec.rb +162 -15
  62. data/spec/grape/entity_spec.rb +25 -0
  63. data/spec/grape/exceptions/validation_errors_spec.rb +19 -0
  64. data/spec/grape/middleware/auth/oauth2_spec.rb +60 -15
  65. data/spec/grape/middleware/base_spec.rb +3 -8
  66. data/spec/grape/middleware/error_spec.rb +2 -2
  67. data/spec/grape/middleware/exception_spec.rb +4 -4
  68. data/spec/grape/middleware/formatter_spec.rb +7 -4
  69. data/spec/grape/middleware/versioner/param_spec.rb +8 -7
  70. data/spec/grape/path_spec.rb +24 -14
  71. data/spec/grape/util/hash_stack_spec.rb +8 -8
  72. data/spec/grape/validations/coerce_spec.rb +75 -33
  73. data/spec/grape/validations/default_spec.rb +57 -0
  74. data/spec/grape/validations/presence_spec.rb +13 -11
  75. data/spec/grape/validations/values_spec.rb +76 -2
  76. data/spec/grape/validations_spec.rb +443 -20
  77. data/spec/spec_helper.rb +2 -2
  78. data/spec/support/content_type_helpers.rb +11 -0
  79. metadata +9 -38
@@ -1,9 +1,7 @@
1
1
  module Grape
2
2
  module ErrorFormatter
3
3
  module Base
4
-
5
4
  class << self
6
-
7
5
  FORMATTERS = {
8
6
  serializable_hash: Grape::ErrorFormatter::Json,
9
7
  json: Grape::ErrorFormatter::Json,
@@ -27,7 +25,6 @@ module Grape
27
25
  spec
28
26
  end
29
27
  end
30
-
31
28
  end
32
29
  end
33
30
  end
@@ -2,7 +2,6 @@ module Grape
2
2
  module ErrorFormatter
3
3
  module Json
4
4
  class << self
5
-
6
5
  def call(message, backtrace, options = {}, env = nil)
7
6
  result = message.is_a?(Hash) ? message : { error: message }
8
7
  if (options[:rescue_options] || {})[:backtrace] && backtrace && !backtrace.empty?
@@ -10,7 +9,6 @@ module Grape
10
9
  end
11
10
  MultiJson.dump(result)
12
11
  end
13
-
14
12
  end
15
13
  end
16
14
  end
@@ -2,7 +2,6 @@ module Grape
2
2
  module ErrorFormatter
3
3
  module Txt
4
4
  class << self
5
-
6
5
  def call(message, backtrace, options = {}, env = nil)
7
6
  result = message.is_a?(Hash) ? MultiJson.dump(message) : message
8
7
  if (options[:rescue_options] || {})[:backtrace] && backtrace && !backtrace.empty?
@@ -11,7 +10,6 @@ module Grape
11
10
  end
12
11
  result
13
12
  end
14
-
15
13
  end
16
14
  end
17
15
  end
@@ -2,7 +2,6 @@ module Grape
2
2
  module ErrorFormatter
3
3
  module Xml
4
4
  class << self
5
-
6
5
  def call(message, backtrace, options = {}, env = nil)
7
6
  result = message.is_a?(Hash) ? message : { message: message }
8
7
  if (options[:rescue_options] || {})[:backtrace] && backtrace && !backtrace.empty?
@@ -10,7 +9,6 @@ module Grape
10
9
  end
11
10
  result.respond_to?(:to_xml) ? result.to_xml(root: :error) : result.to_s
12
11
  end
13
-
14
12
  end
15
13
  end
16
14
  end
@@ -1,7 +1,6 @@
1
1
  module Grape
2
2
  module Exceptions
3
3
  class Base < StandardError
4
-
5
4
  BASE_MESSAGES_KEY = 'grape.errors.messages'
6
5
  BASE_ATTRIBUTES_KEY = 'grape.errors.attributes'
7
6
  FALLBACK_LOCALE = :en
@@ -62,7 +61,6 @@ module Grape
62
61
  message = ::I18n.translate(key, options)
63
62
  message.present? ? message : ::I18n.translate(key, options.merge(locale: FALLBACK_LOCALE))
64
63
  end
65
-
66
64
  end
67
65
  end
68
66
  end
@@ -2,12 +2,9 @@
2
2
  module Grape
3
3
  module Exceptions
4
4
  class IncompatibleOptionValues < Base
5
-
6
5
  def initialize(option1, value1, option2, value2)
7
6
  super(message: compose_message("incompatible_option_values", option1: option1, value1: value1, option2: option2, value2: value2))
8
7
  end
9
-
10
8
  end
11
-
12
9
  end
13
10
  end
@@ -2,12 +2,9 @@
2
2
  module Grape
3
3
  module Exceptions
4
4
  class InvalidFormatter < Base
5
-
6
5
  def initialize(klass, to_format)
7
6
  super(message: compose_message("invalid_formatter", klass: klass, to_format: to_format))
8
7
  end
9
-
10
8
  end
11
-
12
9
  end
13
10
  end
@@ -1,14 +1,10 @@
1
1
  # encoding: utf-8
2
2
  module Grape
3
3
  module Exceptions
4
-
5
4
  class InvalidVersionerOption < Base
6
-
7
5
  def initialize(strategy)
8
6
  super(message: compose_message("invalid_versioner_option", strategy: strategy))
9
7
  end
10
-
11
8
  end
12
-
13
9
  end
14
10
  end
@@ -2,11 +2,9 @@
2
2
  module Grape
3
3
  module Exceptions
4
4
  class InvalidWithOptionForRepresent < Base
5
-
6
5
  def initialize
7
6
  super(message: compose_message("invalid_with_option_for_represent"))
8
7
  end
9
-
10
8
  end
11
9
  end
12
10
  end
@@ -2,13 +2,9 @@
2
2
  module Grape
3
3
  module Exceptions
4
4
  class MissingMimeType < Base
5
-
6
5
  def initialize(new_format)
7
6
  super(message: compose_message("missing_mime_type", new_format: new_format))
8
7
  end
9
-
10
8
  end
11
-
12
9
  end
13
-
14
10
  end
@@ -2,12 +2,9 @@
2
2
  module Grape
3
3
  module Exceptions
4
4
  class MissingOption < Base
5
-
6
5
  def initialize(option)
7
6
  super(message: compose_message("missing_option", option: option))
8
7
  end
9
-
10
8
  end
11
-
12
9
  end
13
10
  end
@@ -2,12 +2,9 @@
2
2
  module Grape
3
3
  module Exceptions
4
4
  class MissingVendorOption < Base
5
-
6
5
  def initialize
7
6
  super(message: compose_message("missing_vendor_option"))
8
7
  end
9
-
10
8
  end
11
-
12
9
  end
13
10
  end
@@ -2,13 +2,9 @@
2
2
  module Grape
3
3
  module Exceptions
4
4
  class UnknownOptions < Base
5
-
6
5
  def initialize(options)
7
6
  super(message: compose_message("unknown_options", options: options))
8
7
  end
9
-
10
8
  end
11
-
12
9
  end
13
-
14
10
  end
@@ -2,11 +2,9 @@
2
2
  module Grape
3
3
  module Exceptions
4
4
  class UnknownValidator < Base
5
-
6
5
  def initialize(validator_type)
7
6
  super(message: compose_message("unknown_validator", validator_type: validator_type))
8
7
  end
9
-
10
8
  end
11
9
  end
12
10
  end
@@ -27,15 +27,16 @@ module Grape
27
27
  private
28
28
 
29
29
  def full_messages
30
- map { |attribute, error| full_message(attribute, error) }
30
+ map { |attribute, error| full_message(attribute, error) }.uniq
31
31
  end
32
32
 
33
33
  def full_message(attribute, error)
34
- I18n.t("grape.errors.format".to_sym, {
35
- default: "%{attribute} %{message}",
34
+ I18n.t(
35
+ "grape.errors.format".to_sym,
36
+ default: "%{attribute} %{message}",
36
37
  attribute: translate_attribute(attribute),
37
- message: error.message
38
- })
38
+ message: error.message
39
+ )
39
40
  end
40
41
  end
41
42
  end
@@ -1,9 +1,7 @@
1
1
  module Grape
2
2
  module Formatter
3
3
  module Base
4
-
5
4
  class << self
6
-
7
5
  FORMATTERS = {
8
6
  json: Grape::Formatter::Json,
9
7
  jsonapi: Grape::Formatter::Json,
@@ -27,7 +25,6 @@ module Grape
27
25
  spec
28
26
  end
29
27
  end
30
-
31
28
  end
32
29
  end
33
30
  end
@@ -2,12 +2,10 @@ module Grape
2
2
  module Formatter
3
3
  module Json
4
4
  class << self
5
-
6
5
  def call(object, env)
7
6
  return object.to_json if object.respond_to?(:to_json)
8
7
  MultiJson.dump(object)
9
8
  end
10
-
11
9
  end
12
10
  end
13
11
  end
@@ -2,7 +2,6 @@ module Grape
2
2
  module Formatter
3
3
  module SerializableHash
4
4
  class << self
5
-
6
5
  def call(object, env)
7
6
  return object if object.is_a?(String)
8
7
  return MultiJson.dump(serialize(object)) if serializable?(object)
@@ -12,24 +11,24 @@ module Grape
12
11
 
13
12
  private
14
13
 
15
- def serializable?(object)
16
- object.respond_to?(:serializable_hash) || object.kind_of?(Array) && !object.map { |o| o.respond_to? :serializable_hash }.include?(false) || object.kind_of?(Hash)
17
- end
14
+ def serializable?(object)
15
+ object.respond_to?(:serializable_hash) || object.kind_of?(Array) && !object.map { |o| o.respond_to? :serializable_hash }.include?(false) || object.kind_of?(Hash)
16
+ end
18
17
 
19
- def serialize(object)
20
- if object.respond_to? :serializable_hash
21
- object.serializable_hash
22
- elsif object.kind_of?(Array) && !object.map { |o| o.respond_to? :serializable_hash }.include?(false)
23
- object.map { |o| o.serializable_hash }
24
- elsif object.kind_of?(Hash)
25
- object.inject({}) do |h, (k, v)|
26
- h[k] = serialize(v)
27
- h
28
- end
29
- else
30
- object
18
+ def serialize(object)
19
+ if object.respond_to? :serializable_hash
20
+ object.serializable_hash
21
+ elsif object.kind_of?(Array) && !object.map { |o| o.respond_to? :serializable_hash }.include?(false)
22
+ object.map { |o| o.serializable_hash }
23
+ elsif object.kind_of?(Hash)
24
+ object.inject({}) do |h, (k, v)|
25
+ h[k] = serialize(v)
26
+ h
31
27
  end
28
+ else
29
+ object
32
30
  end
31
+ end
33
32
  end
34
33
  end
35
34
  end
@@ -2,11 +2,9 @@ module Grape
2
2
  module Formatter
3
3
  module Txt
4
4
  class << self
5
-
6
5
  def call(object, env)
7
6
  object.respond_to?(:to_txt) ? object.to_txt : object.to_s
8
7
  end
9
-
10
8
  end
11
9
  end
12
10
  end
@@ -2,12 +2,10 @@ module Grape
2
2
  module Formatter
3
3
  module Xml
4
4
  class << self
5
-
6
5
  def call(object, env)
7
6
  return object.to_xml if object.respond_to?(:to_xml)
8
7
  raise Grape::Exceptions::InvalidFormatter.new(object.class, 'xml')
9
8
  end
10
-
11
9
  end
12
10
  end
13
11
  end
@@ -1,8 +1,7 @@
1
1
  module Grape
2
2
  class Request < Rack::Request
3
-
4
3
  def params
5
- @env['grape.request.params'] = begin
4
+ @params ||= begin
6
5
  params = Hashie::Mash.new(super)
7
6
  if env['rack.routing_args']
8
7
  args = env['rack.routing_args'].dup
@@ -15,7 +14,7 @@ module Grape
15
14
  end
16
15
 
17
16
  def headers
18
- @env['grape.request.headers'] ||= @env.dup.inject({}) do |h, (k, v)|
17
+ @headers ||= env.dup.inject({}) do |h, (k, v)|
19
18
  if k.to_s.start_with? 'HTTP_'
20
19
  k = k[5..-1].gsub('_', '-').downcase.gsub(/^.|[-_\s]./) { |x| x.upcase }
21
20
  h[k] = v
@@ -23,6 +22,5 @@ module Grape
23
22
  h
24
23
  end
25
24
  end
26
-
27
25
  end
28
26
  end
@@ -14,7 +14,7 @@ en:
14
14
  missing_mime_type:
15
15
  problem: 'missing mime type for %{new_format}'
16
16
  resolution:
17
- "you can choose exist mime type from Grape::ContentTypes::CONTENT_TYPES
17
+ "you can choose existing mime type from Grape::ContentTypes::CONTENT_TYPES
18
18
  or add your own with content_type :%{new_format}, 'application/%{new_format}'
19
19
  "
20
20
  invalid_with_option_for_represent:
@@ -5,9 +5,10 @@ module Grape::Middleware::Auth
5
5
  {
6
6
  token_class: 'AccessToken',
7
7
  realm: 'OAuth API',
8
- parameter: %w(bearer_token oauth_token),
8
+ parameter: %w(bearer_token oauth_token access_token),
9
9
  accepted_headers: %w(HTTP_AUTHORIZATION X_HTTP_AUTHORIZATION X-HTTP_AUTHORIZATION REDIRECT_X_HTTP_AUTHORIZATION),
10
- header: [/Bearer (.*)/i, /OAuth (.*)/i]
10
+ header: [/Bearer (.*)/i, /OAuth (.*)/i],
11
+ required: true
11
12
  }
12
13
  end
13
14
 
@@ -15,9 +16,17 @@ module Grape::Middleware::Auth
15
16
  verify_token(token_parameter || token_header)
16
17
  end
17
18
 
19
+ def request
20
+ @request ||= Grape::Request.new(env)
21
+ end
22
+
23
+ def params
24
+ @params ||= request.params
25
+ end
26
+
18
27
  def token_parameter
19
28
  Array(options[:parameter]).each do |p|
20
- return request[p] if request[p]
29
+ return params[p] if params[p]
21
30
  end
22
31
  nil
23
32
  end
@@ -45,7 +54,7 @@ module Grape::Middleware::Auth
45
54
  token = token_class.verify(token)
46
55
  if token
47
56
  if token.respond_to?(:expired?) && token.expired?
48
- error_out(401, 'expired_token')
57
+ error_out(401, 'invalid_grant')
49
58
  else
50
59
  if !token.respond_to?(:permission_for?) || token.permission_for?(env)
51
60
  env['api.token'] = token
@@ -53,8 +62,8 @@ module Grape::Middleware::Auth
53
62
  error_out(403, 'insufficient_scope')
54
63
  end
55
64
  end
56
- else
57
- error_out(401, 'invalid_token')
65
+ elsif !!options[:required]
66
+ error_out(401, 'invalid_grant')
58
67
  end
59
68
  end
60
69
 
@@ -27,14 +27,13 @@ module Grape
27
27
 
28
28
  # @abstract
29
29
  # Called before the application is called in the middleware lifecycle.
30
- def before; end
30
+ def before
31
+ end
32
+
31
33
  # @abstract
32
34
  # Called after the application is called in the middleware lifecycle.
33
35
  # @return [Response, nil] a Rack SPEC response or nil to call the application afterwards.
34
- def after; end
35
-
36
- def request
37
- Grape::Request.new(env)
36
+ def after
38
37
  end
39
38
 
40
39
  def response
@@ -54,9 +53,10 @@ module Grape
54
53
  end
55
54
 
56
55
  def mime_types
57
- content_types.invert
56
+ content_types.each_with_object({}) { |(k, v), types_without_params|
57
+ types_without_params[k] = v.split(';').first
58
+ }.invert
58
59
  end
59
-
60
60
  end
61
61
  end
62
62
  end