├── var ├── name ├── title ├── created ├── version ├── organization ├── authors ├── copyrights ├── summary ├── repositories ├── requirements ├── description └── resources ├── lib ├── dnote.yml ├── dnote │ ├── templates │ │ ├── yaml │ │ │ ├── file.erb │ │ │ ├── label.erb │ │ │ └── list.erb │ │ ├── xoxo.erb │ │ ├── yaml.erb │ │ ├── xoxo │ │ │ ├── file.erb │ │ │ ├── label.erb │ │ │ └── list.erb │ │ ├── soap.erb │ │ ├── soap │ │ │ ├── file.erb │ │ │ ├── list.erb │ │ │ └── label.erb │ │ ├── json │ │ │ ├── file.erb │ │ │ ├── label.erb │ │ │ └── list.erb │ │ ├── json.erb │ │ ├── rdoc │ │ │ ├── list.erb │ │ │ ├── file.erb │ │ │ └── label.erb │ │ ├── text │ │ │ ├── list.erb │ │ │ ├── label.erb │ │ │ └── file.erb │ │ ├── xml │ │ │ ├── list.erb │ │ │ ├── file.erb │ │ │ └── label.erb │ │ ├── md │ │ │ ├── list.erb │ │ │ ├── file.erb │ │ │ └── label.erb │ │ ├── text.erb │ │ ├── rdoc.erb │ │ ├── md.erb │ │ ├── xml.erb │ │ ├── html │ │ │ ├── list.erb │ │ │ ├── file.erb │ │ │ └── label.erb │ │ └── html.erb │ ├── version.rb │ ├── core_ext.rb │ ├── rake │ │ └── dnotetask.rb │ ├── note.rb │ ├── format.rb │ ├── session.rb │ └── notes.rb └── dnote.rb ├── .reap ├── ignore ├── pack.reap ├── test.reap └── spec.reap ├── Gemfile ├── .yardopts ├── bin └── dnote ├── .gitignore ├── try ├── sample.bas ├── sample.rb └── sample.js ├── .travis.yml ├── Assembly ├── .ruby ├── COPYING.rdoc ├── MANIFEST ├── test └── notes_case.rb ├── work ├── site.rb └── command.rb ├── README.rdoc ├── .gemspec └── HISTORY.rdoc /var/name: -------------------------------------------------------------------------------- 1 | dnote -------------------------------------------------------------------------------- /var/title: -------------------------------------------------------------------------------- 1 | DNote -------------------------------------------------------------------------------- /lib/dnote.yml: -------------------------------------------------------------------------------- 1 | ../.ruby -------------------------------------------------------------------------------- /var/created: -------------------------------------------------------------------------------- 1 | 2009-10-09 -------------------------------------------------------------------------------- /var/version: -------------------------------------------------------------------------------- 1 | 1.7.0 2 | -------------------------------------------------------------------------------- /.reap/ignore: -------------------------------------------------------------------------------- 1 | ri 2 | work 3 | -------------------------------------------------------------------------------- /var/organization: -------------------------------------------------------------------------------- 1 | RubyWorks -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source :rubygems 2 | gemspec 3 | -------------------------------------------------------------------------------- /var/authors: -------------------------------------------------------------------------------- 1 | --- 2 | - Thomas Sawyer 3 | -------------------------------------------------------------------------------- /var/copyrights: -------------------------------------------------------------------------------- 1 | --- 2 | - (c) 2009 Thomas Sawyer, Rubyworks 3 | -------------------------------------------------------------------------------- /var/summary: -------------------------------------------------------------------------------- 1 | Extract developer's notes from source code. 2 | -------------------------------------------------------------------------------- /.yardopts: -------------------------------------------------------------------------------- 1 | --protected 2 | --private 3 | lib/**/*.rb 4 | - 5 | [A-Z]*.* 6 | -------------------------------------------------------------------------------- /var/repositories: -------------------------------------------------------------------------------- 1 | --- 2 | upstream: git://github.com/rubyworks/dnote.git 3 | -------------------------------------------------------------------------------- /lib/dnote/templates/yaml/file.erb: -------------------------------------------------------------------------------- 1 | <%= require 'yaml'; notes.by_file.to_yaml %> 2 | -------------------------------------------------------------------------------- /lib/dnote/templates/yaml/label.erb: -------------------------------------------------------------------------------- 1 | <%= require 'yaml'; notes.by_label.to_yaml %> 2 | -------------------------------------------------------------------------------- /bin/dnote: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require "dnote" 3 | DNote::Session.main(*ARGV) 4 | 5 | -------------------------------------------------------------------------------- /lib/dnote/templates/xoxo.erb: -------------------------------------------------------------------------------- 1 | <%= 2 | require 'xoxo' 3 | notes.by_label.to_xoxo 4 | %> 5 | -------------------------------------------------------------------------------- /lib/dnote/templates/yaml.erb: -------------------------------------------------------------------------------- 1 | <%= require 'yaml'; notes.map{ |n| n.to_h_raw }.to_yaml %> 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .reap/digest 2 | .yardoc 3 | doc 4 | log 5 | pkg 6 | ri 7 | tmp 8 | site/doc 9 | web -------------------------------------------------------------------------------- /lib/dnote/templates/xoxo/file.erb: -------------------------------------------------------------------------------- 1 | <%= 2 | require 'xoxo' 3 | notes.by_file.to_xoxo 4 | %> 5 | -------------------------------------------------------------------------------- /lib/dnote/templates/yaml/list.erb: -------------------------------------------------------------------------------- 1 | <%= require 'yaml'; notes.map{ |n| n.to_h_raw }.to_yaml %> 2 | -------------------------------------------------------------------------------- /lib/dnote/templates/xoxo/label.erb: -------------------------------------------------------------------------------- 1 | <%= 2 | require 'xoxo' 3 | notes.by_label.to_xoxo 4 | %> 5 | -------------------------------------------------------------------------------- /lib/dnote/templates/soap.erb: -------------------------------------------------------------------------------- 1 | <%= 2 | require 'soap/marshal' 3 | ::SOAP::Marshal.marshal(notes.to_a) 4 | %> 5 | -------------------------------------------------------------------------------- /var/requirements: -------------------------------------------------------------------------------- 1 | --- 2 | - lemon (test) 3 | - ae (test) 4 | - detroit (build) 5 | - reap (build) 6 | 7 | -------------------------------------------------------------------------------- /lib/dnote.rb: -------------------------------------------------------------------------------- 1 | require 'dnote/version' 2 | require 'dnote/session' 3 | 4 | # TEST: This is a test of an arbitrary label. 5 | -------------------------------------------------------------------------------- /lib/dnote/templates/soap/file.erb: -------------------------------------------------------------------------------- 1 | <%= 2 | require 'soap/marshal' 3 | ::SOAP::Marshal.marshal(notes.by_file) 4 | %> 5 | -------------------------------------------------------------------------------- /lib/dnote/templates/soap/list.erb: -------------------------------------------------------------------------------- 1 | <%= 2 | require 'soap/marshal' 3 | ::SOAP::Marshal.marshal(notes.to_a) 4 | %> 5 | -------------------------------------------------------------------------------- /lib/dnote/templates/soap/label.erb: -------------------------------------------------------------------------------- 1 | <%= 2 | require 'soap/marshal' 3 | ::SOAP::Marshal.marshal(notes.by_label) 4 | %> 5 | -------------------------------------------------------------------------------- /var/description: -------------------------------------------------------------------------------- 1 | DNote makes it easy to extract developer's notes from source code, 2 | and supports almost any language. 3 | -------------------------------------------------------------------------------- /.reap/pack.reap: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | trip :pack do 4 | system "gem build .gemspec" 5 | system "mv *.gem pkg/" 6 | end 7 | 8 | -------------------------------------------------------------------------------- /try/sample.bas: -------------------------------------------------------------------------------- 1 | ' Here is a sample of Javascript. 2 | ' 3 | ' TODO: This is what we will do. 4 | ' 5 | ' FIXME: And this is something urgant 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/dnote/templates/json/file.erb: -------------------------------------------------------------------------------- 1 | <%= 2 | begin 3 | require 'json' 4 | rescue LoadError 5 | require 'json_pure' 6 | end 7 | notes.by_file.to_json 8 | %> 9 | -------------------------------------------------------------------------------- /lib/dnote/templates/json/label.erb: -------------------------------------------------------------------------------- 1 | <%= 2 | begin 3 | require 'json' 4 | rescue LoadError 5 | require 'json_pure' 6 | end 7 | notes.by_label.to_json 8 | %> 9 | -------------------------------------------------------------------------------- /lib/dnote/templates/json.erb: -------------------------------------------------------------------------------- 1 | <%= 2 | begin 3 | require 'json' 4 | rescue LoadError 5 | require 'json_pure' 6 | end 7 | notes.map{ |n| n.to_h_raw }.to_json 8 | %> 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | rvm: 3 | - 1.8.7 4 | - 1.9.2 5 | - 1.9.3 6 | - rbx-2.0 7 | - jruby 8 | - ree 9 | script: "bundle exec ruby-test test/*.rb" 10 | 11 | -------------------------------------------------------------------------------- /lib/dnote/templates/json/list.erb: -------------------------------------------------------------------------------- 1 | <%= 2 | begin 3 | require 'json' 4 | rescue LoadError 5 | require 'json_pure' 6 | end 7 | notes.map{ |n| n.to_h_raw }.to_json 8 | %> 9 | -------------------------------------------------------------------------------- /lib/dnote/templates/xoxo/list.erb: -------------------------------------------------------------------------------- 1 | <%= 2 | require 'xoxo' 3 | notes.map{ |n| 4 | { 'label'=>n.label, 'text'=>n.textline, 'file'=>n.file, 'line'=>n.line } 5 | }.to_xoxo 6 | %> 7 | -------------------------------------------------------------------------------- /.reap/test.reap: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | file "lib/**/*.rb" do 4 | system "lemon -Ilib test/*_case.rb" 5 | end 6 | 7 | trip :test do 8 | system "lemon -Ilib test/*_case.rb" 9 | end 10 | 11 | -------------------------------------------------------------------------------- /try/sample.rb: -------------------------------------------------------------------------------- 1 | # Here is a sample of Javascript. 2 | # 3 | # TODO: This is what we will do. 4 | # 5 | # FIXME: And this is something urgant 6 | 7 | def sample 8 | n = 100 9 | n 10 | end 11 | 12 | -------------------------------------------------------------------------------- /try/sample.js: -------------------------------------------------------------------------------- 1 | // Here is a sample of Javascript. 2 | // 3 | // TODO: This is what we will do. 4 | // 5 | // FIXME: And this is something urgant 6 | 7 | function sample() [ 8 | var n = 100; 9 | return n; 10 | } 11 | 12 | -------------------------------------------------------------------------------- /lib/dnote/templates/rdoc/list.erb: -------------------------------------------------------------------------------- 1 | = <%= title %> 2 | 3 | <% notes.each_with_index do |note, index| %> 4 | * <%= note.label %>: <%= h note.textline %> (<%= note.file %>: <%= note.line %>) 5 | <% if note.code? %><%= "\n" + note.code.tabset(4).rstrip + "\n\n" %><% end %> 6 | <% end %> 7 | 8 | -------------------------------------------------------------------------------- /lib/dnote/templates/text/list.erb: -------------------------------------------------------------------------------- 1 | <%= title %> 2 | 3 | 4 | <% notes.each_with_index do |note, index| %> 5 | <%= "%4s %s %s" % [index+1, note.label, note.text.gsub("\n", ' ')] %> (<%= note.file %>:<%= note.line %>) 6 | <% if note.code? %><%= "\n" + note.code.tabset(8) + "\n" %><% end %> 7 | <% end %> 8 | 9 | -------------------------------------------------------------------------------- /lib/dnote/templates/rdoc/file.erb: -------------------------------------------------------------------------------- 1 | = <%= title %> 2 | <% notes.by_file.each do |file, list| %> 3 | 4 | == file://<%= file %> 5 | 6 | <% list.each do |note| %> 7 | * <%= note %> (<%= note.line %>) 8 | <% if note.code? %><%= "\n" + note.code.tabset(4).rstrip + "\n\n" %><% end %> 9 | <% end; end %> 10 | 11 | -------------------------------------------------------------------------------- /var/resources: -------------------------------------------------------------------------------- 1 | --- 2 | home: http://rubyworks.github.com/dnote 3 | code: http://github.com/rubyworks/dnote 4 | wiki: http://wiki.github.com/rubyworks/dnote 5 | api: http://rubydoc.info/gems/dnote 6 | bugs: http://github.com/rubyworks/dnote/issues 7 | mail: http://groups.google.com/groups/rubyworks-mailinglist 8 | 9 | -------------------------------------------------------------------------------- /lib/dnote/templates/text/label.erb: -------------------------------------------------------------------------------- 1 | <%= title %> 2 | 3 | <% notes.by_label.each do |label, line_notes| %> 4 | 5 | <%= label %> 6 | 7 | 8 | <% line_notes.each_with_index do |note, i| %> 9 | <%= note.text.indent(6).sub(' ' * 6,(" %3s. " % [i+1])) %> (<%= note.file %>:<%= note.line %>) 10 | <% end; end %> 11 | 12 | -------------------------------------------------------------------------------- /.reap/spec.reap: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | file "Profile", "Version" do 4 | system "pom spec -u" 5 | system "pom gemspec -u" 6 | end 7 | 8 | #file "Version" do 9 | # system "pom spec -u" 10 | # system "pom gemspec -u" 11 | #end 12 | 13 | file "Manifest", "MANIFEST" do 14 | system "pom gemspec -u" 15 | end 16 | 17 | -------------------------------------------------------------------------------- /lib/dnote/version.rb: -------------------------------------------------------------------------------- 1 | module DNote 2 | 3 | # 4 | def self.metadata 5 | @metadata ||= ( 6 | require 'yaml' 7 | YAML.load_file(File.dirname(__FILE__) + '/../dnote.yml') 8 | ) 9 | end 10 | 11 | # 12 | def self.const_missing(name) 13 | metadata[name.to_s.downcase] || super(name) 14 | end 15 | 16 | end 17 | -------------------------------------------------------------------------------- /lib/dnote/templates/xml/list.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | <% notes.each do |note| %> 4 | 5 | <%= h note.textline %> 6 | <% if note.code? %> 7 | 8 | 10 | ]]> 11 | <% end %> 12 | 13 | <% end %> 14 | 15 | -------------------------------------------------------------------------------- /lib/dnote/templates/md/list.erb: -------------------------------------------------------------------------------- 1 | <%= title %> 2 | 3 | <%= '=' * title.size %> 4 | 5 | 6 | <% notes.each_with_index do |note, index| %> 7 | <%= "%s" % [index+1] %>. <%= note.label %>: <%= h note.textline %> (<%= note.file %>: <%= note.line %>) 8 | <% if note.code? %><%= "\n" + note.code.tabset(4).rstrip + "\n\n" %><% end %> 9 | <% end %> 10 | 11 | -------------------------------------------------------------------------------- /lib/dnote/templates/md/file.erb: -------------------------------------------------------------------------------- 1 | <%= title %> 2 | 3 | <%= '=' * title.size %> 4 | 5 | <% notes.by_file.each do |file, list| %> 6 | 7 | file://<%= file %> 8 | <%= '-' * (file.size + 7) %> 9 | 10 | <% list.each do |note| %> 11 | * <%= note %> (<%= note.line %>) 12 | <% if note.code? %><%= "\n" + note.code.tabset(4).rstrip + "\n\n" %><% end %> 13 | <% end; end %> 14 | 15 | -------------------------------------------------------------------------------- /lib/dnote/templates/text.erb: -------------------------------------------------------------------------------- 1 | <%= title %> 2 | 3 | <% notes.by_label.each do |label, line_notes| %> 4 | 5 | <%= label %> 6 | 7 | 8 | <% line_notes.each_with_index do |note, i| %> 9 | <%= note.text.indent(6).sub(' ' * 6,(" %3s. " % [i+1])) %> (<%= note.file %>:<%= note.line %>) 10 | <% if note.code? %><%= "\n" + note.code.tabset(8) + "\n" %><% end %> 11 | <% end; end %> 12 | 13 | -------------------------------------------------------------------------------- /lib/dnote/templates/text/file.erb: -------------------------------------------------------------------------------- 1 | <%= title %> 2 | 3 | <% notes.by_file.each do |file, line_notes| %> 4 | 5 | <%= file %>: 6 | 7 | <% line_notes.sort!{ |a,b| a.line <=> b.line } %><% line_notes.each do |note| %> 8 | <%= note.to_s.indent(4).sub(' ' * 4,' * ') %> (<%= note.line %>) 9 | <% if note.code? %><%= "\n" + note.code.tabset(10) + "\n" %><% end %> 10 | <% end; end %> 11 | 12 | -------------------------------------------------------------------------------- /lib/dnote/templates/rdoc.erb: -------------------------------------------------------------------------------- 1 | = <%= title %><% notes.by_label_file.each do |label, per_file| %> 2 | 3 | == <%= label %> 4 | <% per_file.each do |file, line_notes| %> 5 | 6 | === file://<%= file %> 7 | 8 | <% line_notes.each do |note| %> 9 | * <%= note %> (<%= note.line %>) 10 | <% if note.code? %><%= "\n" + note.code.tabset(4).rstrip + "\n\n" %><% end %> 11 | <% end; end; end %> 12 | 13 | -------------------------------------------------------------------------------- /lib/dnote/templates/rdoc/label.erb: -------------------------------------------------------------------------------- 1 | = <%= title %><% notes.by_label_file.each do |label, per_file| %> 2 | 3 | == <%= label %> 4 | <% per_file.each do |file, line_notes| %> 5 | 6 | === file://<%= file %> 7 | 8 | <% line_notes.each do |note| %> 9 | * <%= note %> (<%= note.line %>) 10 | <% if note.code? %><%= "\n" + note.code.tabset(4).rstrip + "\n\n" %><% end %> 11 | <% end; end; end %> 12 | 13 | -------------------------------------------------------------------------------- /lib/dnote/templates/md.erb: -------------------------------------------------------------------------------- 1 | <%= title %> 2 | 3 | <%= '=' * title.to_s.size %> 4 | 5 | <% notes.by_label_file.each do |label, per_file| %> 6 | 7 | <%= label %> 8 | 9 | <%= '-' * (label.size) %> 10 | 11 | <% per_file.each do |file, line_notes| %> 12 | 13 | ### file://<%= file %> 14 | 15 | <% line_notes.each do |note| %> 16 | * <%= note %> (<%= note.line %>) 17 | <% if note.code? %><%= "\n" + note.code.tabset(4).rstrip + "\n\n" %><% end %> 18 | <% end; end; end %> 19 | 20 | -------------------------------------------------------------------------------- /lib/dnote/templates/md/label.erb: -------------------------------------------------------------------------------- 1 | <%= title %> 2 | 3 | <%= '=' * title.size %> 4 | 5 | <% notes.by_label_file.each do |label, per_file| %> 6 | 7 | <%= label %> 8 | 9 | <%= '-' * (label.size) %> 10 | 11 | <% per_file.each do |file, line_notes| %> 12 | 13 | ### file://<%= file %> 14 | 15 | <% line_notes.each do |note| %> 16 | * <%= note %> (<%= note.line %>) 17 | <% if note.code? %><%= "\n" + note.code.tabset(4).rstrip + "\n\n" %><% end %> 18 | <% end; end; end %> 19 | 20 | -------------------------------------------------------------------------------- /Assembly: -------------------------------------------------------------------------------- 1 | --- 2 | email: 3 | service : Email 4 | mailto : 5 | - ruby-talk@ruby-lang.org 6 | - rubyworks-mailinglist@googlegroups.com 7 | 8 | gem: 9 | active: true 10 | 11 | github: 12 | gh_pages: web 13 | 14 | yard: 15 | priority: 2 16 | 17 | dnote: 18 | labels : ~ 19 | lines : 5 20 | output : 21 | - log/NOTES.rdoc 22 | 23 | vclog: 24 | output: 25 | - log/Changes.rdoc 26 | - log/History.rdoc 27 | 28 | #lemon: 29 | # tests : ~ 30 | # exclude : ~ 31 | # loadpath : ~ 32 | # requires : ~ 33 | # live : false 34 | # active : false 35 | 36 | -------------------------------------------------------------------------------- /lib/dnote/templates/xml.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | <% notes.by_label_file.each do |label, per_file| %> 4 | 22 | <% end %> 23 | 24 | -------------------------------------------------------------------------------- /lib/dnote/templates/xml/file.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | <% notes.by_file_label.each do |file, per_label| %> 4 | 5 | <% per_label.each do |label, lnotes| %> 6 | 20 | <% end %> 21 | 22 | <% end %> 23 | 24 | -------------------------------------------------------------------------------- /lib/dnote/templates/xml/label.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | <% notes.by_label_file.each do |label, per_file| %> 4 | 22 | <% end %> 23 | 24 | -------------------------------------------------------------------------------- /lib/dnote/templates/html/list.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | <%= title %> 4 | 20 | 21 | 22 | 23 |
24 |

