hyperdrive 0.0.5 → 0.0.6

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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +12 -4
  3. data/Gemfile +2 -0
  4. data/hyperdrive.gemspec +6 -2
  5. data/lib/hyperdrive/docs.rb +10 -11
  6. data/lib/hyperdrive/dsl/resource.rb +10 -15
  7. data/lib/hyperdrive/dsl.rb +49 -3
  8. data/lib/hyperdrive/endpoint.rb +65 -0
  9. data/lib/hyperdrive/errors/missing_required_param.rb +19 -0
  10. data/lib/hyperdrive/errors/not_acceptable.rb +18 -0
  11. data/lib/hyperdrive/errors.rb +13 -8
  12. data/lib/hyperdrive/filter.rb +20 -0
  13. data/lib/hyperdrive/hateoas.rb +45 -0
  14. data/lib/hyperdrive/middleware/accept.rb +16 -0
  15. data/lib/hyperdrive/middleware/content_negotiation.rb +19 -0
  16. data/lib/hyperdrive/middleware/cors.rb +36 -0
  17. data/lib/hyperdrive/middleware/error.rb +35 -0
  18. data/lib/hyperdrive/middleware/request_method.rb +31 -0
  19. data/lib/hyperdrive/middleware/required_params.rb +35 -0
  20. data/lib/hyperdrive/middleware/resource.rb +18 -0
  21. data/lib/hyperdrive/middleware/sanitize_params.rb +25 -0
  22. data/lib/hyperdrive/middleware.rb +10 -0
  23. data/lib/hyperdrive/param.rb +44 -0
  24. data/lib/hyperdrive/resource.rb +74 -30
  25. data/lib/hyperdrive/server.rb +16 -17
  26. data/lib/hyperdrive/utils.rb +27 -0
  27. data/lib/hyperdrive/values.rb +37 -3
  28. data/lib/hyperdrive/version.rb +1 -1
  29. data/lib/hyperdrive.rb +21 -7
  30. data/spec/hyperdrive/docs_spec.rb +14 -8
  31. data/spec/hyperdrive/dsl/resource_spec.rb +27 -37
  32. data/spec/hyperdrive/dsl_spec.rb +52 -0
  33. data/spec/hyperdrive/endpoint_spec.rb +22 -0
  34. data/spec/hyperdrive/filter_spec.rb +34 -0
  35. data/spec/hyperdrive/hateoas_spec.rb +42 -0
  36. data/spec/hyperdrive/middleware/accept_spec.rb +15 -0
  37. data/spec/hyperdrive/middleware/content_negotiation_spec.rb +29 -0
  38. data/spec/hyperdrive/middleware/cors_spec.rb +47 -0
  39. data/spec/hyperdrive/middleware/error_spec.rb +25 -0
  40. data/spec/hyperdrive/middleware/request_method_spec.rb +28 -0
  41. data/spec/hyperdrive/middleware/required_params_spec.rb +43 -0
  42. data/spec/hyperdrive/middleware/resource_spec.rb +15 -0
  43. data/spec/hyperdrive/middleware/sanitize_params_spec.rb +24 -0
  44. data/spec/hyperdrive/param_spec.rb +34 -0
  45. data/spec/hyperdrive/resource_spec.rb +87 -25
  46. data/spec/hyperdrive/server_spec.rb +48 -7
  47. data/spec/hyperdrive/utils_spec.rb +38 -0
  48. data/spec/hyperdrive/values_spec.rb +37 -0
  49. data/spec/spec_helper.rb +71 -12
  50. metadata +106 -11
  51. data/.coveralls.yml +0 -1
  52. data/lib/hyperdrive/dsl/main.rb +0 -19
  53. data/lib/hyperdrive/response.rb +0 -42
  54. data/spec/hyperdrive/dsl/main_spec.rb +0 -16
  55. data/spec/hyperdrive/response_spec.rb +0 -23
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 26dee10778c01ac284135a3aeac9a9f33af6741c
4
- data.tar.gz: f6e4adf3982bef03df48d8fdc333b09fead28833
3
+ metadata.gz: 898b32e10dc9b2836f112818125fcb5a07f28ce0
4
+ data.tar.gz: 917e3c1f3f9ba879170bbbaefc216593de6e2288
5
5
  SHA512:
