ghost_writer 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ script: "bundle exec rake spec build"
3
+ rvm:
4
+ - 1.9.3
5
+ notifications:
6
+ email:
7
+ on_success: never
data/README.md CHANGED
@@ -1,4 +1,5 @@
1
1
  # GhostWriter
2
+ [![Build Status](https://travis-ci.org/joker1007/ghost_writer.png)](https://travis-ci.org/joker1007/ghost_writer)
2
3
 
3
4
  Generate API examples from params and response of controller specs
4
5
 
@@ -20,6 +21,18 @@ Or install it yourself as:
20
21
 
21
22
  Write controller spec:
22
23
  ```ruby
24
+ # spec_helper
25
+ RSpec.configure do |config|
26
+ config.include GhostWriter
27
+ config.after(:suite) do
28
+ GhostWriter.generate_api_doc
29
+ end
30
+
31
+ GhostWriter.output_dir = "api_docs" # Optional (default is "api_examples")
32
+ GhostWriter.github_base_url = "https://github.com/joker1007/ghost_writer/tree/master/output_examples" # Optional
33
+ end
34
+
35
+ # posts_controller_spec
23
36
  require 'spec_helper'
24
37
 
25
38
  describe PostsController do
@@ -28,6 +41,11 @@ describe PostsController do
28
41
  get :index
29
42
  response.should be_success
30
43
  end
44
+
45
+ it "should be success", generate_api_doc: "index_error" do # if metadata value is string, use it as filename
46
+ get :index
47
+ response.status.should eq 404
48
+ end
31
49
  end
32
50
  end
33
51
  ```
@@ -40,6 +58,14 @@ GENERATE_API_DOC=1 bundle exec rspec spec
40
58
 
41
59
  If you don't set environment variable, this gem doesn't generate docs.
42
60
 
61
+ ## Output Example
62
+ Please look at [output_examples](https://github.com/joker1007/ghost_writer/tree/master/output_examples)
63
+
64
+ ## Config
65
+ If output_dir is set, generate docs at `[Rails.root]/doc/[output_dir]`
66
+
67
+ If github_base_url is set, link index is based on the url, like output_examples
68
+
43
69
  ## TODO
44
70
  - support more output formats (now markdown only)
45
71
 
data/lib/ghost_writer.rb CHANGED
@@ -1,44 +1,58 @@
1
1
  require "ghost_writer/version"
2
+ require "ghost_writer/document"
3
+ require "ghost_writer/document_index"
2
4
  require "active_support/concern"
3
5
 
4
6
  module GhostWriter
5
7
  extend ActiveSupport::Concern
6
8
 
7
- mattr_accessor :output_dir
9
+ module Format
10
+ autoload "Markdown", "ghost_writer/format/markdown"
11
+ end
12
+
13
+ DOCUMENT_INDEX_FILENAME = "document_index.markdown"
14
+
15
+ class << self
16
+ attr_accessor :output_dir, :github_base_url
8
17
 
9
- def generate_api_doc
10
- @@output_path = @@output_dir ? Rails.root + "doc" + @@output_dir : Rails.root + "doc" + "api_examples"
11
- unless File.exist?(doc_dir)
12
- FileUtils.mkdir_p(doc_dir)
18
+ def documents
19
+ @documents ||= []
20
+ @documents
13
21
  end
14
22
 
15
- doc = File.open(File.join(doc_dir, "#{doc_name}.markdown"), "w")
23
+ def generate_api_doc
24
+ if ENV["GENERATE_API_DOC"]
25
+ unless File.exist?(output_path)
26
+ FileUtils.mkdir_p(output_path)
27
+ end
28
+ document_index = GhostWriter::DocumentIndex.new(output_path + DOCUMENT_INDEX_FILENAME, documents)
29
+ document_index.write_file
30
+ @documents.each(&:write_file)
31
+ @documents.clear
32
+ end
33
+ end
16
34
 
17
- doc.puts headword("#{described_class} #{doc_name.titleize}", 1)
18
- doc.puts headword("access path:", 2)
19
- doc.puts quote("#{request.env["REQUEST_METHOD"]} #{request.env["PATH_INFO"]}")
20
- doc.puts ""
21
- doc.puts headword("request params:", 2)
22
- doc.puts quote(controller.params.reject {|key, val| key == "controller" || key == "action"}.inspect, :ruby)
23
- doc.puts ""
24
- doc.puts headword("status code:", 2)
25
- doc.puts quote(response.status.inspect)
26
- doc.puts ""
27
- doc.puts headword("response:", 2)
28
- if controller.params[:format] && controller.params[:format].to_sym == :json
29
- puts_json_data(doc)
30
- else
31
- doc.puts quote(response.body)
35
+ def output_path
36
+ output_dir ? Rails.root + "doc" + output_dir : Rails.root + "doc" + "api_examples"
32
37
  end
33
- doc.puts ""
34
- doc.puts "Generated by \"#{example.full_description}\" at #{example.location}"
35
- doc.puts ""
36
- doc.close
38
+ end
39
+
40
+ def collect_example
41
+ document = GhostWriter::Document.new(File.join(doc_dir, "#{doc_name}.markdown"), {
42
+ title: "#{described_class} #{doc_name.titleize}",
43
+ description: example.full_description.dup,
44
+ location: example.location.dup,
45
+ url_example: "#{request.env["REQUEST_METHOD"]} #{request.env["PATH_INFO"]}",
46
+ param_example: controller.params.reject {|key, val| key == "controller" || key == "action"},
47
+ status_example: response.status.inspect,
48
+ response_example: response.body,
49
+ })
50
+ GhostWriter.documents << document
37
51
  end
38
52
 
39
53
  private
40
54
  def doc_dir
41
- @@output_path + described_class.to_s.underscore
55
+ GhostWriter.output_path + described_class.to_s.underscore
42
56
  end
43
57
 
44
58
  def doc_name
@@ -49,32 +63,10 @@ module GhostWriter
49
63
  end
50
64
  end
51
65
 
52
- # TODO: outputのフォーマットを選択可能に
53
- def headword(text, level = 1)
54
- "#{'#'*level} #{text}\n"
55
- end
56
-
57
- def paragraph(text)
58
- text + "\n\n"
59
- end
60
-
61
- def quote(text, quote_format = nil)
62
- "```#{quote_format}\n#{text}\n```"
63
- end
64
-
65
- def puts_json_data(doc)
66
- data = ActiveSupport::JSON.decode(response.body)
67
- if data.is_a?(Array) || data.is_a?(Hash)
68
- doc.puts quote(JSON.pretty_generate(data), :javascript)
69
- else
70
- doc.puts quote(data)
71
- end
72
- end
73
-
74
66
  included do
75
67
  after do
76
68
  if example.metadata[:type] == :controller && example.metadata[:generate_api_doc]
77
- generate_api_doc if ENV["GENERATE_API_DOC"]
69
+ collect_example if ENV["GENERATE_API_DOC"]
78
70
  end
79
71
  end
80
72
  end
@@ -0,0 +1,71 @@
1
+ class GhostWriter::Document
2
+ attr_reader :title, :description, :location, :url_example, :param_example, :status_example, :response_example, :output, :relative_path
3
+
4
+ def initialize(output, attrs)
5
+ extend(GhostWriter::Format::Markdown)
6
+ @output = output
7
+ @relative_path = Pathname.new(output).relative_path_from(GhostWriter.output_path)
8
+ @title = attrs[:title]
9
+ @description = attrs[:description]
10
+ @location = attrs[:location]
11
+ @url_example = attrs[:url_example]
12
+ @param_example = attrs[:param_example]
13
+ @status_example = attrs[:status_example]
14
+ @response_example = attrs[:response_example]
15
+ end
16
+
17
+ def write_file
18
+ unless File.exist?(File.dirname(output))
19
+ FileUtils.mkdir_p(File.dirname(output))
20
+ end
21
+ doc = File.open(output, "w")
22
+
23
+ doc.write paragraph(<<EOP)
24
+ #{headword(title, 1)}
25
+ EOP
26
+
27
+ doc.write paragraph(<<EOP)
28
+ #{headword("access path:", 2)}
29
+ #{quote(url_example)}
30
+ EOP
31
+
32
+ doc.write paragraph(<<EOP)
33
+ #{headword("request params:", 2)}
34
+ #{quote(param_example.inspect, :ruby)}
35
+ EOP
36
+
37
+ doc.write paragraph(<<EOP)
38
+ #{headword("status code:", 2)}
39
+ #{quote(status_example)}
40
+ EOP
41
+
42
+ doc.write paragraph(<<EOP)
43
+ #{headword("response:", 2)}
44
+ #{quote_response(response_example)}
45
+ EOP
46
+
47
+ doc.write paragraph(<<EOP)
48
+ "Generated by \"#{description}\" at #{location}"
49
+ EOP
50
+
51
+ doc.close
52
+ end
53
+
54
+ private
55
+ def quote_response(body)
56
+ if param_example[:format] && param_example[:format].to_sym == :json
57
+ quote(arrange_json(response_example), :javascript)
58
+ else
59
+ quote(response_example, param_example[:format])
60
+ end
61
+ end
62
+
63
+ def arrange_json(body)
64
+ data = ActiveSupport::JSON.decode(body)
65
+ if data.is_a?(Array) || data.is_a?(Hash)
66
+ JSON.pretty_generate(data)
67
+ else
68
+ data
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,30 @@
1
+ class GhostWriter::DocumentIndex
2
+ attr_reader :output, :documents
3
+
4
+ def initialize(output, documents)
5
+ extend(GhostWriter::Format::Markdown)
6
+ @output = output
7
+ @documents = documents
8
+ end
9
+
10
+ def write_file
11
+ if GhostWriter.github_base_url
12
+ base_url = GhostWriter.github_base_url + "/"
13
+ else
14
+ base_url = ""
15
+ end
16
+
17
+ document_list = documents.map do |document|
18
+ list(
19
+ link(document.description, base_url + "#{document.relative_path}")
20
+ )
21
+ end
22
+
23
+ index_file = File.open(output, "w")
24
+ index_file.write paragraph(<<EOP)
25
+ #{headword("API Examples")}
26
+ #{document_list.join("\n")}
27
+ EOP
28
+ index_file.close
29
+ end
30
+ end
@@ -0,0 +1,27 @@
1
+ module GhostWriter
2
+ module Format
3
+ module Markdown
4
+ private
5
+ # TODO: outputのフォーマットを選択可能に
6
+ def headword(text, level = 1)
7
+ "#{'#'*level} #{text}"
8
+ end
9
+
10
+ def paragraph(text)
11
+ text + "\n"
12
+ end
13
+
14
+ def quote(text, quote_format = nil)
15
+ "```#{quote_format}\n#{text}\n```"
16
+ end
17
+
18
+ def list(text, level = 1)
19
+ "#{" " * (level - 1)}- #{text}"
20
+ end
21
+
22
+ def link(text, url)
23
+ "[#{text}](#{url})"
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,3 +1,3 @@
1
1
  module GhostWriter
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -0,0 +1,24 @@
1
+ # AnonymousController Index
2
+
3
+ ## access path:
4
+ ```
5
+ GET /anonymous
6
+ ```
7
+
8
+ ## request params:
9
+ ```ruby
10
+ {"param1"=>"value"}
11
+ ```
12
+
13
+ ## status code:
14
+ ```
15
+ 200
16
+ ```
17
+
18
+ ## response:
19
+ ```
20
+ [{"id":1,"name":"name"},{"id":2,"name":"name"}]
21
+ ```
22
+
23
+ "Generated by "GET /anonymous returns Resources array" at ./spec/lib/ghost_writer_spec.rb:38"
24
+
@@ -0,0 +1,27 @@
1
+ # AnonymousController Show
2
+
3
+ ## access path:
4
+ ```
5
+ GET /anonymous/1.json
6
+ ```
7
+
8
+ ## request params:
9
+ ```ruby
10
+ {"param1"=>"value", "params2"=>["value1", "value2"], "id"=>"1", "format"=>"json"}
11
+ ```
12
+
13
+ ## status code:
14
+ ```
15
+ 200
16
+ ```
17
+
18
+ ## response:
19
+ ```javascript
20
+ {
21
+ "id": 1,
22
+ "name": "name"
23
+ }
24
+ ```
25
+
26
+ "Generated by "GET /anonymous/1.json returns a Resource json" at ./spec/lib/ghost_writer_spec.rb:49"
27
+
@@ -0,0 +1,4 @@
1
+ # API Examples
2
+ - [GET /anonymous returns Resources array](https://github.com/joker1007/ghost_writer/tree/master/output_examples/anonymous_controller/index.markdown)
3
+ - [GET /anonymous/1.json returns a Resource json](https://github.com/joker1007/ghost_writer/tree/master/output_examples/anonymous_controller/show.markdown)
4
+
@@ -28,12 +28,27 @@ describe GhostWriter do
28
28
  ]
29
29
  render json: collection.as_json
30
30
  end
31
+
32
+ def show
33
+ render json: {id: 1, name: "name"}
34
+ end
35
+ end
36
+
37
+ describe "GET /anonymous" do
38
+ it "returns Resources array", generate_api_doc: true do
39
+ begin
40
+ get :index, param1: "value"
41
+ response.should be_success
42
+ rescue Exception => e
43
+ p e
44
+ end
45
+ end
31
46
  end
32
47
 
33
- describe "GET index" do
34
- it "should be success", generate_api_doc: true do
48
+ describe "GET /anonymous/1.json" do
49
+ it "returns a Resource json", generate_api_doc: true do
35
50
  begin
36
- get :index
51
+ get :show, id: 1, format: :json, param1: "value", params2: ["value1", "value2"]
37
52
  response.should be_success
38
53
  rescue Exception => e
39
54
  p e
@@ -59,17 +74,30 @@ describe GhostWriter do
59
74
 
60
75
  it "generate api doc file" do
61
76
  group.run(NullObject.new)
77
+ GhostWriter.generate_api_doc
62
78
  File.exist?(Rails.root + "doc" + "api_examples" + "anonymous_controller" + "index.markdown").should be_true
79
+ File.read(Rails.root + "doc" + "api_examples" + "anonymous_controller" + "index.markdown").should =~ /# AnonymousController Index/
80
+ end
81
+
82
+ context "Given github_base_url" do
83
+ let(:github_base_url) { "https://github.com/joker1007/ghost_writer/tree/master/output_examples" }
84
+ it "create index file written github links" do
85
+ GhostWriter.github_base_url = github_base_url
86
+ group.run(NullObject.new)
87
+ GhostWriter.generate_api_doc
88
+ File.read(Rails.root + "doc" + "api_examples" + GhostWriter::DOCUMENT_INDEX_FILENAME).should =~ /#{github_base_url}/
89
+ end
63
90
  end
64
91
  end
65
92
 
66
- context 'ENV["GENERATE_API_DOC"] is true' do
93
+ context 'ENV["GENERATE_API_DOC"] is false' do
67
94
  before do
68
95
  ENV["GENERATE_API_DOC"] = nil
69
96
  end
70
97
 
71
98
  it "does not generate api doc file" do
72
99
  group.run(NullObject.new)
100
+ GhostWriter.generate_api_doc
73
101
  File.exist?(Rails.root + "doc" + "api_examples" + "anonymous_controller" + "index.markdown").should be_false
74
102
  end
75
103
  end
@@ -91,7 +119,9 @@ describe GhostWriter do
91
119
  it "generate api doc file" do
92
120
  ENV["GENERATE_API_DOC"] = "1"
93
121
  group.run(NullObject.new)
122
+ GhostWriter.generate_api_doc
94
123
  File.exist?(Rails.root + "doc" + output_dir + "anonymous_controller" + "index.markdown").should be_true
124
+ File.read(Rails.root + "doc" + output_dir + "anonymous_controller" + "index.markdown").should =~ /# AnonymousController Index/
95
125
  end
96
126
  end
97
127
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ghost_writer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-01-08 00:00:00.000000000 Z
12
+ date: 2013-01-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -99,13 +99,20 @@ extensions: []
99
99
  extra_rdoc_files: []
100
100
  files:
101
101
  - .gitignore
102
+ - .travis.yml
102
103
  - Gemfile
103
104
  - LICENSE.txt
104
105
  - README.md
105
106
  - Rakefile
106
107
  - ghost_writer.gemspec
107
108
  - lib/ghost_writer.rb
109
+ - lib/ghost_writer/document.rb
110
+ - lib/ghost_writer/document_index.rb
111
+ - lib/ghost_writer/format/markdown.rb
108
112
  - lib/ghost_writer/version.rb
113
+ - output_examples/anonymous_controller/index.markdown
114
+ - output_examples/anonymous_controller/show.markdown
115
+ - output_examples/document_index.markdown
109
116
  - spec/lib/ghost_writer_spec.rb
110
117
  - spec/rails_app/.gitignore
111
118
  - spec/rails_app/README.rdoc
@@ -160,7 +167,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
160
167
  version: '0'
161
168
  segments:
162
169
  - 0
163
- hash: 1116131178365198946
170
+ hash: 2108698769650766932
164
171
  required_rubygems_version: !ruby/object:Gem::Requirement
165
172
  none: false
166
173
  requirements:
@@ -169,7 +176,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
169
176
  version: '0'
170
177
  segments:
171
178
  - 0
172
- hash: 1116131178365198946
179
+ hash: 2108698769650766932
173
180
  requirements: []
174
181
  rubyforge_project:
175
182
  rubygems_version: 1.8.23