├── spec ├── dummy │ ├── log │ │ └── .gitkeep │ ├── spec │ ├── app │ │ ├── mailers │ │ │ └── .gitkeep │ │ ├── models │ │ │ ├── .gitkeep │ │ │ └── recipe.rb │ │ ├── helpers │ │ │ └── application_helper.rb │ │ ├── controllers │ │ │ ├── application_controller.rb │ │ │ └── recipes_controller.rb │ │ ├── views │ │ │ └── layouts │ │ │ │ └── application.html.erb │ │ └── assets │ │ │ ├── stylesheets │ │ │ └── application.css │ │ │ └── javascripts │ │ │ └── application.js │ ├── lib │ │ └── assets │ │ │ └── .gitkeep │ ├── public │ │ ├── favicon.ico │ │ ├── 500.html │ │ ├── 422.html │ │ └── 404.html │ ├── config │ │ ├── routes.rb │ │ ├── environment.rb │ │ ├── locales │ │ │ └── en.yml │ │ ├── initializers │ │ │ ├── mime_types.rb │ │ │ ├── backtrace_silencers.rb │ │ │ ├── session_store.rb │ │ │ ├── wrap_parameters.rb │ │ │ ├── inflections.rb │ │ │ └── secret_token.rb │ │ ├── boot.rb │ │ ├── database.yml │ │ ├── environments │ │ │ ├── development.rb │ │ │ ├── test.rb │ │ │ └── production.rb │ │ └── application.rb │ ├── doc │ │ ├── toc.md │ │ └── recipes.md │ ├── config.ru │ ├── db │ │ ├── migrate │ │ │ └── 20130607075126_create_recipes.rb │ │ └── schema.rb │ ├── Rakefile │ └── script │ │ └── rails ├── spec_helper.rb └── requests │ └── recipes_spec.rb ├── Rakefile ├── lib ├── autodoc │ ├── version.rb │ ├── rspec.rb │ ├── templates │ │ ├── document.md.erb │ │ └── toc.md.erb │ ├── configuration.rb │ ├── documents.rb │ └── document.rb └── autodoc.rb ├── Gemfile ├── .gitignore ├── CHANGELOG.md ├── autodoc.gemspec ├── LICENSE.txt └── README.md /spec/dummy/log/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spec/dummy/spec: -------------------------------------------------------------------------------- 1 | ../../spec -------------------------------------------------------------------------------- /spec/dummy/app/mailers/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spec/dummy/app/models/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spec/dummy/lib/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spec/dummy/public/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | -------------------------------------------------------------------------------- /lib/autodoc/version.rb: -------------------------------------------------------------------------------- 1 | module Autodoc 2 | VERSION = "0.2.6" 3 | end 4 | -------------------------------------------------------------------------------- /spec/dummy/app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /spec/dummy/config/routes.rb: -------------------------------------------------------------------------------- 1 | Dummy::Application.routes.draw do 2 | resources :recipes, only: [:show, :create] 3 | end 4 | -------------------------------------------------------------------------------- /spec/dummy/app/models/recipe.rb: -------------------------------------------------------------------------------- 1 | class Recipe < ActiveRecord::Base 2 | attr_accessible :name, :type 3 | self.inheritance_column = :_type 4 | end 5 | -------------------------------------------------------------------------------- /spec/dummy/doc/toc.md: -------------------------------------------------------------------------------- 1 | ## Table of Contents 2 | * [recipes.md](recipes.md) 3 | * [GET /recipes/:id](recipes.md#get-recipesid) 4 | * [POST /recipes](recipes.md#post-recipes) 5 | -------------------------------------------------------------------------------- /spec/dummy/config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require ::File.expand_path('../config/environment', __FILE__) 4 | run Dummy::Application 5 | -------------------------------------------------------------------------------- /spec/dummy/config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the rails application 2 | require File.expand_path('../application', __FILE__) 3 | 4 | # Initialize the rails application 5 | Dummy::Application.initialize! 6 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gemspec 4 | 5 | group :test do 6 | gem "pry-rails" 7 | gem "rspec-rails" 8 | gem "weak_parameters" 9 | gem "protected_attributes" 10 | gem "rack-test" 11 | end 12 | -------------------------------------------------------------------------------- /lib/autodoc/rspec.rb: -------------------------------------------------------------------------------- 1 | require "rspec" 2 | 3 | RSpec.configuration.after(:each, autodoc: true) do 4 | Autodoc.documents.append(self) 5 | end 6 | 7 | RSpec.configuration.after(:suite) do 8 | Autodoc.documents.write 9 | end 10 | -------------------------------------------------------------------------------- /spec/dummy/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | protect_from_forgery 3 | 4 | respond_to :json 5 | 6 | rescue_from WeakParameters::ValidationError do 7 | head 400 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/dummy/config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Sample localization file for English. Add more files in this directory for other locales. 2 | # See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points. 3 | 4 | en: 5 | hello: "Hello world" 6 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | # Mime::Type.register_alias "text/html", :iphone 6 | -------------------------------------------------------------------------------- /spec/dummy/db/migrate/20130607075126_create_recipes.rb: -------------------------------------------------------------------------------- 1 | class CreateRecipes < ActiveRecord::Migration 2 | def change 3 | create_table :recipes do |t| 4 | t.string :name 5 | t.integer :type 6 | 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /spec/dummy/config/boot.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | gemfile = File.expand_path('../../../../Gemfile', __FILE__) 3 | 4 | if File.exist?(gemfile) 5 | ENV['BUNDLE_GEMFILE'] = gemfile 6 | require 'bundler' 7 | Bundler.setup 8 | end 9 | 10 | $:.unshift File.expand_path('../../../../lib', __FILE__) -------------------------------------------------------------------------------- /spec/dummy/Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | # Add your own tasks in files placed in lib/tasks ending in .rake, 3 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 4 | 5 | require File.expand_path('../config/application', __FILE__) 6 | 7 | Dummy::Application.load_tasks 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle/ 4 | .config 5 | .yardoc 6 | Gemfile.lock 7 | InstalledFiles 8 | _yardoc 9 | coverage 10 | lib/bundler/man 11 | log/*.log 12 | pkg 13 | pkg/ 14 | rdoc 15 | spec/dummy/.sass-cache 16 | spec/dummy/db/*.sqlite3 17 | spec/dummy/log/*.log 18 | spec/dummy/tmp/ 19 | spec/reports 20 | tmp 21 | -------------------------------------------------------------------------------- /spec/dummy/script/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application. 3 | 4 | APP_PATH = File.expand_path('../../config/application', __FILE__) 5 | require File.expand_path('../../config/boot', __FILE__) 6 | require 'rails/commands' 7 | -------------------------------------------------------------------------------- /spec/dummy/app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Dummy 5 | <%= stylesheet_link_tag "application", :media => "all" %> 6 | <%= javascript_include_tag "application" %> 7 | <%= csrf_meta_tags %> 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /lib/autodoc/templates/document.md.erb: -------------------------------------------------------------------------------- 1 | <%# coding: UTF-8 -%> 2 | ## <%= title %> 3 | <%= description %> 4 | <%= parameters_section %> 5 | ### request 6 | ``` 7 | <%= method %> <%= path %> 8 | ``` 9 | <%= request_body_section %> 10 | ### response 11 | ```ruby 12 | Status: <%= response_status %><%= response_headers %> 13 | response: <%= response_body %> 14 | ``` 15 | -------------------------------------------------------------------------------- /spec/dummy/app/controllers/recipes_controller.rb: -------------------------------------------------------------------------------- 1 | class RecipesController < ApplicationController 2 | validates :create do 3 | string :name, required: true, except: %w[alice bob] 4 | integer :type, only: 1..3 5 | end 6 | 7 | def show 8 | respond_with Recipe.find(params[:id]) 9 | end 10 | 11 | def create 12 | respond_with Recipe.create(params.slice(:name, :type)) 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/autodoc.rb: -------------------------------------------------------------------------------- 1 | require "autodoc/configuration" 2 | require "autodoc/document" 3 | require "autodoc/documents" 4 | require "autodoc/version" 5 | require "autodoc/rspec" if ENV["AUTODOC"] 6 | 7 | module Autodoc 8 | class << self 9 | def documents 10 | @documents ||= Documents.new 11 | end 12 | 13 | def configuration 14 | @configuration ||= Configuration.new 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Dummy::Application.config.session_store :cookie_store, key: '_dummy_session' 4 | 5 | # Use the database for sessions instead of the cookie-based default, 6 | # which shouldn't be used to store highly confidential information 7 | # (create the session table with "rails generate session_migration") 8 | # Dummy::Application.config.session_store :active_record_store 9 | -------------------------------------------------------------------------------- /lib/autodoc/templates/toc.md.erb: -------------------------------------------------------------------------------- 1 | <%# coding: UTF-8 -%> 2 | ## Table of Contents 3 | <% @table.sort.each do |pathname, documents| -%> 4 | * [<%= pathname.basename %>](<%= pathname.basename %>) 5 | <% documents.group_by(&:title).each do |title, documents| -%> 6 | <% documents.each_with_index do |document, index| -%> 7 | <% suffix = index == 0 ? "" : "-#{index}" -%> 8 | * [<%= title %>](<%= "#{pathname.basename}##{document.identifier}#{suffix}" %>) 9 | <% end -%> 10 | <% end -%> 11 | <% end -%> 12 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | # 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] 9 | end 10 | 11 | # Disable root element in JSON by default. 12 | ActiveSupport.on_load(:active_record) do 13 | self.include_root_in_json = false 14 | end 15 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format 4 | # (all these examples are active by default): 5 | # ActiveSupport::Inflector.inflections do |inflect| 6 | # inflect.plural /^(ox)$/i, '\1en' 7 | # inflect.singular /^(ox)en/i, '\1' 8 | # inflect.irregular 'person', 'people' 9 | # inflect.uncountable %w( fish sheep ) 10 | # end 11 | # 12 | # These inflection rules are supported but not enabled by default: 13 | # ActiveSupport::Inflector.inflections do |inflect| 14 | # inflect.acronym 'RESTful' 15 | # end 16 | -------------------------------------------------------------------------------- /spec/dummy/app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, 6 | * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the top of the 9 | * compiled file, but it's generally better to create a new file per style scope. 10 | * 11 | *= require_self 12 | *= require_tree . 13 | */ 14 | -------------------------------------------------------------------------------- /spec/dummy/config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite version 3.x 2 | # gem install sqlite3 3 | # 4 | # Ensure the SQLite 3 gem is defined in your Gemfile 5 | # gem 'sqlite3' 6 | development: 7 | adapter: sqlite3 8 | database: db/development.sqlite3 9 | pool: 5 10 | timeout: 5000 11 | 12 | # Warning: The database defined as "test" will be erased and 13 | # re-generated from your development database when you run "rake". 14 | # Do not set this db to the same as development or production. 15 | test: 16 | adapter: sqlite3 17 | database: db/test.sqlite3 18 | pool: 5 19 | timeout: 5000 20 | 21 | production: 22 | adapter: sqlite3 23 | database: db/production.sqlite3 24 | pool: 5 25 | timeout: 5000 26 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/secret_token.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | # Make sure the secret is at least 30 characters and all random, 6 | # no regular words or you'll be exposed to dictionary attacks. 7 | Dummy::Application.config.secret_token = '2da8e59639e7c92b0da46798ed97a45be5834c72cdd9a4ad141dadc5b1175f991b05ef02dde00f9c27cadb3790311e642010b89ed1dad598539980a1ae88be61' 8 | Dummy::Application.config.secret_key_base = '7bea42a7812ad8da2fa753be701ed39a8b35f014c0cfcc876ad08180f2b25504e67ed24fc4470c51b229bbbc4731454c912e4609902e1c0bba65fb86037f7f1a' 9 | -------------------------------------------------------------------------------- /spec/dummy/app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, 5 | // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path. 6 | // 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 8 | // the compiled file. 9 | // 10 | // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD 11 | // GO AFTER THE REQUIRES BELOW. 12 | // 13 | //= require jquery 14 | //= require jquery_ujs 15 | //= require_tree . 16 | -------------------------------------------------------------------------------- /spec/dummy/public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