6
- metadata.gz: bc67ee73c8f6866e8200bfd861a857a61b95dbb4f5573b361471b6bb7130dce6b04e622a996cf75ff3e047ea98359682922ea21374ab3cf136db49e978f07ae9
7
- data.tar.gz: eb7e54a97ebd50a00ca77c748bd027987c3f883e5cd9f5c8759bae37f0478cd0bb0e5c01eade331f02261b6019d7dde4b8169235e82f89e98aba46e3059a5311
6
+ metadata.gz: 055bf9088099afc3dadb018b7042a44a2589a52d91117e7c7e70eeeaf1e4fc16642980d6a5500a84fd3cffac9511c164f6ed3c51687469cdc24dfbc57b5986ad
7
+ data.tar.gz: 6dd19b1ddbd02b84a400ddd3aa22a952a7b06172e6164badd1de7a03b8b3037fb2f66ec90c9c206277c7dda561f89108739e3ab273f0f11bc9285f7c77b23a81
data/.travis.yml CHANGED
@@ -1,6 +1,14 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.1.0
4
- - 1.9.3
5
- - jruby-head
6
- - rbx-2.1.1
3
+ - 2.1.1
4
+ - 1.9.3
5
+ - jruby-head
6
+ - rbx-2.1.1
7
+ matrix:
8
+ allow_failures:
9
+ - rvm: jruby-head
10
+ - rvm: rbx-2.1.1
11
+ env:
12
+ global:
13
+ secure: Tl3oBsBZRI5bMrtNH5mtTDvSTlTxNbsKaAnQ7KQ2KMyiH/eHolOSIbUYB9NB3GRMC6XSyFShRCaYPA/ET36HI+Jw3PFT/XADdiOdFqlk6VyJEUPznNQ1nlp8AsyNn+IexZXIl8DdqmZxGHlY6YKDbQV/U1v+FeW8X5j3TLN1mcw=
14
+ before_install: gem install bundler
data/Gemfile CHANGED
@@ -19,4 +19,6 @@ group :test do
19
19
  gem 'minitest-spec-context', require: false
20
20
  gem 'minitest-reporters', require: false
21
21
  gem 'rack-test', require: false
22
+ gem 'rubinius-coverage', require: false, platforms: :rbx
23
+ gem 'codeclimate-test-reporter', require: false
22
24
  end
data/hyperdrive.gemspec CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |gem|
9
9
  gem.authors = ['StyleSeek Engineering']
10
10
  gem.email = ['engineering@styleseek.com']
11
11
  gem.summary = %q{Hypermedia State Machine}
12
- gem.description = %q{Ruby DSL for defining self-documenting, HATEOAS™ complaint, Hypermedia API endpoints.}
12
+ gem.description = %q{Ruby DSL for defining self-documenting, HATEOAS™ compliant, Hypermedia API endpoints.}
13
13
  gem.homepage = "https://github.com/styleseek/hyperdrive"
14
14
  gem.license = "MIT"
15
15
 
@@ -18,7 +18,11 @@ Gem::Specification.new do |gem|
18
18
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
19
  gem.require_paths = ["lib"]
20
20
 
21
- gem.add_dependency 'rack'
22
21
  gem.add_dependency 'linguistics'
22
+ gem.add_dependency 'oj'
23
+ gem.add_dependency 'rack'
24
+ gem.add_dependency 'rack-cache'
25
+ gem.add_dependency 'rack-ssl'
26
+ gem.add_dependency 'rack-accept'
23
27
  gem.add_dependency 'thor'
24
28
  end
@@ -12,13 +12,13 @@ module Hyperdrive
12
12
  out = ""
13
13
  resources.each_value do |resource|
14
14
  out += header(resource.name)
15
- out += paragraph(resource.desc)
15
+ out += paragraph(resource.description)
16
16
  out += header("Endpoint URL", 2)
17
17
  out += paragraph(bullet(code(resource.endpoint), 1))
18
18
  out += header("Params", 2)
19
- out += list(resource.allowed_params)
19
+ out += list(resource.params.map { |_,param| param.to_hash })
20
20
  out += header("Filters", 2)
21
- out += list(resource.filters)
21
+ out += list(resource.filters.map { |_,filter| filter.to_hash })
22
22
  end
23
23
  out
24
24
  end
@@ -53,16 +53,15 @@ module Hyperdrive
53
53
 
54
54
  def list(items)
55
55
  list = ""
56
- items.each do |key, value|
57
- list += bullet(bold(key), 1)
56
+ items.each do |item|
57
+ list += bullet(bold(item[:name]), 1)
58
+ item.each do |key, value|
59
+ list += bullet(italics(key), 2)
58
60
 
