doc 0.0.0.0 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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