grape 0.6.0 → 0.6.1

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 (84) hide show
  1. checksums.yaml +8 -8
  2. data/.rubocop.yml +65 -0
  3. data/.travis.yml +4 -0
  4. data/CHANGELOG.md +17 -1
  5. data/Gemfile +1 -0
  6. data/README.md +16 -8
  7. data/RELEASING.md +105 -0
  8. data/grape.gemspec +1 -1
  9. data/lib/grape.rb +1 -0
  10. data/lib/grape/api.rb +88 -54
  11. data/lib/grape/cookies.rb +4 -6
  12. data/lib/grape/endpoint.rb +81 -69
  13. data/lib/grape/error_formatter/base.rb +5 -4
  14. data/lib/grape/error_formatter/json.rb +3 -3
  15. data/lib/grape/error_formatter/txt.rb +1 -1
  16. data/lib/grape/error_formatter/xml.rb +4 -4
  17. data/lib/grape/exceptions/base.rb +7 -7
  18. data/lib/grape/exceptions/incompatible_option_values.rb +13 -0
  19. data/lib/grape/exceptions/invalid_formatter.rb +1 -1
  20. data/lib/grape/exceptions/invalid_versioner_option.rb +1 -1
  21. data/lib/grape/exceptions/invalid_with_option_for_represent.rb +1 -4
  22. data/lib/grape/exceptions/missing_mime_type.rb +1 -1
  23. data/lib/grape/exceptions/missing_option.rb +1 -1
  24. data/lib/grape/exceptions/missing_vendor_option.rb +1 -1
  25. data/lib/grape/exceptions/unknown_options.rb +1 -1
  26. data/lib/grape/exceptions/unknown_validator.rb +1 -1
  27. data/lib/grape/exceptions/validation.rb +2 -2
  28. data/lib/grape/exceptions/validation_errors.rb +1 -1
  29. data/lib/grape/formatter/base.rb +5 -4
  30. data/lib/grape/formatter/serializable_hash.rb +7 -6
  31. data/lib/grape/http/request.rb +2 -2
  32. data/lib/grape/locale/en.yml +2 -0
  33. data/lib/grape/middleware/auth/base.rb +3 -3
  34. data/lib/grape/middleware/auth/basic.rb +1 -1
  35. data/lib/grape/middleware/auth/oauth2.rb +18 -20
  36. data/lib/grape/middleware/base.rb +1 -1
  37. data/lib/grape/middleware/error.rb +19 -19
  38. data/lib/grape/middleware/filter.rb +3 -3
  39. data/lib/grape/middleware/formatter.rb +29 -23
  40. data/lib/grape/middleware/versioner.rb +1 -1
  41. data/lib/grape/middleware/versioner/accept_version_header.rb +8 -6
  42. data/lib/grape/middleware/versioner/header.rb +16 -14
  43. data/lib/grape/middleware/versioner/param.rb +7 -7
  44. data/lib/grape/middleware/versioner/path.rb +7 -9
  45. data/lib/grape/parser/base.rb +3 -2
  46. data/lib/grape/path.rb +1 -1
  47. data/lib/grape/route.rb +6 -4
  48. data/lib/grape/util/content_types.rb +2 -1
  49. data/lib/grape/util/deep_merge.rb +5 -5
  50. data/lib/grape/util/hash_stack.rb +2 -2
  51. data/lib/grape/validations.rb +34 -30
  52. data/lib/grape/validations/coerce.rb +6 -5
  53. data/lib/grape/validations/default.rb +0 -1
  54. data/lib/grape/validations/presence.rb +1 -1
  55. data/lib/grape/validations/regexp.rb +2 -2
  56. data/lib/grape/validations/values.rb +16 -0
  57. data/lib/grape/version.rb +1 -1
  58. data/spec/grape/api_spec.rb +229 -210
  59. data/spec/grape/endpoint_spec.rb +56 -54
  60. data/spec/grape/entity_spec.rb +31 -33
  61. data/spec/grape/exceptions/missing_mime_type_spec.rb +3 -9
  62. data/spec/grape/middleware/auth/basic_spec.rb +8 -8
  63. data/spec/grape/middleware/auth/digest_spec.rb +5 -5
  64. data/spec/grape/middleware/auth/oauth2_spec.rb +23 -23
  65. data/spec/grape/middleware/base_spec.rb +6 -6
  66. data/spec/grape/middleware/error_spec.rb +11 -15
  67. data/spec/grape/middleware/exception_spec.rb +45 -25
  68. data/spec/grape/middleware/formatter_spec.rb +56 -45
  69. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +25 -25
  70. data/spec/grape/middleware/versioner/header_spec.rb +54 -54
  71. data/spec/grape/middleware/versioner/param_spec.rb +17 -18
  72. data/spec/grape/middleware/versioner/path_spec.rb +6 -6
  73. data/spec/grape/middleware/versioner_spec.rb +1 -1
  74. data/spec/grape/util/hash_stack_spec.rb +26 -27
  75. data/spec/grape/validations/coerce_spec.rb +39 -34
  76. data/spec/grape/validations/default_spec.rb +12 -13
  77. data/spec/grape/validations/presence_spec.rb +18 -22
  78. data/spec/grape/validations/regexp_spec.rb +9 -9
  79. data/spec/grape/validations/values_spec.rb +64 -0
  80. data/spec/grape/validations_spec.rb +127 -70
  81. data/spec/shared/versioning_examples.rb +5 -5
  82. data/spec/support/basic_auth_encode_helpers.rb +0 -1
  83. data/spec/support/versioned_helpers.rb +5 -6
  84. metadata +10 -4