We're sorry, but something went wrong.

23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /spec/dummy/public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

The change you wanted was rejected.

23 |

Maybe you tried to change something you didn't have access to.

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /spec/dummy/public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

The page you were looking for doesn't exist.

23 |

You may have mistyped the address or the page may have moved.

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | ENV["RAILS_ENV"] ||= "test" 2 | require File.expand_path("../../spec/dummy/config/environment", __FILE__) 3 | require "rspec/rails" 4 | require "rspec/autorun" 5 | 6 | Autodoc.configuration.toc = true 7 | Autodoc.configuration.path = "spec/dummy/doc" 8 | 9 | RSpec.configure do |config| 10 | # If you"re not using ActiveRecord, or you"d prefer not to run each of your 11 | # examples within a transaction, remove the following line or assign false 12 | # instead of true. 13 | config.use_transactional_fixtures = true 14 | 15 | # If true, the base class of anonymous controllers will be inferred 16 | # automatically. This will be the default behavior in future versions of 17 | # rspec-rails. 18 | config.infer_base_class_for_anonymous_controllers = false 19 | 20 | config.treat_symbols_as_metadata_keys_with_true_values = true 21 | end 22 | -------------------------------------------------------------------------------- /spec/dummy/config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Dummy::Application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Show full error reports and disable caching 10 | config.consider_all_requests_local = true 11 | config.action_controller.perform_caching = false 12 | 13 | # Don't care if the mailer can't send 14 | config.action_mailer.raise_delivery_errors = false 15 | 16 | # Print deprecation notices to the Rails logger 17 | config.active_support.deprecation = :log 18 | 19 | # Expands the lines which load the assets 20 | config.assets.debug = true 21 | end 22 | -------------------------------------------------------------------------------- /lib/autodoc/configuration.rb: -------------------------------------------------------------------------------- 1 | module Autodoc 2 | class Configuration 3 | class << self 4 | def property(name, &default) 5 | define_method(name) do 6 | if instance_variable_defined?("@#{name}") 7 | instance_variable_get("@#{name}") 8 | else 9 | instance_variable_set("@#{name}", instance_exec(&default)) 10 | end 11 | end 12 | 13 | attr_writer name 14 | end 15 | end 16 | 17 | property :path do 18 | "doc" 19 | end 20 | 21 | property :headers do 22 | %w[Location] 23 | end 24 | 25 | property :template do 26 | File.read(File.expand_path("../templates/document.md.erb", __FILE__)) 27 | end 28 | 29 | property :toc_template do 30 | File.read(File.expand_path("../templates/toc.md.erb", __FILE__)) 31 | end 32 | 33 | property :toc do 34 | false 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /spec/dummy/doc/recipes.md: -------------------------------------------------------------------------------- 1 | ## GET /recipes/:id 2 | Returns the recipe. 3 | 4 | ### request 5 | ``` 6 | GET /recipes/:id 7 | ``` 8 | 9 | ### response 10 | ```ruby 11 | Status: 200 12 | response: 13 | { 14 | "id": 1, 15 | "name": "test", 16 | "type": 2, 17 | "created_at": "2013-11-30T19:04:12.608Z", 18 | "updated_at": "2013-11-30T19:04:12.608Z" 19 | } 20 | ``` 21 | 22 | ## POST /recipes 23 | Creates a new recipe. 24 | 25 | ### parameters 26 | * `name` string (required, except: `["alice", "bob"]`) 27 | * `type` integer (only: `1..3`) 28 | 29 | ### request 30 | ``` 31 | POST /recipes 32 | ``` 33 | 34 | ``` 35 | name=name&type=1 36 | ``` 37 | 38 | ### response 39 | ```ruby 40 | Status: 201 41 | Location: http://www.example.com/recipes/1 42 | response: 43 | { 44 | "id": 1, 45 | "name": "name", 46 | "type": 1, 47 | "created_at": "2013-11-30T19:04:12.684Z", 48 | "updated_at": "2013-11-30T19:04:12.684Z" 49 | } 50 | ``` 51 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.2.6 2 | * fix ToC generator 3 | 4 | ## 0.2.5 5 | * add ToC generator 6 | * use Autodoc.configuration.path to change documents path 7 | 8 | ## 0.2.4 9 | * add Documents class 10 | 11 | ## 0.2.3 12 | * clean up dependencies & implementation 13 | 14 | ## 0.2.2 15 | * fix default documented headers 16 | 17 | ## 0.2.1 18 | * fix load dependency on rspec 19 | 20 | ## 0.2.0 21 | * remove dependency on awesome_print 22 | 23 | ## 0.1.9 24 | * fix encoding error at ruby 1.9 or earlier versions 25 | 26 | ## 0.1.8 27 | * support rack-test 28 | 29 | ## 0.1.7 30 | * change response format from Ruby hash table to JSON object 31 | 32 | ## 0.1.6 33 | * support WeakParameter's :description option 34 | 35 | ## 0.1.1 36 | * custom template 37 | 38 | ## 0.1.0 39 | * AUTODOC environment variable 40 | 41 | ## 0.0.8 42 | * show request body 43 | 44 | ## 0.0.3 45 | * configurable response header 46 | 47 | ## 0.0.1 48 | * 1st release on 2013-06-07 49 | -------------------------------------------------------------------------------- /lib/autodoc/documents.rb: -------------------------------------------------------------------------------- 1 | require "pathname" 2 | 3 | module Autodoc 4 | class Documents 5 | def initialize 6 | @table = Hash.new {|table, key| table[key] = [] } 7 | end 8 | 9 | def append(context) 10 | document = Autodoc::Document.new(context.clone) 11 | @table[document.pathname] << document 12 | end 13 | 14 | def write 15 | write_toc if Autodoc.configuration.toc 16 | write_documents 17 | end 18 | 19 | private 20 | 21 | def write_documents 22 | @table.each do |pathname, documents| 23 | pathname.parent.mkpath 24 | pathname.open("w") {|file| file << documents.map(&:render).join("\n").rstrip + "\n" } 25 | end 26 | end 27 | 28 | def write_toc 29 | toc_path.parent.mkpath 30 | toc_path.open("w") {|file| file << render_toc } 31 | end 32 | 33 | def render_toc 34 | ERB.new(Autodoc.configuration.toc_template, nil, "-").result(binding) 35 | end 36 | 37 | def toc_path 38 | Pathname.new(Autodoc.configuration.path) + "toc.md" 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /spec/dummy/db/schema.rb: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | # This file is auto-generated from the current state of the database. Instead 3 | # of editing this file, please use the migrations feature of Active Record to 4 | # incrementally modify your database, and then regenerate this schema definition. 5 | # 6 | # Note that this schema.rb definition is the authoritative source for your 7 | # database schema. If you need to create the application database on another 8 | # system, you should be using db:schema:load, not running all the migrations 9 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 10 | # you'll amass, the slower it'll run and the greater likelihood for issues). 11 | # 12 | # It's strongly recommended to check this file into your version control system. 13 | 14 | ActiveRecord::Schema.define(:version => 20130607075126) do 15 | 16 | create_table "recipes", :force => true do |t| 17 | t.string "name" 18 | t.integer "type" 19 | t.datetime "created_at", :null => false 20 | t.datetime "updated_at", :null => false 21 | end 22 | 23 | end 24 | -------------------------------------------------------------------------------- /autodoc.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path("../lib", __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require "autodoc/version" 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "autodoc" 8 | spec.version = Autodoc::VERSION 9 | spec.authors = ["Ryo Nakamura"] 10 | spec.email = ["r7kamura@gmail.com"] 11 | spec.summary = "Auto-generate JSON API documents from your request-specs." 12 | spec.homepage = "https://github.com/r7kamura/autodoc" 13 | spec.license = "MIT" 14 | 15 | spec.files = `git ls-files`.split($/) 16 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 17 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 18 | spec.require_paths = ["lib"] 19 | 20 | spec.add_dependency "actionpack" 21 | spec.add_dependency "rspec" 22 | spec.add_development_dependency "bundler", "~> 1.3" 23 | spec.add_development_dependency "rails", ">= 3.2.11" 24 | spec.add_development_dependency "rake" 25 | spec.add_development_dependency "sqlite3" 26 | end 27 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Ryo Nakamura 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /spec/dummy/config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Dummy::Application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Configure static asset server for tests with Cache-Control for performance 11 | config.serve_static_assets = true 12 | config.static_cache_control = "public, max-age=3600" 13 | 14 | # Show full error reports and disable caching 15 | config.consider_all_requests_local = true 16 | config.action_controller.perform_caching = false 17 | 18 | # Raise exceptions instead of rendering exception templates 19 | config.action_dispatch.show_exceptions = false 20 | 21 | # Disable request forgery protection in test environment 22 | config.action_controller.allow_forgery_protection = false 23 | 24 | # Print deprecation notices to the stderr 25 | config.active_support.deprecation = :stderr 26 | 27 | config.eager_load = false 28 | end 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Autodoc 2 | Auto-generate JSON API documents from your request-specs. 3 | 4 | ## Installation 5 | ```ruby 6 | gem "autodoc", group: :test 7 | ``` 8 | 9 | ## Usage 10 | Run rspec with AUTODOC=1 to generate documents for the specs tagged with `:autodoc`. 11 | example: [doc/recipes.md](https://github.com/r7kamura/autodoc/blob/master/spec/dummy/doc/recipes.md), [doc/toc.md](https://github.com/r7kamura/autodoc/blob/master/spec/dummy/doc/toc.md) 12 | 13 | ```sh 14 | # shell-command 15 | AUTODOC=1 rspec 16 | ``` 17 | 18 | ```ruby 19 | # spec/requests/recipes_spec.rb 20 | describe "Recipes" do 21 | describe "POST /recipes", autodoc: true do 22 | it "creates a new recipe" do 23 | post "/recipes.json", name: "alice", type: 1 24 | response.status.should == 201 25 | end 26 | end 27 | end 28 | ``` 29 | 30 | ### Configuration 31 | You can configure `Autodoc.configuration` to change its behavior: 32 | 33 | * path - [String] location to put files (default: ./doc) 34 | * headers - [Array] keys of documented response header (default: ["Location"]) 35 | * template - [String] ERB template for each document (default: [document.md.erb](https://github.com/r7kamura/autodoc/blob/master/lib/autodoc/templates/document.md.erb)) 36 | * toc_template - [String] ERB template for ToC (default: [toc.md.erb](https://github.com/r7kamura/autodoc/blob/master/lib/autodoc/templates/toc.md.erb)) 37 | * toc - [Boolean] whether to generate toc.md (default: false) 38 | 39 | ```ruby 40 | # example 41 | Autodoc.configuration.path = "doc/api" 42 | Autodoc.configuration.toc = true 43 | ``` 44 | -------------------------------------------------------------------------------- /spec/requests/recipes_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe "Recipes" do 4 | let(:env) do 5 | { "HTTP_ACCEPT" => "application/json" } 6 | end 7 | 8 | let(:params) do 9 | {} 10 | end 11 | 12 | describe "GET /recipes/:id" do 13 | let(:recipe) do 14 | Recipe.create(name: "test", type: 2) 15 | end 16 | 17 | context "with valid condition (using Rack::Test)", :autodoc do 18 | include Rack::Test::Methods 19 | 20 | it "returns the recipe" do 21 | get "/recipes/#{recipe.id}", params, env 22 | last_response.status.should == 200 23 | end 24 | end 25 | end 26 | 27 | describe "POST /recipes" do 28 | before do 29 | params[:name] = "name" 30 | params[:type] = 1 31 | end 32 | 33 | context "without required param" do 34 | before do 35 | params.delete(:name) 36 | end 37 | 38 | it "returns 400" do 39 | post "/recipes", params, env 40 | response.status.should == 400 41 | end 42 | end 43 | 44 | context "with other typed param" do 45 | before do 46 | params[:type] = "x" 47 | end 48 | 49 | it "returns 400" do 50 | post "/recipes", params, env 51 | response.status.should == 400 52 | end 53 | end 54 | 55 | context "without non-required param" do 56 | before do 57 | params.delete(:type) 58 | end 59 | 60 | it "creates a new recipe" do 61 | post "/recipes", params, env 62 | response.status.should == 201 63 | end 64 | end 65 | 66 | context "with valid condition", :autodoc do 67 | it "creates a new recipe" do 68 | post "/recipes", params, env 69 | response.status.should == 201 70 | end 71 | end 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /spec/dummy/config/application.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../boot', __FILE__) 2 | 3 | require "active_record/railtie" 4 | require "action_controller/railtie" 5 | 6 | Bundler.require(*Rails.groups) 7 | require "autodoc" 8 | 9 | module Dummy 10 | class Application < Rails::Application 11 | # Settings in config/environments/* take precedence over those specified here. 12 | # Application configuration should go into files in config/initializers 13 | # -- all .rb files in that directory are automatically loaded. 14 | 15 | # Custom directories with classes and modules you want to be autoloadable. 16 | # config.autoload_paths += %W(#{config.root}/extras) 17 | 18 | # Only load the plugins named here, in the order given (default is alphabetical). 19 | # :all can be used as a placeholder for all plugins not explicitly named. 20 | # config.plugins = [ :exception_notification, :ssl_requirement, :all ] 21 | 22 | # Activate observers that should always be running. 23 | # config.active_record.observers = :cacher, :garbage_collector, :forum_observer 24 | 25 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 26 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 27 | # config.time_zone = 'Central Time (US & Canada)' 28 | 29 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 30 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 31 | # config.i18n.default_locale = :de 32 | 33 | # Configure the default encoding used in templates for Ruby 1.9. 34 | config.encoding = "utf-8" 35 | 36 | # Configure sensitive parameters which will be filtered from the log file. 37 | config.filter_parameters += [:password] 38 | 39 | # Enable escaping HTML in JSON. 40 | config.active_support.escape_html_entities_in_json = true 41 | 42 | # Use SQL instead of Active Record's schema dumper when creating the database. 43 | # This is necessary if your schema can't be completely dumped by the schema dumper, 44 | # like if you have constraints or database-specific column types 45 | # config.active_record.schema_format = :sql 46 | 47 | # Enforce whitelist mode for mass assignment. 48 | # This will create an empty whitelist of attributes available for mass-assignment for all models 49 | # in your app. As such, your models will need to explicitly whitelist or blacklist accessible 50 | # parameters by using an attr_accessible or attr_protected declaration. 51 | config.active_record.whitelist_attributes = true 52 | 53 | # Version of your assets, change this if you want to expire all your assets 54 | config.assets.version = '1.0' 55 | end 56 | end 57 | 58 | -------------------------------------------------------------------------------- /spec/dummy/config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Dummy::Application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb 3 | 4 | # Code is not reloaded between requests 5 | config.cache_classes = true 6 | 7 | # Full error reports are disabled and caching is turned on 8 | config.consider_all_requests_local = false 9 | config.action_controller.perform_caching = true 10 | 11 | # Disable Rails's static asset server (Apache or nginx will already do this) 12 | config.serve_static_assets = false 13 | 14 | # Compress JavaScripts and CSS 15 | config.assets.compress = true 16 | 17 | # Don't fallback to assets pipeline if a precompiled asset is missed 18 | config.assets.compile = false 19 | 20 | # Generate digests for assets URLs 21 | config.assets.digest = true 22 | 23 | # Defaults to nil and saved in location specified by config.assets.prefix 24 | # config.assets.manifest = YOUR_PATH 25 | 26 | # Specifies the header that your server uses for sending files 27 | # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache 28 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx 29 | 30 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 31 | # config.force_ssl = true 32 | 33 | # See everything in the log (default is :info) 34 | # config.log_level = :debug 35 | 36 | # Prepend all log lines with the following tags 37 | # config.log_tags = [ :subdomain, :uuid ] 38 | 39 | # Use a different logger for distributed setups 40 | # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) 41 | 42 | # Use a different cache store in production 43 | # config.cache_store = :mem_cache_store 44 | 45 | # Enable serving of images, stylesheets, and JavaScripts from an asset server 46 | # config.action_controller.asset_host = "http://assets.example.com" 47 | 48 | # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added) 49 | # config.assets.precompile += %w( search.js ) 50 | 51 | # Disable delivery errors, bad email addresses will be ignored 52 | # config.action_mailer.raise_delivery_errors = false 53 | 54 | # Enable threaded mode 55 | # config.threadsafe! 56 | 57 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 58 | # the I18n.default_locale when a translation can not be found) 59 | config.i18n.fallbacks = true 60 | 61 | # Send deprecation notices to registered listeners 62 | config.active_support.deprecation = :notify 63 | 64 | # Log the query plan for queries taking more than this (works 65 | # with SQLite, MySQL, and PostgreSQL) 66 | # config.active_record.auto_explain_threshold_in_seconds = 0.5 67 | 68 | config.eager_load = true 69 | 70 | config.assets.js_compressor = :uglifier 71 | end 72 | -------------------------------------------------------------------------------- /lib/autodoc/document.rb: -------------------------------------------------------------------------------- 1 | require "action_dispatch/http/request" 2 | require "erb" 3 | require "pathname" 4 | 5 | module Autodoc 6 | class Document 7 | def self.render(*args) 8 | new(*args).render 9 | end 10 | 11 | def initialize(context) 12 | @context = context 13 | end 14 | 15 | def pathname 16 | @path ||= begin 17 | payload = @context.example.file_path.gsub(%r<\./spec/requests/(.+)_spec\.rb>, '\1.md') 18 | Pathname.new(Autodoc.configuration.path) + payload 19 | end 20 | end 21 | 22 | def render 23 | ERB.new(Autodoc.configuration.template, nil, "-").result(binding) 24 | end 25 | 26 | def title 27 | "#{method} #{path}" 28 | end 29 | 30 | def identifier 31 | title.gsub(" ", "-").gsub(/[:\/]/, "").downcase 32 | end 33 | 34 | private 35 | 36 | def request 37 | @request ||= begin 38 | if using_rack_test? 39 | ActionDispatch::Request.new(@context.last_request.env) 40 | else 41 | @context.request 42 | end 43 | end 44 | end 45 | 46 | def response 47 | @response ||= begin 48 | if using_rack_test? 49 | @context.last_response 50 | else 51 | @context.response 52 | end 53 | end 54 | end 55 | 56 | def method 57 | request.method 58 | end 59 | 60 | def request_body 61 | request.body.string 62 | end 63 | 64 | def response_status 65 | response.status 66 | end 67 | 68 | def response_header(header) 69 | response.headers[header] 70 | end 71 | 72 | def response_body_raw 73 | response.body 74 | end 75 | 76 | def controller 77 | request.params[:controller] 78 | end 79 | 80 | def action 81 | request.params[:action] 82 | end 83 | 84 | def using_rack_test? 85 | !!defined?(Rack::Test::Methods) && @context.class.ancestors.include?(Rack::Test::Methods) 86 | end 87 | 88 | def transaction 89 | @transaction ||= Autodoc::Transaction.build(@context) 90 | end 91 | 92 | def description 93 | "#{@context.example.description.capitalize}." 94 | end 95 | 96 | def path 97 | @context.example.full_description[%r<(GET|POST|PUT|DELETE) ([^ ]+)>, 2] 98 | end 99 | 100 | def response_body 101 | "\n" + JSON.pretty_generate(JSON.parse(response_body_raw)) 102 | rescue JSON::ParserError 103 | end 104 | 105 | def request_body_section 106 | if has_request_body? 107 | "\n```\n#{request_body}\n```\n" 108 | end 109 | end 110 | 111 | def parameters_section 112 | if has_validators? && parameters.present? 113 | "\n### parameters\n#{parameters}\n" 114 | end 115 | end 116 | 117 | def parameters 118 | validators.map {|validator| Parameter.new(validator) }.join("\n") 119 | end 120 | 121 | def has_request_body? 122 | request_body.present? 123 | end 124 | 125 | def has_validators? 126 | !!(defined?(WeakParameters) && validators) 127 | end 128 | 129 | def validators 130 | WeakParameters.stats[controller][action].try(:validators) 131 | end 132 | 133 | def response_headers 134 | Autodoc.configuration.headers.map do |header| 135 | "\n#{header}: #{response_header(header)}" if response_header(header) 136 | end.compact.join 137 | end 138 | 139 | class Parameter 140 | attr_reader :validator 141 | 142 | def initialize(validator) 143 | @validator = validator 144 | end 145 | 146 | def to_s 147 | "#{body}#{payload}" 148 | end 149 | 150 | private 151 | 152 | def body 153 | "* `#{validator.key}` #{validator.type}" 154 | end 155 | 156 | def payload 157 | string = "" 158 | string << " (#{assets.join(', ')})" if assets.any? 159 | string << " - #{validator.options[:description]}" if validator.options[:description] 160 | string 161 | end 162 | 163 | def assets 164 | @assets ||= [required, only, except].compact 165 | end 166 | 167 | def required 168 | "required" if validator.required? 169 | end 170 | 171 | def only 172 | "only: `#{validator.options[:only].inspect}`" if validator.options[:only] 173 | end 174 | 175 | def except 176 | "except: `#{validator.options[:except].inspect}`" if validator.options[:except] 177 | end 178 | end 179 | end 180 | end 181 | --------------------------------------------------------------------------------