Documentation in 21st century
by Ivan Necas
About me
-
Ruby & Rails for 3 years
-
Currently at the Red Hat
-
-
Agenda
-
motivation
-
design
-
implementation
-
future
API Documentation status quo
What language feature do we use?
Comments
-
Comments lie
Robert Martin: Clean Code
-
The proper use of comments is to compensate for our failure to express ourself in code
Robert Martin: Clean Code
Avoid it
class Validator
def initialize(name, options = {})
@options = options
# ...
end
# ...
end
Write readable code?
class Validator
def initialize(name, options = {})
@options = options.assert_option_keys!(:message)
# ...
end
# ...
end
What about DRY?
class PresenceValidator < Validator
def initialize(name, options = {})
super
# ...
end
# ...
end
Another Approach?
-
Programming by contract
Dave Thomas, Andy Hunt: Pragmatic Programmer
-
Programming into a language
Steve McConell: Code Complete
-
Is something missing in you language?
-
Put it there
Hard Way
-
Sending patches into ruby-core
a.k.a Jose Valim approach
-
Interesting for sure
-
But improbable
Easier way
-
Using meta-programming features
-
Designing DSL for describing code
-
And have fun with it
ActiveDoc
-
DSL for documentation that is:
- Executable
- Descriptive
- Referenceable
-
Using real code
instead of comments
Describe Method Arguments
takes :name, String
def hello_world(name)
#...
end
Duck Typing
takes :name, :duck => :to_s
def hello_world(name)
#...
end
Duck Typing
takes :name, :duck => [:to_s, :match]
def hello_world(name)
#...
end
Regular Expressions
takes :name, /^\w+( \w+){1,}$/
def hello_world(name)
#...
end
Option Hashes
It is a lot of pain
It does not have to be
takes :options, Hash do
takes :name, String
takes :gender, [:male, :female]
end
def hello_world(options)
#...
end
When nothing fits?
takes(:num) { |val| val > 0 }
def fact(num)
return num == 0 ? 1 : num * num-1
end
Validations
takes :name, String
def hello(name)
"Hello #{name}"
end
hello(123)
ArgumentError: Wrong value for argument 'name'
Validations
@svenfuchs: OH uh, oh. now your documentation is a run-time dependency
Validation can be turned off
Prove it's up to date in tests
Generated Documentation
takes :name, String
# ==== Attributes:
# * +name+ :: (String)
def hello(name)
"Hello #{name}"
end
Memoize Example
require 'decorate'
class Fact
extend Decorate::Memoize
memoize
def factorial(n)
return n == 0 ? 1 : n*factorial(n-1)
end
end
Memoize Example
def memoize
Decorate.around_decorator do |call|
@_memoize ||= Hash.new {|h,k| h[k] = {} }
unless @_memoize[call.message].has_key?(call.args)
@_memoize[call.message][call.args] = call.yield
end
@_memoize[call.message][call.args]
end
end
Challenge Accepted
takes :name, Symbol
def takes(name, *args)
#...
end
How?
ActiveDoc.preload! do
files_to_preload.each {|f| require f }
end
How?
if ActiveDoc.preloading?
def method_missing(method, *args)
unless method == :takes && ActiveDoc.preloading?
super
end
end
end
How?
Load again with defined methods
if ActiveDoc.preloading?
files_to_preload.each {|f| load f }
end
Future
Discussion
Stabilization
REST API doc
Thank You!
@iNecas
Twitter
Freenode:
- #brugcz
- #rubyonrails.cz
- #katello