jsus 0.2.5 → 0.2.6

Sign up to get free protection for your applications and to get access to all the features.
data/lib/jsus/tag.rb CHANGED
@@ -1,8 +1,8 @@
1
- #
2
- # Tag is basically just a string that contains a package name and a name for class
3
- # (or not necessarily a class) which the given SourceFile provides/requires/extends.
4
- #
5
1
  module Jsus
2
+ #
3
+ # Tag is basically just a string that contains a package name and a name for class
4
+ # (or not necessarily a class) which the given SourceFile provides/requires/extends/replaces.
5
+ #
6
6
  class Tag
7
7
  attr_accessor :package, :external # :nodoc:
8
8
 
@@ -57,7 +57,7 @@ module Jsus
57
57
  #
58
58
  # Returns a well-formed name for the tag.
59
59
  # Options:
60
- # * +:short:+ whether the tag should try using short form
60
+ # * +:short:+ -- whether the tag should try using short form
61
61
  #
62
62
  # Important note: only non-external tags support short forms.
63
63
  #
data/lib/jsus/util.rb ADDED
@@ -0,0 +1,7 @@
1
+ module Jsus
2
+ module Util
3
+ autoload :Tree, 'jsus/util/tree'
4
+ autoload :Documenter, 'jsus/util/documenter'
5
+ autoload :Validator, 'jsus/util/validator'
6
+ end # Util
7
+ end # Jsus
@@ -0,0 +1,118 @@
1
+ module Jsus
2
+ module Util
3
+ # Very opinionated documenter class. It uses Murdoc to generate the
4
+ # documentation using the template and stylesheet bundled with the
5
+ # jsus gem file. Also generates indices for navigation.
6
+ class Documenter
7
+ # Default documenter options
8
+ DEFAULT_OPTIONS = {:highlight_source => true}
9
+
10
+ # Documenter options
11
+ attr_accessor :options
12
+
13
+ # Constructor. Accepts options as the argument.
14
+ def initialize(options = DEFAULT_OPTIONS)
15
+ require "murdoc"
16
+ self.options = options
17
+ rescue LoadError
18
+ raise "You should install murdoc gem in order to produce documentation"
19
+ end
20
+
21
+ # Generates documentation tree into the given directory.
22
+ def generate(doc_dir = Dir.pwd)
23
+ #FileUtils.rm_rf(doc_dir)
24
+ FileUtils.mkdir_p(doc_dir)
25
+ template_path = File.dirname(__FILE__) + "/../../../markup"
26
+ template = File.read("#{template_path}/template.haml")
27
+ index_template = File.read("#{template_path}/index_template.haml")
28
+ stylesheet_path = "#{template_path}/stylesheet.css"
29
+ documented_sources.traverse(true) do |node|
30
+ if node.value # leaf
31
+ dir = doc_dir + File.dirname(node.full_path)
32
+ FileUtils.mkdir_p(dir)
33
+ file_from_contents(dir + "/#{node.name}.html", create_documentation_for_source(node.value, template))
34
+ else
35
+ dir = doc_dir + node.full_path
36
+ FileUtils.mkdir_p(dir)
37
+ FileUtils.cp(stylesheet_path, dir)
38
+ file_from_contents(dir + "/index.html", create_index_for_node(node, index_template))
39
+ end
40
+ end
41
+ end
42
+
43
+ # Adds a source file to the documented source tree
44
+ def <<(source) # :nodoc:
45
+ filename = File.basename(source.filename)
46
+ if source.package
47
+
48
+ tree["#{source.package.name}/#{filename}"] = source
49
+ else
50
+ tree["#{filename}"] = source
51
+ end
52
+ self
53
+ end
54
+
55
+ def tree # :nodoc:
56
+ @tree ||= Tree.new
57
+ end
58
+
59
+ # Scope for documentation in pathspec format. See Jsus::Util::Tree::Node#find_children_matching
60
+ def current_scope
61
+ @current_scope ||= default_scope
62
+ end
63
+
64
+ def default_scope # :nodoc:
65
+ ["/**/*"]
66
+ end
67
+
68
+ def current_scope=(scope) # :nodoc:
69
+ @current_scope = scope
70
+ end
71
+
72
+ # Sets documenter to exclusive scope for documentation.
73
+ # Exclusive scope overrides all the other scopes.
74
+ def only(scope)
75
+ result = clone
76
+ result.current_scope = [scope].flatten
77
+ result
78
+ end
79
+
80
+ # Sets documenter to additive scope for documentation.
81
+ # Additive scopes match any of the pathspecs given
82
+ def or(scope)
83
+ result = clone
84
+ result.current_scope = current_scope + [scope].flatten
85
+ result
86
+ end
87
+
88
+ # Returns the tree with documented sources.
89
+ def documented_sources
90
+ @documented_sources ||= documented_sources!
91
+ end
92
+
93
+ def documented_sources! # :nodoc:
94
+ doctree = Tree.new
95
+ current_scope.map {|pathspec| tree.find_nodes_matching(pathspec) }.
96
+ flatten.each {|s| doctree.insert(s.full_path, s.value)}
97
+ doctree
98
+ end
99
+
100
+ protected
101
+
102
+ def create_documentation_for_source(source, template) # :nodoc:
103
+ skipped_lines = 0
104
+ content = source.original_content.gsub(/\A\s*\/\*.*?\*\//m) {|w| skipped_lines += w.split("\n").size; "" }
105
+ annotator = Murdoc::Annotator.new(content, :javascript, options)
106
+ Murdoc::Formatter.new(template).render(:paragraphs => annotator.paragraphs, :header => source.header, :source => source, :skipped_lines => skipped_lines)
107
+ end
108
+
109
+ def create_index_for_node(node, template) # :nodoc:
110
+ Haml::Engine.new(template).render(self, :node => node)
111
+ end
112
+
113
+ def file_from_contents(filename, contents) # :nodoc:
114
+ File.open(filename, "w+") {|f| f << contents }
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,201 @@
1
+ module Jsus
2
+ module Util
3
+ #
4
+ # Jsus::Tree is a basic hierarchical tree structure class
5
+ # What it does, basically, is maintaining hierarchical filesystem-like
6
+ # structure (with node names like /namespace/inner/item) and supporting
7
+ # lookups via #glob method.
8
+ #
9
+ # Example:
10
+ #
11
+ # tree = Jsus::Tree.new
12
+ # tree["/folder/item_0"] = "Hello"
13
+ # tree["/folder/item_1"] = "World"
14
+ # tree["/other/soul"] = "Empty"
15
+ # tree.glob("/*") # => 3 Jsus::Node-s (`root`, `folder`, `other`)
16
+ # tree.glob("/**/*") # => 6 Jsus::Node-s (`root`, `folder`, `other`, `item_0`, `item_1`, `soul`)
17
+ # tree["/something"] # => nil
18
+ # tree["/folder/item_1"] # => Jsus::Node
19
+ # tree["/other/soul"] = nil
20
+ # tree.leaves(true) # => 2 Jsus::Node-s (no `soul` node)
21
+ # tree.leaves(false) # => 3 Jsus::Node-s
22
+ #
23
+ class Tree
24
+ PATH_SEPARATOR = "/"
25
+
26
+
27
+ class <<self
28
+ # Utility functions
29
+
30
+ # Splits path into components
31
+ # Jsus::Tree.components_from_path("/hello/world") # => ["hello", "world"]
32
+ def components_from_path(path)
33
+ raise "Empty path given: #{path.inspect}" if !path || path == ""
34
+ path = path.to_s.dup
35
+ path.split(PATH_SEPARATOR).reject {|comp| comp == "" }
36
+ end
37
+ alias_method :get_path_components, :components_from_path
38
+
39
+ # Joins components into a pathspec
40
+ # Jsus::Tree.path_from_components(["hello", "world"]) # => "/hello/world"
41
+ def path_from_components(components)
42
+ "#{PATH_SEPARATOR}#{components.join(PATH_SEPARATOR)}"
43
+ end
44
+ end
45
+
46
+ #
47
+ # Jsus::Tree node class.
48
+ # Most of the time you only need to extract #value from the node,
49
+ # although sometimes you might need to refer to #parent node and #full_path
50
+ #
51
+ class Node
52
+ # Contains assigned value
53
+ attr_accessor :value
54
+ # Contains reference to parent node, nil for root node
55
+ attr_accessor :parent
56
+ # Contains array with path components
57
+ attr_accessor :path_components
58
+
59
+ # Initializes full path and value for the node
60
+ def initialize(full_path, value = nil)
61
+ self.full_path = full_path
62
+ self.value = value
63
+ end
64
+
65
+ # Contains full path to the node, such as '/hello/world'
66
+ attr_reader :full_path
67
+ # Contains node basename, such as 'world' for '/hello/world'
68
+ attr_reader :name
69
+ # Assigns node's full path and automatically deduces path components,
70
+ # basename etc.
71
+ def full_path=(full_path)
72
+ @full_path = full_path
73
+ @path_components = Tree.get_path_components(full_path)
74
+ @name = @path_components[-1]
75
+ end
76
+
77
+ # Returns node's direct descendants
78
+ def children
79
+ @children ||= []
80
+ end
81
+
82
+ # Finds a node child by basename
83
+ def find_child(name)
84
+ children.detect {|child| child.name == name }
85
+ end
86
+
87
+ # Creates a child with given name and value
88
+ def create_child(name, value = nil)
89
+ full_path = Tree.path_from_components(path_components + [name])
90
+ node = Node.new(full_path, value)
91
+ children << node
92
+ node.parent = self
93
+ node
94
+ end
95
+
96
+ # Finds a child with given name or creates a child with given name and
97
+ # value
98
+ def find_or_create_child(name, value = nil)
99
+ find_child(name) || create_child(name, value)
100
+ end
101
+
102
+ # Finds children matching the given pathspec
103
+ # Pathspec format:
104
+ # '**' -- this node and all the children nodes that have children
105
+ # 'smth*' -- nodes beginning with smth
106
+ # 'smth*else' -- nodes beginning with smth and ending with else
107
+ # <string without asterisks> -- plain node lookup by name
108
+ # Returns array with search results
109
+ def find_children_matching(pathspec)
110
+ case pathspec
111
+ when "**"
112
+ [self] + children.select {|child| child.has_children? }
113
+ when /\*/
114
+ regexp = Regexp.new("^" + Regexp.escape(pathspec).gsub("\\*", ".*") + "$")
115
+ children.select {|child| !child.has_children? && child.name =~ regexp }
116
+ else
117
+ [find_child(pathspec)].compact
118
+ end
119
+ end
120
+
121
+ # Returns whether this node has children
122
+ def has_children?
123
+ !children.empty?
124
+ end
125
+ end
126
+
127
+ # Root node of the tree
128
+ def root
129
+ @root ||= Node.new("/", nil)
130
+ end
131
+
132
+ # Looks up a node by direct path. Does not support wildcards
133
+ def lookup(path)
134
+ path_components = self.class.get_path_components(path)
135
+ path_components.inject(root) do |result, component|
136
+ if result
137
+ result.find_child(component)
138
+ end
139
+ end
140
+ end
141
+
142
+ def [](path)
143
+ node = lookup(path)
144
+ node ? node.value : nil
145
+ end
146
+
147
+
148
+
149
+ # Searches for nodes by a given pathspec
150
+ # See Jsus::Util::Tree::Node#find_children_matching for more details
151
+ def find_nodes_matching(pathspec)
152
+ self.class.get_path_components(pathspec).inject([root]) do |nodes, component|
153
+ nodes.map {|node| node.find_children_matching(component) }.flatten
154
+ end
155
+ end
156
+
157
+ # Returns values for nodes matching given pathspec
158
+ # See Jsus::Util::Tree::Node#find_children_matching for more details
159
+ def glob(pathspec)
160
+ find_nodes_matching(pathspec).map {|node| node.value }
161
+ end
162
+
163
+ # Inserts a node with given value into the tree
164
+ def insert(full_path, value = nil)
165
+ node = create_all_nodes_if_needed(full_path)
166
+ node.value = value
167
+ node
168
+ end
169
+ alias_method :[]=, :insert
170
+
171
+ # Traverses the tree.
172
+ # When given true as the argument, traverses all nodes.
173
+ # Otherwise, only leaves.
174
+ def traverse(all_nodes = false)
175
+ node_list = [root]
176
+ while !node_list.empty?
177
+ node = node_list.shift
178
+ yield node if all_nodes || !node.has_children?
179
+ node.children.each {|child| node_list << child }
180
+ end
181
+ end
182
+
183
+ # Returns a list of leaves.
184
+ # Returns only leaves with set values by default
185
+ def leaves(only_with_value = true)
186
+ result = []
187
+ traverse {|node| result << node if !only_with_value || node.value }
188
+ result
189
+ end
190
+
191
+ protected
192
+
193
+ def create_all_nodes_if_needed(full_path) # :nodoc:
194
+ self.class.get_path_components(full_path).inject(root) do |result, component|
195
+ result.find_or_create_child(component)
196
+ end
197
+ end
198
+
199
+ end
200
+ end
201
+ end
@@ -0,0 +1,8 @@
1
+ module Jsus
2
+ module Util
3
+ module Validator
4
+ autoload :Base, 'jsus/util/validator/base'
5
+ autoload :Mooforge, 'jsus/util/validator/mooforge'
6
+ end # Validator
7
+ end # Util
8
+ end # Jsus
@@ -0,0 +1,48 @@
1
+ module Jsus
2
+ module Util
3
+ # Base for any validator class.
4
+ module Validator
5
+ class Base
6
+ # Constructor accepts pool or array or container and adds every file
7
+ # to its source files set.
8
+ def initialize(pool_or_array_or_container)
9
+ self.source_files = pool_or_array_or_container
10
+ end
11
+
12
+ # Returns source files for validation
13
+ def source_files
14
+ @source_files ||= []
15
+ end
16
+ alias_method :sources, :source_files
17
+
18
+ # Sets source files for validation
19
+ def source_files=(pool_or_array_or_container)
20
+ case pool_or_array_or_container
21
+ when Pool
22
+ @source_files = pool_or_array_or_container.sources.to_a
23
+ when Array
24
+ @source_files = pool_or_array_or_container
25
+ when Container
26
+ @source_files = pool_or_array_or_container.to_a
27
+ end
28
+ end
29
+ alias_method :sources=, :source_files=
30
+
31
+ # Returns whether or not given sources conform to given set of rules
32
+ def validate
33
+ validation_errors.empty?
34
+ end
35
+
36
+ # List of validation errors, override this method on descendant classes.
37
+ def validation_errors
38
+ []
39
+ end
40
+
41
+ # Shortcut for creating and validating a list of items
42
+ def self.validate(*args)
43
+ new(*args).validate
44
+ end
45
+ end
46
+ end
47
+ end # Util
48
+ end
@@ -0,0 +1,25 @@
1
+ module Jsus
2
+ module Util
3
+ module Validator
4
+ # Mooforge validator checks every file for the following:
5
+ # * Presence of header
6
+ # * Presence of authors field
7
+ # * Presence of license field
8
+ class Mooforge < Base
9
+ def validation_errors # :nodoc:
10
+ @validation_errors ||= sources.inject([]) do |result, sf|
11
+ if !sf.header
12
+ result << "#{sf.filename} is missing header"
13
+ elsif !sf.header["authors"]
14
+ result << "#{sf.filename} is missing authors"
15
+ elsif !sf.header["license"]
16
+ result << "#{sf.filename} is missing license"
17
+ else
18
+ result
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end # Util
25
+ end
@@ -147,7 +147,7 @@ describe Jsus::Pool do
147
147
  subject { Jsus::Pool.new(input_dir) }
148
148
 
149
149
  it "should return a tree with all the source elements in it" do
150
- subject.source_tree["/Core/Class.js"].value.should be_a(Jsus::SourceFile)
150
+ subject.source_tree["/Core/Class.js"].should be_a(Jsus::SourceFile)
151
151
  end
152
152
 
153
153
  it "should not choke when sources got no referenced package" do
@@ -161,11 +161,11 @@ describe Jsus::Pool do
161
161
  subject { Jsus::Pool.new(input_dir) }
162
162
 
163
163
  it "should return a tree with all the source elements in it" do
164
- subject.provides_tree.glob("/Core/Class")[0].value.should == Jsus::Tag["Core/Class"]
164
+ subject.provides_tree.glob("/Core/Class")[0].should == Jsus::Tag["Core/Class"]
165
165
  end
166
166
 
167
167
  it "should allow wildcards" do
168
- subject.provides_tree.glob("/Core/*")[0].value.should == Jsus::Tag["Core/Class"]
168
+ subject.provides_tree.glob("/Core/*")[0].should == Jsus::Tag["Core/Class"]
169
169
  end
170
170
  end
171
171