active_doc 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/Gemfile +6 -0
- data/History.txt +38 -0
- data/LICENSE +22 -0
- data/README.rdoc +202 -0
- data/active_doc.gemspec +32 -0
- data/lib/active_doc.rb +83 -0
- data/lib/active_doc/described_method.rb +38 -0
- data/lib/active_doc/descriptions.rb +1 -0
- data/lib/active_doc/descriptions/method_argument_description.rb +426 -0
- data/lib/active_doc/rake/task.rb +48 -0
- data/lib/active_doc/rdoc_generator.rb +21 -0
- data/spec/active_doc/method_argument_description_spec.rb +341 -0
- data/spec/active_doc/rdoc_generator_spec.rb +85 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/support/documented_class.rb +53 -0
- metadata +116 -0
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'active_doc'
|
2
|
+
|
3
|
+
module ActiveDoc
|
4
|
+
module Rake
|
5
|
+
# Defines a Rake tasks for generating RDoc documentation from active_doc.
|
6
|
+
# User proc to load files you want to generate RDoc for.
|
7
|
+
#
|
8
|
+
# The simplest use of it goes something like:
|
9
|
+
#
|
10
|
+
# ActiveDoc::Rake::Task.new do
|
11
|
+
# require 'file_one.rb' # files you want to describe
|
12
|
+
# require 'file_two.rb'
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
#
|
16
|
+
# This will define a task named <tt>active_doc</tt> described as 'Generate ActiveDoc RDoc documentation'.
|
17
|
+
class Task
|
18
|
+
class GenerateRDocRunner #:nodoc:
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
end
|
22
|
+
|
23
|
+
def run
|
24
|
+
ActiveDoc::RdocGenerator.write_rdoc do |file, documented_methods|
|
25
|
+
STDOUT.puts "Generating RDoc for #{file}..."
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize(task_name = "active_doc", desc = "Generate ActiveDoc RDoc documentation")
|
31
|
+
@task_name, @desc = task_name, desc
|
32
|
+
yield self if block_given?
|
33
|
+
define_task
|
34
|
+
end
|
35
|
+
|
36
|
+
def define_task #:nodoc:
|
37
|
+
desc @desc
|
38
|
+
task @task_name do
|
39
|
+
runner.run
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def runner #:nodoc:
|
44
|
+
GenerateRDocRunner.new
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module ActiveDoc
|
2
|
+
class RdocGenerator
|
3
|
+
def self.for_method(base, method_name)
|
4
|
+
if documented_method = ActiveDoc.documented_method(base, method_name)
|
5
|
+
documented_method.to_rdoc
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.write_rdoc(source_file_path = nil)
|
10
|
+
files_and_methods = ActiveDoc.documented_methods.sort_by { |x| x.origin_line }.group_by { |x| x.origin_file }
|
11
|
+
files_and_methods.delete_if { |file| file != source_file_path } if source_file_path
|
12
|
+
files_and_methods.each do |origin_file, documented_methods|
|
13
|
+
offset = 0
|
14
|
+
yield origin_file, documented_methods if block_given?
|
15
|
+
documented_methods.each do |documented_method|
|
16
|
+
offset = documented_method.write_rdoc(offset)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,341 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
class PhoneBook
|
3
|
+
include ActiveDoc
|
4
|
+
attr_accessor :owner
|
5
|
+
|
6
|
+
def initialize(owner)
|
7
|
+
@numbers = []
|
8
|
+
PhoneBook.register(self)
|
9
|
+
end
|
10
|
+
|
11
|
+
takes :contact_name, String
|
12
|
+
def add(contact_name, number)
|
13
|
+
@numbers << [contact_name, number]
|
14
|
+
end
|
15
|
+
|
16
|
+
takes :owner, String
|
17
|
+
def self.find_for_owner(owner)
|
18
|
+
@phone_books && @phone_books[owner]
|
19
|
+
end
|
20
|
+
|
21
|
+
class << self
|
22
|
+
takes :phone_book, PhoneBook
|
23
|
+
def register(phone_book)
|
24
|
+
@phone_books ||= {}
|
25
|
+
@phone_books[phone_book.owner] = phone_book
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.phone_books
|
30
|
+
return @phone_books.values
|
31
|
+
end
|
32
|
+
|
33
|
+
def size
|
34
|
+
return @numbers.size
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe ActiveDoc::Descriptions::MethodArgumentDescription do
|
39
|
+
describe "more described methods" do
|
40
|
+
subject { PhoneBook.new("Peter Smith") }
|
41
|
+
context "instance method" do
|
42
|
+
context "with wrong type" do
|
43
|
+
it "raises ArgumentError" do
|
44
|
+
lambda { subject.add(:catty_smith, "123456") }.should raise_error ArgumentError
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "with correct type" do
|
49
|
+
it "does not raise ArgumentError" do
|
50
|
+
subject.add("Catty Smith", "123456")
|
51
|
+
lambda { subject.add("Catty Smith", "123456") }.should_not raise_error ArgumentError
|
52
|
+
lambda { subject.add("Catty Smith", 123456) }.should_not raise_error ArgumentError
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context "without argument validation" do
|
57
|
+
it "does not raise ArgumentError" do
|
58
|
+
lambda { subject.size }.should_not raise_error ArgumentError
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context "class method" do
|
64
|
+
context "with wrong type" do
|
65
|
+
it "raises ArgumentError" do
|
66
|
+
lambda { subject.class.find_for_owner(:peter_smith) }.should raise_error ArgumentError
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context "with correct type" do
|
71
|
+
it "does not raise ArgumentError" do
|
72
|
+
lambda { subject.class.find_for_owner("Peter Smith") }.should_not raise_error ArgumentError
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "without argument validation" do
|
77
|
+
it "does not raise ArgumentError" do
|
78
|
+
lambda { subject.class.phone_books }.should_not raise_error ArgumentError
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context "for description of optional parameter" do
|
85
|
+
subject do
|
86
|
+
Class.new do
|
87
|
+
include ActiveDoc
|
88
|
+
takes :conjunction, String
|
89
|
+
def join(conjunction = ","); end
|
90
|
+
end.new
|
91
|
+
end
|
92
|
+
|
93
|
+
it "validates only when argument is given" do
|
94
|
+
lambda{ subject.join }.should_not raise_error ArgumentError
|
95
|
+
lambda{ subject.join(";") }.should_not raise_error ArgumentError
|
96
|
+
lambda{ subject.join(2) }.should raise_error ArgumentError
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
context "with nested description of hash" do
|
101
|
+
let :subject_class do
|
102
|
+
Class.new do
|
103
|
+
include ActiveDoc
|
104
|
+
takes :options, Hash do
|
105
|
+
takes :conjunction, String
|
106
|
+
end
|
107
|
+
def join(options); end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
context "when described key is not specified" do
|
112
|
+
subject { lambda { subject_class.new.join({})} }
|
113
|
+
|
114
|
+
it { should_not raise_error ArgumentError }
|
115
|
+
end
|
116
|
+
|
117
|
+
context "when described key has wrong value" do
|
118
|
+
subject { lambda { subject_class.new.join(:conjunction => 2)} }
|
119
|
+
|
120
|
+
it { should raise_error ArgumentError }
|
121
|
+
end
|
122
|
+
|
123
|
+
context "when described key has valid value" do
|
124
|
+
subject { lambda { subject_class.new.join(:conjunction => ",")} }
|
125
|
+
|
126
|
+
it { should_not raise_error ArgumentError }
|
127
|
+
end
|
128
|
+
|
129
|
+
context "when undescribed key is given" do
|
130
|
+
subject { lambda { subject_class.new.join(:last_conjunction => "and")} }
|
131
|
+
|
132
|
+
it { should raise_error ArgumentError }
|
133
|
+
end
|
134
|
+
|
135
|
+
describe "Rdoc comment" do
|
136
|
+
subject { ActiveDoc::RdocGenerator.for_method(subject_class, :join) }
|
137
|
+
|
138
|
+
it { should == <<RDOC }
|
139
|
+
# ==== Attributes:
|
140
|
+
# * +options+ :: (Hash):
|
141
|
+
# * +:conjunction+ :: (String)
|
142
|
+
RDOC
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
describe "none expectation specified" do
|
147
|
+
let :subject_class do
|
148
|
+
Class.new do
|
149
|
+
include ActiveDoc
|
150
|
+
takes :conjunction, :desc => "String between items when joining"
|
151
|
+
def join(conjunction); end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
describe "Validation" do
|
156
|
+
subject { lambda { subject_class.new.join(";") } }
|
157
|
+
|
158
|
+
it { should_not raise_error ArgumentError }
|
159
|
+
end
|
160
|
+
|
161
|
+
describe "Rdoc comment" do
|
162
|
+
subject { ActiveDoc::RdocGenerator.for_method(subject_class, :join)}
|
163
|
+
|
164
|
+
it { should == <<RDOC }
|
165
|
+
# ==== Attributes:
|
166
|
+
# * +conjunction+ :: String between items when joining
|
167
|
+
RDOC
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
describe "type argument expectation" do
|
172
|
+
let :subject_class do
|
173
|
+
Class.new do
|
174
|
+
include ActiveDoc
|
175
|
+
takes :conjunction, String
|
176
|
+
def join(conjunction); end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
describe "Validation" do
|
181
|
+
context "for valid value" do
|
182
|
+
subject { lambda { subject_class.new.join(";") } }
|
183
|
+
|
184
|
+
it { should_not raise_error ArgumentError }
|
185
|
+
end
|
186
|
+
|
187
|
+
context "for invalid value" do
|
188
|
+
subject { lambda { subject_class.new.join(1) } }
|
189
|
+
|
190
|
+
it { should raise_error ArgumentError }
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
describe "Rdoc comment" do
|
195
|
+
subject { ActiveDoc::RdocGenerator.for_method(subject_class, :join)}
|
196
|
+
|
197
|
+
it { should == <<RDOC }
|
198
|
+
# ==== Attributes:
|
199
|
+
# * +conjunction+ :: (String)
|
200
|
+
RDOC
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
describe "regexp argument expectation" do
|
205
|
+
let :subject_class do
|
206
|
+
Class.new do
|
207
|
+
include ActiveDoc
|
208
|
+
takes :conjunction, /^(and|or)$/
|
209
|
+
def join(conjunction) ; end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
describe "Validation" do
|
214
|
+
context "for valid value" do
|
215
|
+
subject { lambda { subject_class.new.join("and") } }
|
216
|
+
|
217
|
+
it { should_not raise_error ArgumentError }
|
218
|
+
end
|
219
|
+
|
220
|
+
context "for invalid value" do
|
221
|
+
subject { lambda { subject_class.new.join("xor") } }
|
222
|
+
|
223
|
+
it { should raise_error ArgumentError }
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
describe "Rdoc comment" do
|
228
|
+
subject { ActiveDoc::RdocGenerator.for_method(subject_class, :join) }
|
229
|
+
|
230
|
+
it { should == <<RDOC }
|
231
|
+
# ==== Attributes:
|
232
|
+
# * +conjunction+ :: (/^(and|or)$/)
|
233
|
+
RDOC
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
describe "array argument expectation" do
|
238
|
+
let :subject_class do
|
239
|
+
Class.new do
|
240
|
+
include ActiveDoc
|
241
|
+
takes :conjunction, %w{and or}
|
242
|
+
def join(conjunction); end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
describe "Validation" do
|
247
|
+
context "for valid value" do
|
248
|
+
subject { lambda { subject_class.new.join("and") } }
|
249
|
+
|
250
|
+
it { should_not raise_error ArgumentError }
|
251
|
+
end
|
252
|
+
|
253
|
+
context "for invalid value" do
|
254
|
+
subject { lambda { subject_class.new.join("xor") } }
|
255
|
+
|
256
|
+
it { should raise_error ArgumentError }
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
describe "Rdoc comment" do
|
261
|
+
subject { ActiveDoc::RdocGenerator.for_method(subject_class, :join) }
|
262
|
+
|
263
|
+
it { should == <<RDOC }
|
264
|
+
# ==== Attributes:
|
265
|
+
# * +conjunction+ :: (["and", "or"])
|
266
|
+
RDOC
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
|
271
|
+
describe "complex condition argument expectation" do
|
272
|
+
let :subject_class do
|
273
|
+
Class.new do
|
274
|
+
include ActiveDoc
|
275
|
+
takes(:number){|args| args[:number] != 0 }
|
276
|
+
def divide(number) ; end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
describe "Validation" do
|
281
|
+
context "for valid value" do
|
282
|
+
subject { lambda { subject_class.new.divide(1) } }
|
283
|
+
|
284
|
+
it { should_not raise_error ArgumentError }
|
285
|
+
end
|
286
|
+
|
287
|
+
context "for invalid value" do
|
288
|
+
subject { lambda { subject_class.new.divide(0) } }
|
289
|
+
|
290
|
+
it { should raise_error ArgumentError }
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
describe "Rdoc comment" do
|
295
|
+
subject { ActiveDoc::RdocGenerator.for_method(subject_class, :divide) }
|
296
|
+
|
297
|
+
it { should == <<RDOC }
|
298
|
+
# ==== Attributes:
|
299
|
+
# * +number+ :: (Complex Condition)
|
300
|
+
RDOC
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
describe "duck typing argument expectation" do
|
305
|
+
let :subject_class do
|
306
|
+
Class.new do
|
307
|
+
include ActiveDoc
|
308
|
+
takes :collection, :duck => :each
|
309
|
+
takes :count, :duck => [:succ, :pred]
|
310
|
+
def sum(collection, count); end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
describe "Validation" do
|
315
|
+
context "for valid value" do
|
316
|
+
subject { lambda { subject_class.new.sum([1,2,3], 2) } }
|
317
|
+
|
318
|
+
it { should_not raise_error ArgumentError }
|
319
|
+
end
|
320
|
+
|
321
|
+
context "for invalid value" do
|
322
|
+
subject { lambda { subject_class.new.sum(:s1_2_3, 2) } }
|
323
|
+
|
324
|
+
it { should raise_error ArgumentError }
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
describe "Rdoc comment" do
|
329
|
+
subject { ActiveDoc::RdocGenerator.for_method(subject_class, :sum) }
|
330
|
+
|
331
|
+
it { should == <<RDOC }
|
332
|
+
# ==== Attributes:
|
333
|
+
# * +collection+ :: (respond to :each)
|
334
|
+
# * +count+ :: (respond to [:succ, :pred])
|
335
|
+
RDOC
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
|
340
|
+
end
|
341
|
+
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActiveDoc::RdocGenerator do
|
4
|
+
let(:documented_class_path) { File.expand_path("../../support/documented_class.rb", __FILE__) }
|
5
|
+
before(:each) do
|
6
|
+
@original_documented_class = File.read(documented_class_path)
|
7
|
+
load documented_class_path
|
8
|
+
end
|
9
|
+
|
10
|
+
after(:each) do
|
11
|
+
File.open(documented_class_path, "w") { |f| f << @original_documented_class }
|
12
|
+
end
|
13
|
+
|
14
|
+
it "writes generated rdoc to file" do
|
15
|
+
ActiveDoc::RdocGenerator.write_rdoc(documented_class_path)
|
16
|
+
documented_class = File.read(documented_class_path)
|
17
|
+
documented_class.should == <<RUBY.chomp
|
18
|
+
class PhoneNumber
|
19
|
+
include ActiveDoc
|
20
|
+
|
21
|
+
takes :contact_name, String, :desc => "Name of person"
|
22
|
+
takes :number, /^\\d+$/, :desc => "Phone number"
|
23
|
+
takes :options, Hash do
|
24
|
+
takes :category, String, :desc => "Category of this contact"
|
25
|
+
end
|
26
|
+
# ==== Attributes:
|
27
|
+
# * +contact_name+ :: (String) :: Name of person
|
28
|
+
# * +number+ :: (/^\\\\d+$/) :: Phone number
|
29
|
+
# * +options+ :: (Hash):
|
30
|
+
# * +:category+ :: (String) :: Category of this contact
|
31
|
+
def initialize(contact_name, number, options = {})
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
class PhoneBook
|
36
|
+
include ActiveDoc
|
37
|
+
attr_accessor :owner
|
38
|
+
|
39
|
+
def initialize(owner)
|
40
|
+
@numbers = []
|
41
|
+
PhoneBook.register(self)
|
42
|
+
end
|
43
|
+
|
44
|
+
takes :contact_name, :ref => "PhoneNumber#initialize"
|
45
|
+
takes :number, :ref => "PhoneNumber#initialize"
|
46
|
+
takes :options, :ref => "PhoneNumber#initialize"
|
47
|
+
# ==== Attributes:
|
48
|
+
# * +contact_name+ :: (String) :: Name of person
|
49
|
+
# * +number+ :: (/^\\\\d+$/) :: Phone number
|
50
|
+
# * +options+ :: (Hash):
|
51
|
+
# * +:category+ :: (String) :: Category of this contact
|
52
|
+
def add(contact_name, number, options = {})
|
53
|
+
@numbers << PhoneNumber.new(contact_name, number, options)
|
54
|
+
end
|
55
|
+
|
56
|
+
takes :owner, String
|
57
|
+
# ==== Attributes:
|
58
|
+
# * +owner+ :: (String)
|
59
|
+
def self.find_for_owner(owner)
|
60
|
+
@phone_books && @phone_books[owner]
|
61
|
+
end
|
62
|
+
|
63
|
+
class << self
|
64
|
+
takes :phone_book, PhoneBook
|
65
|
+
# ==== Attributes:
|
66
|
+
# * +phone_book+ :: (PhoneBook)
|
67
|
+
def register(phone_book)
|
68
|
+
@phone_books ||= {}
|
69
|
+
@phone_books[phone_book.owner] = phone_book
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.phone_books
|
74
|
+
return @phone_books.values
|
75
|
+
end
|
76
|
+
|
77
|
+
def size
|
78
|
+
return @numbers.size
|
79
|
+
end
|
80
|
+
end
|
81
|
+
RUBY
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|