Documentation in 21st century
by Ivan Necas

About me

Agenda

API Documentation status quo

What language feature do we use?

Comments

Comments

Solution?

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?

Hard Way

Easier way

ActiveDoc

What it can do?

Describe Method Arguments


takes :name, String
def hello_world(name)
  #...
end

Static typing? Really?

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

Who likes RegExps?

I <3<3<3 RegExps

Regular Expressions


takes :name, /^\w+( \w+){1,}$/
def hello_world(name)
  #...
end

Option Hashes


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

What it gives to me?

Validations


takes :name, String
def hello(name)
  "Hello #{name}"
end

Validations

Generated Documentation


takes :name, String
# ==== Attributes:
# * +name+ :: (String)
def hello(name)
  "Hello #{name}"
end

Implementation

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

Who uses ActiveDoc?

ActiveDoc

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?


if ActiveDoc.preloading?
  files_to_preload.each {|f| load f }
end

Future

Questions?

Thank You!