59
- value.each do |subkey, subvalue|
60
- list += bullet(italics(subkey), 2)
61
-
62
- if subvalue.kind_of? Array
63
- list += bullet(code_options(subvalue), 3)
61
+ if value.kind_of? Array
62
+ list += bullet(code_options(value), 3)
64
63
  else
65
- list += bullet(subvalue, 3)
64
+ list += bullet(value, 3)
66
65
  end
67
66
  end
68
67
  end
@@ -3,18 +3,19 @@
3
3
  module Hyperdrive
4
4
  module DSL
5
5
  class Resource
6
+ include Values
6
7
  attr_reader :resource
7
- def initialize(key, &block)
8
- @resource = ::Hyperdrive::Resource.new(key)
9
- instance_eval(&block) if block_given?
8
+ def initialize(name, hyperdrive_config)
9
+ @resource = ::Hyperdrive::Resource.new(name, hyperdrive_config)
10
+ instance_eval(&Proc.new) if block_given?
10
11
  end
11
12
 
12
13
  def name(name)
13
14
  resource.name = name
14
15
  end
15
16
 
16
- def desc(description)
17
- resource.desc = description
17
+ def description(description)
18
+ resource.description = description
18
19
  end
19
20
 
20
21
  def param(*args)
@@ -25,17 +26,11 @@ module Hyperdrive
25
26
  resource.register_filter(*args)
26
27
  end
27
28
 
28
- def request(method, &block)
29
- unless definable_request_methods.include? method
30
- raise Errors::DSL::UnknownArgument.new(method, 'request')
29
+ def request(request_method)
30
+ unless definable_request_methods.include? request_method
31
+ raise Errors::DSL::UnknownArgument.new(request_method, 'request')
31
32
  end
32
- resource.define_request_handler(method, block)
33
- end
34
-
35
- private
36
-
37
- def definable_request_methods
38
- [:get, :post, :put, :patch, :delete].freeze
33
+ resource.register_request_handler(request_method, Proc.new)
39
34
  end
40
35
  end
41
36
  end
@@ -1,9 +1,55 @@
1
1
  # encoding: utf-8
2
2
 
3
- require 'hyperdrive/dsl/main'
4
3
  require 'hyperdrive/dsl/resource'
5
4
 
5
+ module Hyperdrive
6
+ module DSL
7
+ include Values
8
+ extend self
9
+ attr_reader :config, :resources
10
+
11
+ def instance
12
+ @config ||= default_config.dup
13
+ @resources ||= {}
14
+ self
15
+ end
16
+
17
+ private
18
+
19
+ def name(name)
20
+ @config[:name] = name
21
+ end
22
+
23
+ def description(description)
24
+ @config[:description] = description
25
+ end
26
+
27
+ def vendor(vendor)
28
+ @config[:vendor] = vendor
29
+ end
30
+
31
+ def media_types(media_types)
32
+ @config[:media_types] = media_types
33
+ end
34
+
35
+ def cors(options = {})
36
+ allowed_options = default_cors_options.keys
37
+ options = Utils.sanitize_keys(allowed_options, options)
38
+ @config[:cors] = config[:cors].merge(options)
39
+ end
40
+
41
+ def resource(name)
42
+ @resources[name] = Resource.new(name, @config, &Proc.new).resource
43
+ end
44
+
45
+ def reset! # not terribly useful outside of a test environment :(
46
+ @config = default_config.dup
47
+ @resources = {}
48
+ end
49
+ end
50
+ end
51
+
6
52
  def hyperdrive(&block)
7
- Hyperdrive::DSL::Main.instance.instance_eval(&block) if block_given?
8
- Hyperdrive::DSL::Main.instance
53
+ Hyperdrive::DSL.instance.instance_eval(&block) if block_given?
54
+ Hyperdrive::DSL.instance
9
55
  end
