rspec-api-documentation 1.1.1.alpha

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 (39) hide show
  1. checksums.yaml +7 -0
  2. data/lib/rspec_api_documentation.rb +63 -0
  3. data/lib/rspec_api_documentation/api_documentation.rb +38 -0
  4. data/lib/rspec_api_documentation/api_formatter.rb +45 -0
  5. data/lib/rspec_api_documentation/client_base.rb +82 -0
  6. data/lib/rspec_api_documentation/configuration.rb +84 -0
  7. data/lib/rspec_api_documentation/curl.rb +61 -0
  8. data/lib/rspec_api_documentation/dsl.rb +17 -0
  9. data/lib/rspec_api_documentation/dsl/callback.rb +25 -0
  10. data/lib/rspec_api_documentation/dsl/endpoint.rb +134 -0
  11. data/lib/rspec_api_documentation/dsl/resource.rb +83 -0
  12. data/lib/rspec_api_documentation/example.rb +49 -0
  13. data/lib/rspec_api_documentation/headers.rb +32 -0
  14. data/lib/rspec_api_documentation/index.rb +7 -0
  15. data/lib/rspec_api_documentation/oauth2_mac_client.rb +70 -0
  16. data/lib/rspec_api_documentation/rack_test_client.rb +58 -0
  17. data/lib/rspec_api_documentation/railtie.rb +7 -0
  18. data/lib/rspec_api_documentation/test_server.rb +31 -0
  19. data/lib/rspec_api_documentation/views/html_example.rb +16 -0
  20. data/lib/rspec_api_documentation/views/html_index.rb +14 -0
  21. data/lib/rspec_api_documentation/views/markup_example.rb +58 -0
  22. data/lib/rspec_api_documentation/views/markup_index.rb +21 -0
  23. data/lib/rspec_api_documentation/views/textile_example.rb +16 -0
  24. data/lib/rspec_api_documentation/views/textile_index.rb +14 -0
  25. data/lib/rspec_api_documentation/writers/combined_json_writer.rb +20 -0
  26. data/lib/rspec_api_documentation/writers/combined_text_writer.rb +106 -0
  27. data/lib/rspec_api_documentation/writers/formatter.rb +11 -0
  28. data/lib/rspec_api_documentation/writers/general_markup_writer.rb +42 -0
  29. data/lib/rspec_api_documentation/writers/html_writer.rb +21 -0
  30. data/lib/rspec_api_documentation/writers/index_writer.rb +16 -0
  31. data/lib/rspec_api_documentation/writers/json_iodocs_writer.rb +111 -0
  32. data/lib/rspec_api_documentation/writers/json_writer.rb +111 -0
  33. data/lib/rspec_api_documentation/writers/textile_writer.rb +21 -0
  34. data/lib/tasks/docs.rake +7 -0
  35. data/templates/rspec_api_documentation/html_example.mustache +106 -0
  36. data/templates/rspec_api_documentation/html_index.mustache +34 -0
  37. data/templates/rspec_api_documentation/textile_example.mustache +68 -0
  38. data/templates/rspec_api_documentation/textile_index.mustache +10 -0
  39. metadata +267 -0