@@ -4,12 +4,9 @@ module Grape
4
4
  class InvalidWithOptionForRepresent < Base
5
5
 
6
6
  def initialize
7
- super(:message => compose_message("invalid_with_option_for_represent"))
7
+ super(message: compose_message("invalid_with_option_for_represent"))
8
8
  end
9
9
 
10
10
  end
11
-
12
11
  end
13
-
14
12
  end
15
-
@@ -4,7 +4,7 @@ module Grape
4
4
  class MissingMimeType < Base
5
5
 
6
6
  def initialize(new_format)
7
- super(:message => compose_message("missing_mime_type", :new_format => new_format))
7
+ super(message: compose_message("missing_mime_type", new_format: new_format))
8
8
  end
9
9
 
10
10
  end
@@ -4,7 +4,7 @@ module Grape
4
4
  class MissingOption < Base
5
5
 
6
6
  def initialize(option)
7
- super(:message => compose_message("missing_option", :option => option))
7
+ super(message: compose_message("missing_option", option: option))
8
8
  end
9
9
 
10
10
  end
@@ -4,7 +4,7 @@ module Grape
4
4
  class MissingVendorOption < Base
5
5
 
6
6
  def initialize
7
- super(:message => compose_message("missing_vendor_option"))
7
+ super(message: compose_message("missing_vendor_option"))
8
8
  end
9
9
 
10
10
  end
@@ -4,7 +4,7 @@ module Grape
4
4
  class UnknownOptions < Base
5
5
 
6
6
  def initialize(options)
7
- super(:message => compose_message("unknown_options", :options => options))
7
+ super(message: compose_message("unknown_options", options: options))
8
8
  end
9
9
 
10
10
  end
@@ -4,7 +4,7 @@ module Grape
4
4
  class UnknownValidator < Base
5
5
 
6
6
  def initialize(validator_type)
7
- super(:message => compose_message("unknown_validator", :validator_type => validator_type))
7
+ super(message: compose_message("unknown_validator", validator_type: validator_type))
8
8
  end
9
9
 
10
10
  end
@@ -14,8 +14,8 @@ module Grape
14
14
 
15
15
  # remove all the unnecessary stuff from Grape::Exceptions::Base like status
16
16
  # and headers when converting a validation error to json or string
17
- def as_json(*a)
18
- self.to_s
17
+ def as_json(*args)
18
+ to_s
19
19
  end
20
20
 
21
21
  def to_s
@@ -31,7 +31,7 @@ module Grape
31
31
  end
32
32
 
33
33
  def full_message(attribute, error)