<%= title %>

25 |
26 |
    27 | <% notes.each do |note| %> 28 |
  1. 29 | <%= note.label %>: <%= h note.text %> 30 | <%= note.file %>: <%= note.line %> 31 | <% if note.code? %> 32 |
    33 | <%= note.code %>
    34 | 
    35 | <$ end %> 36 |
  2. 37 | <% end %> 38 |
39 |
40 |
41 | 42 | 43 | -------------------------------------------------------------------------------- /.ruby: -------------------------------------------------------------------------------- 1 | --- 2 | source: 3 | - var 4 | authors: 5 | - name: Thomas Sawyer 6 | email: transfire@gmail.com 7 | copyrights: 8 | - holder: Thomas Sawyer, Rubyworks 9 | year: '2009' 10 | replacements: [] 11 | alternatives: [] 12 | requirements: 13 | - name: lemon 14 | groups: 15 | - test 16 | development: true 17 | - name: ae 18 | groups: 19 | - test 20 | development: true 21 | - name: detroit 22 | groups: 23 | - build 24 | development: true 25 | - name: reap 26 | groups: 27 | - build 28 | development: true 29 | dependencies: [] 30 | conflicts: [] 31 | repositories: 32 | - uri: git://github.com/rubyworks/dnote.git 33 | scm: git 34 | name: upstream 35 | resources: 36 | home: http://rubyworks.github.com/dnote 37 | code: http://github.com/rubyworks/dnote 38 | wiki: http://wiki.github.com/rubyworks/dnote 39 | api: http://rubydoc.info/gems/dnote 40 | bugs: http://github.com/rubyworks/dnote/issues 41 | mail: http://groups.google.com/groups/rubyworks-mailinglist 42 | extra: {} 43 | load_path: 44 | - lib 45 | revision: 0 46 | created: '2009-10-09' 47 | summary: Extract developer's notes from source code. 48 | title: DNote 49 | version: 1.7.0 50 | name: dnote 51 | description: ! 'DNote makes it easy to extract developer''s notes from source code, 52 | 53 | and supports almost any language.' 54 | organization: RubyWorks 55 | date: '2011-11-11' 56 | -------------------------------------------------------------------------------- /lib/dnote/templates/html/file.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | <%= title %> 4 | 20 | 21 | 22 | 23 |
24 |

