betterdocs 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +5 -0
  3. data/.gitignore +11 -0
  4. data/.rspec +2 -0
  5. data/.travis.yml +9 -0
  6. data/COPYING +202 -0
  7. data/Gemfile +8 -0
  8. data/LICENSE +202 -0
  9. data/README.md +124 -0
  10. data/Rakefile +25 -0
  11. data/VERSION +1 -0
  12. data/betterdocs.gemspec +48 -0
  13. data/lib/betterdocs.rb +27 -0
  14. data/lib/betterdocs/controller_collector.rb +50 -0
  15. data/lib/betterdocs/dsl.rb +9 -0
  16. data/lib/betterdocs/dsl/common.rb +26 -0
  17. data/lib/betterdocs/dsl/controller.rb +9 -0
  18. data/lib/betterdocs/dsl/controller/action.rb +126 -0
  19. data/lib/betterdocs/dsl/controller/action/param.rb +25 -0
  20. data/lib/betterdocs/dsl/controller/action/response.rb +47 -0
  21. data/lib/betterdocs/dsl/controller/controller.rb +31 -0
  22. data/lib/betterdocs/dsl/controller/controller_base.rb +21 -0
  23. data/lib/betterdocs/dsl/json_params.rb +8 -0
  24. data/lib/betterdocs/dsl/json_params/param.rb +31 -0
  25. data/lib/betterdocs/dsl/json_type_mapper.rb +27 -0
  26. data/lib/betterdocs/dsl/naming.rb +32 -0
  27. data/lib/betterdocs/dsl/representer.rb +29 -0
  28. data/lib/betterdocs/dsl/result.rb +10 -0
  29. data/lib/betterdocs/dsl/result/collection_property.rb +9 -0
  30. data/lib/betterdocs/dsl/result/link.rb +37 -0
  31. data/lib/betterdocs/dsl/result/property.rb +53 -0
  32. data/lib/betterdocs/generator/config_shortcuts.rb +28 -0
  33. data/lib/betterdocs/generator/markdown.rb +151 -0
  34. data/lib/betterdocs/generator/markdown/templates/README.md.erb +9 -0
  35. data/lib/betterdocs/generator/markdown/templates/section.md.erb +132 -0
  36. data/lib/betterdocs/global.rb +143 -0
  37. data/lib/betterdocs/json_params_representer.rb +37 -0
  38. data/lib/betterdocs/json_params_representer_collector.rb +48 -0
  39. data/lib/betterdocs/mix_into_controller.rb +19 -0
  40. data/lib/betterdocs/rake_tasks.rb +5 -0
  41. data/lib/betterdocs/representer.rb +42 -0
  42. data/lib/betterdocs/result_representer.rb +68 -0
  43. data/lib/betterdocs/result_representer_collector.rb +82 -0
  44. data/lib/betterdocs/section.rb +6 -0
  45. data/lib/betterdocs/tasks/doc.rake +55 -0
  46. data/lib/betterdocs/version.rb +8 -0
  47. data/spec/controller_dsl_spec.rb +143 -0
  48. data/spec/generator/markdown_spec.rb +5 -0
  49. data/spec/json_params_representer_spec.rb +79 -0
  50. data/spec/json_type_mapper_spec.rb +33 -0
  51. data/spec/result_representer_dsl_spec.rb +183 -0
  52. data/spec/result_representer_spec.rb +182 -0
  53. data/spec/spec_helper.rb +19 -0
  54. metadata +234 -0
