jsus 0.3.1 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +1 -1
- data/.travis.yml +1 -1
- data/.yardopts +9 -0
- data/CHANGELOG +21 -0
- data/Gemfile +3 -2
- data/{README → README.md} +10 -4
- data/TODO +1 -3
- data/VERSION +1 -1
- data/bin/jsus +33 -10
- data/jsus.gemspec +12 -7
- data/lib/jsus.rb +31 -9
- data/lib/jsus/container.rb +96 -24
- data/lib/jsus/middleware.rb +160 -43
- data/lib/jsus/package.rb +65 -25
- data/lib/jsus/packager.rb +15 -10
- data/lib/jsus/pool.rb +54 -40
- data/lib/jsus/source_file.rb +105 -80
- data/lib/jsus/tag.rb +65 -39
- data/lib/jsus/util.rb +8 -6
- data/lib/jsus/util/code_generator.rb +21 -0
- data/lib/jsus/util/documenter.rb +34 -6
- data/lib/jsus/util/file_cache.rb +16 -5
- data/lib/jsus/util/inflection.rb +9 -2
- data/lib/jsus/util/tree.rb +54 -18
- data/lib/jsus/util/validator/base.rb +13 -5
- data/lib/jsus/util/validator/mooforge.rb +8 -2
- data/markup/stylesheet.css +84 -16
- data/spec/data/ChainDependencies/app/javascripts/Class/package.yml +2 -2
- data/spec/jsus/middleware_spec.rb +25 -0
- data/spec/jsus/pool_spec.rb +9 -4
- metadata +23 -7
data/lib/jsus/tag.rb
CHANGED
@@ -3,8 +3,13 @@ module Jsus
|
|
3
3
|
# Tag is basically just a string that contains a package name and a name for class
|
4
4
|
# (or not necessarily a class) which the given SourceFile provides/requires/extends/replaces.
|
5
5
|
#
|
6
|
+
# @example
|
7
|
+
# "Core/Class" is a tag
|
6
8
|
class Tag
|
7
|
-
|
9
|
+
# Owner package
|
10
|
+
attr_accessor :package
|
11
|
+
# Whether tag is external
|
12
|
+
attr_accessor :external
|
8
13
|
|
9
14
|
# Constructors
|
10
15
|
|
@@ -14,6 +19,8 @@ module Jsus
|
|
14
19
|
# The way it works may seem a bit tricky but actually it parses name/options
|
15
20
|
# combinations in different ways and may be best described by examples:
|
16
21
|
#
|
22
|
+
# @example
|
23
|
+
#
|
17
24
|
# a = Tag.new("Class") # :package_name => "", :name => "Class", :external => false
|
18
25
|
# b = Tag.new("Core/Class") # :package_name => "Core", :name => "Class", :external => true
|
19
26
|
# core = Package.new(...) # let's consider its name is 'Core'
|
@@ -23,8 +30,14 @@ module Jsus
|
|
23
30
|
# e = Tag.new("Core/Class", :package => mash) # :package_name => "Core", :name => "Class", :external => true
|
24
31
|
#
|
25
32
|
# Between all those, tags b,c,d and e are equal, meaning they all use
|
26
|
-
# the same spot in Hash or
|
33
|
+
# the same spot in Hash or wherever else.
|
27
34
|
#
|
35
|
+
# @param [String] tag name
|
36
|
+
# @param [Hash] options
|
37
|
+
# @option options [String] :package_name owner package name
|
38
|
+
# @option options [Jsus::Package] :package :owner package
|
39
|
+
# @option options [Boolean] :external whether tag is considered external
|
40
|
+
# @api public
|
28
41
|
def initialize(name, options = {})
|
29
42
|
normalized_options = Tag.normalize_name_and_options(name, options)
|
30
43
|
[:name, :package, :package_name, :external].each do |field|
|
@@ -32,7 +45,9 @@ module Jsus
|
|
32
45
|
end
|
33
46
|
end
|
34
47
|
|
35
|
-
|
48
|
+
# When given a tag instead of tag name, just returns it.
|
49
|
+
# @api public
|
50
|
+
def self.new(tag_or_name, *args, &block)
|
36
51
|
if tag_or_name.kind_of?(Tag)
|
37
52
|
tag_or_name
|
38
53
|
else
|
@@ -40,30 +55,29 @@ module Jsus
|
|
40
55
|
end
|
41
56
|
end
|
42
57
|
|
43
|
-
#
|
58
|
+
# Alias for Tag.new
|
59
|
+
# @api public
|
44
60
|
def self.[](*args)
|
45
61
|
new(*args)
|
46
62
|
end
|
47
63
|
|
48
64
|
# Public API
|
49
65
|
|
50
|
-
#
|
51
|
-
#
|
52
|
-
#
|
66
|
+
# @returns [Boolean] whether tag is external
|
67
|
+
# @api public
|
53
68
|
def external?
|
54
69
|
!!external
|
55
70
|
end
|
56
71
|
|
57
|
-
#
|
58
|
-
#
|
59
|
-
#
|
60
|
-
#
|
61
|
-
#
|
62
|
-
#
|
63
|
-
#
|
64
|
-
#
|
65
|
-
#
|
66
|
-
# Tag.new('Core/Class', :package => core).name(:short => true) # => 'Class'
|
72
|
+
# @param [Hash] options
|
73
|
+
# @option options [Boolean] :short whether the tag should try using short form
|
74
|
+
# @note only non-external tags support short forms.
|
75
|
+
# @example
|
76
|
+
# Tag.new('Core/Class').name(:short => true) # => 'Core/Class'
|
77
|
+
# core = Package.new(...) # let's consider its name is 'Core'
|
78
|
+
# Tag.new('Core/Class', :package => core).name(:short => true) # => 'Class'
|
79
|
+
# @return [String] a well-formed name for the tag.
|
80
|
+
# @api public
|
67
81
|
def name(options = {})
|
68
82
|
if !package_name || package_name.empty? || (options[:short] && !external?)
|
69
83
|
@name
|
@@ -73,17 +87,19 @@ module Jsus
|
|
73
87
|
end
|
74
88
|
alias_method :to_s, :name
|
75
89
|
|
76
|
-
#
|
90
|
+
# @returns [String] package name or an empty string
|
91
|
+
# @api public
|
77
92
|
def package_name
|
78
93
|
@package_name ||= (@package ? @package.name : "")
|
79
94
|
end
|
80
95
|
|
81
|
-
#
|
96
|
+
# @return [Boolean] whether name is empty
|
97
|
+
# @api public
|
82
98
|
def empty?
|
83
99
|
@name.empty?
|
84
100
|
end
|
85
101
|
|
86
|
-
#
|
102
|
+
# @api public
|
87
103
|
def ==(other)
|
88
104
|
if other.kind_of?(Tag)
|
89
105
|
self.name == other.name
|
@@ -92,9 +108,26 @@ module Jsus
|
|
92
108
|
end
|
93
109
|
end
|
94
110
|
|
111
|
+
# @api semipublic
|
112
|
+
def eql?(other)
|
113
|
+
self.==(other)
|
114
|
+
end
|
115
|
+
|
116
|
+
# @api semipublic
|
117
|
+
def hash
|
118
|
+
self.name.hash
|
119
|
+
end
|
120
|
+
|
121
|
+
# @return [String] human-readable representation
|
122
|
+
# @api public
|
123
|
+
def inspect
|
124
|
+
"<Jsus::Tag: #{name}>"
|
125
|
+
end
|
126
|
+
|
95
127
|
# Private API
|
96
128
|
|
97
|
-
|
129
|
+
# @api private
|
130
|
+
def self.normalize_name_and_options(name, options = {})
|
98
131
|
result = {}
|
99
132
|
name.gsub!(%r(^(\.)?/), "")
|
100
133
|
if name.index("/")
|
@@ -112,39 +145,32 @@ module Jsus
|
|
112
145
|
result
|
113
146
|
end
|
114
147
|
|
115
|
-
|
148
|
+
# @api private
|
149
|
+
def self.normalized_options_to_full_name(options)
|
116
150
|
[options[:package_name], options[:name]].compact.join("/")
|
117
151
|
end
|
118
152
|
|
119
|
-
|
153
|
+
# @api private
|
154
|
+
def self.name_and_options_to_full_name(name, options = {})
|
120
155
|
normalized_options_to_full_name(normalize_name_and_options(name, options))
|
121
156
|
end
|
122
157
|
|
123
|
-
|
158
|
+
# @api private
|
159
|
+
def self.normalize_package_name(name)
|
124
160
|
package_chunks = name.split("/")
|
125
161
|
package_chunks.map do |pc|
|
126
162
|
Jsus::Util::Inflection.random_case_to_mixed_case(pc)
|
127
163
|
end.join("/")
|
128
164
|
end # normalize_name
|
129
165
|
|
130
|
-
|
166
|
+
# @api private
|
167
|
+
def package_name=(new_value)
|
131
168
|
@package_name = new_value
|
132
169
|
end
|
133
170
|
|
134
|
-
|
171
|
+
# @api private
|
172
|
+
def name=(new_value)
|
135
173
|
@name = new_value
|
136
174
|
end
|
137
|
-
|
138
|
-
def eql?(other) # :nodoc:
|
139
|
-
self.==(other)
|
140
|
-
end
|
141
|
-
|
142
|
-
def hash # :nodoc:
|
143
|
-
self.name.hash
|
144
|
-
end
|
145
|
-
|
146
|
-
def inspect # :nodoc
|
147
|
-
"<Jsus::Tag: #{name}>"
|
148
|
-
end
|
149
175
|
end
|
150
|
-
end
|
176
|
+
end
|
data/lib/jsus/util.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
module Jsus
|
2
|
+
# Utility namespace.
|
2
3
|
module Util
|
3
|
-
autoload :Tree,
|
4
|
-
autoload :Documenter,
|
5
|
-
autoload :Validator,
|
6
|
-
autoload :Inflection,
|
7
|
-
autoload :FileCache,
|
4
|
+
autoload :Tree, 'jsus/util/tree'
|
5
|
+
autoload :Documenter, 'jsus/util/documenter'
|
6
|
+
autoload :Validator, 'jsus/util/validator'
|
7
|
+
autoload :Inflection, 'jsus/util/inflection'
|
8
|
+
autoload :FileCache, 'jsus/util/file_cache'
|
9
|
+
autoload :CodeGenerator, 'jsus/util/code_generator'
|
8
10
|
end # Util
|
9
|
-
end # Jsus
|
11
|
+
end # Jsus
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Jsus
|
2
|
+
module Util
|
3
|
+
# Code generation routines.
|
4
|
+
module CodeGenerator
|
5
|
+
class <<self
|
6
|
+
# @return [String] javascript for includes for a list of given paths
|
7
|
+
# @api public
|
8
|
+
def generate_includes(paths)
|
9
|
+
script = %{
|
10
|
+
(function(prefix, loader) {
|
11
|
+
var sources = %sources%;
|
12
|
+
if (!loader) loader = function(path) {
|
13
|
+
document.write('<scr' + 'ipt src="' + (prefix || '') + path + '"></script>');
|
14
|
+
}
|
15
|
+
for (var i = 0, j = sources.length; i < j; i++) loader(sources[i]);
|
16
|
+
})(window.prefix, window.loader);}.sub("%sources%", JSON.pretty_generate(paths))
|
17
|
+
end # generate_includes
|
18
|
+
end # class <<self
|
19
|
+
end # module CodeGenerator
|
20
|
+
end # module Util
|
21
|
+
end # module Jsus
|
data/lib/jsus/util/documenter.rb
CHANGED
@@ -11,6 +11,10 @@ module Jsus
|
|
11
11
|
attr_accessor :options
|
12
12
|
|
13
13
|
# Constructor. Accepts options as the argument.
|
14
|
+
# @param [Hash] options
|
15
|
+
# @option options [Boolean] :highlight_source use syntax highlighting
|
16
|
+
# (via pygments)
|
17
|
+
# @api public
|
14
18
|
def initialize(options = DEFAULT_OPTIONS)
|
15
19
|
require "murdoc"
|
16
20
|
self.options = options
|
@@ -19,6 +23,9 @@ module Jsus
|
|
19
23
|
end
|
20
24
|
|
21
25
|
# Generates documentation tree into the given directory.
|
26
|
+
#
|
27
|
+
# @param [String] output directory
|
28
|
+
# @api public
|
22
29
|
def generate(doc_dir = Dir.pwd)
|
23
30
|
#FileUtils.rm_rf(doc_dir)
|
24
31
|
FileUtils.mkdir_p(doc_dir)
|
@@ -41,6 +48,9 @@ module Jsus
|
|
41
48
|
end
|
42
49
|
|
43
50
|
# Adds a source file to the documented source tree
|
51
|
+
#
|
52
|
+
# @param [Jsus::SourceFile] pushed source
|
53
|
+
# @api public
|
44
54
|
def <<(source) # :nodoc:
|
45
55
|
filename = File.basename(source.filename)
|
46
56
|
if source.package
|
@@ -52,25 +62,34 @@ module Jsus
|
|
52
62
|
self
|
53
63
|
end
|
54
64
|
|
55
|
-
|
65
|
+
# @return [Jsus::Util::Tree] tree with all sources
|
66
|
+
# @api public
|
67
|
+
def tree
|
56
68
|
@tree ||= Tree.new
|
57
69
|
end
|
58
70
|
|
59
71
|
# Scope for documentation in pathspec format. See Jsus::Util::Tree::Node#find_children_matching
|
72
|
+
# @return [Array] scope
|
73
|
+
# @api public
|
60
74
|
def current_scope
|
61
75
|
@current_scope ||= default_scope
|
62
76
|
end
|
63
77
|
|
64
|
-
|
78
|
+
# @return [Array] default documentation scope
|
79
|
+
# @api semipublic
|
80
|
+
def default_scope
|
65
81
|
["/**/*"]
|
66
82
|
end
|
67
83
|
|
68
|
-
|
84
|
+
# @api semipublic
|
85
|
+
def current_scope=(scope)
|
69
86
|
@current_scope = scope
|
70
87
|
end
|
71
88
|
|
72
89
|
# Sets documenter to exclusive scope for documentation.
|
73
90
|
# Exclusive scope overrides all the other scopes.
|
91
|
+
# @param [Array, String] documentation scope
|
92
|
+
# @api public
|
74
93
|
def only(scope)
|
75
94
|
result = clone
|
76
95
|
result.current_scope = [scope].flatten
|
@@ -79,18 +98,24 @@ module Jsus
|
|
79
98
|
|
80
99
|
# Sets documenter to additive scope for documentation.
|
81
100
|
# Additive scopes match any of the pathspecs given
|
101
|
+
#
|
102
|
+
# @param [Array, String] documentation scope
|
103
|
+
# @api public
|
82
104
|
def or(scope)
|
83
105
|
result = clone
|
84
106
|
result.current_scope = current_scope + [scope].flatten
|
85
107
|
result
|
86
108
|
end
|
87
109
|
|
88
|
-
#
|
110
|
+
# @return [Jsus::Util::Tree] tree with documented sources only
|
111
|
+
# @api public
|
89
112
|
def documented_sources
|
90
113
|
@documented_sources ||= documented_sources!
|
91
114
|
end
|
92
115
|
|
93
|
-
|
116
|
+
# @see #documented_sources
|
117
|
+
# @api private
|
118
|
+
def documented_sources!
|
94
119
|
doctree = Tree.new
|
95
120
|
current_scope.map {|pathspec| tree.find_nodes_matching(pathspec) }.
|
96
121
|
flatten.each {|s| doctree.insert(s.full_path, s.value)}
|
@@ -99,6 +124,7 @@ module Jsus
|
|
99
124
|
|
100
125
|
protected
|
101
126
|
|
127
|
+
# @api private
|
102
128
|
def create_documentation_for_source(source, template) # :nodoc:
|
103
129
|
skipped_lines = 0
|
104
130
|
content = source.original_content.gsub(/\A\s*\/\*.*?\*\//m) {|w| skipped_lines += w.split("\n").size; "" }
|
@@ -106,13 +132,15 @@ module Jsus
|
|
106
132
|
Murdoc::Formatter.new(template).render(:paragraphs => annotator.paragraphs, :header => source.header, :source => source, :skipped_lines => skipped_lines)
|
107
133
|
end
|
108
134
|
|
135
|
+
# @api private
|
109
136
|
def create_index_for_node(node, template) # :nodoc:
|
110
137
|
Haml::Engine.new(template).render(self, :node => node)
|
111
138
|
end
|
112
139
|
|
140
|
+
# @api private
|
113
141
|
def file_from_contents(filename, contents) # :nodoc:
|
114
142
|
File.open(filename, "w+") {|f| f << contents }
|
115
143
|
end
|
116
144
|
end
|
117
145
|
end
|
118
|
-
end
|
146
|
+
end
|
data/lib/jsus/util/file_cache.rb
CHANGED
@@ -6,13 +6,18 @@ module Jsus
|
|
6
6
|
#
|
7
7
|
class FileCache
|
8
8
|
# Initializes filecache to given directory
|
9
|
+
# @param [String] output directory
|
10
|
+
# @api public
|
9
11
|
def initialize(path)
|
10
12
|
@path = path
|
11
13
|
end # initialize
|
12
14
|
|
13
15
|
# Creates a file with given value for given key in cache directory
|
14
16
|
#
|
15
|
-
#
|
17
|
+
# @param [String] key
|
18
|
+
# @param [String] value
|
19
|
+
# @return [String] actual path for stored file.
|
20
|
+
# @api public
|
16
21
|
def write(key, value)
|
17
22
|
item_path = generate_path(key)
|
18
23
|
FileUtils.mkdir_p(File.dirname(item_path))
|
@@ -20,21 +25,26 @@ module Jsus
|
|
20
25
|
item_path
|
21
26
|
end # write
|
22
27
|
|
23
|
-
#
|
24
|
-
#
|
28
|
+
# @param [String] key
|
29
|
+
# @return [String, nil] path to cached file or nil
|
30
|
+
# @api public
|
25
31
|
def read(key)
|
26
32
|
item_path = generate_path(key)
|
27
33
|
File.exists?(item_path) ? item_path : nil
|
28
34
|
end # read
|
29
35
|
alias_method :exists?, :read
|
30
36
|
|
31
|
-
#
|
32
|
-
#
|
37
|
+
# @param [String] key
|
38
|
+
# @yield block with routine to call on cache miss
|
39
|
+
# @return [String] path to stored file
|
40
|
+
# @api public
|
33
41
|
def fetch(key, &block)
|
34
42
|
read(key) || write(key, yield)
|
35
43
|
end # fetch
|
36
44
|
|
37
45
|
# Deletes cache entry for given key.
|
46
|
+
# @param [String] key
|
47
|
+
# @api public
|
38
48
|
def delete(key)
|
39
49
|
item_path = generate_path(key)
|
40
50
|
if File.exists?(item_path)
|
@@ -48,6 +58,7 @@ module Jsus
|
|
48
58
|
#
|
49
59
|
# Default strategy: append key to cache directory
|
50
60
|
# (slashes are replaced with dots)
|
61
|
+
# @api private
|
51
62
|
def generate_path(key)
|
52
63
|
key = key.gsub(File::SEPARATOR, ".")
|
53
64
|
File.join(@path, key)
|
data/lib/jsus/util/inflection.rb
CHANGED
@@ -4,31 +4,38 @@ module Jsus
|
|
4
4
|
module Inflection
|
5
5
|
class <<self
|
6
6
|
# Converts strings with various punctuation to pascal case
|
7
|
+
# @example
|
7
8
|
# hello_world => HelloWorld
|
8
9
|
# Oh.My.God => OhMyGod
|
9
10
|
# iAmCamelCase => IAmCamelCase
|
10
11
|
# some_Weird_._punctuation => SomeWeirdPunctuation
|
12
|
+
# @api public
|
11
13
|
def random_case_to_mixed_case(string)
|
12
14
|
string.split(/[^a-zA-Z]+/).map {|chunk| capitalize(chunk) }.join
|
13
15
|
end # random_case_to_mixed_case
|
14
16
|
|
15
17
|
# Same as #random_case_to_mixed_case, but preserves dots
|
16
|
-
#
|
18
|
+
# @example
|
19
|
+
# color.fx => Color.Fx
|
20
|
+
# @api public
|
17
21
|
def random_case_to_mixed_case_preserve_dots(string)
|
18
22
|
string.split(".").map {|c| random_case_to_mixed_case(c) }.join(".")
|
19
23
|
end # random_case_to_mixed_case
|
20
24
|
|
21
25
|
# Capitalizes first letter (doesn't do anything else to other letters, unlike String#capitalize)
|
26
|
+
# @api public
|
22
27
|
def capitalize(string)
|
23
28
|
string[0,1].capitalize + string[1..-1].to_s
|
24
29
|
end # capitalize
|
25
30
|
|
26
31
|
# Downcases first letter
|
32
|
+
# @api public
|
27
33
|
def decapitalize(string)
|
28
34
|
string[0,1].downcase + string[1..-1].to_s
|
29
35
|
end # decapitalize
|
30
36
|
|
31
37
|
# Translates MixedCase string to camel-case
|
38
|
+
# @api public
|
32
39
|
def snake_case(string)
|
33
40
|
decapitalize(string.gsub(/(.)([A-Z])([a-z]+)/) {|_| "#{$1}_#{$2.downcase}#{$3}"}.
|
34
41
|
gsub(/[^A-Za-z_]+/, "_"))
|
@@ -36,4 +43,4 @@ module Jsus
|
|
36
43
|
end # class <<self
|
37
44
|
end # module Inflection
|
38
45
|
end # module Util
|
39
|
-
end # module Jsus
|
46
|
+
end # module Jsus
|