<%= title %>

25 |
26 | <% notes.by_file_label.each do |file, per_label| %> 27 |

<%= file %>

28 |
    29 | <% per_label.each do |label, lnotes| %> 30 |
  1. <%= label %>

      31 | <% lnotes.each do |note| %> 32 |
    1. 33 | <%= h note.textline %> <%= note.line %> 34 | <% if note.code? %> 35 |
      36 | <%= note.code %>
      37 | 
      38 | <% end %> 39 |
    2. 40 | <% end %> 41 |
  2. 42 | <% end %> 43 |
44 | <% end %> 45 |
46 |
47 | 48 | 49 | -------------------------------------------------------------------------------- /lib/dnote/templates/html/label.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | <%= title %> 4 | 20 | 21 | 22 | 23 |
24 |

<%= title %>

25 |
26 | <% notes.by_label_file.each do |label, per_file| %> 27 |

<%= label %>

28 |
    29 | <% per_file.each do |file, line_notes| %> 30 |
  1. <%= file %>

      31 | <% line_notes.sort!{ |a,b| a.line <=> b.line } %> 32 | <% line_notes.each do |note| %> 33 |
    1. 34 | <%= h note.text %> <%= note.line %> 35 | <% if note.code? %> 36 |
      37 | <%= note.code %>
      38 | 
      39 | <% end %> 40 |
    2. 41 | <% end %> 42 |
  2. 43 | <% end %> 44 |
45 | <% end %> 46 |
47 |
48 | 49 | 50 | -------------------------------------------------------------------------------- /COPYING.rdoc: -------------------------------------------------------------------------------- 1 | = COPYRIGHT NOTICES 2 | 3 | == DNote 4 | 5 | Copyright:: (c) 2006 Thomas Sawyer, Rubyworks 6 | License:: BSD-2-Clause 7 | Website:: http://rubyworks.github.com/tapout 8 | 9 | Copyright 2006 Thomas Sawyer, Rubyworks. All rights reserved. 10 | 11 | Redistribution and use in source and binary forms, with or without 12 | modification, are permitted provided that the following conditions are met: 13 | 14 | 1. Redistributions of source code must retain the above copyright notice, 15 | this list of conditions and the following disclaimer. 16 | 17 | 2. Redistributions in binary form must reproduce the above copyright 18 | notice, this list of conditions and the following disclaimer in the 19 | documentation and/or other materials provided with the distribution. 20 | 21 | THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 22 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 23 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 | COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 28 | OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 30 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | -------------------------------------------------------------------------------- /lib/dnote/templates/html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | <%= title %> 4 | 20 | 21 | 22 | 23 |
24 |

<%= title %>

25 |
26 | <% notes.by_label_file.each do |label, per_file| %> 27 |

<%= label %>

28 |
    29 | <% per_file.each do |file, line_notes| %> 30 |
  1. <%= file %>

      31 | <% line_notes.sort!{ |a,b| a.line <=> b.line } %> 32 | <% line_notes.each do |note| %> 33 |
    1. 34 | <%= h note.text %> <%= note.line %> 35 | <% if note.code? %> 36 |
      37 | <%= note.code %>
      38 | 
      39 | <% end %> 40 |
    2. 41 | <% end %> 42 |
  2. 43 | <% end %> 44 |