@@ -0,0 +1,65 @@
1
+ # encoding: utf-8
2
+
3
+ module Hyperdrive
4
+ class Endpoint
5
+
6
+ def self.call(env)
7
+ @env = env
8
+ @request = Rack::Request.new(@env)
9
+ @media_type = env['hyperdrive.media_type']
10
+ @params = env['hyperdrive.params']
11
+ @resource = env['hyperdrive.resource']
12
+ @headers = Hyperdrive::Values.default_headers.dup
13
+ @headers.merge!('Allow' => resource.allowed_methods, 'Content-Type' => @media_type)
14
+
15
+ response.finish
16
+ end
17
+
18
+ private
19
+
20
+ class << self
21
+ attr_reader :env, :request, :media_type, :params, :resource
22
+ attr_accessor :headers
23
+ end
24
+
25
+ def self.json?
26
+ media_type =~ /json$/
27
+ end
28
+
29
+ def self.xml?
30
+ media_type =~ /xml$/
31
+ end
32
+
33
+ def self.render(body)
34
+ case body
35
+ when Array, Hash
36
+ Oj.dump(body, mode: :compat) if json?
37
+ when String
38
+ body
39
+ else
40
+ body.to_s
41
+ end
42
+ end
43
+
44
+ def self.status
45
+ case env['REQUEST_METHOD']
46
+ when 'POST'
47
+ 201
48
+ when 'DELETE'
49
+ 204
50
+ else
51
+ 200
52
+ end
53
+ end
54
+
55
+ def self.body
56
+ body = instance_eval(&resource.request_handler(env['REQUEST_METHOD']))
57
+ body = '' if env['REQUEST_METHOD'] == 'DELETE'
58
+ body
59
+ end
60
+
61
+ def self.response
62
+ ::Rack::Response.new(body, status, headers)
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,19 @@
1
+ module Hyperdrive
2
+ module Errors
3
+ class MissingRequiredParam < HTTPError
4
+ attr_reader :param, :http_request_method
5
+ def initialize(param, http_request_method)
6
+ @param = param
7
+ @http_request_method = http_request_method
8
+ end
9
+
10
+ def message
11
+ "The #{param} param is required by #{http_request_method} requests."
12
+ end
13
+
14
+ def http_status_code
15
+ 400
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,18 @@
1
+ module Hyperdrive
2
+ module Errors
3
+ class NotAcceptable < HTTPError
4
+ def initialize(http_accept)
5
+ @http_accept = http_accept
6
+ end
7
+
8
+ def message
9
+ "This resource is not capable of generating content in the format requested by the Accept headers (#{@http_accept})."
10
+ end
11
+
12
+ def http_status_code
13
+ 406
14
+ end
15
+ end
16
+ end
17
+ end
18
+
@@ -1,10 +1,15 @@
1
1
  # encoding: utf-8
2
2
 
