respect-rails 0.1.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 (110) hide show
  1. data/FAQ.md +98 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +291 -0
  4. data/RELATED_WORK.md +47 -0
  5. data/RELEASE_NOTES.md +20 -0
  6. data/Rakefile +32 -0
  7. data/app/assets/javascripts/respect/rails/schemas.js +32 -0
  8. data/app/assets/stylesheets/respect/rails/schemas.css +160 -0
  9. data/app/controllers/respect/rails/schemas_controller.rb +36 -0
  10. data/app/helpers/respect/rails/schemas_helper.rb +78 -0
  11. data/app/views/layouts/respect/rails/schemas.html.erb +14 -0
  12. data/app/views/respect/rails/request_validation_exception.html.erb +38 -0
  13. data/app/views/respect/rails/schemas/doc.html.erb +158 -0
  14. data/app/views/respect/rails/schemas/index.html.erb +4 -0
  15. data/config/routes.rb +4 -0
  16. data/lib/respect/rails/action_def.rb +37 -0
  17. data/lib/respect/rails/action_schema.rb +41 -0
  18. data/lib/respect/rails/application_info.rb +26 -0
  19. data/lib/respect/rails/controller_helper.rb +107 -0
  20. data/lib/respect/rails/engine.rb +63 -0
  21. data/lib/respect/rails/engine_info.rb +27 -0
  22. data/lib/respect/rails/headers_helper.rb +11 -0
  23. data/lib/respect/rails/headers_simplifier.rb +31 -0
  24. data/lib/respect/rails/info.rb +72 -0
  25. data/lib/respect/rails/request_def.rb +38 -0
  26. data/lib/respect/rails/request_helper.rb +101 -0
  27. data/lib/respect/rails/request_schema.rb +102 -0
  28. data/lib/respect/rails/response_def.rb +43 -0
  29. data/lib/respect/rails/response_helper.rb +67 -0
  30. data/lib/respect/rails/response_schema.rb +84 -0
  31. data/lib/respect/rails/response_schema_set.rb +60 -0
  32. data/lib/respect/rails/route_info.rb +101 -0
  33. data/lib/respect/rails/version.rb +5 -0
  34. data/lib/respect/rails.rb +74 -0
  35. data/lib/tasks/respect_tasks.rake +4 -0
  36. data/test/dummy/README.rdoc +261 -0
  37. data/test/dummy/Rakefile +7 -0
  38. data/test/dummy/app/assets/javascripts/application.js +15 -0
  39. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  40. data/test/dummy/app/controllers/application_controller.rb +3 -0
  41. data/test/dummy/app/controllers/automatic_validation_controller.rb +300 -0
  42. data/test/dummy/app/controllers/caught_exception_controller.rb +58 -0
  43. data/test/dummy/app/controllers/disabled_controller.rb +37 -0
  44. data/test/dummy/app/controllers/manual_validation_controller.rb +63 -0
  45. data/test/dummy/app/controllers/no_schema_controller.rb +17 -0
  46. data/test/dummy/app/controllers/skipped_automatic_validation_controller.rb +35 -0
  47. data/test/dummy/app/helpers/application_helper.rb +2 -0
  48. data/test/dummy/app/helpers/respect/application_macros.rb +10 -0
  49. data/test/dummy/app/helpers/respect/circle_schema.rb +16 -0
  50. data/test/dummy/app/helpers/respect/point_schema.rb +19 -0
  51. data/test/dummy/app/helpers/respect/rgba_schema.rb +18 -0
  52. data/test/dummy/app/views/automatic_validation/request_format.html.erb +1 -0
  53. data/test/dummy/app/views/automatic_validation/request_format.pdf.erb +1 -0
  54. data/test/dummy/app/views/caught_exception/response_validator.html.erb +1 -0
  55. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  56. data/test/dummy/config/application.rb +58 -0
  57. data/test/dummy/config/boot.rb +10 -0
  58. data/test/dummy/config/database.yml +25 -0
  59. data/test/dummy/config/environment.rb +5 -0
  60. data/test/dummy/config/environments/development.rb +37 -0
  61. data/test/dummy/config/environments/production.rb +67 -0
  62. data/test/dummy/config/environments/test.rb +37 -0
  63. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  64. data/test/dummy/config/initializers/inflections.rb +15 -0
  65. data/test/dummy/config/initializers/mime_types.rb +5 -0
  66. data/test/dummy/config/initializers/respect.rb +9 -0
  67. data/test/dummy/config/initializers/secret_token.rb +7 -0
  68. data/test/dummy/config/initializers/session_store.rb +8 -0
  69. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  70. data/test/dummy/config/locales/en.yml +5 -0
  71. data/test/dummy/config/routes.rb +38 -0
  72. data/test/dummy/config.ru +4 -0
  73. data/test/dummy/db/development.sqlite3 +0 -0
  74. data/test/dummy/db/schema.rb +16 -0
  75. data/test/dummy/db/test.sqlite3 +0 -0
  76. data/test/dummy/lib/exts/Rgba.rb +11 -0
  77. data/test/dummy/lib/exts/circle.rb +11 -0
  78. data/test/dummy/lib/exts/point.rb +11 -0
  79. data/test/dummy/log/development.log +6 -0
  80. data/test/dummy/log/test.log +851 -0
  81. data/test/dummy/public/404.html +26 -0
  82. data/test/dummy/public/422.html +26 -0
  83. data/test/dummy/public/500.html +25 -0
  84. data/test/dummy/public/favicon.ico +0 -0
  85. data/test/dummy/script/rails +6 -0
  86. data/test/functional/automatic_validation_controller_test.rb +131 -0
  87. data/test/functional/caught_exception_controller_test.rb +50 -0
  88. data/test/functional/disabled_controller_test.rb +16 -0
  89. data/test/functional/manual_validation_controller_test.rb +24 -0
  90. data/test/functional/no_schema_controller_test.rb +12 -0
  91. data/test/functional/respect/rails/schemas_controller_test.rb +18 -0
  92. data/test/functional/skipped_automatic_validation_controller_test.rb +12 -0
  93. data/test/headers_can_dumped_in_json.sh +33 -0
  94. data/test/integration/navigation_test.rb +38 -0
  95. data/test/request_headers_validation_in_dev_mode.sh +33 -0
  96. data/test/test_helper.rb +17 -0
  97. data/test/unit/action_schema_test.rb +21 -0
  98. data/test/unit/application_info_test.rb +11 -0
  99. data/test/unit/controller_helper_test.rb +4 -0
  100. data/test/unit/engine_info_test.rb +11 -0
  101. data/test/unit/engine_test.rb +7 -0
  102. data/test/unit/info_test.rb +42 -0
  103. data/test/unit/request_def_test.rb +22 -0
  104. data/test/unit/request_helper_test.rb +67 -0
  105. data/test/unit/request_schema_test.rb +164 -0
  106. data/test/unit/response_def_test.rb +9 -0
  107. data/test/unit/response_helper_test.rb +73 -0
  108. data/test/unit/response_schema_set_test.rb +18 -0
  109. data/test/unit/response_schema_test.rb +147 -0
  110. metadata +334 -0