45 | <% end %> 46 |
47 |
48 | 49 | 50 | -------------------------------------------------------------------------------- /MANIFEST: -------------------------------------------------------------------------------- 1 | #!mast .ruby .yardopts bin lib man spec test try [A-Z]*.* 2 | .ruby 3 | .yardopts 4 | bin/dnote 5 | lib/dnote/core_ext.rb 6 | lib/dnote/format.rb 7 | lib/dnote/note.rb 8 | lib/dnote/notes.rb 9 | lib/dnote/rake/dnotetask.rb 10 | lib/dnote/session.rb 11 | lib/dnote/templates/html/file.erb 12 | lib/dnote/templates/html/label.erb 13 | lib/dnote/templates/html/list.erb 14 | lib/dnote/templates/html.erb 15 | lib/dnote/templates/json/file.erb 16 | lib/dnote/templates/json/label.erb 17 | lib/dnote/templates/json/list.erb 18 | lib/dnote/templates/json.erb 19 | lib/dnote/templates/md/file.erb 20 | lib/dnote/templates/md/label.erb 21 | lib/dnote/templates/md/list.erb 22 | lib/dnote/templates/md.erb 23 | lib/dnote/templates/rdoc/file.erb 24 | lib/dnote/templates/rdoc/label.erb 25 | lib/dnote/templates/rdoc/list.erb 26 | lib/dnote/templates/rdoc.erb 27 | lib/dnote/templates/soap/file.erb 28 | lib/dnote/templates/soap/label.erb 29 | lib/dnote/templates/soap/list.erb 30 | lib/dnote/templates/soap.erb 31 | lib/dnote/templates/text/file.erb 32 | lib/dnote/templates/text/label.erb 33 | lib/dnote/templates/text/list.erb 34 | lib/dnote/templates/text.erb 35 | lib/dnote/templates/xml/file.erb 36 | lib/dnote/templates/xml/label.erb 37 | lib/dnote/templates/xml/list.erb 38 | lib/dnote/templates/xml.erb 39 | lib/dnote/templates/xoxo/file.erb 40 | lib/dnote/templates/xoxo/label.erb 41 | lib/dnote/templates/xoxo/list.erb 42 | lib/dnote/templates/xoxo.erb 43 | lib/dnote/templates/yaml/file.erb 44 | lib/dnote/templates/yaml/label.erb 45 | lib/dnote/templates/yaml/list.erb 46 | lib/dnote/templates/yaml.erb 47 | lib/dnote/version.rb 48 | lib/dnote.rb 49 | test/notes_case.rb 50 | try/sample.bas 51 | try/sample.js 52 | try/sample.rb 53 | HISTORY.rdoc 54 | README.rdoc 55 | COPYING.rdoc 56 | -------------------------------------------------------------------------------- /test/notes_case.rb: -------------------------------------------------------------------------------- 1 | require 'lemon' 2 | require 'ae' 3 | require 'dnote/notes' 4 | 5 | testcase DNote::Notes do 6 | 7 | concern "Coverage of DNote::Notes class." 8 | 9 | method :labels do 10 | test 'returns the list of labels' do 11 | notes = DNote::Notes.new([], :labels=>['TODO']) 12 | notes.labels.assert == ['TODO'] #DNote::Notes::DEFAULT_LABELS 13 | end 14 | end 15 | 16 | method :files do 17 | test 'returns the files attribute' do 18 | notes = DNote::Notes.new(["example1.rb"]) 19 | notes.assert.files == ["example1.rb"] 20 | notes = DNote::Notes.new([], :paths => ["example2.rb"]) 21 | notes.assert.files == ["example2.rb"] 22 | end 23 | end 24 | 25 | method :files do 26 | test 'changes the paths attribute' do 27 | notes = DNote::Notes.new([]) 28 | notes.files = ["example1.rb"] 29 | notes.assert.files == ["example1.rb"] 30 | end 31 | end 32 | 33 | method :match_general do 34 | test 'works' do 35 | notes = DNote::Notes.new([]) 36 | line, lineno, file = "# TODO: Do something or another!", 1, "foo.rb" 37 | rec = notes.match_general(line, lineno, file) 38 | rec.to_h.assert == {'label'=>"TODO",'file'=>file,'line'=>lineno,'text'=>"Do something or another!"} 39 | end 40 | end 41 | 42 | method :match_special do 43 | test 'works' do 44 | notes = DNote::Notes.new([], :labels=>['TODO']) 45 | line, lineno, file = "# TODO: Do something or another!", 1, "foo.rb" 46 | rec = notes.match_special(line, lineno, file) 47 | rec.to_h.assert == {'label'=>"TODO",'file'=>file,'line'=>lineno,'text'=>"Do something or another!"} 48 | end 49 | end 50 | 51 | method :counts do 52 | test{ raise NotImplementedError } 53 | end 54 | 55 | method :notes do 56 | test{ raise NotImplementedError } 57 | end 58 | 59 | method :parse do 60 | test{ raise NotImplementedError } 61 | end 62 | 63 | end 64 | 65 | -------------------------------------------------------------------------------- /lib/dnote/core_ext.rb: -------------------------------------------------------------------------------- 1 | module Enumerable 2 | 3 | # Taken from Ruby Facets. 4 | def group_by #:yield: 5 | #h = k = e = nil 6 | r = Hash.new 7 | each{ |e| (r[yield(e)] ||= []) << e } 8 | r 9 | end unless method_defined?(:group_by) 10 | 11 | end 12 | 13 | module DNote 14 | 15 | # Extensions for String class. 16 | # These methods are taken directly from Ruby Facets. 17 | # 18 | module String 19 | 20 | # Provides a margin controlled string. 21 | # 22 | # x = %Q{ 23 | # | This 24 | # | is 25 | # | margin controlled! 26 | # }.margin 27 | # 28 | # 29 | # NOTE: This may still need a bit of tweaking. 30 | # 31 | # CREDIT: Trans 32 | 33 | def margin(n=0) 34 | #d = /\A.*\n\s*(.)/.match( self )[1] 35 | #d = /\A\s*(.)/.match( self)[1] unless d 36 | d = ((/\A.*\n\s*(.)/.match(self)) || 37 | (/\A\s*(.)/.match(self)))[1] 38 | return '' unless d 39 | if n == 0 40 | gsub(/\n\s*\Z/,'').gsub(/^\s*[#{d}]/, '') 41 | else 42 | gsub(/\n\s*\Z/,'').gsub(/^\s*[#{d}]/, ' ' * n) 43 | end 44 | end 45 | 46 | # Preserves relative tabbing. 47 | # The first non-empty line ends up with n spaces before nonspace. 48 | # 49 | # CREDIT: Gavin Sinclair 50 | 51 | def tabto(n) 52 | if self =~ /^( *)\S/ 53 | indent(n - $1.length) 54 | else 55 | self 56 | end 57 | end 58 | 59 | # Indent left or right by n spaces. 60 | # (This used to be called #tab and aliased as #indent.) 61 | # 62 | # CREDIT: Gavin Sinclair 63 | # CREDIT: Trans 64 | 65 | def indent(n) 66 | if n >= 0 67 | gsub(/^/, ' ' * n) 68 | else 69 | gsub(/^ {0,#{-n}}/, "") 70 | end 71 | end 72 | 73 | # 74 | # 75 | def tabset(n) 76 | i = lines.map do |line| 77 | line.strip.empty? ? nil : line.index(/\S/) 78 | end 79 | x = i.compact.min 80 | t = n - x.to_i 81 | t = 0 if t < 0 82 | indent(t) 83 | end 84 | 85 | end 86 | 87 | class ::String #:nodoc: 88 | include DNote::String 89 | end 90 | 91 | end 92 | 93 | -------------------------------------------------------------------------------- /lib/dnote/rake/dnotetask.rb: -------------------------------------------------------------------------------- 1 | module DNote 2 | 3 | # = Developmer's Notes Rake Task 4 | # 5 | class RakeTask < Rake::TaskLib 6 | 7 | require 'rake/clean' 8 | 9 | # Default note labels to looked for in source code. 10 | DEFAULT_LABELS = ['TODO', 'FIXME', 'OPTIMIZE', 'DEPRECATE'] 11 | 12 | # File paths to search. 13 | attr_accessor :files 14 | 15 | # Labels to document. Defaults are: TODO, FIXME, OPTIMIZE and DEPRECATE. 16 | attr_accessor :labels 17 | 18 | # Formats (xml, html, rdoc, rdoc/list and so on). 19 | attr_accessor :formats 20 | 21 | # Exclude paths. 22 | attr_accessor :exclude 23 | 24 | # Ignore paths based on any part of pathname. 25 | attr_accessor :ignore 26 | 27 | # Output directory to save notes file. Defaults to dnote/ under 28 | # the project log directory (eg. log/dnote/). 29 | attr_accessor :output 30 | 31 | # Title to use if temaplte can use it. 32 | attr_accessor :title 33 | 34 | # 35 | def output=(path) 36 | @output = Pathname.new(path) 37 | end 38 | 39 | # 40 | def init 41 | require 'dnote' 42 | require 'dnote/format' 43 | @files = "**/*.rb" 44 | @output = 'log/dnote' 45 | @formats = ['index'] 46 | @labels = nil #DEFAULT_LABELS 47 | end 48 | 49 | # 50 | def define 51 | desc "Collect Developer's Notes" 52 | task 'dnote' do 53 | document 54 | end 55 | task 'dnote:clobber' do 56 | clean 57 | end 58 | task :clobber => ['dnote:clobber'] 59 | end 60 | 61 | # Generate notes document(s). 62 | def document 63 | abort "dnote: #{output} is not a directory" unless output.directory? 64 | 65 | session = ::DNote::Session.new do |s| 66 | s.paths = files 67 | s.exclude = exclude 68 | s.ignore = ignore 69 | s.labels = labels #|| DEFAULT_LABELS 70 | s.title = title 71 | s.output = output 72 | s.dryrun = application.options.dryrun #trial? 73 | end 74 | 75 | formats.each do |format| 76 | if format == 'index' 77 | session.format = 'html' 78 | session.output = File.join(self.output, 'index.html') 79 | else 80 | session.format = format 81 | end 82 | session.run 83 | report "Updated #{output.to_s.sub(Dir.pwd+'/','')}" unless trial? 84 | end 85 | end 86 | 87 | # Reset output directory, marking it as out-of-date. 88 | def reset 89 | #if File.directory?(output) 90 | File.utime(0,0,output) unless $NOOP 91 | puts "Marked #{output} as out-of-date" 92 | #end 93 | end 94 | 95 | # Remove output files. 96 | def clean 97 | #if File.directory?(output) 98 | formats.each do |format| 99 | if format == 'index' 100 | file = (output + "index.html").to_s 101 | else 102 | ext = ::DNote::Format::EXTENSIONS[format] || format 103 | file = (output + "notes.#{ext}").to_s 104 | end 105 | rm(file) 106 | report "Removed #{output}" 107 | end 108 | #else 109 | # rm(output) 110 | # report "Removed #{output}" 111 | #end 112 | end 113 | 114 | end 115 | 116 | end 117 | -------------------------------------------------------------------------------- /work/site.rb: -------------------------------------------------------------------------------- 1 | module DNote 2 | 3 | require 'dnote/notes' 4 | 5 | # Site class is used to build a "pretty" output set. 6 | # The template files are saved to the +output+ directory. 7 | # Additional +formats+ can be saved to the directory 8 | # as well. 9 | 10 | class Site 11 | 12 | # Default output directory is +log/dnote/+. 13 | DEFAULT_OUTPUT = Pathname.new('log/dnote') 14 | 15 | # Title to use in any headers. 16 | attr_accessor :title 17 | 18 | # Directory to save output. 19 | attr_accessor :output 20 | 21 | # Additional Formats to supply besides the html (xml, rdoc, markdown, etc.) 22 | attr_accessor :formats 23 | 24 | # Notes object. 25 | attr_reader :notes 26 | 27 | def initialize(paths, options) 28 | initialize_defaults 29 | 30 | self.title = options[:title] if options[:title] 31 | 32 | self.output = options.delete(:output) if options[:output] 33 | self.formats = options.delete(:formats) if options[:formats] 34 | 35 | @notes = Notes.new(paths, options) 36 | end 37 | 38 | # 39 | def initialize_defaults 40 | @output = DEFAULT_OUTPUT 41 | @title = "Development Notes" 42 | @formats = ['html'] 43 | end 44 | 45 | # 46 | def output=(path) 47 | raise "output cannot be root" if File.expand_path(path) == "/" 48 | @output = Pathname.new(path) 49 | end 50 | 51 | # 52 | def document 53 | fu.mkdir_p(output) 54 | 55 | # produce requested additional formats 56 | formats.each do |format| 57 | =begin 58 | tdir = tempdir(format) 59 | 60 | # copy non-erb files 61 | files = Dir.entries(tdir) - ['.', '..'] 62 | files = files.reject{ |file| File.extname(file) == '.erb' } 63 | files.each do |file| 64 | dest = File.dirname(file).sub(tdir, '') 65 | fu.cp_r(File.join(tdir, file), output) 66 | end 67 | 68 | # write the erb templates 69 | templates(format).each do |temp| 70 | file = File.join(tdir, temp) 71 | erb = ERB.new(File.read(file)) 72 | text = erb.result(binding) 73 | write(temp.chomp('.erb'), text) 74 | end 75 | =end 76 | text = notes.to(format) 77 | write("notes.#{format}", text) 78 | end 79 | end 80 | 81 | # Reset output directory, marking it as out-of-date. 82 | def reset 83 | if File.directory?(output) 84 | File.utime(0,0,output) unless $NOOP 85 | puts "marked #{output}" 86 | end 87 | end 88 | 89 | # Remove output directory. 90 | def clean 91 | if File.directory?(output) 92 | fu.rm_r(output) 93 | puts "removed #{output}" 94 | end 95 | end 96 | 97 | # 98 | tempdir(format) 99 | "#{__DIR__}/templates/#{format}" 100 | end 101 | 102 | # TODO: Don't use chdir. 103 | def templates(format) 104 | temps = [] 105 | Dir.chdir(tempdir(format)) do 106 | temps = Dir['**/*.erb'] 107 | end 108 | temps 109 | end 110 | 111 | # Save file to output. 112 | # 113 | def write(fname, text) 114 | file = output + fname 115 | fu.mkdir_p(file.parent) 116 | File.open(file, 'w') { |f| f << text } unless $NOOP 117 | end 118 | 119 | def __DIR__ 120 | File.dirname(__FILE__) 121 | end 122 | 123 | # 124 | def fu 125 | @fu ||= ( 126 | if $NOOP and $DEBUG 127 | FileUtils::DryRun 128 | elsif $NOOP 129 | FileUtils::Noop 130 | elsif $DEBUG 131 | FileUtils::Verbose 132 | else 133 | FileUtils 134 | end 135 | ) 136 | end 137 | 138 | end 139 | 140 | end 141 | -------------------------------------------------------------------------------- /lib/dnote/note.rb: -------------------------------------------------------------------------------- 1 | module DNote 2 | 3 | # The Note class encapsulates a single note made in a source file. 4 | # 5 | # Each note instance holds a reference, +notes+, to the set of notes 6 | # being generated for a given session. This allows the note to access 7 | # general options applicable to all notes. 8 | class Note 9 | 10 | # Number of lines to provide in source context. 11 | #CONTEXT_DEPTH = 5 12 | 13 | # Set of notes to which this note belongs. 14 | attr :notes 15 | 16 | # The file in which the note is made. 17 | attr :file 18 | 19 | # The type of note. 20 | attr :label 21 | 22 | # The line number of the note. 23 | attr :line 24 | 25 | # The verbatim text of the note. 26 | attr :text 27 | 28 | # Remark marker used in parsing the note. 29 | attr :mark 30 | 31 | # Contextual lines of code. 32 | attr :capture 33 | 34 | # Initialize new Note instance. 35 | def initialize(notes, file, label, line, text, mark) 36 | @notes = notes 37 | 38 | @file = file 39 | @label = label 40 | @line = line 41 | @text = text.rstrip 42 | @mark = mark 43 | @capture = [] 44 | end 45 | 46 | # Convert to string representation. 47 | def to_s 48 | "#{label}: #{text}" 49 | end 50 | 51 | # Convert to string representation. 52 | def to_str 53 | "#{label}: #{text}" 54 | end 55 | 56 | # Remove newlines from note text. 57 | def textline 58 | text.gsub("\n", " ") 59 | end 60 | 61 | # Sort by file name and line number. 62 | def <=>(other) 63 | s = file <=> other.file 64 | return s unless s == 0 65 | line <=> other.line 66 | end 67 | 68 | # Convert to Hash. 69 | #-- 70 | # TODO: Add +url+? 71 | # TODO: Add +code+? Problem is that xml needs code in CDATA. 72 | #++ 73 | def to_h 74 | { 'label'=>label, 'text'=>textline, 'file'=>file, 'line'=>line } 75 | end 76 | 77 | # Convert to Hash, leaving the note text verbatim. 78 | def to_h_raw 79 | { 'label'=>label, 'text'=>text, 'file'=>file, 'line'=>line, 'code'=>code } 80 | end 81 | 82 | # Convert to JSON. 83 | def to_json(*args) 84 | to_h_raw.to_json(*args) 85 | end 86 | 87 | # Convert to YAML. 88 | def to_yaml(*args) 89 | to_h_raw.to_yaml(*args) 90 | end 91 | 92 | # Return line URL based on URL template. If no template was set, then 93 | # returns the file. 94 | def url 95 | if notes.url 96 | notes.url % [file, line] 97 | else 98 | file 99 | end 100 | end 101 | 102 | # 103 | def code 104 | unindent(capture).join 105 | end 106 | 107 | # Is there code to show? 108 | def code? 109 | !capture.empty? 110 | end 111 | 112 | =begin 113 | # This isn't being used currently b/c the URL solution as deeemd better, 114 | # but the code is here for custom templates. 115 | def capture 116 | @context ||= ( 117 | lines = file_cache(file) #.lines.to_a 118 | count = line() 119 | count +=1 while /^\s*#{mark}/ =~ lines[count] 120 | lines[count, context_depth] 121 | ) 122 | end 123 | 124 | # Read in +file+, parse into lines and cache. 125 | def file_cache(file) 126 | @@file_cache ||= {} 127 | @@file_cache[file] ||= File.read(file).lines.to_a 128 | end 129 | =end 130 | 131 | private 132 | 133 | # Remove blank space from lines. 134 | def unindent(lines) 135 | dents = [] 136 | lines.each do |line| 137 | if md = /^([\ ]*)/.match(line) 138 | size = md[1].size 139 | dents << md[1] 140 | end 141 | end 142 | dent = dents.min{ |a,b| a.size <=> b.size } 143 | lines.map do |line| 144 | line.sub(dent, '') 145 | end 146 | end 147 | 148 | end 149 | 150 | end 151 | -------------------------------------------------------------------------------- /lib/dnote/format.rb: -------------------------------------------------------------------------------- 1 | module DNote 2 | 3 | # = Notes Formatter 4 | # 5 | #-- 6 | # TODO: Need good CSS file. 7 | # 8 | # TODO: Need XSL? 9 | #++ 10 | class Format 11 | 12 | require 'fileutils' 13 | require 'erb' 14 | require 'rexml/text' 15 | require 'dnote/core_ext' 16 | 17 | #DEFAULT_OUTPUT_DIR = "log/dnote" 18 | 19 | EXTENSIONS = { 'text'=>'txt', 'soap'=>'xml', 'xoxo'=>'xml' } 20 | 21 | # 22 | attr :notes 23 | 24 | # 25 | attr_accessor :format 26 | 27 | # 28 | attr_accessor :subtype 29 | 30 | # 31 | attr_accessor :output 32 | 33 | # 34 | attr_accessor :template 35 | 36 | # 37 | attr_accessor :title 38 | 39 | # 40 | attr_accessor :dryrun 41 | 42 | # 43 | def initialize(notes, options={}) 44 | @notes = notes 45 | @format = 'text' 46 | @subtype = 'label' 47 | @title = "Developer's Notes" 48 | @dryrun = false 49 | options.each{ |k,v| __send__("#{k}=", v) if v } 50 | yield(self) if block_given? 51 | end 52 | 53 | # 54 | def render 55 | if notes.empty? 56 | $stderr << "No #{notes.labels.join(', ')} notes.\n" 57 | else 58 | case format 59 | when 'custom' 60 | render_custom 61 | else 62 | render_template 63 | end 64 | end 65 | end 66 | 67 | # C U S T O M 68 | 69 | # 70 | def render_custom 71 | #raise ArgumentError unless File.exist?(template) 72 | result = erb(template) 73 | publish(result) 74 | end 75 | 76 | # T E M P L A T E 77 | 78 | # 79 | def render_template 80 | template = File.join(File.dirname(__FILE__), 'templates', "#{format}.erb") 81 | raise "No such format - #{format}" unless File.exist?(template) 82 | result = erb(template) 83 | publish(result) 84 | end 85 | 86 | private 87 | 88 | # 89 | def erb(file) 90 | scope = ErbScope.new(:notes=>notes, :title=>title) 91 | scope.render(file) 92 | end 93 | 94 | # 95 | def publish(result, fname=nil) 96 | if output 97 | write(result, fname) 98 | else 99 | puts(result) 100 | end 101 | $stderr << "(" + notes.counts.map{|l,n| "#{n} #{l}s"}.join(', ') + ")\n" 102 | end 103 | 104 | # 105 | def write(result, fname=nil) 106 | if output.to_s[-1,1] == '/' || File.directory?(output) 107 | fmt = format.split('/').first 108 | ext = EXTENSIONS[fmt] || fmt 109 | file = File.join(output, fname || "notes.#{ext}") 110 | else 111 | file = output 112 | end 113 | if dryrun? 114 | puts "mkdir: #{File.dirname(file)}" 115 | puts "write: #{file}" 116 | else 117 | dir = File.dirname(file) 118 | fu.mkdir(dir) unless File.exist?(dir) 119 | File.open(file, 'w'){ |f| f << result } 120 | end 121 | return file 122 | end 123 | 124 | # 125 | def dryrun? 126 | @dryrun 127 | end 128 | 129 | # 130 | def debug? 131 | $DEBUG 132 | end 133 | 134 | # 135 | def fu 136 | @fu ||= ( 137 | if dryrun? and debug? 138 | FileUtils::DryRun 139 | elsif dryrun? 140 | FileUtils::Noop 141 | elsif debug? 142 | FileUtils::Verbose 143 | else 144 | FileUtils 145 | end 146 | ) 147 | end 148 | 149 | # 150 | class ErbScope 151 | # 152 | def initialize(data={}) 153 | @data = data 154 | end 155 | # 156 | def render(file) 157 | erb = ERB.new(File.read(file), nil, '<>') 158 | erb.result(binding) 159 | end 160 | # 161 | def h(string) 162 | REXML::Text.normalize(string) 163 | end 164 | # 165 | def method_missing(s, *a) 166 | @data[s.to_sym] 167 | end 168 | end 169 | 170 | end 171 | 172 | end 173 | 174 | -------------------------------------------------------------------------------- /work/command.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | module DNote 4 | require 'optparse' 5 | require 'dnote' 6 | 7 | def self.run 8 | options = {} 9 | format = 'rdoc' 10 | 11 | opts = OptionParser.new do |opt| 12 | 13 | opt.banner = "Usage: dnote [OPTIONS] path1 [path2 ...]" 14 | 15 | opt.separator(" ") 16 | opt.separator("OUTPUT FORMAT: (choose one)") 17 | 18 | opt.on("--gnu", "Plain text format (default)") do 19 | options[:format] = 'gnu' 20 | end 21 | 22 | opt.on("--gnufile", "Plain text format by file") do 23 | options[:format] = 'gnufile' 24 | end 25 | 26 | opt.on("--rdoc", "RDoc comment format") do 27 | options[:format] = 'rdoc' 28 | end 29 | 30 | opt.on("--markdown", "Markdown wiki format") do 31 | options[:format] = 'markdown' 32 | end 33 | 34 | opt.on("--soap", "SOAP XML envelope format") do 35 | options[:format] = 'soap' 36 | end 37 | 38 | opt.on("--xoxo", "XOXO microformat format") do 39 | options[:format] = 'xoxo' 40 | end 41 | 42 | opt.on("--xml", "XML markup format") do 43 | options[:format] = 'xml' 44 | end 45 | 46 | opt.on("--html", "HTML markup format") do 47 | options[:format] = 'html' 48 | end 49 | 50 | opt.on("--yaml", "YAML serialization format") do 51 | options[:format] = 'yaml' 52 | end 53 | 54 | opt.on("--json", "JSON serialization format") do 55 | options[:format] = 'json' 56 | end 57 | 58 | # TODO 59 | opt.on("--format", "-f NAME", "Select a format") do |format| 60 | options[:format] = format 61 | end 62 | 63 | opt.on("--template", "-t FILE", "Use a custom Erb template") do |file| 64 | options[:format] = 'custom' 65 | options[:template] = file 66 | end 67 | 68 | opt.separator(" ") 69 | opt.separator("OTHER OPTIONS:") 70 | 71 | opt.on("--label", "labels to collect") do |lbl| 72 | options[:labels] ||= [] 73 | options[:labels] << lbl 74 | end 75 | 76 | # TODO 77 | opt.on("--exclude", "-x PATH", "exclude file or directory") do |path| 78 | options[:exclude] ||= [] 79 | options[:exclude] << path 80 | end 81 | 82 | opt.on("--title", "-T TITLE", "title to use in headers") do |title| 83 | options[:title] = title 84 | end 85 | 86 | opt.on("--output", "-o PATH", "name of file or directory") do |path| 87 | options[:output] = path 88 | end 89 | 90 | opt.separator(" ") 91 | opt.separator("STANDARD OPTIONS:") 92 | 93 | #opt.on("--verbose", "-v", "extra verbose output") do 94 | # options[:verbose] = true 95 | #end 96 | 97 | opt.on("--debug", "debug mode") do 98 | $DEBUG = true 99 | end 100 | 101 | #opt.on("--quiet", "-q", "surpress non-essential output") do 102 | # options[:quiet] = true 103 | #end 104 | 105 | #opt.on("--noharm", "-n", "only pretend to write to disk") do 106 | # options[:noharm] = true 107 | #end 108 | 109 | opt.on("--dryrun", "-n", "do not actually write to disk") do 110 | options[:dryrun] = true 111 | end 112 | 113 | #opt.on("--trace", "debug and verbose modes combined") do 114 | # $DEBUG = true 115 | # options[:verbose] = true 116 | #end 117 | 118 | opt.on_tail('--help', '-h', "show this help information") do 119 | puts opt 120 | exit 121 | end 122 | 123 | end 124 | 125 | begin 126 | opts.parse! 127 | rescue => err 128 | puts err 129 | exit 1 130 | end 131 | 132 | paths = ARGV.dup 133 | paths = ['**/*.rb'] if paths.empty? 134 | 135 | notes = Notes.new(paths, options[:labels]) 136 | format = Format.new(notes, options) 137 | format.render 138 | 139 | # NOTE: If DNote were a class. 140 | 141 | #if output 142 | # dnote.save(format, output) 143 | #else 144 | # dnote.display(format) 145 | #end 146 | end 147 | 148 | end 149 | 150 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | = DNote 2 | 3 | * {Homepage}[http://rubyworks.github.com/dnote] 4 | * {Mailing List}[http://googlegroups.com/group/rubyworks-mailinglist] 5 | * {Source Code}[http://github.com/rubyworks/dnote] 6 | 7 | 8 | == DESCRIPTION 9 | 10 | Extract development notes from source code and generate some nice 11 | output formats for them. 12 | 13 | 14 | == SYNOPSIS 15 | 16 | === Note Structure 17 | 18 | DNote scans for the common note patterns used by developers of many languages in the form of an 19 | all-caps labels followed by a colon. To be more specific, for DNote to recognize a note, 20 | it needs to follow this simple set of rules: 21 | 22 | 1) Notes start with an all-caps label punctuated with a colon, followed by the note's text. 23 | 24 | # LABEL: description ... 25 | 26 | 2) Any note that requires more than one line must remain flush to the left 27 | margin (the margin is set by the first line). This is done because RDoc will mistake 28 | the note for a pre block if it is indented. 29 | 30 | # LABEL: description ... 31 | # continue ... 32 | 33 | 3) An alternative to the previous limitation is to indent the whole note, making it 34 | a <pre> block when rendered by RDoc. Then the text layout is free-form. 35 | 36 | # This is a description of something... 37 | # 38 | # LABEL: description ... 39 | # continue ... 40 | 41 | That's all there is to it, if I can convince the developers of RDoc to recognize labels, 42 | we may eventually be able to relax the flush rule too, which would be very nice. 43 | 44 | There is also a command-line option, --no-colon, which deactives the need for 45 | a colon after the note label. However this often produces false positives, so it's use is 46 | discouraged. 47 | 48 | 49 | === Generating Notes 50 | 51 | As you can see the commandline interface is pretty straight-forward. 52 | 53 | USAGE: 54 | 55 | dnote [OPTIONS] path1 [path2 ...] 56 | 57 | OUTPUT FORMAT: (choose one) 58 | -f, --format NAME select a format [text] 59 | -c, --custom FILE use a custom ERB template 60 | --file shortcut for text/file format 61 | --list shortcut for text/list format 62 | 63 | OTHER OPTIONS: 64 | -l, --label LABEL labels to collect 65 | --[no-]colon match labels with/without colon suffix 66 | -m, --marker MARK alternative remark marker 67 | -u --url TEMPLATE url template for line entries (for HTML) 68 | -x, --exclude PATH exclude file or directory 69 | -i, --ignore NAME ignore based on any part of the pathname 70 | -t, --title TITLE title to use in header 71 | -o, --output PATH name of file or directory 72 | -n, --dryrun do not actually write to disk 73 | --debug debug mode 74 | 75 | COMMAND OPTIONS: 76 | -T, --templates list available format templates 77 | -h, --help show this help information 78 | 79 | The default path is **/*.rb and the default format is -f rdoc. 80 | Here is an example of DNote's current notes in RDoc format: 81 | 82 | = Development Notes 83 | 84 | == TODO 85 | 86 | === file://lib/dnote/notes.rb 87 | 88 | * TODO: Add ability to read header notes. They often 89 | have a outline format, rather then the single line. (19) 90 | * TODO: Need good CSS file. (22) 91 | * TODO: Need XSL? (24) 92 | 93 | === file://plug/syckle/services/dnote.rb 94 | 95 | * TODO: Should this service be part of the +site+ cycle? (18) 96 | 97 | (4 TODOs) 98 | 99 | 100 | == INSTALLATION 101 | 102 | The usual RubyGem's command will do the trick. 103 | 104 | $ sudo gem install dnote 105 | 106 | 107 | == COPYRIGHT 108 | 109 | Copyright (c) 2006 Thomas Sawyer, Rubyworks 110 | 111 | DNote is distributable in accordance with the terms of the *FreeBSD* license. 112 | 113 | See COPYING.rdoc for details. 114 | -------------------------------------------------------------------------------- /.gemspec: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'yaml' 4 | 5 | metadata = YAML.load_file('.ruby') 6 | 7 | manifest = Dir.glob('manifest{,.txt}', File::FNM_CASEFOLD).first 8 | 9 | 10 | Gem::Specification.new do |gemspec| 11 | 12 | scm = case 13 | when File.directory?('.git') 14 | :git 15 | end 16 | 17 | files = case 18 | when manifest 19 | File.readlines(manifest). 20 | map{ |line| line.strip }. 21 | reject{ |line| line.empty? || line[0,1] == '#' } 22 | when scm == :git 23 | `git ls-files -z`.split("\0") 24 | else 25 | Dir.glob('{**/}{.*,*}') # TODO: be more specific using standard locations ? 26 | end.select{ |path| File.file?(path) } 27 | 28 | patterns = { 29 | :bin_files => 'bin/*', 30 | :lib_files => 'lib/{**/}*.rb', 31 | :ext_files => 'ext/{**/}extconf.rb', 32 | :doc_files => '*.{txt,rdoc,md,markdown,tt,textile}', 33 | :test_files => '{test/{**/}*_test.rb,spec/{**/}*_spec.rb}' 34 | } 35 | 36 | glob_files = lambda { |pattern| 37 | Dir.glob(pattern).select { |path| 38 | File.file?(path) && files.include?(path) 39 | } 40 | } 41 | 42 | #files = glob_files[patterns[:files]] 43 | 44 | executables = glob_files[patterns[:bin_files]].map do |path| 45 | File.basename(path) 46 | end 47 | 48 | extensions = glob_files[patterns[:ext_files]].map do |path| 49 | File.basename(path) 50 | end 51 | 52 | # build-out the gemspec 53 | 54 | case metadata['revision'] 55 | when 0 56 | gemspec.name = metadata['name'] 57 | gemspec.version = metadata['version'] 58 | gemspec.summary = metadata['summary'] 59 | gemspec.description = metadata['description'] 60 | 61 | metadata['authors'].each do |author| 62 | gemspec.authors << author['name'] 63 | 64 | if author.has_key?('email') 65 | if gemspec.email 66 | gemspec.email << author['email'] 67 | else 68 | gemspec.email = [author['email']] 69 | end 70 | end 71 | end 72 | 73 | gemspec.licenses = metadata['copyrights'].map{ |c| c['license'] }.compact 74 | 75 | metadata['requirements'].each do |req| 76 | name = req['name'] 77 | version = req['version'] 78 | groups = req['groups'] || [] 79 | 80 | case version 81 | when /^(.*?)\+$/ 82 | version = ">= #{$1}" 83 | when /^(.*?)\-$/ 84 | version = "< #{$1}" 85 | when /^(.*?)\~$/ 86 | version = "~> #{$1}" 87 | end 88 | 89 | if groups.empty? or groups.include?('runtime') 90 | # populate runtime dependencies 91 | if gemspec.respond_to?(:add_runtime_dependency) 92 | gemspec.add_runtime_dependency(name,*version) 93 | else 94 | gemspec.add_dependency(name,*version) 95 | end 96 | else 97 | # populate development dependencies 98 | if gemspec.respond_to?(:add_development_dependency) 99 | gemspec.add_development_dependency(name,*version) 100 | else 101 | gemspec.add_dependency(name,*version) 102 | end 103 | end 104 | end 105 | 106 | # convert external dependencies into a requirements 107 | if metadata['external_dependencies'] 108 | ##gemspec.requirements = [] unless metadata['external_dependencies'].empty? 109 | metadata['external_dependencies'].each do |req| 110 | gemspec.requirements << req.to_s 111 | end 112 | end 113 | 114 | # determine homepage from resources 115 | homepage = metadata['resources'].find{ |key, url| key =~ /^home/ } 116 | gemspec.homepage = homepage.last if homepage 117 | 118 | gemspec.require_paths = metadata['load_path'] || ['lib'] 119 | gemspec.post_install_message = metadata['install_message'] 120 | 121 | # RubyGems specific metadata 122 | gemspec.files = files 123 | gemspec.extensions = extensions 124 | gemspec.executables = executables 125 | 126 | if Gem::VERSION < '1.7.' 127 | gemspec.default_executable = gemspec.executables.first 128 | end 129 | 130 | gemspec.test_files = glob_files[patterns[:test_files]] 131 | 132 | unless gemspec.files.include?('.document') 133 | gemspec.extra_rdoc_files = glob_files[patterns[:doc_files]] 134 | end 135 | end 136 | end 137 | -------------------------------------------------------------------------------- /HISTORY.rdoc: -------------------------------------------------------------------------------- 1 | = RELEASE HISTORY 2 | 3 | 4 | == 1.7.0 / 2011-10-25 5 | 6 | This release finishes adding code context support to all formats. Use the `-c` 7 | option to set the number of lines to include. Note also that the rake task has 8 | been moved to `dnote/rake/dnotetask`. 9 | 10 | Changes: 11 | 12 | * Support code contexts in all formats. 13 | * Rake task moved to `dnote/rake/dnotetask`. 14 | 15 | 16 | == 1.6.1 / 2011-05-18 17 | 18 | Last release reused the `-c` option for context lines. This clobbered 19 | the shortcut option for `--custom`. To fix, this release capitalizes 20 | the custom shortcut to `-C`. 21 | 22 | Changes: 23 | 24 | * Capitalize custom shortcut option (-c becomes -C). 25 | 26 | 27 | == 1.6.0 / 2011-05-17 28 | 29 | This release adds code conext output. Select the number of 30 | context lines to display with the `-c` option. Currently 31 | only the HTML, YAML and JSON formats support context. 32 | 33 | This release also fixes the false note match that can arise when 34 | a class or module name with double-colons starts a comment line. 35 | We have changed the regex to ALWAYS REQUIRE a space after the 36 | label colon. Please make a note of it! 37 | 38 | Changes: 39 | 40 | * Add code context count option and output. 41 | * Regex changed to REQUIRE space after label colon. 42 | 43 | 44 | == 1.5.0 / 2010-11-27 45 | 46 | This release adds a `--url` option for defining a URL template 47 | string used in certain HTML/XML formats. In addition a parsing 48 | bug was corrected that limited some notes to one line. 49 | 50 | Changes: 51 | 52 | * Add support for configurable URLs. 53 | * Fix multi-line note parsing bug. 54 | 55 | 56 | == 1.4.0 / 2010-06-26 57 | 58 | This release adds auto-determination of marker based on file 59 | extension. This means DNote can now scan multiple file types 60 | in a single pass. The supported types are fairly small at this 61 | point, but cover the command cases and will expand in the future. 62 | The fallback marker is '#'. You can still force a particular 63 | marker using the --marker option. 64 | 65 | Changes: 66 | 67 | * Add support for automatic markers. 68 | 69 | 70 | == 1.3.1 / 2010-06-11 71 | 72 | This release fixes a simple bug. Some version of Ruby do not have 73 | the Enumerable#group_by method. This release provides it if it is 74 | missing. 75 | 76 | Changes: 77 | 78 | * Bug fix, provide #group_by when it is not present. 79 | 80 | 81 | == 1.3.0 / 2010-06-04 82 | 83 | This release adds support for alternate comment markers. Simply 84 | supply the marker with the -m option. 85 | 86 | Changes: 87 | 88 | * Add support for languages other than Ruby. 89 | 90 | 91 | == 1.2.0 / 2010-02-18 92 | 93 | By request this release adds additional formats, allowing notes to be 94 | sorted by filename as well a label. In addition, paths can now be 95 | excluded from the search. To implement these changes it was necessary 96 | to makes some significant under-the-hood adjustments, and this required 97 | making some adjustments to the design of the templates and the 98 | command-line interface. 99 | 100 | If are using any of your own custom templates you will have to make 101 | adjustments accordingly --which basically entails exchanging +notes.each+ 102 | for +notes.by_label_file.each+. Also, notice that the serialization formats 103 | have changed accordingly to match the improved underlying model. 104 | 105 | On the command-line, the format type itself is no longer used as the 106 | option name, e.g. "--yaml", but instead use "-f", e.g. "-f yaml". This 107 | opens DNote up to many more templates. Use "-T" to get a list of all 108 | template format available. 109 | 110 | Changes: 111 | 112 | * Improve underlying model by adding a Note class. 113 | * Add --exclude and --ignore options. 114 | * Add new formats and rename "gnu" formats to "text". 115 | * Use --format/-f to designate formats. 116 | * Renamed --template/-t option to --custom/-c. 117 | * Add --templates/-T to list all format templates. 118 | 119 | 120 | == 1.1.0 / 2010-02-06 121 | 122 | This relase primarily adjusts the way output it rendered 123 | underthehood. Serialization formats are rendered as 124 | before but markup formats now use Erb. In the process 125 | two new serialization formats have been added: soap and xoxo. 126 | 127 | Also, this release renames the lib/plugin directory to 128 | lib/plugins (plural) per the new convention of the latest 129 | Plugin gem. This only matters to Syckle users. 130 | 131 | Changes: 132 | 133 | * Add Soap and XOXO formats. 134 | * Add actual --output option. 135 | * Move lib/plugin to lib/plugins. 136 | 137 | 138 | == 1.0.0 / 2009-10-25 139 | 140 | This relase adds support for arbitrary note labels. 141 | Any all-caps word followed by a colon will be 142 | picked-up. Common labels like TODO and FIXME are 143 | recognized without the trailing ':'. 144 | 145 | Changes: 146 | 147 | * Added support for arbitrary labels. 148 | 149 | 150 | == 0.9.0 / 2009-10-10 151 | 152 | This release adds a syckle plugin and improves output. 153 | 154 | Changes: 155 | 156 | * Add syckle plugin, based on old Notes plugin. 157 | * Improved HTML output ( tag should have been ). 158 | * If output not specified, sends rdoc to stdout. 159 | * If no paths specified, will scan '**/*.rb' 160 | 161 | 162 | == 0.8.0 / 2009-10-09 163 | 164 | This is the initial release of DNote. DNote is a spin-off 165 | of a Syckle (formerlly Reap) plugin which scans source 166 | code and compiles a list of developer notes. 167 | 168 | Changes: 169 | 170 | * Happy Birthday! 171 | 172 | -------------------------------------------------------------------------------- /lib/dnote/session.rb: -------------------------------------------------------------------------------- 1 | module DNote 2 | 3 | require 'dnote/core_ext' 4 | require 'dnote/notes' 5 | require 'dnote/format' 6 | 7 | # User session which is used by commandline interface. 8 | # 9 | # By making this a class it makes it easy for external 10 | # libraries to use this library just as if they were 11 | # calling the commandline, but without the need to shellout. 12 | # 13 | class Session 14 | 15 | # Directory relative to this script. This is used 16 | # to lookup the available format templates. 17 | DIR = File.dirname(__FILE__) 18 | 19 | # Default format. 20 | DEFAULT_FORMAT = "text" 21 | 22 | # Default title. 23 | DEFAULT_TITLE = "Developer's Notes" 24 | 25 | # Paths to include. 26 | attr_accessor :paths 27 | 28 | # Paths to exclude (match by pathname). 29 | attr_reader :exclude 30 | 31 | # Paths to ignore (match by basename). 32 | attr_reader :ignore 33 | 34 | # Labels to lookup. 35 | # By default these are TODO, FIXME and OPTIMIZE. 36 | attr_accessor :labels 37 | 38 | # Selected labels can optionally do without the colon. 39 | attr_accessor :colon 40 | 41 | # Alternate remark marker. Useful to other languages besides Ruby. 42 | attr_accessor :marker 43 | 44 | # Output format. 45 | attr_accessor :format 46 | 47 | # If custom format, specify template file. 48 | attr_accessor :template 49 | 50 | # Some format put a title at the top of the output. 51 | # The default is "Developer's Notes". 52 | attr_accessor :title 53 | 54 | # Output to a file instead of STDOUT. 55 | attr_accessor :output 56 | 57 | # If output path given, don't actually write to disk. 58 | attr_accessor :dryrun 59 | 60 | # String template for line URLs (mainly for HTML format). For example, 61 | # DNote uses GitHub so we could use a link template: 62 | # 63 | # "https://github.com/rubyworks/dnote/blob/master/%s#L%s" 64 | # 65 | attr_accessor :url 66 | 67 | # Number of lines of context to display. The default is zero. 68 | attr_accessor :context 69 | 70 | private 71 | 72 | # New Session. 73 | def initialize(options={}) 74 | options ||= {} 75 | initialize_defaults 76 | options.each{ |k,v| __send__("#{k}=", v) } 77 | yield(self) if block_given? 78 | end 79 | 80 | # Set default values for attributes. 81 | def initialize_defaults 82 | @paths = [] 83 | @labels = [] 84 | @exclude = [] 85 | @ignore = [] 86 | @format = DEFAULT_FORMAT 87 | @title = DEFAULT_TITLE 88 | @dryrun = false 89 | @marker = nil 90 | @url = nil 91 | @context = 0 92 | end 93 | 94 | public 95 | 96 | # Set exclude list ensuring that the value is an array. 97 | def exclude=(list) 98 | @exclude = [list].compact.flatten.compact 99 | end 100 | 101 | # Set ignore list ensuring that the value is an array. 102 | def ignore=(list) 103 | @ignore = [list].compact.flatten.compact 104 | end 105 | 106 | # Run session. 107 | def run 108 | notes = Notes.new(files, :labels=>labels, :colon=>colon, :marker=>marker, :url=>url, :context=>context) 109 | formatter = Format.new(notes) do |f| 110 | f.format = format 111 | f.template = template 112 | f.title = title 113 | f.output = output 114 | end 115 | formatter.render 116 | end 117 | 118 | # Collect path globs and remove exclusions. 119 | # This method uses #paths, #exclude and #ignore to 120 | # compile the list of files. 121 | def files 122 | list = [paths].flatten.compact 123 | list = ['**/*.rb'] if list.empty? 124 | list = glob(list) 125 | list = list - glob(exclude) 126 | list.reject do |path| 127 | path.split('/').any?{ |part| ignore.any?{ |ig| File.fnmatch?(ig, part) } } 128 | end 129 | end 130 | 131 | # Collect the file glob of each path given. If 132 | # a path is a directory, inclue all content. 133 | def glob(paths) 134 | paths.map do |path| 135 | if File.directory?(path) 136 | Dir.glob(File.join(path, '**/*')) 137 | else 138 | Dir.glob(path) 139 | end 140 | end.flatten.uniq 141 | end 142 | 143 | # Set special labels. 144 | #def labels=(labels) 145 | # @labels = ( 146 | # case labels 147 | # when String 148 | # labels.split(/[:;,]/) 149 | # else 150 | # labels = [labels].flatten.compact.uniq.map{ |s| s.to_s } 151 | # end 152 | # ) 153 | #end 154 | 155 | # Commandline interface. 156 | def self.main(*argv) 157 | require 'optparse' 158 | 159 | session = Session.new 160 | 161 | opts = OptionParser.new do |opt| 162 | opt.banner = "DNote v#{DNote::VERSION}" 163 | 164 | opt.separator(" ") 165 | opt.separator("USAGE:\n dnote [OPTIONS] path1 [path2 ...]") 166 | 167 | opt.separator(" ") 168 | opt.separator("OUTPUT FORMAT: (choose one)") 169 | 170 | opt.on("--format", "-f NAME", "select a format [text]") do |format| 171 | session.format = format 172 | end 173 | 174 | opt.on("--custom", "-C FILE", "use a custom ERB template") do |file| 175 | session.format = 'custom' 176 | session.template = file 177 | end 178 | 179 | opt.on("--file", "shortcut for text/file format") do 180 | session.format = 'text/file' 181 | end 182 | 183 | opt.on("--list", "shortcut for text/list format") do 184 | session.format = 'text/list' 185 | end 186 | 187 | opt.separator(" ") 188 | opt.separator("OTHER OPTIONS:") 189 | 190 | opt.on("--label", "-l LABEL", "labels to collect") do |lbl| 191 | session.labels.concat(lbl.split(':')) 192 | end 193 | 194 | opt.on("--[no-]colon", "match labels with/without colon suffix") do |val| 195 | session.colon = val 196 | end 197 | 198 | opt.on("--marker", "-m MARK", "alternative remark marker") do |mark| 199 | session.marker = mark 200 | end 201 | 202 | opt.on("--url", "-u TEMPLATE", "url template for line entries (for HTML)") do |url| 203 | session.url = url 204 | end 205 | 206 | opt.on("--context", "-c INTEGER", "number of lines of context to display") do |int| 207 | session.context = int.to_i 208 | end 209 | 210 | opt.on("--exclude", "-x PATH", "exclude file or directory") do |path| 211 | session.exclude << path 212 | end 213 | 214 | opt.on("--ignore", "-i NAME", "ignore file based on any part of pathname") do |name| 215 | session.ignore << name 216 | end 217 | 218 | opt.on("--title", "-t TITLE", "title to use in header") do |title| 219 | session.title = title 220 | end 221 | 222 | opt.on("--output", "-o PATH", "save to file or directory") do |path| 223 | session.output = path 224 | end 225 | 226 | opt.on("--dryrun", "-n", "do not actually write to disk") do 227 | session.dryrun = true 228 | end 229 | 230 | opt.on("--debug", "debug mode") do 231 | $DEBUG = true 232 | $VERBOSE = true 233 | end 234 | 235 | opt.separator(" ") 236 | opt.separator("COMMAND OPTIONS:") 237 | 238 | opt.on_tail('--templates', "-T", "list available format templates") do 239 | tdir = File.join(DIR, 'templates') 240 | tfiles = Dir[File.join(tdir, '**/*.erb')] 241 | tnames = tfiles.map{ |tname| tname.sub(tdir+'/', '').chomp('.erb') } 242 | groups = tnames.group_by{ |tname| tname.split('/').first } 243 | groups.sort.each do |(type, names)| 244 | puts("%-18s " * names.size % names.sort) 245 | end 246 | exit 247 | end 248 | 249 | opt.on_tail('--help', '-h', "show this help information") do 250 | puts opt 251 | exit 252 | end 253 | end 254 | 255 | begin 256 | opts.parse!(argv) 257 | session.paths.replace(argv) 258 | session.run 259 | rescue => err 260 | raise err if $DEBUG 261 | puts err 262 | exit 1 263 | end 264 | end 265 | 266 | end 267 | 268 | end 269 | 270 | -------------------------------------------------------------------------------- /lib/dnote/notes.rb: -------------------------------------------------------------------------------- 1 | require 'pathname' 2 | require 'dnote/note' 3 | 4 | module DNote 5 | 6 | # = Developer Notes 7 | # 8 | # This class goes through you source files and compiles a list 9 | # of any labeled comments. Labels are all-cap single word prefixes 10 | # to a comment ending in a colon. 11 | # 12 | # Special labels do not require the colon. By default these are 13 | # +TODO+, +FIXME+, +OPTIMIZE+, +THINK+ and +DEPRECATE+. 14 | # 15 | #-- 16 | # TODO: Add ability to read header notes. They often 17 | # have a outline format, rather then the single line. 18 | #++ 19 | class Notes 20 | include Enumerable 21 | 22 | # Default paths (all ruby scripts). 23 | DEFAULT_PATHS = ["**/*.rb"] 24 | 25 | # Default note labels to look for in source code. (NOT CURRENTLY USED!) 26 | DEFAULT_LABELS = ['TODO', 'FIXME', 'OPTIMIZE', 'THINK', 'DEPRECATE'] 27 | 28 | # Files to search for notes. 29 | attr_accessor :files 30 | 31 | # Labels to document. Defaults are: +TODO+, +FIXME+, +OPTIMIZE+ and +DEPRECATE+. 32 | attr_accessor :labels 33 | 34 | # Require label colon? Default is +true+. 35 | attr_accessor :colon 36 | 37 | # Specific remark marker (+nil+ for auto). 38 | attr_accessor :marker 39 | 40 | # Link template. 41 | attr_accessor :url 42 | 43 | # Number of lines of context to show. 44 | attr_accessor :context 45 | 46 | # New set of notes for give +files+ and optional special labels. 47 | def initialize(files, options={}) 48 | @files = [files].flatten 49 | @labels = [options[:labels] || DEFAULT_LABELS].flatten.compact 50 | @colon = options[:colon].nil? ? true : options[:colon] 51 | @marker = options[:marker] #|| '#' 52 | @url = options[:url] 53 | @context = options[:context] || 0 54 | 55 | @remark = {} 56 | 57 | parse 58 | end 59 | 60 | # Array of notes. 61 | def notes 62 | @notes 63 | end 64 | 65 | # Notes counts by label. 66 | def counts 67 | @counts ||= ( 68 | h = {} 69 | by_label.each do |label, notes| 70 | h[label] = notes.size 71 | end 72 | h 73 | ) 74 | end 75 | 76 | # Iterate through notes. 77 | def each(&block) 78 | notes.each(&block) 79 | end 80 | 81 | # No notes? 82 | def empty? 83 | notes.empty? 84 | end 85 | 86 | # Gather notes. 87 | #-- 88 | # TODO: Play golf with Notes#parse. 89 | #++ 90 | def parse 91 | records = [] 92 | files.each do |fname| 93 | next unless File.file?(fname) 94 | #next unless fname =~ /\.rb$/ # TODO: should this be done? 95 | mark = remark(fname) 96 | lineno, note, text, capt = 0, nil, nil, nil 97 | File.readlines(fname).each do |line| 98 | #while line = f.gets 99 | lineno += 1 100 | note = match(line, lineno, fname) 101 | if note 102 | #file = fname 103 | text = note.text 104 | capt = note.capture 105 | #note = {'label'=>label,'file'=>file,'line'=>line_no,'note'=>text} 106 | records << note 107 | else 108 | if text 109 | case line 110 | when /^\s*#{mark}+\s*$/, /^\s*#{mark}\-\-/, /^\s*#{mark}\+\+/ 111 | text.strip! 112 | text = nil 113 | when /^\s*#{mark}/ 114 | if text[-1,1] == "\n" 115 | text << line.gsub(/^\s*#{mark}\s*/,'') 116 | else 117 | text << "\n" << line.gsub(/^\s*#{mark}\s*/,'') 118 | end 119 | else 120 | text.strip! 121 | text = nil 122 | end 123 | else 124 | if line !~ /^\s*#{mark}/ 125 | capt << line if capt && capt.size < context 126 | end 127 | end 128 | end 129 | #end 130 | end 131 | end 132 | 133 | @notes = records.sort 134 | end 135 | 136 | # Is this line a note? 137 | def match(line, lineno, file) 138 | if labels.empty? 139 | match_general(line, lineno, file) 140 | else 141 | match_special(line, lineno, file) 142 | end 143 | end 144 | 145 | # Match special notes. 146 | def match_special(line, lineno, file) 147 | rec = nil 148 | labels.each do |label| 149 | if md = match_special_regex(label, file).match(line) 150 | text = md[1] 151 | #rec = {'label'=>label,'file'=>file,'line'=>lineno,'note'=>text} 152 | rec = Note.new(self, file, label, lineno, text, remark(file)) 153 | end 154 | end 155 | rec 156 | end 157 | 158 | #-- 159 | # TODO: ruby-1.9.1-p378 reports: `match': invalid byte sequence in UTF-8 160 | #++ 161 | def match_special_regex(label, file) 162 | mark = remark(file) 163 | if colon 164 | /#{mark}\s*#{Regexp.escape(label)}[:]\s+(.*?)$/ 165 | else 166 | /#{mark}\s*#{Regexp.escape(label)}[:]?\s+(.*?)$/ 167 | end 168 | end 169 | 170 | # Match notes that are labeled with a colon. 171 | def match_general(line, lineno, file) 172 | rec = nil 173 | if md = match_general_regex(file).match(line) 174 | label, text = md[1], md[2] 175 | #rec = {'label'=>label,'file'=>file,'line'=>lineno,'note'=>text} 176 | rec = Note.new(self, file, label, lineno, text, remark(file)) 177 | end 178 | return rec 179 | end 180 | 181 | # Keep in mind that general non-colon matches have a higher potential 182 | # of false positives. 183 | def match_general_regex(file) 184 | mark = remark(file) 185 | if colon 186 | /#{mark}\s*([A-Z]+)[:]\s+(.*?)$/ 187 | else 188 | /#{mark}\s*([A-Z]+)\s+(.*?)$/ 189 | end 190 | end 191 | 192 | # Organize notes into a hash with labels for keys. 193 | def by_label 194 | @by_label ||= ( 195 | list = {} 196 | notes.each do |note| 197 | list[note.label] ||= [] 198 | list[note.label] << note 199 | list[note.label].sort #!{ |a,b| a.line <=> b.line } 200 | end 201 | list 202 | ) 203 | end 204 | 205 | # Organize notes into a hash with filename for keys. 206 | def by_file 207 | @by_file ||= ( 208 | list = {} 209 | notes.each do |note| 210 | list[note.file] ||= [] 211 | list[note.file] << note 212 | list[note.file].sort! #!{ |a,b| a.line <=> b.line } 213 | end 214 | list 215 | ) 216 | end 217 | 218 | # Organize notes into a hash with labels for keys, followed 219 | # by a hash with filename for keys. 220 | def by_label_file 221 | @by_label ||= ( 222 | list = {} 223 | notes.each do |note| 224 | list[note.label] ||= {} 225 | list[note.label][note.file] ||= [] 226 | list[note.label][note.file] << note 227 | list[note.label][note.file].sort! #{ |a,b| a.line <=> b.line } 228 | end 229 | list 230 | ) 231 | end 232 | 233 | # Organize notes into a hash with filenames for keys, followed 234 | # by a hash with labels for keys. 235 | def by_file_label 236 | @by_file ||= ( 237 | list = {} 238 | notes.each do |note| 239 | list[note.file] ||= {} 240 | list[note.file][note.label] ||= [] 241 | list[note.file][note.label] << note 242 | list[note.file][note.label].sort! #{ |a,b| a.line <=> b.line } 243 | end 244 | list 245 | ) 246 | end 247 | 248 | # Convert to an array of hashes. 249 | def to_a 250 | notes.map{ |n| n.to_h } 251 | end 252 | 253 | # Same as #by_label. 254 | def to_h 255 | by_label 256 | end 257 | 258 | # 259 | def remark(file) 260 | @remark[File.extname(file)] ||= ( 261 | mark = guess_marker(file) 262 | Regexp.escape(mark) 263 | ) 264 | end 265 | 266 | # Guess marker based on file extension. Fallsback to '#' 267 | # if the extension is unknown. 268 | # 269 | # TODO: Continue to add comment types. 270 | def guess_marker(file) 271 | return @marker if @marker # forced marker 272 | case File.extname(file) 273 | when '.js', '.c', 'cpp', '.css' 274 | '//' 275 | when '.bas' 276 | "'" 277 | when '.sql', '.ada' 278 | '--' 279 | when '.asm' 280 | ';' 281 | else 282 | '#' 283 | end 284 | end 285 | 286 | # Convert to array of hashes then to YAML. 287 | #def to_yaml 288 | # require 'yaml' 289 | # to_a.to_yaml 290 | #end 291 | 292 | # Convert to array of hashes then to JSON. 293 | #def to_json 294 | # begin 295 | # require 'json' 296 | # rescue LoadError 297 | # require 'json_pure' 298 | # end 299 | # to_a.to_json 300 | #end 301 | 302 | # Convert to array of hashes then to a SOAP XML envelope. 303 | #def to_soap 304 | # require 'soap/marshal' 305 | # SOAP::Marshal.marshal(to_a) 306 | #end 307 | 308 | # XOXO microformat. 309 | #-- 310 | # TODO: Would to_xoxo be better organized by label and or file? 311 | #++ 312 | #def to_xoxo 313 | # require 'xoxo' 314 | # to_a.to_xoxo 315 | #end 316 | 317 | end 318 | 319 | end 320 | 321 | --------------------------------------------------------------------------------