3
- require 'hyperdrive/errors/http_error'
4
- require 'hyperdrive/errors/bad_request'
5
- require 'hyperdrive/errors/internal_server_error'
6
- require 'hyperdrive/errors/method_not_allowed'
7
- require 'hyperdrive/errors/not_found'
8
- require 'hyperdrive/errors/not_implemented'
9
- require 'hyperdrive/errors/unauthorized'
10
- require 'hyperdrive/errors/dsl/unknown_argument'
3
+ # DSL Errors
4
+ require 'hyperdrive/errors/dsl/unknown_argument'
5
+
6
+ # HTTP Errors
7
+ require 'hyperdrive/errors/http_error' # 500 (Catch All)
8
+ require 'hyperdrive/errors/bad_request' # 400 (Generic)
9
+ require 'hyperdrive/errors/internal_server_error' # 500 (Generic)
10
+ require 'hyperdrive/errors/method_not_allowed' # 405
11
+ require 'hyperdrive/errors/missing_required_param' # 400 (Specific)
12
+ require 'hyperdrive/errors/not_acceptable' # 406
13
+ require 'hyperdrive/errors/not_found' # 404
14
+ require 'hyperdrive/errors/not_implemented' # 501
15
+ require 'hyperdrive/errors/unauthorized' # 401
@@ -0,0 +1,20 @@
1
+ # encoding: utf-8
2
+
3
+ module Hyperdrive
4
+ class Filter < Param
5
+ def initialize(name, description, options = {})
6
+ @name = name.to_s
7
+ @description = description
8
+ options = default_options.merge(options)
9
+ @required = if options[:required] == true
10
+ %w(GET HEAD)
11
+ elsif options[:required] == false
12
+ []
13
+ else
14
+ Array(options[:required])
15
+ end
16
+ @type = options[:type]
17
+ @constraints = "#{required_constraint} #{options[:constraints]}"
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,45 @@
1
+ # encoding: utf-8
2
+
3
+ module Hyperdrive
4
+ class HATEOAS
5
+ extend Hyperdrive::Values
6
+
7
+ def self.call(env)
8
+ if hyperdrive.resources.empty? || env['PATH_INFO'] != '/'
9
+ raise Hyperdrive::Errors::NotFound
10
+ end
11
+
12
+ endpoints = hyperdrive.resources.map do |_,resource|
13
+ resource.to_hash
14
+ end
15
+
16
+ api = {
17
+ _links: { self: { href: '/' } },
18
+ name: hyperdrive.config[:name],
19
+ description: hyperdrive.config[:description],
20
+ vendor: hyperdrive.config[:vendor],
21
+ resources: endpoints
22
+ }
23
+
24
+ media_types = %w(hal+json json).map do |media_type|
25
+ "application/vnd.#{hyperdrive.config[:vendor]}+#{media_type}"
26
+ end
27
+
28
+ media_types += %w(application/hal+json application/json)
29
+ content_type = env['hyperdrive.accept'].best_of(media_types)
30
+ body = if content_type =~ /json$/
31
+ Oj.dump(api, mode: :compat)
32
+ else
33
+ raise Hyperdrive::Errors::NotAcceptable.new(env['HTTP_ACCEPT'])
34
+ end
35
+
36
+ status = 200
37
+ headers = {}
38
+ headers['Access-Control-Allow-Origin'] = '*'
39
+ headers['Access-Control-Allow-Methods'] = 'GET, HEAD, OPTIONS'
40
+ headers['Allow'] = 'GET, HEAD, OPTIONS'
41
+ headers['Content-Type'] = content_type
42
+ [status, headers, [body]]
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,16 @@
1
+ # encoding: utf-8
2
+
3
+ module Hyperdrive
4
+ module Middleware
5
+ class Accept
6
+ def initialize(app)
7
+ @app = app
8
+ end
9
+
10
+ def call(env)
11
+ env['hyperdrive.accept'] = Rack::Accept::MediaType.new(env['HTTP_ACCEPT'])
12
+ @app.call(env)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,19 @@
1
+ # encoding: utf-8
2
+
3
+ module Hyperdrive
4
+ module Middleware
5
+ class ContentNegotiation
6
+ def initialize(app)
7
+ @app = app
8
+ end
9
+
10
+ def call(env)
11
+ accept = env['hyperdrive.accept']
12
+ acceptable_content_types = env['hyperdrive.resource'].acceptable_content_types(env['REQUEST_METHOD'])
13
+ env['hyperdrive.media_type'] = accept.best_of(acceptable_content_types)
14
+ raise Hyperdrive::Errors::NotAcceptable.new(env['HTTP_ACCEPT']) unless env['hyperdrive.media_type']
15
+ @app.call(env)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,36 @@
1
+ # encoding: utf-8
2
+
3
+ module Hyperdrive
4
+ module Middleware
5
+ class CORS
6
+ def initialize(app, options = {})
7
+ @app = app
8
+ @options = options
9
+ end
10
+
11
+ def call(env)
12
+ status, headers, body = @app.call(env)
13
+ allowed_methods = '*'
14
+ unless env['hyperdrive.resource'].nil?
15
+ allowed_methods = env['hyperdrive.resource'].allowed_methods
16
+ end
17
+ cors = cors_headers(allowed_methods)
18
+ headers.merge!(cors)
19
+ [status, headers, body]
20
+ end
21
+
22
+ private
23
+
24
+ def cors_headers(allowed_methods = '*')
25
+ {
26
+ 'Access-Control-Allow-Origin' => Array(@options[:origins]).join(", "),
27
+ 'Access-Control-Allow-Methods' => Array(allowed_methods).join(", "),
28
+ 'Access-Control-Allow-Headers' => Array(@options[:allow_headers]).join(", "),
29
+ 'Access-Control-Allow-Credentials' => (@options[:credentials] || false).to_s,
30
+ 'Access-Control-Max-Age' => @options[:max_age].to_i.to_s,
31
+ 'Access-Control-Expose-Headers' => Array(@options[:expose_headers]).join(", ")
32
+ }
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,35 @@
1
+ # encoding: utf-8
2
+
3
+ module Hyperdrive
4
+ module Middleware
5
+ class Error
6
+ include Hyperdrive::Values
7
+
8
+ def initialize(app)
9
+ @app = app
10
+ end
11
+
12
+ def call(env)
13
+ @app.call(env)
14
+ rescue Hyperdrive::Errors::HTTPError => e
15
+ status = e.http_status_code
16
+ headers = { 'Content-Type' => 'application/json',
17
+ 'Allow' => supported_request_methods.join(', ') }
18
+ body = Oj.dump(error_message(e), mode: :compat)
19
+ [status, headers, [body]]
20
+ end
21
+
22
+ private
23
+
24
+ def error_message(e)
25
+ {
26
+ _links: { root: { href: '/', title: 'API Root' } },
27
+ error: {
28
+ type: "#{e.class.to_s.split('::').last}",
29
+ message: e.message
30
+ }
31
+ }
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,31 @@
1
+ # encoding: utf-8
2
+
3
+ module Hyperdrive
4
+ module Middleware
5
+ class RequestMethod
6
+ def initialize(app)
7
+ @app = app
8
+ end
9
+
10
+ def call(env)
11
+ http_request_method = env['REQUEST_METHOD']
12
+
13
+ unless request_method_supported?(http_request_method)
14
+ raise Hyperdrive::Errors::NotImplemented.new(http_request_method)
15
+ end
16
+
17
+ unless env['hyperdrive.resource'].request_method_allowed?(http_request_method)
18
+ raise Hyperdrive::Errors::MethodNotAllowed.new(http_request_method)
19
+ end
20
+
21
+ @app.call(env)
22
+ end
23
+
24
+ private
25
+
26
+ def request_method_supported?(http_request_method)
27
+ Hyperdrive::Values.http_request_methods.key?(http_request_method)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,35 @@
1
+ # encoding: utf-8
2
+
3
+ module Hyperdrive
4
+ module Middleware
5
+ class RequiredParams
6
+ def initialize(app)
7
+ @app = app
8
+ end
9
+
10
+ def call(env)
11
+ @env = env
12
+ if %w(GET HEAD).include? env['REQUEST_METHOD']
13
+ check_required_params(env['hyperdrive.resource'].filters)
14
+ elsif %W(POST PUT PATCH DELETE).include? env['REQUEST_METHOD']
15
+ check_required_params(env['hyperdrive.resource'].params)
16
+ end
17
+ @app.call(env)
18
+ end
19
+
20
+ def check_required_params(params)
21
+ params.each do |param, options|
22
+ if @env['hyperdrive.resource'].required?(param, @env['REQUEST_METHOD'])
23
+ if @env['hyperdrive.params'].key?(param)
24
+ if @env['hyperdrive.params'][param] == ''
25
+ raise Errors::MissingRequiredParam.new(param, @env['REQUEST_METHOD'])
26
+ end
27
+ else
28
+ raise Errors::MissingRequiredParam.new(param, @env['REQUEST_METHOD'])
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,18 @@
1
+ # encoding: utf-8
2
+
3
+ # Internal: Adds the requested resource to rack's env
4
+ module Hyperdrive
5
+ module Middleware
6
+ class Resource
7
+ def initialize(app, resource)
8
+ @app = app
9
+ @resource = resource
10
+ end
11
+
12
+ def call(env)
13
+ env['hyperdrive.resource'] = @resource
14
+ @app.call(env)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+
3
+ module Hyperdrive
4
+ module Middleware
5
+ class SanitizeParams
6
+ def initialize(app)
7
+ @app = app
8
+ end
9
+
10
+ def call(env)
11
+ unless %w(OPTIONS TRACE).include? env['REQUEST_METHOD']
12
+ request = Rack::Request.new(env)
13
+ params = Hyperdrive::Utils.symbolize_keys(request.params)
14
+ if %w(GET HEAD).include? env['REQUEST_METHOD']
15
+ params_to_keep = env['hyperdrive.resource'].filters.keys
16
+ else
17
+ params_to_keep = env['hyperdrive.resource'].params.keys
18
+ end
19
+ env['hyperdrive.params'] = Hyperdrive::Utils.sanitize_keys(params_to_keep, params)
20
+ end
21
+ @app.call(env)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,10 @@
1
+ # encoding: utf-8
2
+
3
+ require 'hyperdrive/middleware/accept'
4
+ require 'hyperdrive/middleware/cors'
5
+ require 'hyperdrive/middleware/content_negotiation'
6
+ require 'hyperdrive/middleware/error'
7
+ require 'hyperdrive/middleware/resource'
8
+ require 'hyperdrive/middleware/request_method'
9
+ require 'hyperdrive/middleware/required_params'
10
+ require 'hyperdrive/middleware/sanitize_params'