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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7eaab971cec433522e7a0c8920b1bf3c37fee9f6
4
+ data.tar.gz: 9f0eb16b2fd3a70892e36f73d5e3d9e5c54fb738
5
+ SHA512:
6
+ metadata.gz: a786afed2c20a213956ef0cface62a978e41fbea4943e1f0c2da94a1d7f001a470714e06ec17ba8e1598195b577d2dbc51fee24f99f7c835a002917e39d7db31
7
+ data.tar.gz: 15e97461f543a737d32e4a226e6b9e9384bda8fbec5c8f5c9af6c4991c9a105bb243c9a9480bd91d4dc0411511ce3d8d261e97084a8352c80e92bb025bd0442f
@@ -0,0 +1,63 @@
1
+ require 'active_support'
2
+ require 'active_support/inflector'
3
+ require 'cgi'
4
+ require 'json'
5
+
6
+ module RspecApiDocumentation
7
+ extend ActiveSupport::Autoload
8
+
9
+ require 'rspec_api_documentation/railtie' if defined?(Rails)
10
+ include ActiveSupport::JSON
11
+
12
+ eager_autoload do
13
+ autoload :Configuration
14
+ autoload :ApiDocumentation
15
+ autoload :ApiFormatter
16
+ autoload :Example
17
+ autoload :Index
18
+ autoload :ClientBase
19
+ autoload :Headers
20
+ end
21
+
22
+ autoload :DSL
23
+ autoload :RackTestClient
24
+ autoload :OAuth2MACClient, "rspec_api_documentation/oauth2_mac_client"
25
+ autoload :TestServer
26
+ autoload :Curl
27
+
28
+ module Writers
29
+ extend ActiveSupport::Autoload
30
+
31
+ autoload :GeneralMarkupWriter
32
+ autoload :HtmlWriter
33
+ autoload :TextileWriter
34
+ autoload :JsonWriter
35
+ autoload :JsonIodocsWriter
36
+ autoload :IndexWriter
37
+ autoload :CombinedTextWriter
38
+ autoload :CombinedJsonWriter
39
+ end
40
+
41
+ module Views
42
+ extend ActiveSupport::Autoload
43
+
44
+ autoload :MarkupIndex
45
+ autoload :MarkupExample
46
+ autoload :HtmlIndex
47
+ autoload :HtmlExample
48
+ autoload :TextileIndex
49
+ autoload :TextileExample
50
+ end
51
+
52
+ def self.configuration
53
+ @configuration ||= Configuration.new
54
+ end
55
+
56
+ def self.documentations
57
+ @documentations ||= configuration.map { |config| ApiDocumentation.new(config) }
58
+ end
59
+
60
+ def self.configure
61
+ yield configuration if block_given?
62
+ end
63
+ end
@@ -0,0 +1,38 @@
1
+ module RspecApiDocumentation
2
+ class ApiDocumentation
3
+ attr_reader :configuration, :index
4
+
5
+ delegate :docs_dir, :format, :to => :configuration
6
+
7
+ def initialize(configuration)
8
+ @configuration = configuration
9
+ @index = Index.new
10
+ end
11
+
12
+ def clear_docs
13
+ if File.exists?(docs_dir)
14
+ FileUtils.rm_rf(docs_dir, :secure => true)
15
+ end
16
+ FileUtils.mkdir_p(docs_dir)
17
+ end
18
+
19
+ def document_example(rspec_example)
20
+ example = Example.new(rspec_example, configuration)
21
+ if example.should_document?
22
+ index.examples << example
23
+ end
24
+ end
25
+
26
+ def write
27
+ writers.each do |writer|
28
+ writer.write(index, configuration)
29
+ end
30
+ end
31
+
32
+ def writers
33
+ [*configuration.format].map do |format|
34
+ RspecApiDocumentation::Writers.const_get("#{format}_writer".classify)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,45 @@
1
+ require 'rspec/core/formatters/base_text_formatter'
2
+
3
+ module RspecApiDocumentation
4
+ class ApiFormatter < RSpec::Core::Formatters::BaseTextFormatter
5
+ def initialize(output)
6
+ super
7
+
8
+ output.puts "Generating API Docs"
9
+ end
10
+
11
+ def start(example_count)
12
+ super
13
+
14
+ RspecApiDocumentation.documentations.each(&:clear_docs)
15
+ end
16
+
17
+ def example_group_started(example_group)
18
+ super
19
+
20
+ output.puts " #{example_group.description}"
21
+ end
22
+
23
+ def example_passed(example)
24
+ super
25
+
26
+ output.puts " * #{example.description}"
27
+
28
+ RspecApiDocumentation.documentations.each do |documentation|
29
+ documentation.document_example(example)
30
+ end
31
+ end
32
+
33
+ def example_failed(example)
34
+ super
35
+
36
+ output.puts " ! #{example.description} (FAILED)"
37
+ end
38
+
39
+ def stop
40
+ super
41
+
42
+ RspecApiDocumentation.documentations.each(&:write)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,82 @@
1
+ module RspecApiDocumentation
2
+ class ClientBase < Struct.new(:context, :options)
3
+ include Headers
4
+
5
+ delegate :example, :app, :to => :context
6
+ delegate :metadata, :to => :example
7
+
8
+ def get(*args)
9
+ process :get, *args
10
+ end
11
+
12
+ def post(*args)
13
+ process :post, *args
14
+ end
15
+
16
+ def put(*args)
17
+ process :put, *args
18
+ end
19
+
20
+ def delete(*args)
21
+ process :delete, *args
22
+ end
23
+
24
+ def head(*args)
25
+ process :head, *args
26
+ end
27
+
28
+ def patch(*args)
29
+ process :patch, *args
30
+ end
31
+
32
+ def response_status
33
+ status
34
+ end
35
+
36
+ private
37
+
38
+ def process(method, path, params = {}, headers ={})
39
+ do_request(method, path, params, headers)
40
+ document_example(method.to_s.upcase, path)
41
+ end
42
+
43
+ def document_example(method, path)
44
+ return unless metadata[:document]
45
+
46
+ input = last_request.env["rack.input"]
47
+ input.rewind
48
+ request_body = input.read
49
+
50
+ request_metadata = {}
51
+
52
+ request_metadata[:request_method] = method
53
+ request_metadata[:request_path] = path
54
+ request_metadata[:request_body] = request_body.empty? ? nil : request_body
55
+ request_metadata[:request_headers] = request_headers
56
+ request_metadata[:request_query_parameters] = query_hash
57
+ request_metadata[:request_content_type] = request_content_type
58
+ request_metadata[:response_status] = status
59
+ request_metadata[:response_status_text] = Rack::Utils::HTTP_STATUS_CODES[status]
60
+ request_metadata[:response_body] = response_body.empty? ? nil : response_body
61
+ request_metadata[:response_headers] = response_headers
62
+ request_metadata[:response_content_type] = response_content_type
63
+ request_metadata[:curl] = Curl.new(method, path, request_body, request_headers)
64
+
65
+ metadata[:requests] ||= []
66
+ metadata[:requests] << request_metadata
67
+ end
68
+
69
+ def query_hash
70
+ strings = query_string.split("&")
71
+ arrays = strings.map do |segment|
72
+ k,v = segment.split("=")
73
+ [k, v && CGI.unescape(v)]
74
+ end
75
+ Hash[arrays]
76
+ end
77
+
78
+ def headers(method, path, params, request_headers)
79
+ request_headers || {}
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,84 @@
1
+ module RspecApiDocumentation
2
+ class Configuration
3
+ include Enumerable
4
+
5
+ attr_reader :parent
6
+
7
+ def initialize(parent = nil)
8
+ @parent = parent
9
+ @settings = parent.settings.clone if parent
10
+ end
11
+
12
+ def groups
13
+ @groups ||= []
14
+ end
15
+
16
+ def define_group(name, &block)
17
+ subconfig = self.class.new(self)
18
+ subconfig.filter = name
19
+ subconfig.docs_dir = self.docs_dir.join(name.to_s)
20
+ yield subconfig
21
+ groups << subconfig
22
+ end
23
+
24
+ def self.add_setting(name, opts = {})
25
+ define_method("#{name}=") { |value| settings[name] = value }
26
+ define_method("#{name}") do
27
+ if settings.has_key?(name)
28
+ settings[name]
29
+ elsif opts[:default].respond_to?(:call)
30
+ opts[:default].call(self)
31
+ else
32
+ opts[:default]
33
+ end
34
+ end
35
+ end
36
+
37
+ add_setting :docs_dir, :default => lambda { |config|
38
+ if defined?(Rails)
39
+ Rails.root.join("doc", "api")
40
+ else
41
+ Pathname.new("doc/api")
42
+ end
43
+ }
44
+
45
+ add_setting :format, :default => :html
46
+ add_setting :template_path, :default => File.expand_path("../../../templates", __FILE__)
47
+ add_setting :filter, :default => :all
48
+ add_setting :exclusion_filter, :default => nil
49
+ add_setting :app, :default => lambda { |config|
50
+ if defined?(Rails)
51
+ Rails.application
52
+ else
53
+ nil
54
+ end
55
+ }
56
+
57
+ add_setting :curl_host, :default => nil
58
+ add_setting :keep_source_order, :default => false
59
+ add_setting :api_name, :default => "API Documentation"
60
+ add_setting :io_docs_protocol, :default => "http"
61
+
62
+ def client_method=(new_client_method)
63
+ RspecApiDocumentation::DSL::Resource.module_eval <<-RUBY
64
+ alias :#{new_client_method} #{client_method}
65
+ undef #{client_method}
66
+ RUBY
67
+
68
+ @client_method = new_client_method
69
+ end
70
+
71
+ def client_method
72
+ @client_method ||= :client
73
+ end
74
+
75
+ def settings
76
+ @settings ||= {}
77
+ end
78
+
79
+ def each(&block)
80
+ yield self
81
+ groups.map { |g| g.each &block }
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,61 @@
1
+ require 'active_support/core_ext/object/to_query'
2
+
3
+ module RspecApiDocumentation
4
+ class Curl < Struct.new(:method, :path, :data, :headers)
5
+ attr_accessor :host
6
+
7
+ def output(config_host)
8
+ self.host = config_host
9
+ send(method.downcase)
10
+ end
11
+
12
+ def post
13
+ "curl \"#{url}\" #{post_data} -X POST #{headers}"
14
+ end
15
+
16
+ def get
17
+ "curl \"#{url}#{get_data}\" -X GET #{headers}"
18
+ end
19
+
20
+ def put
21
+ "curl \"#{url}\" #{post_data} -X PUT #{headers}"
22
+ end
23
+
24
+ def delete
25
+ "curl \"#{url}\" #{post_data} -X DELETE #{headers}"
26
+ end
27
+
28
+ def head
29
+ "curl \"#{url}#{get_data}\" -X HEAD #{headers}"
30
+ end
31
+
32
+ def patch
33
+ "curl \"#{url}\" #{post_data} -X PATCH #{headers}"
34
+ end
35
+
36
+ def url
37
+ "#{host}#{path}"
38
+ end
39
+
40
+ def headers
41
+ super.map do |k, v|
42
+ "\\\n\t-H \"#{format_header(k, v)}\""
43
+ end.join(" ")
44
+ end
45
+
46
+ def get_data
47
+ "?#{data}" unless data.blank?
48
+ end
49
+
50
+ def post_data
51
+ escaped_data = data.to_s.gsub("'", "\\u0027")
52
+ "-d '#{escaped_data}'"
53
+ end
54
+
55
+ private
56
+ def format_header(header, value)
57
+ formatted_header = header.gsub(/^HTTP_/, '').titleize.split.join("-")
58
+ "#{formatted_header}: #{value}"
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,17 @@
1
+ require "rspec_api_documentation/dsl/resource"
2
+ require "rspec_api_documentation/dsl/endpoint"
3
+ require "rspec_api_documentation/dsl/callback"
4
+
5
+ def self.resource(*args, &block)
6
+ options = if args.last.is_a?(Hash) then args.pop else {} end
7
+ options[:api_doc_dsl] = :resource
8
+ options[:resource_name] = args.first
9
+ options[:document] ||= :all
10
+ args.push(options)
11
+ describe(*args, &block)
12
+ end
13
+
14
+ RSpec.configuration.include RspecApiDocumentation::DSL::Resource, :api_doc_dsl => :resource
15
+ RSpec.configuration.include RspecApiDocumentation::DSL::Endpoint, :api_doc_dsl => :endpoint
16
+ RSpec.configuration.include RspecApiDocumentation::DSL::Callback, :api_doc_dsl => :callback
17
+ RSpec.configuration.backtrace_exclusion_patterns << %r{lib/rspec_api_documentation/dsl\.rb}
@@ -0,0 +1,25 @@
1
+ module RspecApiDocumentation::DSL
2
+ module Callback
3
+ extend ActiveSupport::Concern
4
+
5
+ delegate :request_method, :request_headers, :request_body, :to => :destination
6
+
7
+ module ClassMethods
8
+ def trigger_callback(&block)
9
+ define_method(:do_callback) do
10
+ require 'rack'
11
+ stub_request(:any, callback_url).to_rack(destination)
12
+ instance_eval &block
13
+ end
14
+ end
15
+ end
16
+
17
+ def destination
18
+ @destination ||= RspecApiDocumentation::TestServer.new(self)
19
+ end
20
+
21
+ def callback_url
22
+ raise "You must define callback_url"
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,134 @@
1
+ require 'rspec/core/formatters/base_formatter'
2
+ require 'rack/utils'
3
+ require 'rack/test/utils'
4
+
5
+ module RspecApiDocumentation::DSL
6
+ module Endpoint
7
+ extend ActiveSupport::Concern
8
+ include Rack::Test::Utils
9
+
10
+ delegate :response_headers, :status, :response_status, :response_body, :to => :rspec_api_documentation_client
11
+
12
+ module ClassMethods
13
+ def example_request(description, params = {}, &block)
14
+ file_path = caller.first[0, caller.first =~ /:/]
15
+
16
+ location = caller.first[0, caller.first =~ /(:in|$)/]
17
+ location = relative_path(location)
18
+
19
+ example description, :location => location, :file_path => file_path do
20
+ do_request(params)
21
+ instance_eval &block if block_given?
22
+ end
23
+ end
24
+
25
+ private
26
+ # from rspec-core
27
+ def relative_path(line)
28
+ line = line.sub(File.expand_path("."), ".")
29
+ line = line.sub(/\A([^:]+:\d+)$/, '\\1')
30
+ return nil if line == '-e:1'
31
+ line
32
+ end
33
+ end
34
+
35
+ def do_request(extra_params = {})
36
+ @extra_params = extra_params
37
+
38
+ params_or_body = nil
39
+ path_or_query = path
40
+
41
+ if method == :get && !query_string.blank?
42
+ path_or_query += "?#{query_string}"
43
+ else
44
+ params_or_body = respond_to?(:raw_post) ? raw_post : params
45
+ end
46
+
47
+ rspec_api_documentation_client.send(method, path_or_query, params_or_body, headers)
48
+ end
49
+
50
+ def query_string
51
+ build_nested_query(params || {})
52
+ end
53
+
54
+ def params
55
+ parameters = example.metadata.fetch(:parameters, {}).inject({}) do |hash, param|
56
+ set_param(hash, param)
57
+ end
58
+ parameters.merge!(extra_params)
59
+ parameters
60
+ end
61
+
62
+ def headers
63
+ return unless example.metadata[:headers]
64
+ example.metadata[:headers].inject({}) do |hash, (header, value)|
65
+ if value.is_a?(Symbol)
66
+ hash[header] = send(value) if respond_to?(value)
67
+ else
68
+ hash[header] = value
69
+ end
70
+ hash
71
+ end
72
+ end
73
+
74
+ def method
75
+ example.metadata[:method]
76
+ end
77
+
78
+ def in_path?(param)
79
+ path_params.include?(param)
80
+ end
81
+
82
+ def path_params
83
+ example.metadata[:route].scan(/:(\w+)/).flatten
84
+ end
85
+
86
+ def path
87
+ example.metadata[:route].gsub(/:(\w+)/) do |match|
88
+ if extra_params.keys.include?($1)
89
+ delete_extra_param($1)
90
+ elsif respond_to?($1)
91
+ send($1)
92
+ else
93
+ match
94
+ end
95
+ end
96
+ end
97
+
98
+ def explanation(text)
99
+ example.metadata[:explanation] = text
100
+ end
101
+
102
+ private
103
+
104
+ def rspec_api_documentation_client
105
+ send(RspecApiDocumentation.configuration.client_method)
106
+ end
107
+
108
+ def extra_params
109
+ return {} if @extra_params.nil?
110
+ @extra_params.inject({}) do |h, (k, v)|
111
+ h[k.to_s] = v
112
+ h
113
+ end
114
+ end
115
+
116
+ def delete_extra_param(key)
117
+ @extra_params.delete(key.to_sym) || @extra_params.delete(key.to_s)
118
+ end
119
+
120
+ def set_param(hash, param)
121
+ key = param[:name]
122
+ return hash if !respond_to?(key) || in_path?(key)
123
+
124
+ if param[:scope]
125
+ hash[param[:scope].to_s] ||= {}
126
+ hash[param[:scope].to_s][key] = send(key)
127
+ else
128
+ hash[key] = send(key)
129
+ end
130
+
131
+ hash
132
+ end
133
+ end
134
+ end