34
- I18n.t(:"grape.errors.format", {
34
+ I18n.t("grape.errors.format".to_sym, {
35
35
  default: "%{attribute} %{message}",
36
36
  attribute: translate_attribute(attribute),
37
37
  message: error.message
@@ -5,10 +5,11 @@ module Grape
5
5
  class << self
6
6
 
7
7
  FORMATTERS = {
8
- :json => Grape::Formatter::Json,
9
- :serializable_hash => Grape::Formatter::SerializableHash,
10
- :txt => Grape::Formatter::Txt,
11
- :xml => Grape::Formatter::Xml
8
+ json: Grape::Formatter::Json,
9
+ jsonapi: Grape::Formatter::Json,
10
+ serializable_hash: Grape::Formatter::SerializableHash,
11
+ txt: Grape::Formatter::Txt,
12
+ xml: Grape::Formatter::Xml
12
13
  }
13
14
 
14
15
  def formatters(options)
@@ -13,18 +13,19 @@ module Grape
13
13
  private
14
14
 
15
15
  def serializable?(object)
16
- object.respond_to?(:serializable_hash) ||
17
- object.kind_of?(Array) && !object.map {|o| o.respond_to? :serializable_hash }.include?(false) ||
18
- object.kind_of?(Hash)
16
+ object.respond_to?(:serializable_hash) || object.kind_of?(Array) && !object.map { |o| o.respond_to? :serializable_hash }.include?(false) || object.kind_of?(Hash)
19
17
  end
20
18
 
21
19
  def serialize(object)
22
20
  if object.respond_to? :serializable_hash
23
21
  object.serializable_hash
24
- elsif object.kind_of?(Array) && !object.map {|o| o.respond_to? :serializable_hash }.include?(false)
25
- object.map {|o| o.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 }
26
24
  elsif object.kind_of?(Hash)
27
- object.inject({}) { |h,(k,v)| h[k] = serialize(v); h }
25
+ object.inject({}) do |h, (k, v)|
26
+ h[k] = serialize(v)
27
+ h
28
+ end
28
29
  else
29
30
  object
30
31
  end
@@ -15,13 +15,13 @@ module Grape
15
15
  end
16
16
 
17
17
  def headers
18
- @env['grape.request.headers'] ||= @env.dup.inject({}) { |h, (k, v)|
18
+ @env['grape.request.headers'] ||= @env.dup.inject({}) do |h, (k, v)|
19
19
  if k.to_s.start_with? 'HTTP_'
20
20
  k = k[5..-1].gsub('_', '-').downcase.gsub(/^.|[-_\s]./) { |x| x.upcase }
21
21
  h[k] = v
22
22
  end
23
23
  h
24
- }
24
+ end
25
25
  end
26
26
 
27
27
  end
@@ -6,6 +6,7 @@ en:
6
6
  coerce: 'is invalid'
7
7
  presence: 'is missing'
8
8
  regexp: 'is invalid'
9
+ values: 'does not have a valid value'
9
10
  missing_vendor_option:
10
11
  problem: 'missing :vendor option.'
11
12
  summary: 'when version using header, you must specify :vendor option. '
@@ -26,3 +27,4 @@ en:
26
27
  resolution: 'available strategy for :using is :path, :header, :param'
27
28
  unknown_validator: 'unknown validator: %{validator_type}'
28
29
  unknown_options: 'unknown options: %{options}'
30
+ incompatible_option_values: '%{option1}: %{value1} is incompatible with %{option2}: %{value2}'
@@ -12,16 +12,16 @@ module Grape
12
12
  end
13
13
 
14
14
  def base_request
15
- raise NotImplementedError.new("You must implement base_request.")
15
+ raise NotImplementedError, "You must implement base_request."
16
16
  end
17
17
 
18
18
  def credentials
19
- base_request.provided?? base_request.credentials : [nil, nil]
19
+ base_request.provided? ? base_request.credentials : [nil, nil]
20
20
  end
21
21
 
22
22
  def before
23
23
  unless authenticator.call(*credentials)
24
- throw :error, :status => 401, :message => "API Authorization Failed."
24
+ throw :error, status: 401, message: "API Authorization Failed."
25
25
  end
26
26
  end
27
27
  end
@@ -10,4 +10,4 @@ module Grape
10
10
  end
11
11
  end
12
12
  end
13
- end
13
+ end
@@ -3,14 +3,14 @@ module Grape::Middleware::Auth
3
3
  class OAuth2 < Grape::Middleware::Base
4
4
  def default_options
5
5
  {
6
- :token_class => 'AccessToken',
7
- :realm => 'OAuth API',
8
- :parameter => %w(bearer_token oauth_token),
9
- :accepted_headers => %w(HTTP_AUTHORIZATION X_HTTP_AUTHORIZATION X-HTTP_AUTHORIZATION REDIRECT_X_HTTP_AUTHORIZATION),
10
- :header => [/Bearer (.*)/i, /OAuth (.*)/i]
6
+ token_class: 'AccessToken',
7
+ realm: 'OAuth API',
8
+ parameter: %w(bearer_token oauth_token),
9
+ accepted_headers: %w(HTTP_AUTHORIZATION X_HTTP_AUTHORIZATION X-HTTP_AUTHORIZATION REDIRECT_X_HTTP_AUTHORIZATION),
10
+ header: [/Bearer (.*)/i, /OAuth (.*)/i]
11
11
  }
12
12
  end
13
-
13
+
14
14
  def before
15
15
  verify_token(token_parameter || token_header)
16
16
  end
@@ -25,9 +25,7 @@ module Grape::Middleware::Auth
25
25
  def token_header
26
26
  return false unless authorization_header
27
27
  Array(options[:header]).each do |regexp|
28
- if authorization_header =~ regexp
29
- return $1
30
- end
28
+ return $1 if authorization_header =~ regexp
31
29
  end
32
30
  nil
33
31
  end
@@ -38,13 +36,14 @@ module Grape::Middleware::Auth
38
36
  end
39
37
  nil
40
38
  end
41
-
39
+
42
40
  def token_class
43
- @klass ||= eval(options[:token_class])
41
+ @klass ||= eval(options[:token_class]) # rubocop:disable Eval
44
42
  end
45
-
43
+
46
44
  def verify_token(token)
47
- if token = token_class.verify(token)
45
+ token = token_class.verify(token)
46
+ if token
48
47
  if token.respond_to?(:expired?) && token.expired?
49
48
  error_out(401, 'expired_token')
50
49
  else
@@ -58,15 +57,14 @@ module Grape::Middleware::Auth
58
57
  error_out(401, 'invalid_token')
59
58
  end
60
59
  end
61
-
60
+
62
61
  def error_out(status, error)
63
62
  throw :error,
64
- :message => error,
65
- :status => status,
66
- :headers => {
67
- 'WWW-Authenticate' => "OAuth realm='#{options[:realm]}', error='#{error}'"
68
- }
63
+ message: error,
64
+ status: status,
65
+ headers: {
66
+ 'WWW-Authenticate' => "OAuth realm='#{options[:realm]}', error='#{error}'"
67
+ }
69
68
  end
70
69
  end
71
70
  end
72
-
@@ -34,7 +34,7 @@ module Grape
34
34
  def after; end
35
35
 
36
36
  def request
37
- Grape::Request.new(self.env)
37
+ Grape::Request.new(env)
38
38
  end
39
39
 
40
40
  def response
@@ -6,15 +6,15 @@ module Grape
6
6
 
7
7
  def default_options
8
8
  {
9
- :default_status => 403, # default status returned on error
10
- :default_message => "",
11
- :format => :txt,
12
- :formatters => {},
13
- :error_formatters => {},
14
- :rescue_all => false, # true to rescue all exceptions
15
- :rescue_options => { :backtrace => false }, # true to display backtrace
16
- :rescue_handlers => {}, # rescue handler blocks
17
- :rescued_errors => []
9
+ default_status: 403, # default status returned on error
10
+ default_message: "",
11
+ format: :txt,
12
+ formatters: {},
13
+ error_formatters: {},
14
+ rescue_all: false, # true to rescue all exceptions
15
+ rescue_options: { backtrace: false }, # true to display backtrace
16
+ rescue_handlers: {}, # rescue handler blocks
17
+ rescued_errors: []
18
18
  }
19
19
  end
20
20
 
@@ -22,13 +22,13 @@ module Grape
22
22
  @env = env
23
23
 
24
24
  begin
25
- error_response(catch(:error){
25
+ error_response(catch(:error) do
26
26
  return @app.call(@env)
27
- })
28
- rescue Exception => e
27
+ end)
28
+ rescue StandardError => e
29
29
  is_rescuable = rescuable?(e.class)
30
30
  if e.is_a?(Grape::Exceptions::Base) && !is_rescuable
31
- handler = lambda {|e| error_response(e) }
31
+ handler = lambda { |arg| error_response(arg) }
32
32
  else
33
33
  raise unless is_rescuable
34
34
  handler = options[:rescue_handlers][e.class] || options[:rescue_handlers][:all]
@@ -44,33 +44,33 @@ module Grape
44
44
 
45
45
  def exec_handler(e, &handler)
46
46
  if handler.lambda? && handler.arity == 0
47
- self.instance_exec(&handler)
47
+ instance_exec(&handler)
48
48
  else
49
- self.instance_exec(e, &handler)
49
+ instance_exec(e, &handler)
50
50
  end
51
51
  end
52
52
 
53
53
  def handle_error(e)
54
- error_response({ :message => e.message, :backtrace => e.backtrace })
54
+ error_response(message: e.message, backtrace: e.backtrace)
55
55
  end
56
56
 
57
57
  def error_response(error = {})
58
58
  status = error[:status] || options[:default_status]
59
59
  message = error[:message] || options[:default_message]
60
- headers = {'Content-Type' => content_type}
60
+ headers = { 'Content-Type' => content_type }
61
61
  headers.merge!(error[:headers]) if error[:headers].is_a?(Hash)
62
62
  backtrace = error[:backtrace] || []
63
63
  rack_response(format_message(message, backtrace), status, headers)
64
64
  end
65
65
 
66
66
  def rack_response(message, status = options[:default_status], headers = { 'Content-Type' => content_type })
67
- Rack::Response.new([ message ], status, headers).finish
67
+ Rack::Response.new([message], status, headers).finish
68
68
  end
69
69
 
70
70
  def format_message(message, backtrace)
71
71
  format = env['api.format'] || options[:format]
72
72
  formatter = Grape::ErrorFormatter::Base.formatter_for(format, options)
73
- throw :error, :status => 406, :message => "The requested format '#{format}' is not supported." unless formatter
73
+ throw :error, status: 406, message: "The requested format '#{format}' is not supported." unless formatter
74
74
  formatter.call(message, backtrace, options, env)
75
75
  end
76
76
 
@@ -3,14 +3,14 @@ module Grape
3
3
  # This is a simple middleware for adding before and after filters
4
4
  # to Grape APIs. It is used like so:
5
5
  #
6
- # use Grape::Middleware::Filter, :before => lambda{ do_something }, :after => lambda{ do_something }
6
+ # use Grape::Middleware::Filter, before: lambda { do_something }, after: lambda { do_something }
7
7
  class Filter < Base
8
8
  def before
9
- app.instance_eval &options[:before] if options[:before]
9
+ app.instance_eval(&options[:before]) if options[:before]
10
10
  end
11
11
 
12
12
  def after
13
- app.instance_eval &options[:after] if options[:after]
13
+ app.instance_eval(&options[:after]) if options[:after]
14
14
  end
15
15
  end
16
16
  end
@@ -6,14 +6,17 @@ module Grape
6
6
 
7
7
  def default_options
8
8
  {
9
- :default_format => :txt,
10
- :formatters => {},
11
- :parsers => {}
9
+ default_format: :txt,
10
+ formatters: {},
11
+ parsers: {}
12
12
  }
13
13
  end
14
14
 
15
15
  def headers
16
- env.dup.inject({}){|h,(k,v)| h[k.to_s.downcase[5..-1]] = v if k.to_s.downcase.start_with?('http_'); h}
16
+ env.dup.inject({}) do |h, (k, v)|
17
+ h[k.to_s.downcase[5..-1]] = v if k.to_s.downcase.start_with?('http_')
18
+ h
19
+ end
17
20
  end
18
21
 
19
22
  def before
@@ -31,7 +34,7 @@ module Grape
31
34
  formatter.call body, env
32
35
  end
33
36
  rescue Grape::Exceptions::InvalidFormatter => e
34
- throw :error, :status => 500, :message => e.message
37
+ throw :error, status: 500, message: e.message
35
38
  end
36
39
  headers['Content-Type'] = content_type_for(env['api.format']) unless headers['Content-Type']
37
40
  Rack::Response.new(bodymap, status, headers).to_a
@@ -42,8 +45,8 @@ module Grape
42
45
  # store read input in env['api.request.input']
43
46
  def read_body_input
44
47
  if (request.post? || request.put? || request.patch? || request.delete?) &&
45
- (! request.form_data? || ! request.media_type) &&
46
- (! request.parseable_data?) &&
48
+ (!request.form_data? || !request.media_type) &&
49
+ (!request.parseable_data?) &&
47
50
  (request.content_length.to_i > 0 || request.env['HTTP_TRANSFER_ENCODING'] == 'chunked')
48
51
 
49
52
  if (input = env['rack.input'])
@@ -68,19 +71,21 @@ module Grape
68
71
  begin
69
72
  body = (env['api.request.body'] = parser.call(body, env))
70
73
  if body.is_a?(Hash)
71
- env['rack.request.form_hash'] = env['rack.request.form_hash'] ?
72
- env['rack.request.form_hash'].merge(body) :
73
- body
74
+ if env['rack.request.form_hash']
75
+ env['rack.request.form_hash'] = env['rack.request.form_hash'].merge(body)
76
+ else
77
+ env['rack.request.form_hash'] = body
78
+ end
74
79
  env['rack.request.form_input'] = env['rack.input']
75
80
  end
76
- rescue Exception => e
77
- throw :error, :status => 400, :message => e.message
81
+ rescue StandardError => e
82
+ throw :error, status: 400, message: e.message
78
83
  end
79
84
  else
80
85
  env['api.request.body'] = body
81
86
  end
82
87
  else
83
- throw :error, :status => 406, :message => "The requested content-type '#{request.media_type}' is not supported."
88
+ throw :error, status: 406, message: "The requested content-type '#{request.media_type}' is not supported."
84
89
  end
85
90
  end
86
91
 
@@ -89,7 +94,7 @@ module Grape
89
94
  if content_type_for(fmt)
90
95
  env['api.format'] = fmt
91
96
  else
92
- throw :error, :status => 406, :message => "The requested format '#{fmt}' is not supported."
97
+ throw :error, status: 406, message: "The requested format '#{fmt}' is not supported."
93
98
  end
94
99
  end
95
100
 
@@ -113,15 +118,15 @@ module Grape
113
118
 
114
119
  def format_from_header
115
120
  mime_array.each do |t|
116
- if mime_types.key?(t)
117
- return mime_types[t]
118
- end
121
+ return mime_types[t] if mime_types.key?(t)
119
122
  end
120
123
  nil
121
124
  end
122
125
 
123
126
  def mime_array
124
- accept = headers['accept'] or return []
127
+ accept = headers['accept']
128
+ return [] unless accept
129
+
125
130
  accept_into_mime_and_quality = %r(
126
131
  (
127
132
  \w+/[\w+.-]+) # eg application/vnd.example.myformat+xml
@@ -129,12 +134,13 @@ module Grape
129
134
  (?:;[^,]*?)? # optionally multiple formats in a row
130
135
  ;\s*q=([\d.]+) # optional "quality" preference (eg q=0.5)
131
136
  )?
132
- )x # x = extended regular expression with comments etc
133
- vendor_prefix_pattern = %r(vnd\.[^+]+\+)
137
+ )x
138
+
139
+ vendor_prefix_pattern = /vnd\.[^+]+\+/
134
140
 
135
- accept.scan(accept_into_mime_and_quality).
136
- sort_by { |_, quality_preference| -quality_preference.to_f }.
137
- map {|mime, _| mime.sub(vendor_prefix_pattern, '') }
141
+ accept.scan(accept_into_mime_and_quality)
142
+ .sort_by { |_, quality_preference| -quality_preference.to_f }
143
+ .map { |mime, _| mime.sub(vendor_prefix_pattern, '') }
138
144
  end
139
145
 
140
146
  end