docjs 0.2 → 0.2.1
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.
- data/README.md +14 -0
- data/bin/docjs +85 -46
- data/bin/docjs.rb +85 -46
- data/docjs.gemspec +2 -2
- data/docs/guides/CUSTOMIZE.md +90 -0
- data/lib/boot.rb +12 -2
- data/lib/code_object/base.rb +0 -4
- data/lib/code_object/converter.rb +10 -11
- data/lib/configs.rb +28 -0
- data/lib/document/document.rb +21 -0
- data/lib/dom/dom.rb +0 -9
- data/lib/generator/generator.rb +168 -53
- data/lib/helper/helper.rb +3 -0
- data/lib/parser/comment.rb +1 -1
- data/lib/parser/comment_parser.rb +8 -4
- data/lib/parser/meta_container.rb +5 -1
- data/lib/parser/parser.rb +36 -10
- data/lib/processor.rb +70 -38
- data/lib/renderer.rb +100 -1
- data/lib/thor.rb +2 -0
- data/lib/token/container.rb +9 -5
- data/lib/token/handler.rb +1 -3
- data/lib/token/token.rb +2 -0
- data/templates/generators/api_index_generator.rb +1 -0
- data/templates/generators/api_pages_generator.rb +2 -0
- data/templates/generators/docs_generator.rb +2 -0
- data/templates/generators/json_generator.rb +16 -0
- data/templates/helpers/template.rb +24 -7
- data/templates/resources/css/application.css +18 -3
- data/templates/resources/scss/_header.scss +2 -2
- data/templates/resources/scss/_helpers.scss +3 -2
- data/templates/resources/scss/application.scss +15 -0
- data/test/parser/intelligent_skip_until.rb +10 -0
- data/test/parser/parser.rb +44 -0
- metadata +50 -47
data/lib/parser/comment.rb
CHANGED
@@ -3,7 +3,7 @@ require_relative '../code_object/converter'
|
|
3
3
|
|
4
4
|
module Parser
|
5
5
|
|
6
|
-
# Together with {Parser::
|
6
|
+
# Together with {Parser::Comment} it acts as an **Interface** between {Parser} and {CodeObject}.
|
7
7
|
# Parser::Comment creates instances of Tokenline, which are then analysed by
|
8
8
|
# {Token::Container#process_token}
|
9
9
|
#
|
@@ -2,8 +2,13 @@ require_relative 'comment'
|
|
2
2
|
require_relative 'exceptions'
|
3
3
|
|
4
4
|
module Parser
|
5
|
+
|
6
|
+
# Creates an instance of {Parser::Comment}, then parses the content and adds the contained doc-
|
7
|
+
# and tokenlines to it.
|
8
|
+
#
|
9
|
+
# @see Comment
|
5
10
|
class CommentParser < StringScanner
|
6
|
-
|
11
|
+
|
7
12
|
def initialize(input)
|
8
13
|
super(input)
|
9
14
|
@comment = Comment.new(input)
|
@@ -18,8 +23,7 @@ module Parser
|
|
18
23
|
#
|
19
24
|
# All other lines are interpreted as doclines
|
20
25
|
#
|
21
|
-
# @return [Parser::Comment]
|
22
|
-
# and tokenlines to it.
|
26
|
+
# @return [Parser::Comment] Attaches all found doc- and tokenlines to the instance of comment
|
23
27
|
def parse
|
24
28
|
# we don't want the linebreak of the comment start in our first docline
|
25
29
|
# i.e. ignore '/**\n'
|
@@ -51,7 +55,7 @@ module Parser
|
|
51
55
|
end
|
52
56
|
end
|
53
57
|
|
54
|
-
# Parses tokens, if the line begins with an
|
58
|
+
# Parses tokens, if the line begins with an `@`
|
55
59
|
# @token_one some other text etc.
|
56
60
|
#
|
57
61
|
# The parser does the following to detect multiline tokens like:
|
@@ -1,6 +1,10 @@
|
|
1
1
|
module Parser
|
2
2
|
|
3
|
-
#
|
3
|
+
# Is included by {CodeObject::Base} and {Parser::Comment} and stores Metainformation like
|
4
|
+
#
|
5
|
+
# - **filepath** of the JavaScript-File, where the comment is extracted from
|
6
|
+
# - **source** of the JavaScript-Scope, which begins just after the comment ends.
|
7
|
+
# - **line_start** - Linenumber of the first scope-line
|
4
8
|
module MetaContainer
|
5
9
|
|
6
10
|
attr_reader :filepath, :source, :line_start
|
data/lib/parser/parser.rb
CHANGED
@@ -1,10 +1,8 @@
|
|
1
1
|
require 'strscan'
|
2
2
|
require_relative 'comment_parser'
|
3
3
|
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
4
|
+
# @see Parser::Parser
|
5
|
+
# @see Parser::CommentParser
|
8
6
|
module Parser
|
9
7
|
|
10
8
|
NO_BR = /((?!\n)\s)/
|
@@ -53,7 +51,35 @@ module Parser
|
|
53
51
|
D_STRING => D_STRING,
|
54
52
|
REGEXP_START => REGEXP_END
|
55
53
|
}
|
56
|
-
|
54
|
+
|
55
|
+
# 
|
56
|
+
#
|
57
|
+
# Turns the incoming javascript-source into a stream of {Parser::Comment comments}. Those comments
|
58
|
+
# contain the parsed doclines, which are simply all lines found in the comment and all tokenlines.
|
59
|
+
#
|
60
|
+
# A tokenline starts with a token like `@token` and can span over multiple lines, if it is intended
|
61
|
+
# by two spaces.
|
62
|
+
#
|
63
|
+
# The comment-scope (i.e. the javascript-language-scope beginning in the next line) will be preserved
|
64
|
+
# as `source` of the comment as well.
|
65
|
+
#
|
66
|
+
# For example it extracts to Comments from the following source
|
67
|
+
#
|
68
|
+
# /**
|
69
|
+
# * @object Person
|
70
|
+
# */
|
71
|
+
# var Person = {}
|
72
|
+
#
|
73
|
+
# /**
|
74
|
+
# * Some documentation here, and there
|
75
|
+
# *
|
76
|
+
# * @object Person.config
|
77
|
+
# */
|
78
|
+
# Person.config = {};
|
79
|
+
#
|
80
|
+
# #=> [#<Parser::Comment tokenlines=1 doclines=0>, #<Parser::Comment tokenlines=1 doclines=2>]
|
81
|
+
#
|
82
|
+
# @see Parser::CommentParser
|
57
83
|
class Parser
|
58
84
|
|
59
85
|
attr_reader :filepath, :offset
|
@@ -78,9 +104,10 @@ module Parser
|
|
78
104
|
end
|
79
105
|
|
80
106
|
|
81
|
-
# Recursivly parses the {#initialize given input} and thereby ignores strings
|
107
|
+
# Recursivly parses the {#initialize given input} and thereby ignores strings and regular-
|
108
|
+
# expressions.
|
82
109
|
#
|
83
|
-
# @todo Rewrite to use
|
110
|
+
# @todo Rewrite to use {StringScanner#intelligent_skip_until}
|
84
111
|
# @return [Array<Parser::Comment>] the parsed comment-stream
|
85
112
|
def parse()
|
86
113
|
@scanner.skip /\s/
|
@@ -108,6 +135,7 @@ module Parser
|
|
108
135
|
end
|
109
136
|
end
|
110
137
|
|
138
|
+
# Reads the contents of `path`, creates a new `Parser` and starts parsing all at once
|
111
139
|
def self.parse_file(path)
|
112
140
|
stream = File.read path
|
113
141
|
Parser.new(stream, :filepath => path).parse
|
@@ -236,8 +264,6 @@ class StringScanner
|
|
236
264
|
|
237
265
|
raise end_of_string_error(pattern) if self.matched.nil?
|
238
266
|
|
239
|
-
return if found.match pattern
|
240
|
-
|
241
267
|
Parser::NON_CODE_PATTERNS.each do |start_pattern, end_pattern|
|
242
268
|
if found.match start_pattern
|
243
269
|
self.skip_escaping_until end_pattern
|
@@ -259,7 +285,7 @@ class StringScanner
|
|
259
285
|
|
260
286
|
raise end_of_string_error(pattern) if self.matched.nil?
|
261
287
|
|
262
|
-
if self.matched.match /\\/
|
288
|
+
if self.matched.match /\\/
|
263
289
|
self.getch
|
264
290
|
skip_escaping_until(pattern)
|
265
291
|
end
|
data/lib/processor.rb
CHANGED
@@ -3,22 +3,52 @@ require_relative 'dom/dom'
|
|
3
3
|
require_relative 'generator/generator'
|
4
4
|
require_relative 'document/document'
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
# @note The prerequisites for Processor to work is that {Logger} and {Configs} are prepared and all
|
7
|
+
# other components are already required. (Like the {Parser::Parser} and the {Dom}). For further
|
8
|
+
# information about how to set up the system, see {#setup_application}.
|
9
|
+
#
|
10
|
+
# The Processor is a component, which is essential for {DocJs} to fullfil it's tasks. While {DocJs}
|
11
|
+
# serves as commandline-interface (CLI), the Processor is the heartpiece of DocJs and it's methods are
|
12
|
+
# triggered directly by DocJs after having everything set up correctly.
|
13
|
+
#
|
14
|
+
# The Processing is divided into the following stages:
|
15
|
+
module Processor
|
9
16
|
|
10
|
-
def self.process_and_render
|
11
|
-
process_files_to_dom
|
12
|
-
perform_all_tasks
|
13
|
-
end
|
14
17
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
+
# @group Stage #1 - Document Processing
|
19
|
+
|
20
|
+
# For each specified Markdown-Document a new instance of {Document::Document} is created and filled
|
21
|
+
# with it's contents. Afterwards the document-nodes are added as children to `Dom.docs`.
|
22
|
+
#
|
23
|
+
# 
|
24
|
+
#
|
25
|
+
def self.prepare_documents
|
26
|
+
# underscores will be replaced with whitespaces as title
|
27
|
+
Configs.docs.each do |doc|
|
28
|
+
|
29
|
+
doc_path = File.expand_path(doc, Configs.wdir)
|
30
|
+
Logger.debug "Working with Document #{doc_path}"
|
31
|
+
|
32
|
+
contents = File.read(doc_path)
|
33
|
+
|
34
|
+
# Those documents get registered in a special {Dom::Node} Dom.docs
|
35
|
+
document = Document::Document.new(doc_path, contents)
|
36
|
+
Dom.docs.add_node(document.path, document)
|
37
|
+
|
38
|
+
# The docs can be accessed via Dom later on
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
18
42
|
|
19
|
-
# @group Stage #
|
43
|
+
# @group Stage #2a - File Processing
|
20
44
|
|
21
|
-
#
|
45
|
+
# Process JavaScript files, whose filenames are stored in `Configs.files` (After having them
|
46
|
+
# provided as commandline-options or in a `docjs.yml`-file)
|
47
|
+
#
|
48
|
+
# Parses each JavaScript file and collects the found comments. After parsing everything all comments
|
49
|
+
# are returned as an Array.
|
50
|
+
#
|
51
|
+
# [ *.js Files ] --(parses)--> [ Comments ]
|
22
52
|
def self.parse_files(files = nil)
|
23
53
|
files ||= Configs.files
|
24
54
|
|
@@ -35,45 +65,47 @@ module Processor
|
|
35
65
|
return comments
|
36
66
|
end
|
37
67
|
|
38
|
-
|
68
|
+
|
69
|
+
# @group Stage #2b - Comment Processing
|
39
70
|
|
40
71
|
# Processing comment-stream and convert to {CodeObject CodeObjects}
|
41
|
-
# This stage also adds the CodeObjects to Dom.
|
72
|
+
# This stage also adds the CodeObjects to {Dom}.
|
73
|
+
#
|
74
|
+
# [ Comments ] --(converts to)--> [ CodeObject ] --(add to)--> [ Dom ]
|
42
75
|
def self.process_comments(comments)
|
43
76
|
|
44
77
|
comments = [comments] unless comments.is_a? Array
|
45
78
|
|
46
79
|
comments.each do |comment|
|
47
|
-
code_object = comment.to_code_object
|
80
|
+
code_object = comment.to_code_object # convert to code_object
|
48
81
|
Logger.debug "Adding to Dom: #{code_object}"
|
49
|
-
Dom.add_node(code_object.path, code_object) unless code_object.nil?
|
82
|
+
Dom.add_node(code_object.path, code_object) unless code_object.nil? # add to dom
|
50
83
|
end
|
51
|
-
end
|
52
|
-
|
84
|
+
end
|
53
85
|
|
54
|
-
# @group Stage #3 -
|
86
|
+
# @group Stage #3 - Template Processing
|
55
87
|
|
56
|
-
|
88
|
+
# Searches for all Generator-Classes, instantiates them and calls {Generator::Generator#perform}
|
89
|
+
# to trigger the content-generation.
|
90
|
+
#
|
91
|
+
# [ Generators ] --(uses)--> [ Dom ]
|
92
|
+
# |
|
93
|
+
# ---(generates)--> [ HTML-Output ]
|
94
|
+
def self.start_generators
|
57
95
|
Generator::Generator.all.each { |task| task.new.perform }
|
58
96
|
end
|
97
|
+
|
98
|
+
|
99
|
+
# @group Combined Stages
|
59
100
|
|
60
|
-
#
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
contents = File.read(doc_path)
|
70
|
-
|
71
|
-
# Those documents get registered in a special {Dom::Node} Dom.docs
|
72
|
-
document = Document::Document.new(doc_path, contents)
|
73
|
-
Dom.docs.add_node(document.path, document)
|
74
|
-
|
75
|
-
# The docs can be accessed via Dom later on
|
76
|
-
end
|
101
|
+
# Combines Stages {.parse_files #2a}, {.process_comments #2b} and {.start_generators #3}
|
102
|
+
def self.process_and_render
|
103
|
+
process_files_to_dom
|
104
|
+
start_generators
|
105
|
+
end
|
106
|
+
|
107
|
+
# Combines Stage {.parse_files #2a} and {.process_comments #2b}
|
108
|
+
def self.process_files_to_dom(files = nil)
|
109
|
+
process_comments parse_files(files)
|
77
110
|
end
|
78
|
-
|
79
111
|
end
|
data/lib/renderer.rb
CHANGED
@@ -1,6 +1,31 @@
|
|
1
1
|
require 'erb'
|
2
2
|
require 'fileutils'
|
3
3
|
|
4
|
+
# The Renderer is the heart-piece of each {Generator::Generator}, but can also be used without them.
|
5
|
+
# It uses ERB-Templates, which are being rendered with a binding to the Renderer-instance.
|
6
|
+
#
|
7
|
+
# It's only method {#render} can be used in multiple ways, which are explained {#render here}.
|
8
|
+
# The Renderer is automatically been set up by the Generator, but to understand how it works we
|
9
|
+
# may setup it ourselves like:
|
10
|
+
#
|
11
|
+
# my_renderer = Renderer.new 'my/template/path', 'layout/application'
|
12
|
+
# my_renderer.render 'test', :to_file => 'output.html'
|
13
|
+
#
|
14
|
+
# This will render the template `my/template/path/test.html.erb` in the layout
|
15
|
+
# `my/template/path/layout/application.html.erb` and saved to `output.html`.
|
16
|
+
#
|
17
|
+
# Most of the time, as with Generators, the renderer will be extended and used like:
|
18
|
+
#
|
19
|
+
# class MyRenderer < Renderer
|
20
|
+
#
|
21
|
+
# def initialize
|
22
|
+
# super('my/template/path', 'layout/application')
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# def index
|
26
|
+
# render 'test', :to_file => 'output.html'
|
27
|
+
# end
|
28
|
+
# end
|
4
29
|
class Renderer
|
5
30
|
|
6
31
|
def initialize(default_path, layout)
|
@@ -8,7 +33,81 @@ class Renderer
|
|
8
33
|
@_layout = layout
|
9
34
|
end
|
10
35
|
|
11
|
-
#
|
36
|
+
# @overload render(template, *opts)
|
37
|
+
# Options **opts**:
|
38
|
+
#
|
39
|
+
# - :layout (String) Default is specified in constructor. For example `'json'` or `'application'`
|
40
|
+
# - :to_file (String) Optional file-path to save the output to.
|
41
|
+
#
|
42
|
+
# @param [String, Symbol] template Template-file **without** file-extension. (Like `index` => `index.html.erb`)
|
43
|
+
# @param [Hash] opts
|
44
|
+
# @return [String, nil] the rendered output
|
45
|
+
#
|
46
|
+
#
|
47
|
+
# @overload render(:partial => template, :collection => [...])
|
48
|
+
# For each item of `collection`, the partial will be rendered once. The output consists of all
|
49
|
+
# those concatenated render-passes.
|
50
|
+
# The value of each item will be bound to a local-variable called like the partial, without leading
|
51
|
+
# _. (i.e. if partial-name = "_test.html.erb" the variable is called `test`
|
52
|
+
#
|
53
|
+
# Options **opts**:
|
54
|
+
#
|
55
|
+
# - :template (String, Symbol) Template-file **without** file-extension. (Like `index` => `index.html.erb`)
|
56
|
+
# - :collection (Array)
|
57
|
+
#
|
58
|
+
# @param [Hash] opts
|
59
|
+
# @return [String] the rendered output
|
60
|
+
#
|
61
|
+
#
|
62
|
+
# @overload render(:partial => template, :locals => {...})
|
63
|
+
# Each key of `locals` will be set to it's value in the local-binding of the partial.
|
64
|
+
#
|
65
|
+
# Options **opts**:
|
66
|
+
#
|
67
|
+
# - :template (String, Symbol) Template-file **without** file-extension. (Like `index` => `index.html.erb`)
|
68
|
+
# - :locals (Hash) Hash of variables, which will be available via local-variables in the partial-binding
|
69
|
+
#
|
70
|
+
# @param [Hash] opts
|
71
|
+
# @return [String] the rendered output
|
72
|
+
#
|
73
|
+
#
|
74
|
+
# @example simple rendering
|
75
|
+
# render 'test', :layout => nil #=> returns a string, containing the rendered template 'test.html.erb'
|
76
|
+
#
|
77
|
+
# @example rendering within a layout
|
78
|
+
# # layout/app.html.erb
|
79
|
+
# <html>
|
80
|
+
# <%= yield %>
|
81
|
+
# </html>
|
82
|
+
#
|
83
|
+
# # MyCustomRenderer < Renderer
|
84
|
+
# render 'test', :layout => 'layout/app' #=> renders 'test.html.erb' within 'app.html.erb'
|
85
|
+
#
|
86
|
+
# @example rendering a partial with collections
|
87
|
+
# # _item.html.erb
|
88
|
+
# <li><%= item %></li>
|
89
|
+
#
|
90
|
+
# # my_view.html.erb
|
91
|
+
# <ul>
|
92
|
+
# <%= render :partial => 'item', :collection => ["Foo", "Bar", "Baz"]
|
93
|
+
# </ul>
|
94
|
+
#
|
95
|
+
# #=> <ul><li>Foo</li><li>Bar</li><li>Baz</li></ul>
|
96
|
+
#
|
97
|
+
# @example setting local-variables within a partial
|
98
|
+
# # _item.html.erb
|
99
|
+
# <strong><%= foo %></strong> <em><%= bar %></em>
|
100
|
+
#
|
101
|
+
# # my_view.html.erb
|
102
|
+
# <%= render :partial => 'item', :locals => { :foo => "Hello", :bar => "World" } %>
|
103
|
+
#
|
104
|
+
# #=> <strong>Hello</strong> <em>World</em>
|
105
|
+
#
|
106
|
+
# @example rendering to file
|
107
|
+
# render 'my_test', :to_file => "test_output.html"
|
108
|
+
#
|
109
|
+
# @note Pretty much inspired by Ruby on Rails
|
110
|
+
# @see http://guides.rubyonrails.org/layouts_and_rendering.html
|
12
111
|
def render(opt = nil, extra_options = {})
|
13
112
|
|
14
113
|
# Prepare Options
|
data/lib/thor.rb
CHANGED
data/lib/token/container.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
require_relative 'exceptions'
|
2
2
|
|
3
3
|
module Token
|
4
|
-
|
4
|
+
|
5
|
+
# Each {CodeObject} serves as Token::Container. This module encapsulates all required methods
|
6
|
+
# to {#process_token convert Tokenlines to Tokens}, {#add_token add Tokens} and {#token query the stored tokens}.
|
5
7
|
module Container
|
6
8
|
|
7
9
|
def initialize
|
@@ -45,12 +47,14 @@ module Token
|
|
45
47
|
@tokens[tokenid] << token
|
46
48
|
end
|
47
49
|
|
48
|
-
#
|
50
|
+
# tries to find matching tokenklass for token i.e. Token::Token::ParamToken for :param
|
49
51
|
# then calls matching tokenhandler (if exists) with data in `this`-context
|
50
|
-
#
|
52
|
+
#
|
53
|
+
# @param [Parser::Tokenline] tokenline consisting of :token and :content
|
54
|
+
#
|
55
|
+
# @todo only throw NoTokenHandler, if this really is the problem
|
51
56
|
def process_token(tokenline)
|
52
|
-
|
53
|
-
# try to find matching tokenklass for token i.e. Token::Token::ParamToken for :param
|
57
|
+
|
54
58
|
begin
|
55
59
|
camelcased = tokenline.token.to_s.capitalize.gsub(/_\w/){|w| w[1].capitalize}
|
56
60
|
tokenklass = Token.const_get "#{camelcased}Token"
|
data/lib/token/handler.rb
CHANGED
@@ -6,9 +6,7 @@ require_relative 'token'
|
|
6
6
|
# The {CodeObject::Converter converter} starts the {Parser::Tokenline tokenline}-processing, by
|
7
7
|
# calling the mixed-in function {Token::Container#process_token}.
|
8
8
|
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
# The illustration above shows the **two modules** included in {Token}:
|
9
|
+
# There are **two modules** included in {Token}:
|
12
10
|
#
|
13
11
|
# 1. {Token::Handler}, which can be used to register new token-handlers.
|
14
12
|
# 2. {Token::Container}, which is included in {CodeObject::Base} to add
|
data/lib/token/token.rb
CHANGED