jsus 0.2.5 → 0.2.6

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/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