@@ -0,0 +1,83 @@
1
+ module RspecApiDocumentation::DSL
2
+ module Resource
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def self.define_action(method)
7
+ define_method method do |*args, &block|
8
+ options = if args.last.is_a?(Hash) then args.pop else {} end
9
+ options[:method] = method
10
+ options[:route] = args.first
11
+ options[:api_doc_dsl] = :endpoint
12
+ args.push(options)
13
+ args[0] = "#{method.to_s.upcase} #{args[0]}"
14
+ context(*args, &block)
15
+ end
16
+ end
17
+
18
+ define_action :get
19
+ define_action :post
20
+ define_action :put
21
+ define_action :delete
22
+ define_action :head
23
+ define_action :patch
24
+
25
+ def callback(*args, &block)
26
+ require 'webmock'
27
+ self.send(:include, WebMock::API)
28
+
29
+ options = if args.last.is_a?(Hash) then args.pop else {} end
30
+ options[:api_doc_dsl] = :callback
31
+ args.push(options)
32
+
33
+ context(*args, &block)
34
+ end
35
+
36
+ def parameter(name, description, options = {})
37
+ parameters.push(options.merge(:name => name.to_s, :description => description))
38
+ end
39
+
40
+ def header(name, value)
41
+ headers[name] = value
42
+ end
43
+
44
+ private
45
+ def parameters
46
+ metadata[:parameters] ||= []
47
+ if superclass_metadata && metadata[:parameters].equal?(superclass_metadata[:parameters])
48
+ metadata[:parameters] = Marshal.load(Marshal.dump(superclass_metadata[:parameters]))
49
+ end
50
+ metadata[:parameters]
51
+ end
52
+
53
+ def headers
54
+ metadata[:headers] ||= {}
55
+ if superclass_metadata && metadata[:headers].equal?(superclass_metadata[:headers])
56
+ metadata[:headers] = Marshal.load(Marshal.dump(superclass_metadata[:headers]))
57
+ end
58
+ metadata[:headers]
59
+ end
60
+
61
+ def parameter_keys
62
+ parameters.map { |param| param[:name] }
63
+ end
64
+ end
65
+
66
+ def app
67
+ RspecApiDocumentation.configuration.app
68
+ end
69
+
70
+ def client
71
+ @client ||= RspecApiDocumentation::RackTestClient.new(self)
72
+ end
73
+
74
+ def no_doc(&block)
75
+ requests = example.metadata[:requests]
76
+ example.metadata[:requests] = []
77
+
78
+ instance_eval &block
79
+
80
+ example.metadata[:requests] = requests
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,49 @@
1
+ module RspecApiDocumentation
2
+ class Example
3
+ attr_reader :example, :configuration
4
+
5
+ def initialize(example, configuration)
6
+ @example = example
7
+ @configuration = configuration
8
+ end
9
+
10
+ def method_missing(method_sym, *args, &block)
11
+ if example.metadata.has_key?(method_sym)
12
+ example.metadata[method_sym]
13
+ else
14
+ example.send(method_sym, *args, &block)
15
+ end
16
+ end
17
+
18
+ def respond_to?(method_sym, include_private = false)
19
+ super || example.metadata.has_key?(method_sym) || example.respond_to?(method_sym, include_private)
20
+ end
21
+
22
+ def http_method
23
+ metadata[:method].to_s.upcase
24
+ end
25
+
26
+ def should_document?
27
+ return false if pending? || !metadata[:resource_name] || !metadata[:document]
28
+ return false if (Array(metadata[:document]) & Array(configuration.exclusion_filter)).length > 0
29
+ return true if (Array(metadata[:document]) & Array(configuration.filter)).length > 0
30
+ return true if configuration.filter == :all
31
+ end
32
+
33
+ def public?
34
+ metadata[:public]
35
+ end
36
+
37
+ def has_parameters?
38
+ respond_to?(:parameters) && parameters.present?
39
+ end
40
+
41
+ def explanation
42
+ metadata[:explanation] || nil
43
+ end
44
+
45
+ def requests
46
+ metadata[:requests] || []
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,32 @@
1
+ module RspecApiDocumentation
2
+ module Headers
3
+ private
4
+
5
+ def env_to_headers(env)
6
+ headers = {}
7
+ env.each do |key, value|
8
+ # HTTP_ACCEPT_CHARSET => Accept-Charset
9
+ if key =~ /^(HTTP_|CONTENT_TYPE)/
10
+ header = key.gsub(/^HTTP_/, '').titleize.split.join("-")
11
+ headers[header] = value
12
+ end
13
+ end
14
+ headers
15
+ end
16
+
17
+ def headers_to_env(headers)
18
+ headers.inject({}) do |hsh, (k, v)|
19
+ new_key = k.upcase.gsub("-", "_")
20
+ new_key = "HTTP_#{new_key}" unless new_key == "CONTENT_TYPE"
21
+ hsh[new_key] = v
22
+ hsh
23
+ end
24
+ end
25
+
26
+ def format_headers(headers)
27
+ headers.map do |key, value|
28
+ "#{key}: #{value}"
29
+ end.join("\n")
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,7 @@
1
+ module RspecApiDocumentation
2
+ class Index
3
+ def examples
4
+ @examples ||= []
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,70 @@
1
+ begin
2
+ require "active_support/secure_random"
3
+ rescue LoadError
4
+ # ActiveSupport::SecureRandom not provided in activesupport >= 3.2
5
+ end
6
+ require "webmock"
7
+ require "rack/oauth2"
8
+
9
+ module RspecApiDocumentation
10
+ class OAuth2MACClient < ClientBase
11
+ include WebMock::API
12
+ attr_accessor :last_response, :last_request
13
+ private :last_response, :last_request
14
+
15
+ def request_headers
16
+ env_to_headers(last_request.env)
17
+ end
18
+
19
+ def response_headers
20
+ last_response.headers
21
+ end
22
+
23
+ def query_string
24
+ last_request.env["QUERY_STRING"]
25
+ end
26
+
27
+ def status
28
+ last_response.status
29
+ end
30
+
31
+ def response_body
32
+ last_response.body
33
+ end
34
+
35
+ def request_content_type
36
+ last_request.content_type
37
+ end
38
+
39
+ def response_content_type
40
+ last_response.content_type
41
+ end
42
+
43
+ protected
44
+
45
+ def do_request(method, path, params, request_headers)
46
+ self.last_response = access_token.send(method, "http://example.com#{path}", :body => params, :header => headers(method, path, params, request_headers))
47
+ end
48
+
49
+ class ProxyApp < Struct.new(:client, :app)
50
+ def call(env)
51
+ env["QUERY_STRING"] = query_string_hack(env)
52
+ client.last_request = Struct.new(:env, :content_type).new(env, env["CONTENT_TYPE"])
53
+ app.call(env.merge("SCRIPT_NAME" => ""))
54
+ end
55
+
56
+ private
57
+ def query_string_hack(env)
58
+ env["QUERY_STRING"].gsub('%5B', '[').gsub('%5D', ']').gsub(/\[\d+/) { |s| "#{$1}[" }
59
+ end
60
+ end
61
+
62
+ def access_token
63
+ @access_token ||= begin
64
+ app = ProxyApp.new(self, context.app)
65
+ stub_request(:any, %r{http://example\.com}).to_rack(app)
66
+ Rack::OAuth2::Client.new(options.merge(:host => "example.com", :scheme => "http")).access_token!
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,58 @@
1
+ module RspecApiDocumentation
2
+ class RackTestClient < ClientBase
3
+
4
+ delegate :last_request, :last_response, :to => :rack_test_session
5
+ private :last_request, :last_response
6
+
7
+ def request_headers
8
+ env_to_headers(last_request.env)
9
+ end
10
+
11
+ def response_headers
12
+ last_response.headers
13
+ end
14
+
15
+ def query_string
16
+ last_request.env["QUERY_STRING"]
17
+ end
18
+
19
+ def status
20
+ last_response.status
21
+ end
22
+
23
+ def response_body
24
+ last_response.body
25
+ end
26
+
27
+ def request_content_type
28
+ last_request.content_type
29
+ end
30
+
31
+ def response_content_type
32
+ last_response.content_type
33
+ end
34
+
35
+ protected
36
+
37
+ def do_request(method, path, params, request_headers)
38
+ rack_test_session.send(method, path, params, headers(method, path, params, request_headers))
39
+ end
40
+
41
+ def headers(*args)
42
+ headers_to_env(super)
43
+ end
44
+
45
+ private
46
+
47
+ def rack_test_session
48
+ @rack_test_session ||= Struct.new(:app) do
49
+ begin
50
+ require "rack/test"
51
+ include Rack::Test::Methods
52
+ rescue LoadError
53
+ raise "#{self.class.name} requires Rack::Test >= 0.5.5. Please add it to your test dependencies."
54
+ end
55
+ end.new(app)
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,7 @@
1
+ module RspecApiDocumentation
2
+ class Railtie < Rails::Railtie
3
+ rake_tasks do
4
+ load "tasks/docs.rake"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,31 @@
1
+ module RspecApiDocumentation
2
+ class TestServer < Struct.new(:context)
3
+ include Headers
4
+
5
+ delegate :example, :to => :context
6
+ delegate :metadata, :to => :example
7
+
8
+ attr_reader :request_method, :request_headers, :request_body
9
+
10
+ def call(env)
11
+ input = env["rack.input"]
12
+ input.rewind
13
+
14
+ @request_method = env["REQUEST_METHOD"]
15
+ @request_headers = env_to_headers(env)
16
+ @request_body = input.read
17
+
18
+ request_metadata = {}
19
+
20
+ request_metadata[:request_method] = @request_method
21
+ request_metadata[:request_path] = env["PATH_INFO"]
22
+ request_metadata[:request_body] = @request_body
23
+ request_metadata[:request_headers] = @request_headers
24
+
25
+ metadata[:requests] ||= []
26
+ metadata[:requests] << request_metadata
27
+
28
+ return [200, {}, [""]]
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,16 @@
1
+ module RspecApiDocumentation
2
+ module Views
3
+ class HtmlExample < MarkupExample
4
+ EXTENSION = 'html'
5
+
6
+ def initialize(example, configuration)
7
+ super
8
+ self.template_name = "rspec_api_documentation/html_example"
9
+ end
10
+
11
+ def extension
12
+ EXTENSION
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,14 @@
1
+ module RspecApiDocumentation
2
+ module Views
3
+ class HtmlIndex < MarkupIndex
4
+ def initialize(index, configuration)
5
+ super
6
+ self.template_name = "rspec_api_documentation/html_index"
7
+ end
8
+
9
+ def examples
10
+ @index.examples.map { |example| HtmlExample.new(example, @configuration) }
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,58 @@
1
+ require 'mustache'
2
+
3
+ module RspecApiDocumentation
4
+ module Views
5
+ class MarkupExample < Mustache
6
+ def initialize(example, configuration)
7
+ @example = example
8
+ @host = configuration.curl_host
9
+ self.template_path = configuration.template_path
10
+ end
11
+
12
+ def method_missing(method, *args, &block)
13
+ @example.send(method, *args, &block)
14
+ end
15
+
16
+ def respond_to?(method, include_private = false)
17
+ super || @example.respond_to?(method, include_private)
18
+ end
19
+
20
+ def dirname
21
+ resource_name.downcase.gsub(/\s+/, '_')
22
+ end
23
+
24
+ def filename
25
+ basename = description.downcase.gsub(/\s+/, '_').gsub(/[^a-z_]/, '')
26
+ basename = Digest::MD5.new.update(description).to_s if basename.blank?
27
+ "#{basename}.#{extension}"
28
+ end
29
+
30
+ def requests
31
+ super.map do |hash|
32
+ hash[:request_headers_text] = format_hash(hash[:request_headers])
33
+ hash[:request_query_parameters_text] = format_hash(hash[:request_query_parameters])
34
+ hash[:response_headers_text] = format_hash(hash[:response_headers])
35
+ if @host
36
+ hash[:curl] = hash[:curl].output(@host) if hash[:curl].is_a? RspecApiDocumentation::Curl
37
+ else
38
+ hash[:curl] = nil
39
+ end
40
+ hash
41
+ end
42
+ end
43
+
44
+ def extension
45
+ raise 'Parent class. This method should not be called.'
46
+ end
47
+
48
+ private
49
+
50
+ def format_hash(hash = {})
51
+ return nil unless hash.present?
52
+ hash.collect do |k, v|
53
+ "#{k}: #{v}"
54
+ end.join("\n")
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,21 @@
1
+ require 'mustache'
2
+
3
+ module RspecApiDocumentation
4
+ module Views
5
+ class MarkupIndex < Mustache
6
+ def initialize(index, configuration)
7
+ @index = index
8
+ @configuration = configuration
9
+ self.template_path = configuration.template_path
10
+ end
11
+
12
+ def api_name
13
+ @configuration.api_name
14
+ end
15
+
16
+ def sections
17
+ RspecApiDocumentation::Writers::IndexWriter.sections(examples, @configuration)
18
+ end
19
+ end
20
+ end
21
+ end