Apipie-dsl is a DSL for documenting DSLs written in Ruby. Instead of traditional
use of #comments
, ApipieDSL lets you describe the code, through the code.
The easiest way to get ApipieDSL up and running with your app is:
echo "gem 'apipie-dsl'" >> Gemfile
bundle install
rails g apipie:install
Now you can start documenting your DSLs (see DSL Reference for more info):
apipie :method, 'Method description' do
required :string, String, 'Required string parameter'
end
def print(string)
# ...
end
echo "gem 'apipie-dsl'" >> Gemfile
# To be able to generate HTML documentation via rake command
echo "gem 'rake'" >> Gemfile
echo "gem 'actionview'" >> Gemfile
bundle install
In case if you want to generate HTML documentation via rake command, I'd suggest
have the following in your Rakefile
:
require 'apipie-dsl'
# Configuration is required!
ApipieDSL.configure do |config|
config.app_name = 'My DSL Docs'
# Matchers are needed to load your code with documentation
config.dsl_classes_matchers = [
"#{File.dirname(__dir__)}/dsl_example/common_classes.rb",
"#{File.dirname(__dir__)}/dsl_example/dsl.rb"
]
# This is important for plain ruby application!
config.rails = false
# ... other configurations
end
spec = Gem::Specification.find_by_name('apipie-dsl')
rakefile = "#{spec.gem_dir}/lib/apipie_dsl/Rakefile"
load(rakefile)
Create a configuration file (e.g. /config/initializers/apipie-dsl.rb
for
Rails). You can set the application name, footer text, DSL and documentation
base URL and turn off validations. You can also choose your favorite markup
language for full descriptions.
-
app_name - Name of your application; used in breadcrumbs navigation.
-
app_info - Application long description.
-
copyright - Copyright information (shown in page footer).
-
doc_base_url - Documentation frontend base url.
-
default_version - Default DSL version to be used (1.0 by default).
-
sections - What sections should the HTML documentation have ([:all] by default).
-
validate - Parameters validation is turned off when set to false. When set to
:explicitly
, you must do the parameters validation yourself. When set to:implicitly
(or just true), your classes' methods are wrapped with generated methods which already contain parameters validation (:implicitly
by default). -
validate_value - Check the value of params against specified validators (true by default).
-
dsl_classes_matchers - For reloading to work properly you need to specify where your DSL classes are. Can be an array if multiple paths are needed.
-
markup - You can choose markup language for descriptions of your application, classes and methods. RDoc is the default but you can choose from
ApipieDSL::Markup::Markdown.new
orApipieDSL::Markup::Textile.new
. In order to use Markdown you needMaruku
gem and for Textile you needRedCloth
. Add those to yourGemfile
and run bundle if you want to use them. You can also add any other markup language processor. -
ignored - An array of class names (strings) (might include methods as well) to be ignored when generating the documentation (e.g.
%w[DSL::Output DSL::HTML#tag]
). -
class_full_names - Use class paths instead of class names as class id. This prevents same named classes overwriting each other (
true
by default). -
link_extension - The extension to use for DSL pages ('.html' by
default). Link extensions in static DSL docs cannot be changed from '.html'. -
languages - List of languages the DSL documentation should be translated into. Empty by default.
-
default_locale - Locale used for generating documentation when no specific locale is set. Set to 'en' by default.
-
locale - Pass locale setter/getter.
config.locale = lambda { |loc| loc ? FastGettext.set_locale(loc) : FastGettext.locale }
-
translate - Pass proc to translate strings using the localization library your project uses. For example see Localization.
-
help_layout - Pass path to a html with custom help section if needed.
ApipieDSL.configure do |config|
config.app_name = "Test app"
config.copyright = "© 2019 Oleh Fedorenko"
config.doc_base_url = "/apipie-dsl"
config.validate = false
config.markup = ApipieDSL::Markup::Markdown.new
config.dsl_controllers_matchers = [
"#{File.dirname(__dir__)}/dsl_example/common.rb",
"#{File.dirname(__dir__)}/dsl_example/dsl.rb"
]
config.app_info["1.0"] = "
This is where you can inform user about your application and DSL
in general.
"
config.sections = %i[all additional]
end
-
short (also short_description) - Short description of the class (it's shown on both the list of classes, and class details).
-
desc (also description and full_description) Full description of the class (shown only in class details).
-
dsl_versions (also dsl_version) What versions does the class define the methods (see Versioning for details).
-
meta - Hash or array with custom metadata.
-
deprecated - Boolean value indicating if the class is marked as
deprecated (false by default). -
show - Class/method is hidden from documentation when set to false (true by default).
Describe your class via:
apipie :class do
# ...
end
Inheritance is supported, so you can specify common params for group of classes in their parent class.
The following keywords are available (all are optional):
-
name - Custom class name (in case if you want to explicitly save full class name e.g. ParentModule::Class)
-
refs/referenced_on - Which references should the class understand to be linked in the documentation (e.g. if the class is mentioned as return value of a method, then the value will be linked with the class).
-
sections - In which sections the class should be shown.
-
property - Object's property (could be an
attr_reader
or public method with return value).
apipie :class, 'Base tag' do
name 'Base::Tag'
sections only: :additional
refs 'BaseTag', 'Base::Tag', 'Tag'
dsl_version 'development'
meta :author => {:name => 'John', :surname => 'Doe'}
deprecated false
description <<-EOS
== Long description
Example class for dsl documentation
...
EOS
end
Then describe methods available to your DSL:
apipie :method do
# ...
end
-
param - Look at Parameter description section for details.
-
returns - Look at Response description section for details.
-
raises - Describe each possible error that can happen while calling this method.
-
param_group - Extract parameters defined via
apipie :param_group do ... end
-
see - Provide reference to another method, this has to be a string with
class_name#method_name
.
apipie :method, 'Short description' do
description 'Full method description'
required :id, String, desc: 'ID for tag'
optional :type, String, desc: 'Optional type', default: ''
param :css_class, String, :desc => 'CSS class', type: :optional, default: ''
keyword :content, Hash, :desc => 'Hash with content' do
optional :text, String, 'Text string', default: 'Default text'
end
returns BaseTag
raises ArgumentError, 'String is expected'
raises ArgumentError, 'Hash is expected'
meta :message => 'Some very important info'
see 'html#tag', 'Link description'
see :link => 'html#tags', :desc => 'Another link description'
show false
end
def tag(id, type = '', css_class = '', content: { text: 'Default text' })
#...
end
Use param
to describe every possible parameter. You can use the Hash validator
in conjunction with a block given to the param method to describe nested
parameters.
-
name- The first argument is the parameter name as a symbol.
-
validator - Second parameter is the parameter validator, choose one from section Validators.
-
desc - Parameter description.
-
required Set this true/false to make it required/optional. Default is true.
param :user, Hash, :desc => 'User info' do
param :username, String, desc: 'Username for login', required: true
param :password, String, desc: 'Password for login', required: true
param :membership, ['standard','premium'], desc: 'User membership'
param :admin_override, String, desc: 'Not shown in documentation', show: false
end
def create
#...
end
Often, params occur together in more methods. These params can be extracted
with def_param_group
and param_group
keywords.
The definition is looked up in the scope of the class. If the group is defined in a different class, it might be referenced by specifying the second argument.
# v1/users_class.rb
def_param_group :address do
param :street, String
param :number, Integer
param :zip, String
end
def_param_group :user do
param :user, Hash do
param :name, String, 'Name of the user'
param_group :address
end
end
apipie :method, 'Create an user' do
param_group :user
end
def create(user)
# ...
end
apipie :method, 'Update an user' do
param_group :user
end
def update(user)
# ...
end
# v2/users_class.rb
apipie :method, 'Create an user' do
param_group :user, V1::UsersClass
end
def create(user)
# ...
end
TODO
# TODO
TODO
TODO
Check the parameter type. Only String, Hash and Array are supported for the sake of simplicity. Read more to find out how to add your own validator.
# TODO
Check parameter value against given regular expression.
param :regexp_param, /^[0-9]* years/, desc: 'regexp param'
Check if parameter value is included in the given array.
param :enum_param, [100, 'one', 'two', 1, 2], desc: 'enum validator'
If you need more complex validation and you know you won't reuse it, you can use the Proc/lambda validator. Provide your own Proc, taking the value of the parameter as the only argument. Return true if value passes validation or return some text about what is wrong otherwise. Don't use the keyword return if you provide an instance of Proc (with lambda it is ok), just use the last statement return property of ruby.
param :proc_param, lambda { |val|
val == 'param value' ? true : "The only good value is 'param value'."
}, desc: 'proc validator'
You can describe hash parameters in depth if you provide a block with a description of nested values.
param :user, Hash, desc: 'User info' do
required :username, String, desc: 'Username for login'
required :password, String, desc: 'Password for login'
param :membership, ['standard','premium'], desc: 'User membership'
end
Check if the parameter is a positive integer number or zero.
required :product_id, :number, desc: 'Identifier of the product'
required :quantity, :number, desc: 'Number of products to order'
Check if the parameter is a decimal number.
required :latitude, :decimal, desc: 'Geographic latitude'
required :longitude, :decimal, desc: 'Geographic longitude'
Check if the parameter is an array.
required :array_param, Array, desc: 'array param'
-
of - Specify the type of items. If not given it accepts an array of any item type.
-
in - Specify an array of valid item values.
Assert things
is an array of any items.
param :things, Array
Assert hits
must be an array of integer values.
param :hits, Array, of: Integer
Assert colors
must be an array of valid string values.
param :colors, Array, in: ['red', 'green', 'blue']
The retrieving of valid items can be deferred until needed using a lambda. It is evaluated only once
param :colors, Array, in: -> { Colors.all.map(&:name) }
You can describe nested parameters in depth if you provide a block with a description of nested values.
param :comments, Array, desc: 'User comments' do
required :name, String, desc: 'Name of the comment'
required :comment, String, desc: 'Full comment'
end
TODO
TODO
The default markup language is
RDoc. It can be changed in
the config file (config.markup=
) to one of these:
-
Markdown - Use
Apipie::Markup::Markdown.new
. You need Maruku gem. -
Textile - Use
Apipie::Markup::Textile.new
. You need RedCloth gem.
Or provide you own object with a to_html(text)
method. For inspiration, this
is how Textile markup usage is implemented:
class Textile
def initialize
require 'RedCloth'
end
def to_html(text)
RedCloth.new(text).to_html
end
end
TODO
TODO
- Circular dependency detected while autoloading constant... Can appear in Rails when using a non-standard class as a validator. Passing the validator as a string (e.g. MyClass -> 'MyClass') solves the issue.