doc 0.0.0.0 → 0.0.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.
@@ -0,0 +1,11 @@
1
+ module Doc
2
+ class ConfigError < Exception
3
+ def initialize(object, message)
4
+ super("#{object.class.name}: #{message}").tap do |e|
5
+ if Exception === message
6
+ e.set_backtrace(message.backtrace)
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,80 @@
1
+ module Doc
2
+ class ConfigObject
3
+ def initialize(default_key, *arguments, &block)
4
+ @hash = {}
5
+ arguments = arguments.dup
6
+ if arguments.last.is_a?(Hash)
7
+ @hash.merge!(arguments.pop)
8
+ end
9
+ unless arguments.empty?
10
+ @hash[default_key] = arguments
11
+ end
12
+
13
+ block.call(self) if block
14
+ end
15
+
16
+ def [](key)
17
+ @hash[key]
18
+ end
19
+
20
+ def []=(key, value)
21
+ @hash[key] = value
22
+ end
23
+
24
+ def keys
25
+ @hash.keys
26
+ end
27
+
28
+ def method_missing(method, *arguments)
29
+ case method.to_s
30
+ when /\!$/
31
+ check_argument_count arguments, 0
32
+ @hash[$`.to_sym] = true
33
+ when /\?$/
34
+ check_argument_count arguments, 0
35
+ @hash[$`.to_sym] && true
36
+ else
37
+ if arguments.empty?
38
+ @hash[method]
39
+ else
40
+ check_argument_count arguments, 1
41
+ @hash[method] = arguments.first
42
+ end
43
+ end
44
+ end
45
+
46
+ def check_options!(required_keys, optional_keys)
47
+ errors = []
48
+
49
+ unless (missing_keys = required_keys - keys).empty?
50
+ errors << "missing required keys: #{missing_keys.join(', ')}"
51
+ end
52
+
53
+ left_keys = keys - required_keys
54
+ optional_keys.each do |key_group|
55
+ key_group = Array(key_group)
56
+ if key_group.length > 1
57
+ if (clashing_keys = keys & key_group).length > 1
58
+ errors << "clash of mutually exclusive keys: #{clashing_keys.join(', ')}"
59
+ end
60
+ end
61
+ left_keys -= key_group
62
+ end
63
+ unless left_keys.empty?
64
+ errors << "unknown keys: #{left_keys.join(', ')}"
65
+ end
66
+
67
+ unless errors.empty?
68
+ raise ConfigError.new(self, errors.join('; '))
69
+ end
70
+ end
71
+
72
+ private
73
+
74
+ def check_argument_count(arguments, accepts)
75
+ if arguments.length != accepts
76
+ raise ArgumentError.new("wrong number of arguments (#{arguments.length} for #{accepts})")
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,38 @@
1
+ module Doc
2
+ class Configurator
3
+ class << self
4
+ def inherited(subclass)
5
+ RootConfig.configurator subclass.name.underscore.split('/').last, subclass
6
+ end
7
+
8
+ def default_config_key(value = nil)
9
+ @default_config_key = value.to_sym if value
10
+ @default_config_key || :default
11
+ end
12
+ end
13
+
14
+ attr_reader :documentor, :config
15
+ def initialize(documentor, *arguments, &block)
16
+ @documentor = documentor
17
+ @config = ConfigObject.new(self.class.default_config_key, *arguments, &block)
18
+ end
19
+
20
+ abstract_method :configure, :tasks
21
+
22
+ private
23
+
24
+ PARSABLE_EXTENSIONS_GLOB = "{#{%w[rb c m C M cc CC mm MM c++ cxx cpp h H hh HH hm h++ hpp hxx y].join(',')}}"
25
+
26
+ def sources_dir
27
+ documentor.sources_dir.tap(&:mkpath)
28
+ end
29
+
30
+ def builder(options)
31
+ Builder.new(documentor, options)
32
+ end
33
+
34
+ def merger(options)
35
+ Merger.new(documentor, options)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,70 @@
1
+ module Doc
2
+ class Configurator
3
+ class Gems < Configurator
4
+ default_config_key :only
5
+
6
+ def configure(update)
7
+ config.check_options!([], [[:only, :except], :versions, :prerelease])
8
+
9
+ [:only, :except].each do |key|
10
+ config[key] = Array(config[key]).map(&:to_s) if config[key]
11
+ end
12
+
13
+ @prerelease = !!config[:prerelease]
14
+ @specs = config[:versions] && config[:versions].to_sym == :all ? all_specs(@prerelease) : latest_specs(@prerelease)
15
+
16
+ if config[:only]
17
+ absent = config[:only] - @specs.map(&:name)
18
+ unless absent.empty?
19
+ raise ConfigError.new(self, "can't find gems: #{absent.join(', ')}")
20
+ end
21
+ end
22
+
23
+ if config[:only]
24
+ @specs = @specs.select{ |spec| config[:only].include?(spec.name) }
25
+ elsif config[:except]
26
+ @specs = @specs.reject{ |spec| config[:except].include?(spec.name) }
27
+ end
28
+ @specs = @specs.sort_by{ |spec| [spec.name.downcase, spec.sort_obj] }
29
+ end
30
+
31
+ def tasks
32
+ @specs.map do |spec|
33
+ main = spec.rdoc_options.each_cons(2).select{ |key, value| %w[--main -m].include?(key) }.map(&:last).first
34
+ Dir.chdir(spec.full_gem_path) do
35
+ file_list = FileList.new
36
+ file_list.include *spec.extra_rdoc_files
37
+ file_list.include *spec.require_paths
38
+
39
+ builder({
40
+ :title => "gem #{spec.full_name}",
41
+ :source_dir => spec.full_gem_path,
42
+ :dir_name => "gem.#{spec.full_name}",
43
+ :paths => file_list,
44
+ :main => main,
45
+ })
46
+ end
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def latest_specs(prerelease)
53
+ Gem::Specification.latest_specs(prerelease)
54
+ end
55
+
56
+ def all_specs(prerelease)
57
+ if prerelease
58
+ Gem::Specification.to_a
59
+ else
60
+ Gem::Specification.select{ |spec| !spec.version.prerelease? }
61
+ end
62
+ end
63
+
64
+ def sort_specs(specs)
65
+ specs.sort_by{ |spec| [spec.name.downcase, spec.name, spec.version] }
66
+ end
67
+ end
68
+ RootConfig.send(:alias_method, :gem, :gems)
69
+ end
70
+ end
@@ -0,0 +1,90 @@
1
+ require 'digest'
2
+
3
+ module Doc
4
+ class Configurator
5
+ class Paths < Configurator
6
+ default_config_key :glob
7
+
8
+ def configure(update)
9
+ config.check_options!([], [:glob, :main, :file_list, :title])
10
+
11
+ @path_pairs = []
12
+ Array(config[:glob]).map do |glob|
13
+ if glob[0, 1] == '~' && (parts = glob.split(File::SEPARATOR, 2)).length == 2
14
+ FSPath(glob).expand_path.glob.map do |path|
15
+ unexpanded_part = FSPath(parts[0])
16
+ @path_pairs << [path, unexpanded_part / path.relative_path_from(unexpanded_part.expand_path)]
17
+ end
18
+ else
19
+ @path_pairs.concat(FSPath(glob).glob)
20
+ end
21
+ end.flatten
22
+
23
+ if @path_pairs.empty?
24
+ raise ConfigError.new(self, "expanding #{config[:glob].join(', ')} gave empty list")
25
+ end
26
+
27
+ @main = Array(config[:main])
28
+
29
+ if config[:file_list]
30
+ @file_list = config[:file_list]
31
+ case @file_list
32
+ when Proc
33
+ if @file_list.arity != 1
34
+ raise ConfigError.new(self, "proc should have on parameter for instance of FileList")
35
+ end
36
+ when Array
37
+ unless @file_list.all?{ |rule| rule =~ /^\+|-/ }
38
+ raise ConfigError.new(self, "all rules must start with + or -")
39
+ end
40
+ else
41
+ raise ConfigError.new(self, "file_list should be either array in form %w[+a/* -b/*] or proc receiving instance of FileList")
42
+ end
43
+ end
44
+
45
+ if @title = config[:title]
46
+ unless @title.is_a?(Proc) && @title.arity == 1
47
+ raise ConfigError.new(self, "title should be an instance of Proc receiving one argument (path)")
48
+ end
49
+ end
50
+ end
51
+
52
+ def tasks
53
+ @path_pairs.map do |pair|
54
+ path, unexpanded_path = pair
55
+ unexpanded_path ||= path
56
+ Dir.chdir(path) do
57
+ paths = nil
58
+ if @file_list
59
+ file_list = FileList.new
60
+ case @file_list
61
+ when Proc
62
+ @file_list.call(file_list)
63
+ when Array
64
+ @file_list.each do |rule|
65
+ file_list.send(rule[0, 1] == '+' ? :include : :exclude, rule[1..-1])
66
+ end
67
+ end
68
+ end
69
+
70
+ main = nil
71
+ if @main
72
+ @main.each do |main|
73
+ break if main = Dir[main].first
74
+ end
75
+ end
76
+
77
+ builder({
78
+ :title => @title ? @title[unexpanded_path].to_s : "path #{unexpanded_path}",
79
+ :source_dir => path,
80
+ :dir_name => "path.#{unexpanded_path.to_s.gsub('_', '').gsub('/', '_').gsub(/[^a-z0-9\-_]/i, '-')}.#{Digest::SHA1.hexdigest(path.to_s)}",
81
+ :paths => file_list,
82
+ :main => main,
83
+ })
84
+ end
85
+ end
86
+ end
87
+ end
88
+ RootConfig.send(:alias_method, :path, :paths)
89
+ end
90
+ end
@@ -0,0 +1,98 @@
1
+ require 'shellwords'
2
+
3
+ module Doc
4
+ class Configurator
5
+ class Rails < Configurator
6
+ default_config_key :version
7
+
8
+ def configure(update)
9
+ config.check_options!([], [:version, :prerelease])
10
+
11
+ search_versions = Array(config[:version] || [nil])
12
+ @versions = search_versions.map do |search_version|
13
+ requirement = Gem::Requirement.new(search_version.is_a?(Integer) ? "~> #{search_version}" : search_version)
14
+ versions = Gem::Specification.find_all_by_name('rails', requirement).map(&:version)
15
+ versions.reject!(&:prerelease?) unless config[:prerelease]
16
+ unless version = versions.sort.last
17
+ raise ConfigError.new(self, "can't find rails version matching: #{search_version}")
18
+ end
19
+ version
20
+ end
21
+ end
22
+
23
+ def tasks
24
+ @versions.map do |version|
25
+ builder({
26
+ :title => "rails-#{version}",
27
+ :dir_name => "rails-#{version}",
28
+ :paths => paths_to_document_for_version(version),
29
+ })
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def version_less_than_3?(version)
36
+ version.segments.first < 3
37
+ end
38
+
39
+ def paths_to_document_for_version(version)
40
+ code = if version_less_than_3?(version)
41
+ <<-RUBY
42
+ require 'rake/rdoctask'
43
+ gem 'rails', ARGV.first
44
+
45
+ Rake::FileList.class_eval do
46
+ alias_method :original_include, :include
47
+ def include(*paths, &block)
48
+ original_include(*fix_paths(*paths), &block)
49
+ end
50
+
51
+ alias_method :original_exclude, :exclude
52
+ def exclude(*paths, &block)
53
+ original_exclude(*fix_paths(*paths), &block)
54
+ end
55
+
56
+ def fix_paths(*paths)
57
+ paths.map do |path|
58
+ if path.is_a?(String)
59
+ path.sub(%r{^vendor\/rails\/([^\/]+)(?=\/)}) do
60
+ name = {'railties' => 'rails'}[$1] || $1
61
+ Gem.loaded_specs[name].full_gem_path
62
+ end
63
+ else
64
+ path
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ Rake::RDocTask.class_eval do
71
+ def define
72
+ puts rdoc_files if name == 'rails'
73
+ end
74
+ end
75
+
76
+ load 'tasks/documentation.rake'
77
+ RUBY
78
+ else
79
+ <<-RUBY
80
+ require 'rake/rdoctask'
81
+ gem 'rails', ARGV.first
82
+
83
+ class RDocTaskWithoutDescriptions < Rake::RDocTask
84
+ def initialize(name = :rdoc)
85
+ super
86
+ puts rdoc_files if name == 'rails'
87
+ end
88
+ end
89
+
90
+ load 'rails/tasks/documentation.rake'
91
+ RUBY
92
+ end
93
+ args = %W[ruby -r rubygems -e #{code} -- #{version}]
94
+ IO.popen(args.shelljoin, &:readlines).map(&:strip)
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,167 @@
1
+ module Doc
2
+ class Configurator
3
+ class Ruby < Doc::Configurator
4
+ smart_autoload :PathInfo, :VersionSpecifier, :Source, :Stdlib
5
+ include Source, Stdlib
6
+
7
+ default_config_key :binary
8
+
9
+ def configure(update)
10
+ config.check_options!([], [[:source, :archive, :version, :binary], :format, :except, :index])
11
+
12
+ @source_dirs = case
13
+ when config[:source]
14
+ Array(config[:source]).map{ |source| from_dir(source) }
15
+ when config[:archive]
16
+ Array(config[:archive]).map{ |archive| from_archive(archive) }
17
+ when config[:version]
18
+ Array(config[:version]).map{ |version| by_version(version, update) }
19
+ else
20
+ Array(config[:binary] || [nil]).map{ |binary| by_binary(binary, update) }
21
+ end
22
+
23
+ @format = (config[:format] || :all).to_sym
24
+ unless avaliable_formats.include?(@format)
25
+ raise "format can be one of: #{avaliable_formats.join(', ')}"
26
+ end
27
+ if [:separate, :integrate].include?(@format)
28
+ @stdlib_config = stdlib_config(update) or raise 'can\'t get stdlib config'
29
+ end
30
+
31
+ @except_regexp = /^(?:lib|ext)\/(?:#{Array(config[:except]).map(&Regexp.method(:escape)).join('|')})(?:.rb$|\/)/
32
+
33
+ if config[:index]
34
+ @index = FSPath(config[:index])
35
+ unless @index.directory? && (@index / 'index.html').file?
36
+ raise 'index should be a path to directory with index.html inside'
37
+ end
38
+ end
39
+ rescue => e
40
+ raise ConfigError.new(self, e)
41
+ end
42
+
43
+ def avaliable_formats
44
+ @avaliable_formats ||= methods.map{ |m| m[/^tasks_(.*)$/, 1] }.compact.map(&:to_sym)
45
+ end
46
+
47
+ def tasks
48
+ @source_dirs.map do |source_dir|
49
+ source_dir.touch
50
+ Dir.chdir(source_dir) do
51
+ send("tasks_#@format", source_dir)
52
+ end
53
+ end
54
+ end
55
+
56
+ def tasks_all(source_dir)
57
+ file_list = FileList.new
58
+ file_list.include(*%w[NEWS LEGAL COPYING GPL LGPL])
59
+ file_list.include("*.#{PARSABLE_EXTENSIONS_GLOB}")
60
+ file_list.include("{lib,ext}/**/*.#{PARSABLE_EXTENSIONS_GLOB}")
61
+ file_list.exclude @except_regexp
62
+
63
+ builder({
64
+ :title => source_dir.basename.to_s,
65
+ :source_dir => source_dir,
66
+ :dir_name => source_dir.basename.to_s,
67
+ :paths => file_list,
68
+ :index => @index,
69
+ })
70
+ end
71
+
72
+ def tasks_separate(source_dir)
73
+ tasks = []
74
+
75
+ core_paths_a = core_paths.to_a
76
+
77
+ file_list = FileList.new
78
+ file_list.add(*core_paths_a)
79
+ file_list.exclude @except_regexp
80
+
81
+ tasks << builder({
82
+ :title => "#{source_dir.basename} core",
83
+ :source_dir => source_dir,
84
+ :dir_name => "#{source_dir.basename}_core",
85
+ :paths => file_list,
86
+ :index => @index,
87
+ })
88
+
89
+ stdlib_tasks = []
90
+ @stdlib_config['targets'].each do |target|
91
+ name = target['target']
92
+ file_list = FileList.new
93
+ file_list.add(*stdlib_paths_for_target(name) - core_paths_a)
94
+ file_list.exclude @except_regexp
95
+
96
+ unless file_list.empty?
97
+ stdlib_tasks << builder({
98
+ :title => name,
99
+ :source_dir => source_dir,
100
+ :dir_name => "#{source_dir.basename}_#{name.gsub(/[^a-z0-9\-_]/i, '-')}",
101
+ :paths => file_list,
102
+ :main => target['mainpage'],
103
+ :no_auto_add_paths => true,
104
+ })
105
+ end
106
+ end
107
+
108
+ tasks << merger({
109
+ :title => "#{source_dir.basename} stdlib",
110
+ :dir_name => "#{source_dir.basename}_stdlib",
111
+ :tasks => stdlib_tasks
112
+ })
113
+
114
+ tasks
115
+ end
116
+
117
+ def tasks_integrate(source_dir)
118
+ file_list = FileList.new
119
+ file_list.add(core_paths)
120
+ @stdlib_config['targets'].each do |target|
121
+ file_list.add(stdlib_paths_for_target(target['target']))
122
+ end
123
+ file_list.exclude @except_regexp
124
+ builder({
125
+ :title => "#{source_dir.basename} +stdlib",
126
+ :source_dir => source_dir,
127
+ :dir_name => "#{source_dir.basename}_with_stdlib",
128
+ :paths => file_list,
129
+ :index => @index,
130
+ })
131
+ end
132
+
133
+ private
134
+
135
+ def core_paths(dir = nil)
136
+ file_list = []
137
+
138
+ dot_document_path = FSPath('.document')
139
+ dot_document_path = dir / dot_document_path if dir
140
+
141
+ if dot_document_path.exist?
142
+ dot_document_path.readlines.map(&:strip).reject{ |line| line.empty? || line[0] == ?# }
143
+ else
144
+ ['*']
145
+ end.each do |glob|
146
+ FSPath.glob(dir ? dir / glob : glob) do |path|
147
+ if path.directory?
148
+ file_list.concat(core_paths(path))
149
+ else
150
+ file_list << path.to_s
151
+ end
152
+ end
153
+ end
154
+
155
+ file_list
156
+ end
157
+
158
+ def stdlib_paths_for_target(name)
159
+ file_list = FileList.new
160
+ file_list.include("{lib,ext}/#{name}{,/**/*}.#{PARSABLE_EXTENSIONS_GLOB}")
161
+ file_list.include("{lib,ext}/#{name}/**/README*")
162
+ file_list.exclude(%r{/extconf.rb$|/test/(?!unit)|/tests/|/sample|/demo/})
163
+ file_list.to_a
164
+ end
165
+ end
166
+ end
167
+ end