@@ -0,0 +1,72 @@
1
+ module Respect
2
+ module Rails
3
+ # The implementation is strongly inspired from
4
+ # ActionDispatch::Routing::RoutesInspector.
5
+ class Info
6
+
7
+ def initialize
8
+ @engines = {}
9
+ @app = ApplicationInfo.new
10
+ @app.routes = collect_routes(::Rails.application.routes.routes)
11
+ @engines[@app.name] = @app
12
+ @toc = build_toc(self.routes)
13
+ end
14
+
15
+ attr_reader :engines
16
+ attr_reader :app
17
+ attr_reader :toc
18
+
19
+ def routes
20
+ @app.routes
21
+ end
22
+
23
+ private
24
+
25
+ def collect_routes(routes, mounted_point = nil)
26
+ result = []
27
+ routes.each do |route|
28
+ route = RouteInfo.new(route, mounted_point)
29
+ next if route.internal?
30
+ if route.engine?
31
+ result += collect_engine_routes(route)
32
+ else
33
+ if route.has_schema?
34
+ result << route
35
+ end
36
+ end
37
+ end
38
+ result
39
+ end
40
+
41
+ def collect_engine_routes(route)
42
+ return unless route.engine?
43
+ engine_info = EngineInfo.new(route.endpoint)
44
+ return if @engines[engine_info.name]
45
+
46
+ routes = route.rack_app.routes
47
+ if routes.is_a?(ActionDispatch::Routing::RouteSet)
48
+ engine_info.routes = collect_routes(routes.routes, route)
49
+ @engines[engine_info.name] = engine_info
50
+ engine_info.routes
51
+ else
52
+ []
53
+ end
54
+ end
55
+
56
+ def build_toc(routes)
57
+ toc = {}
58
+ routes.each do |route|
59
+ next if route.engine?
60
+ if route.has_schema?
61
+ controller = route.controller_name
62
+ action = route.action_name
63
+ toc[controller] ||= {}
64
+ toc[controller][action] = route
65
+ end
66
+ end
67
+ toc
68
+ end
69
+
70
+ end # class Info
71
+ end # module Rails
72
+ end # module Respect
@@ -0,0 +1,38 @@
1
+ module Respect
2
+ module Rails
3
+ class RequestDef
4
+
5
+ class << self
6
+ def eval(*args, &block)
7
+ new(*args).eval(&block)
8
+ end
9
+ end
10
+
11
+ def initialize(*args)
12
+ @request_schema = RequestSchema.new(*args)
13
+ end
14
+
15
+ def eval(&block)
16
+ block.call(self)
17
+ @request_schema
18
+ end
19
+
20
+ def path_parameters(&block)
21
+ @request_schema.path_parameters = HashSchema.define(&block)
22
+ end
23
+
24
+ def body_parameters(&block)
25
+ @request_schema.body_parameters = HashSchema.define(&block)
26
+ end
27
+
28
+ def query_parameters(&block)
29
+ @request_schema.query_parameters = HashSchema.define(&block)
30
+ end
31
+
32
+ def headers(&block)
33
+ @request_schema.headers = HashSchema.define(&block)
34
+ end
35
+
36
+ end # class RequestDef
37
+ end
38
+ end # module Respect
@@ -0,0 +1,101 @@
1
+ module Respect
2
+ module Rails
3
+ module RequestHelper
4
+ extend ActiveSupport::Concern
5
+
6
+ # Return whether this request validates the schema.
7
+ # You can get the validation error via {#last_validation_error}.
8
+ def validate_schema?
9
+ begin
10
+ validate_schema
11
+ rescue Respect::Rails::RequestValidationError
12
+ false
13
+ end
14
+ end
15
+
16
+ # Raise a {Respect::Rails::RequestValidationError} exception if this
17
+ # request does not validate the schema.
18
+ def validate_schema
19
+ log_msg = " Request validation: "
20
+ @validated = nil
21
+ measure = Benchmark.realtime do
22
+ @validated = request_schema.validate?(self) unless request_schema.nil?
23
+ end
24
+ if @validated.nil?
25
+ log_msg += "none"
26
+ else
27
+ if @validated == true
28
+ log_msg += "success"
29
+ else
30
+ log_msg += "failure"
31
+ end
32
+ end
33
+ log_msg += " (%.1fms)" % [ measure * 1000 ]
34
+ ::Rails.logger.info log_msg
35
+ if @validated == false
36
+ last_validation_error.context.each do |msg|
37
+ ::Rails.logger.info " #{msg}"
38
+ end
39
+ raise last_validation_error
40
+ end
41
+ true
42
+ end
43
+
44
+ def response_schema(http_status)
45
+ unless action_schema.nil? || action_schema.responses.nil?
46
+ action_schema.responses[http_status]
47
+ end
48
+ end
49
+
50
+ def request_schema
51
+ action_schema.request if action_schema
52
+ end
53
+
54
+ attr_reader :action_schema
55
+ attr_writer :action_schema
56
+ private :action_schema=
57
+
58
+ def has_schema?
59
+ !!action_schema
60
+ end
61
+
62
+ def last_validation_error
63
+ request_schema.last_error
64
+ end
65
+
66
+ # Returns the sanitized parameters if the schema validation has succeed.
67
+ def sane_params
68
+ request_schema.sanitized_params if request_schema
69
+ end
70
+
71
+ # Returns +nil+ if never validated, +true+ if validated successfully and
72
+ # +false+ if validation failed.
73
+ def validated
74
+ @validated
75
+ end
76
+
77
+ # Returns +true+ if the validation has been done and succeed and +false+
78
+ # otherwise.
79
+ def validated?
80
+ !!validated
81
+ end
82
+
83
+ # Sanitize all the request's parameters (path, query and body) *in-place*.
84
+ # if the schema validation has succeed. Validate it first if it has not
85
+ # been yet.
86
+ def sanitize_params!
87
+ if request_schema
88
+ if validated.nil?
89
+ validate_schema
90
+ end
91
+ if validated?
92
+ request_schema.sanitize!(self)
93
+ end
94
+ end
95
+ end
96
+
97
+ end # module RequestHelper
98
+ end # module Rails
99
+ end # module Respect
100
+
101
+ ActionDispatch::Request.send :include, Respect::Rails::RequestHelper
@@ -0,0 +1,102 @@
1
+ module Respect
2
+ module Rails
3
+ class RequestSchema
4
+ include HeadersSimplifier
5
+
6
+ class << self
7
+ def define(*args, &block)
8
+ RequestDef.eval(*args, &block)
9
+ end
10
+ end
11
+
12
+ def initialize(controller, action)
13
+ @controller = controller
14
+ @action = action
15
+ @default_path_parameters = HashSchema.define do |s|
16
+ s.string "controller", equal_to: @controller.to_s, doc: false
17
+ s.string "action", equal_to: @action.to_s, doc: false
18
+ end
19
+ @path_parameters = @default_path_parameters.dup
20
+ @body_parameters = HashSchema.new
21
+ @query_parameters = HashSchema.new
22
+ @headers = HashSchema.new
23
+ end
24
+
25
+ attr_reader :controller, :action
26
+
27
+ attr_accessor :headers
28
+
29
+ attr_accessor :body_parameters, :query_parameters
30
+
31
+ attr_reader :path_parameters, :default_path_parameters
32
+
33
+ # Merge +path_parameters+ with {#default_path_parameters} and store it.
34
+ def path_parameters=(path_parameters)
35
+ @path_parameters = @default_path_parameters.merge(path_parameters)
36
+ end
37
+
38
+ attr_reader :sanitized_params
39
+
40
+ # Validate the given +request+.
41
+ # Raise a {RequestValidationError} if an error occur.
42
+ # Returns +true+ on success.
43
+ def validate(request)
44
+ # Validate requests.
45
+ unless Respect::Rails::Engine.disable_request_headers_validation
46
+ begin
47
+ headers.validate(request.headers)
48
+ rescue Respect::ValidationError => e
49
+ raise RequestValidationError.new(e, :headers, simplify_headers(request.headers))
50
+ end
51
+ end
52
+ [ :path, :query, :body ].each do |name|
53
+ begin
54
+ send("#{name}_parameters").validate(request.params)
55
+ rescue Respect::ValidationError => e
56
+ raise RequestValidationError.new(e, name, request.params)
57
+ end
58
+ end
59
+ # Build sane parameters.
60
+ @sanitized_params = {}
61
+ [ :path, :query, :body ].each do |name|
62
+ @sanitized_params.merge!(send("#{name}_parameters").sanitized_object)
63
+ end
64
+ true
65
+ end
66
+
67
+ def validate?(request)
68
+ begin
69
+ validate(request)
70
+ true
71
+ rescue RequestValidationError => e
72
+ @last_error = e
73
+ false
74
+ end
75
+ end
76
+
77
+ # Return the last validation error that happens during the
78
+ # validation process. (set by {#validate?})
79
+ # Reset each time {#validate?} is called.
80
+ attr_reader :last_error
81
+
82
+ # Validate the request and sanitize its parameters if the validation succeed.
83
+ # You can disable the sanitization with
84
+ # {Respect::Rails::Engine.sanitize_request_parameters}.
85
+ def validate!(request)
86
+ valid = validate?(request)
87
+ if valid
88
+ sanitize!(request)
89
+ end
90
+ valid
91
+ end
92
+
93
+ # Sanitize all the request's parameters (path, query and body) *in-place*.
94
+ def sanitize!(request)
95
+ [ :path, :query, :body ].each do |name|
96
+ send("#{name}_parameters").sanitize_object!(request.params)
97
+ end
98
+ end
99
+
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,43 @@
1
+ module Respect
2
+ module Rails
3
+ class ResponseDef
4
+
5
+ class << self
6
+ def eval(*args, &block)
7
+ new(*args).eval(&block)
8
+ end
9
+ end
10
+
11
+ def initialize(*args)
12
+ @response_schema = ResponseSchema.new(*args)
13
+ end
14
+
15
+ def eval(&block)
16
+ block.call(self)
17
+ @response_schema
18
+ end
19
+
20
+ # Define the schema of the response body.
21
+ # @option options [Boolean] hash (true) whether the body is hash schema.
22
+ def body(options = {}, &block)
23
+ @response_schema.body = (
24
+ if options.fetch(:hash, true)
25
+ HashSchema.define(&block)
26
+ else
27
+ Schema.define(&block)
28
+ end
29
+ )
30
+ end
31
+
32
+ def headers(options = {}, &block)
33
+ @response_schema.headers = HashSchema.define(options, &block)
34
+ end
35
+
36
+ # Set the documentation to +text+.
37
+ def documentation(text)
38
+ @response_schema.documentation = text
39
+ end
40
+
41
+ end # class ResponseDef
42
+ end # module Rails
43
+ end # module Respect
@@ -0,0 +1,67 @@
1
+ module Respect
2
+ module Rails
3
+ module ResponseHelper
4
+ extend ActiveSupport::Concern
5
+
6
+ attr_reader :schema
7
+ attr_writer :schema
8
+ private :schema=
9
+
10
+ def has_schema?
11
+ !!@schema
12
+ end
13
+
14
+ # Return whether this response validates the schema.
15
+ # You can get the validation error via {#last_validation_error}.
16
+ def validate_schema?
17
+ begin
18
+ validate_schema
19
+ rescue Respect::Rails::ResponseValidationError => e
20
+ false
21
+ end
22
+ end
23
+
24
+ # Raise a {Respect::Rails::ResponseValidationError} exception if this
25
+ # response does not validate the schema.
26
+ def validate_schema
27
+ log_msg = " Response validation: "
28
+ valid = nil
29
+ measure = Benchmark.realtime do
30
+ if schema && content_type == Mime::JSON
31
+ valid = schema.validate?(self)
32
+ end
33
+ end
34
+ if valid.nil?
35
+ log_msg += "none"
36
+ else
37
+ if valid
38
+ log_msg += "success"
39
+ else
40
+ log_msg += "failure"
41
+ end
42
+ end
43
+ log_msg += " (%.1fms)" % [ measure * 1000 ]
44
+ ::Rails.logger.info log_msg
45
+ if valid == false
46
+ last_validation_error.context.each do |msg|
47
+ ::Rails.logger.info " #{msg}"
48
+ end
49
+ if Respect::Rails::Engine.catch_response_validation_error
50
+ self.body = last_validation_error.to_json
51
+ self.status = :internal_server_error
52
+ else
53
+ raise last_validation_error
54
+ end
55
+ end
56
+ true
57
+ end
58
+
59
+ def last_validation_error
60
+ schema.last_error
61
+ end
62
+
63
+ end # module ResponseHelper
64
+ end # module Rails
65
+ end # module Respect
66
+
67
+ ActionDispatch::Response.send :include, Respect::Rails::ResponseHelper
@@ -0,0 +1,84 @@
1
+ module Respect
2
+ module Rails
3
+ class ResponseSchema
4
+ include HeadersSimplifier
5
+ include Respect::DocHelper
6
+
7
+ class << self
8
+
9
+ def http_status(status)
10
+ Rack::Utils.status_code(status)
11
+ end
12
+
13
+ # FIXME(Nicolas Despres): Move me to another module/class.
14
+ def symbolize_http_status(http_status)
15
+ h = Rack::Utils::HTTP_STATUS_CODES
16
+ (h[http_status] || h[500]).downcase.gsub(/\s|-/, '_').to_sym
17
+ end
18
+
19
+ def define(*args, &block)
20
+ ResponseDef.eval(*args, &block)
21
+ end
22
+
23
+ # Read the response schema definition from the given +filename+ and
24
+ # evaluate it as a block passed to {ResponseSchema.define}
25
+ def from_file(status, filename)
26
+ define(status) do |r|
27
+ r.instance_eval(File.read(filename), filename)
28
+ end
29
+ end
30
+ end
31
+
32
+ def initialize(status = :ok)
33
+ @status = status
34
+ @headers = HashSchema.new
35
+ end
36
+
37
+ attr_reader :status
38
+
39
+ def http_status
40
+ self.class.http_status(@status)
41
+ end
42
+
43
+ attr_accessor :body
44
+
45
+ def ==(other)
46
+ @status == other.status && @body == other.body
47
+ end
48
+
49
+ def validate(response)
50
+ if headers
51
+ begin
52
+ headers.validate(response.headers)
53
+ rescue Respect::ValidationError => e
54
+ raise Respect::Rails::ResponseValidationError.new(e, :headers, simplify_headers(response.headers))
55
+ end
56
+ end
57
+ if body
58
+ decoded_body = ActiveSupport::JSON.decode(response.body)
59
+ begin
60
+ body.validate(decoded_body)
61
+ rescue Respect::ValidationError => e
62
+ raise Respect::Rails::ResponseValidationError.new(e, :body, decoded_body)
63
+ end
64
+ end
65
+ true
66
+ end
67
+
68
+ def validate?(response)
69
+ begin
70
+ validate(response)
71
+ true
72
+ rescue Respect::Rails::ResponseValidationError => e
73
+ @last_error = e
74
+ false
75
+ end
76
+ end
77
+
78
+ attr_reader :last_error
79
+
80
+ attr_accessor :headers
81
+ attr_accessor :documentation
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,60 @@
1
+ module Respect
2
+ module Rails
3
+ class ResponseSchemaSet
4
+
5
+ class << self
6
+ # FIXME(Nicolas Despres): Move me to another module/class.
7
+ def symbolize_status(status)
8
+ case status
9
+ when Symbol
10
+ status
11
+ when String
12
+ if status =~ /^\d+$/
13
+ symbolize_status(status.to_i)
14
+ else
15
+ status.to_sym
16
+ end
17
+ when Numeric
18
+ ResponseSchema.symbolize_http_status(status.to_i)
19
+ else
20
+ raise ArgumentError, "cannot normalize status '#{status}:#{status.class}'"
21
+ end
22
+ end
23
+ end
24
+
25
+ # Initialize a new ResponseSchemaSet object for the given controller's
26
+ # action and collect response' schema from their respective file.
27
+ def initialize(controller_name, action_name)
28
+ @controller_name = controller_name
29
+ @action_name = action_name
30
+ @set = {}
31
+ end
32
+
33
+ attr_reader :controller_name, :action_name
34
+
35
+ def method_missing(method_name, *arguments, &block)
36
+ define_response(method_name, *arguments, &block)
37
+ end
38
+
39
+ def [](http_status)
40
+ @set[http_status]
41
+ end
42
+
43
+ delegate :each, :empty?, to: :@set
44
+
45
+ def <<(response_schema)
46
+ @set[response_schema.http_status] = response_schema
47
+ end
48
+
49
+ def is(status, *arguments, &block)
50
+ define_response(status, *arguments, &block)
51
+ end
52
+
53
+ def define_response(status, *arguments, &block)
54
+ status = self.class.symbolize_status(status)
55
+ self << ResponseSchema.define(status, *arguments, &block)
56
+ end
57
+
58
+ end
59
+ end # module Rails
60
+ end # module Respect
@@ -0,0 +1,101 @@
1
+ module Respect
2
+ module Rails
3
+ # A wrapper around ActionDispatch::Routing::Route. It provides only
4
+ # access to the information we need when generating doc.
5
+ # It is very strongly inspired from ActionDispatch::Routing::RouteWrapper class
6
+ # which is internal and designed to be used with
7
+ # ActionDispoatch::Routing::RouteInspector. These classes are not meant
8
+ # to be used by external user as far as I can read in rails code base.
9
+ # So, we made up our own so that we can easily adjust it to our need.
10
+ class RouteInfo
11
+ include Comparable
12
+
13
+ def initialize(route, mount_point)
14
+ @route = route
15
+ if !mount_point.is_a?(self.class) && !mount_point.nil? && !mount_point.engine?
16
+ raise "'#{mount_point.inspect}' must be a route to an engine if set"
17
+ end
18
+ @mount_point = mount_point
19
+ end
20
+
21
+ attr_reader :route, :mount_point
22
+
23
+ def path
24
+ path = @route.path.spec.to_s
25
+ if @mount_point
26
+ path = @mount_point.path + path
27
+ end
28
+ if path.length > 1 && path =~ %r{/$}
29
+ path.chop!
30
+ end
31
+ path
32
+ end
33
+
34
+ def internal?
35
+ path =~ %r{\A#{::Rails.application.config.assets.prefix}} \
36
+ || controller_name =~ %r{\Arails/(info|welcome)}
37
+ end
38
+
39
+ def <=>(other)
40
+ self.path <=> other.path
41
+ end
42
+
43
+ def verb
44
+ @route.verb.source.gsub(/[$^]/, '')
45
+ end
46
+
47
+ def controller_name
48
+ @route.requirements[:controller] || ':controller'
49
+ end
50
+
51
+ def action_name
52
+ @route.requirements[:action] || ':action'
53
+ end
54
+
55
+ def schema
56
+ @schema = ActionSchema.from_controller(controller_name, action_name)
57
+ end
58
+
59
+ def has_schema?
60
+ schema && schema.has_schema?
61
+ end
62
+
63
+ def spec
64
+ "#{verb} #{path}".strip
65
+ end
66
+
67
+ def url
68
+ if schema
69
+ options = schema_set.default_url_options
70
+ else
71
+ options = @route.defaults.merge({ format: 'json' })
72
+ end
73
+ ::Rails.application.routes.url_for(options)
74
+ end
75
+
76
+ def endpoint
77
+ rack_app ? rack_app : nil
78
+ end
79
+
80
+ def rack_app(app = @route.app)
81
+ @rack_app ||= begin
82
+ class_name = app.class.name.to_s
83
+ if class_name == "ActionDispatch::Routing::Mapper::Constraints"
84
+ rack_app(app.app)
85
+ elsif ActionDispatch::Routing::Redirect === app || class_name !~ /^ActionDispatch::Routing/
86
+ app
87
+ end
88
+ end
89
+ end
90
+
91
+ def engine?
92
+ rack_app && rack_app.respond_to?(:routes)
93
+ end
94
+
95
+ def anchor
96
+ "#{controller_name}_#{action_name}"
97
+ end
98
+
99
+ end # class RouteInfo
100
+ end # module Rails
101
+ end # module Respect
@@ -0,0 +1,5 @@
1
+ module Respect
2
+ module Rails
3
+ VERSION = "0.1.0"
4
+ end
5
+ end