@@ -0,0 +1,124 @@
1
+ Betterdocs API Documentation
2
+ ============================
3
+
4
+ [![Code Climate](https://codeclimate.com/repos/51128561f3ea0022cc027f31/badges/2a9719de54628d821871/gpa.png)](https://codeclimate.com/repos/51128561f3ea0022cc027f31/feed)
5
+
6
+ DESCRIPTION
7
+ -----------
8
+
9
+ This library can be used to document a rails based API.
10
+
11
+ LICENSE
12
+ -------
13
+
14
+ Apache License Version 2.0, see also the COPYING file.
15
+
16
+ USAGE EXAMPLES
17
+ --------------
18
+
19
+ These are some examples. Note that they are neither complete, nor work out of the box.
20
+
21
+ This api generator requires that you follow the [representer pattern](http://nicksda.apotomo.de/2011/12/ruby-on-rest-introducing-the-representer-pattern/) to decorate the objects you want to render. Betterdocs comes with it's own representer support baked in.
22
+
23
+ ## Controller
24
+
25
+ class ThingsController < ApplicationController
26
+ # :nocov: Documentation
27
+ doc :action do
28
+ section :things_list
29
+ title 'A List of things ⇄ [Details](things_details.md)'
30
+ description to <<-end
31
+ This action shows a list of things. **Markdown** can be used.
32
+ end
33
+
34
+ param :order do
35
+ description to <<-end
36
+ Optional parameter to order the list.
37
+ end
38
+ required no
39
+ value 'created_at:ASC'
40
+ end
41
+
42
+ response do
43
+ generate_fake_result_with_representer
44
+ end
45
+ end
46
+ # :nocov:
47
+ def index
48
+ render json: real_result_with_representer
49
+ end
50
+
51
+ # :nocov: Documentation
52
+ doc :action do
53
+ section :things_details
54
+ title 'Things Details ⇄ [List](things_list.md)'
55
+ description to <<-end
56
+ The details of a thing. You must give an id for the thing
57
+ end
58
+
59
+ param :thing_id do
60
+ description 'The id of the thing you want to see. Required.'
61
+ required yes # this is the default
62
+ value 38
63
+ end
64
+
65
+ response do
66
+ generate_fake_details_response_with_representer
67
+ end
68
+ end
69
+ # :nocov:
70
+ def show
71
+ render json: real_result_with_representer
72
+ end
73
+ end
74
+
75
+ ## Representer
76
+
77
+ Betterdocs comes with its own representer class. It is not documented
78
+ yet but works kind of like [ROAR](https://github.com/apotonick/roar).
79
+
80
+ module ThingsRepresenter
81
+
82
+ extend ActiveSupport::Concern
83
+ include Betterdocs::Representer
84
+
85
+ property :microfleem_count, if: -> { has_microfleems? }, as: :number_of_fleems do
86
+ description 'If we have microfleems, return the count'
87
+ types Integer
88
+ example '5000'
89
+ end
90
+
91
+ property :title, as: :name do
92
+ description to <<-end
93
+ Represent the internal field "title" as the name of the thing
94
+ end
95
+ types String
96
+ example 'my_name'
97
+ end
98
+
99
+ link :self do
100
+ description to <<-end
101
+ Link to this resource itself
102
+ (<a href="opinion_details.md">things details</a>)
103
+ end
104
+ url { thing_url(id) }
105
+ end
106
+ end
107
+
108
+ AUTHORS
109
+ -------
110
+ Florian Frank <flori@ping.de>
111
+
112
+ TODO
113
+ ----
114
+ - Implement some kind of scheme for versioning or at least better commit messages;
115
+ we also may want to keep other peoples commits if possible (how?).
116
+ - Refactor configuration to avoid singleton antipattern
117
+ - Create method in generator that considers a given string to be markdown
118
+ formatted and compiles it to html (as a work around to put multiline
119
+ descriptions inside of html tables in a markdown)
120
+ - Display enums as possible values in representers
121
+ - Automatically document error result documents
122
+ - Use private flag action/controller to skip docu creation by default. Make it
123
+ configurable to create private API as well.
124
+
@@ -0,0 +1,25 @@
1
+ # vim: set filetype=ruby et sw=2 ts=2:
2
+
3
+ require 'gem_hadar'
4
+
5
+ GemHadar do
6
+ name 'betterdocs'
7
+ author 'betterplace Developers'
8
+ email 'developers@betterplace.org'
9
+ homepage "http://github.com/betterplace/#{name}"
10
+ summary 'Betterplace API documentation library'
11
+ description "This library provides tools to generate API documention for a web site's REST-ful JSON API."
12
+ test_dir 'spec'
13
+ ignore '.*.sw[pon]', 'pkg', 'Gemfile.lock', 'coverage', '.rvmrc',
14
+ '.ruby-version', '.AppleDouble', 'tags', '.DS_Store', '.utilsrc', '.bundle'
15
+ readme 'README.md'
16
+ title "#{name.camelize} -- "
17
+
18
+ dependency 'tins', '~>1.3', '>=1.3.5'
19
+ dependency 'rails', '>=3', '<5'
20
+ dependency 'term-ansicolor', '~>1.3'
21
+ development_dependency 'simplecov'
22
+ development_dependency 'rspec'
23
+ end
24
+
25
+ task :default => :spec
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.0
@@ -0,0 +1,48 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # stub: betterdocs 0.2.0 ruby lib
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "betterdocs"
6
+ s.version = "0.2.0"
7
+
8
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
9
+ s.require_paths = ["lib"]
10
+ s.authors = ["betterplace Developers"]
11
+ s.date = "2016-03-30"
12
+ s.description = "This library provides tools to generate API documention for a web site's REST-ful JSON API."
13
+ s.email = "developers@betterplace.org"
14
+ s.extra_rdoc_files = ["README.md", "lib/betterdocs.rb", "lib/betterdocs/controller_collector.rb", "lib/betterdocs/dsl.rb", "lib/betterdocs/dsl/common.rb", "lib/betterdocs/dsl/controller.rb", "lib/betterdocs/dsl/controller/action.rb", "lib/betterdocs/dsl/controller/action/param.rb", "lib/betterdocs/dsl/controller/action/response.rb", "lib/betterdocs/dsl/controller/controller.rb", "lib/betterdocs/dsl/controller/controller_base.rb", "lib/betterdocs/dsl/json_params.rb", "lib/betterdocs/dsl/json_params/param.rb", "lib/betterdocs/dsl/json_type_mapper.rb", "lib/betterdocs/dsl/naming.rb", "lib/betterdocs/dsl/representer.rb", "lib/betterdocs/dsl/result.rb", "lib/betterdocs/dsl/result/collection_property.rb", "lib/betterdocs/dsl/result/link.rb", "lib/betterdocs/dsl/result/property.rb", "lib/betterdocs/generator/config_shortcuts.rb", "lib/betterdocs/generator/markdown.rb", "lib/betterdocs/global.rb", "lib/betterdocs/json_params_representer.rb", "lib/betterdocs/json_params_representer_collector.rb", "lib/betterdocs/mix_into_controller.rb", "lib/betterdocs/rake_tasks.rb", "lib/betterdocs/representer.rb", "lib/betterdocs/result_representer.rb", "lib/betterdocs/result_representer_collector.rb", "lib/betterdocs/section.rb", "lib/betterdocs/version.rb"]
15
+ s.files = [".codeclimate.yml", ".gitignore", ".rspec", ".travis.yml", "COPYING", "Gemfile", "LICENSE", "README.md", "Rakefile", "VERSION", "betterdocs.gemspec", "lib/betterdocs.rb", "lib/betterdocs/controller_collector.rb", "lib/betterdocs/dsl.rb", "lib/betterdocs/dsl/common.rb", "lib/betterdocs/dsl/controller.rb", "lib/betterdocs/dsl/controller/action.rb", "lib/betterdocs/dsl/controller/action/param.rb", "lib/betterdocs/dsl/controller/action/response.rb", "lib/betterdocs/dsl/controller/controller.rb", "lib/betterdocs/dsl/controller/controller_base.rb", "lib/betterdocs/dsl/json_params.rb", "lib/betterdocs/dsl/json_params/param.rb", "lib/betterdocs/dsl/json_type_mapper.rb", "lib/betterdocs/dsl/naming.rb", "lib/betterdocs/dsl/representer.rb", "lib/betterdocs/dsl/result.rb", "lib/betterdocs/dsl/result/collection_property.rb", "lib/betterdocs/dsl/result/link.rb", "lib/betterdocs/dsl/result/property.rb", "lib/betterdocs/generator/config_shortcuts.rb", "lib/betterdocs/generator/markdown.rb", "lib/betterdocs/generator/markdown/templates/README.md.erb", "lib/betterdocs/generator/markdown/templates/section.md.erb", "lib/betterdocs/global.rb", "lib/betterdocs/json_params_representer.rb", "lib/betterdocs/json_params_representer_collector.rb", "lib/betterdocs/mix_into_controller.rb", "lib/betterdocs/rake_tasks.rb", "lib/betterdocs/representer.rb", "lib/betterdocs/result_representer.rb", "lib/betterdocs/result_representer_collector.rb", "lib/betterdocs/section.rb", "lib/betterdocs/tasks/doc.rake", "lib/betterdocs/version.rb", "spec/controller_dsl_spec.rb", "spec/generator/markdown_spec.rb", "spec/json_params_representer_spec.rb", "spec/json_type_mapper_spec.rb", "spec/result_representer_dsl_spec.rb", "spec/result_representer_spec.rb", "spec/spec_helper.rb"]
16
+ s.homepage = "http://github.com/betterplace/betterdocs"
17
+ s.rdoc_options = ["--title", "Betterdocs -- ", "--main", "README.md"]
18
+ s.rubygems_version = "2.5.1"
19
+ s.summary = "Betterplace API documentation library"
20
+ s.test_files = ["spec/controller_dsl_spec.rb", "spec/generator/markdown_spec.rb", "spec/json_params_representer_spec.rb", "spec/json_type_mapper_spec.rb", "spec/result_representer_dsl_spec.rb", "spec/result_representer_spec.rb", "spec/spec_helper.rb"]
21
+
22
+ if s.respond_to? :specification_version then
23
+ s.specification_version = 4
24
+
25
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
26
+ s.add_development_dependency(%q<gem_hadar>, ["~> 1.6.1"])
27
+ s.add_development_dependency(%q<simplecov>, [">= 0"])
28
+ s.add_development_dependency(%q<rspec>, [">= 0"])
29
+ s.add_runtime_dependency(%q<tins>, [">= 1.3.5", "~> 1.3"])
30
+ s.add_runtime_dependency(%q<rails>, ["< 5", ">= 3"])
31
+ s.add_runtime_dependency(%q<term-ansicolor>, ["~> 1.3"])
32
+ else
33
+ s.add_dependency(%q<gem_hadar>, ["~> 1.6.1"])
34
+ s.add_dependency(%q<simplecov>, [">= 0"])
35
+ s.add_dependency(%q<rspec>, [">= 0"])
36
+ s.add_dependency(%q<tins>, [">= 1.3.5", "~> 1.3"])
37
+ s.add_dependency(%q<rails>, ["< 5", ">= 3"])
38
+ s.add_dependency(%q<term-ansicolor>, ["~> 1.3"])
39
+ end
40
+ else
41
+ s.add_dependency(%q<gem_hadar>, ["~> 1.6.1"])
42
+ s.add_dependency(%q<simplecov>, [">= 0"])
43
+ s.add_dependency(%q<rspec>, [">= 0"])
44
+ s.add_dependency(%q<tins>, [">= 1.3.5", "~> 1.3"])
45
+ s.add_dependency(%q<rails>, ["< 5", ">= 3"])
46
+ s.add_dependency(%q<term-ansicolor>, ["~> 1.3"])
47
+ end
48
+ end
@@ -0,0 +1,27 @@
1
+ require 'tins/xt'
2
+ require 'rails'
3
+
4
+ module Betterdocs
5
+ class << self
6
+ def rails
7
+ ::Rails
8
+ end
9
+
10
+ alias trait proc
11
+ public :trait
12
+ end
13
+ end
14
+
15
+ require 'betterdocs/version'
16
+ require 'betterdocs/dsl'
17
+ require 'betterdocs/result_representer'
18
+ require 'betterdocs/result_representer_collector'
19
+ require 'betterdocs/controller_collector'
20
+ require 'betterdocs/json_params_representer_collector'
21
+ require 'betterdocs/json_params_representer'
22
+ require 'betterdocs/global'
23
+ require 'betterdocs/section'
24
+ require 'betterdocs/mix_into_controller'
25
+ require 'betterdocs/generator/config_shortcuts'
26
+ require 'betterdocs/generator/markdown'
27
+ require 'betterdocs/rake_tasks'
@@ -0,0 +1,50 @@
1
+ module Betterdocs
2
+ class ControllerCollector
3
+ def initialize
4
+ @actions = {}
5
+ @element = nil
6
+ @controller = nil
7
+ end
8
+
9
+ attr_accessor :controller
10
+
11
+ attr_writer :element
12
+
13
+ def actions
14
+ @actions.values.reject(&:private)
15
+ end
16
+
17
+ def action(action_name)
18
+ action_name = action_name.to_sym
19
+ @actions[action_name]
20
+ end
21
+
22
+ def section
23
+ @controller.ask_and_send(:section)
24
+ end
25
+
26
+ def add_element(klass, type, &block)
27
+ element = build_element(klass, type, &block)
28
+ element.add_to_collector(self)
29
+ end
30
+
31
+ def configure_current_element(action_name)
32
+ if @element
33
+ @element.configure_for_action(action_name)
34
+ @actions[action_name] = @element
35
+ end
36
+ @element = nil
37
+ self
38
+ end
39
+
40
+ def to_s
41
+ ([ @controller, '=' * 79, actions * ("-" * 79 + "\n"), '' ]) * "\n"
42
+ end
43
+
44
+ private
45
+
46
+ def build_element(klass, type, &block)
47
+ Dsl::Controller.const_get(type.to_s.camelcase).new(klass, &block)
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,9 @@
1
+ module Betterdocs
2
+ module Dsl
3
+ end
4
+ end
5
+
6
+ require 'betterdocs/dsl/naming'
7
+ require 'betterdocs/dsl/controller'
8
+ require 'betterdocs/dsl/result'
9
+ require 'betterdocs/dsl/json_params'
@@ -0,0 +1,26 @@
1
+ module Betterdocs
2
+ module Dsl
3
+ module Common
4
+ extend Tins::Constant
5
+
6
+ constant :yes, true
7
+
8
+ constant :no, false
9
+
10
+ def set_context(context)
11
+ @__context__ = context
12
+ self
13
+ end
14
+
15
+ private
16
+
17
+ def method_missing(name, *a, &b)
18
+ if @__context__
19
+ @__context__.__send__(name, *a, &b)
20
+ else
21
+ super
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,9 @@
1
+ module Betterdocs
2
+ module Dsl
3
+ module Controller
4
+ end
5
+ end
6
+ end
7
+
8
+ require 'betterdocs/dsl/controller/action'
9
+ require 'betterdocs/dsl/controller/controller'
@@ -0,0 +1,126 @@
1
+ require 'betterdocs/dsl/controller/controller_base'
2
+ require 'betterdocs/dsl/common.rb'
3
+
4
+ class Betterdocs::Dsl::Controller::Action < Betterdocs::Dsl::Controller::ControllerBase
5
+ require 'betterdocs/dsl/controller/action/param'
6
+ require 'betterdocs/dsl/controller/action/response'
7
+
8
+ dsl_accessor :action
9
+
10
+ alias name action
11
+
12
+ dsl_accessor :title do "All about: #{action}" end
13
+
14
+ dsl_accessor :section do
15
+ controller.docs.controller.full?(:section) || :misc
16
+ end
17
+
18
+ dsl_accessor :action_method
19
+
20
+ dsl_accessor :http_method do
21
+ case action
22
+ when :show, :index
23
+ :GET
24
+ when :update
25
+ :PUT
26
+ when :destroy
27
+ :DELETE
28
+ when :create
29
+ :POST
30
+ else
31
+ raise ArgumentError, "Cannot automatically derive http_method for"\
32
+ " #{name.inspect}, specify manually"
33
+ end
34
+ end
35
+
36
+ dsl_accessor :params do {} end
37
+
38
+ dsl_accessor :json_params_representer
39
+
40
+ def json_params_like(klass)
41
+ json_params_representer klass.docs
42
+ self
43
+ end
44
+
45
+ def json_params
46
+ json_params_representer.full?(:params)
47
+ end
48
+
49
+ def json_params_example_json
50
+ if params = json_params_representer.full?(:params)
51
+ data = {}
52
+ params.each_with_object(data) do |(name, param), d|
53
+ d[name] = param.value
54
+ end
55
+ JSON.pretty_generate(JSON.load(JSON.dump(data)), quirks_mode: true) # sigh, don't ask…
56
+ end
57
+ end
58
+
59
+ dsl_accessor :private, false
60
+
61
+ def param(name, &block)
62
+ name = name.to_sym
63
+ if block
64
+ param = Betterdocs::Dsl::Controller::Action::Param.new(name, &block)
65
+ param.value or param.value params.size + 1
66
+ params[name] = param
67
+ else
68
+ params[name]
69
+ end
70
+ end
71
+
72
+ dsl_accessor :responses do {} end
73
+
74
+ def response(name = :default, &block)
75
+ if block
76
+ responses[name] = Betterdocs::Dsl::Controller::Action::Response.new(
77
+ name, &block
78
+ ).set_context(self)
79
+ else
80
+ responses[name]
81
+ end
82
+ end
83
+
84
+ dsl_accessor :description, 'TODO'
85
+
86
+ def configure_for_action(action_name)
87
+ action action_name
88
+ action_method controller.instance_method(action_name)
89
+ end
90
+
91
+ def include_params(callee)
92
+ instance_eval(&callee)
93
+ end
94
+
95
+ def url
96
+ url_params = params.select { |_, param| param.use_in_url? }
97
+ Betterdocs.rails.application.routes.url_for(
98
+ {
99
+ controller: controller.name.underscore.sub(/_controller\z/, ''),
100
+ action: action,
101
+ } | url_params | Betterdocs::Global.config.api_url_options
102
+ )
103
+ end
104
+
105
+ def request
106
+ "#{http_method.to_s.upcase} #{url}"
107
+ end
108
+
109
+ def to_s
110
+ (
111
+ [ request, '' ] +
112
+ [ inspect, '' ] +
113
+ params.map { |name, param|
114
+ "#{name}(=#{param.value}): #{param.description}"
115
+ } + [ '', description, '', action_method.source_location * ':', '' ]
116
+ ) * "\n"
117
+ end
118
+
119
+ def inspect
120
+ "#{controller}##{action}(#{params.keys * ', '})"
121
+ end
122
+
123
+ def add_to_collector(collector)
124
+ collector.element = self
125
+ end
126
+ end