augment 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ == 1.0.0 / 2007-10-27
2
+
3
+ * Works with a minimal set of backends and frontends
4
+ * Birthday!
5
+
@@ -0,0 +1,27 @@
1
+ COPYING
2
+ TODO
3
+ History.txt
4
+ Manifest.txt
5
+ README.txt
6
+ Rakefile
7
+ bin/augment
8
+ lib/augment.rb
9
+ lib/layer.rb
10
+ lib/flet.rb
11
+ lib/backends/backend.rb
12
+ lib/backends/coloring_backend.rb
13
+ lib/backends/test_unit_backend.rb
14
+ lib/frontends/frontend.rb
15
+ lib/frontends/ansi_color_frontend.rb
16
+ lib/frontends/html_frontend.rb
17
+ lib/frontends/augment.el
18
+ spec/ansi_frontend_spec.rb
19
+ spec/color_backend_spec.rb
20
+ spec/emacs-frontend-test.el
21
+ spec/html_frontend_spec.rb
22
+ spec/spec_helper.rb
23
+ spec/test_backend_spec.rb
24
+ spec/fixtures/augment-output.txt
25
+ spec/fixtures/layers.json
26
+ spec/fixtures/drinks/lib/drink.rb
27
+ spec/fixtures/drinks/test/test_drink.rb
@@ -0,0 +1,103 @@
1
+ augment
2
+ by Phil Hagelberg (c) 2007
3
+ http://augment.rubyforge.org
4
+
5
+ == Description
6
+
7
+ Augment is a framework for gathering metadata from code and displaying
8
+ it. This metadata would include test failures, test coverage levels,
9
+ complexity metrics, and others. Display frontends will be pluggable so
10
+ as to interface with many editors.
11
+
12
+ == Usage
13
+
14
+ The +augment+ executable gathers metadata in the form of layers for a
15
+ given file via a backend. Some backends gather data for a file other
16
+ than the original one passed in. (The test backend will store data for
17
+ the test if you pass in the implementation.)
18
+
19
+ Example:
20
+
21
+ $ augment test lib/foo.rb # will store metadata for test/test_foo.rb
22
+
23
+ The various frontends have their own executables that provide access
24
+ to the layer metadata once it's stored. The simplest frontend is
25
+ +augment_color+ which outputs the layers via ANSI color codes to the
26
+ shell:
27
+
28
+ $ augment_color test/test_foo.rb
29
+
30
+ Most other frontends are editor-specific.
31
+
32
+ == Design
33
+
34
+ Augment is designed to be generalized; if you want to collect
35
+ different kinds of data you can just write a new backend. If you want
36
+ to display the data in an unsupported editor or output format, you can
37
+ write a new frontend.
38
+
39
+ === Backends
40
+
41
+ Each backend takes a filename or glob and outputs the metadata about
42
+ that file into the .augment directory, created in the project root.
43
+ Metadata is stored as layer information in JSON. Each backend also
44
+ needs an initiator command to kick off the process that gathers the
45
+ metadata, with the exception of auto-initiating backends like autotest.
46
+
47
+ === Frontends
48
+
49
+ Most frontends are implemented within an editor or IDE. They watch
50
+ the .augment directory for changes and apply them to the open buffers
51
+ when they happen. The user can choose what augmentations he wants
52
+ displayed either globally or on a per-file basis. He can also control
53
+ when backend initiators should be started, though usually these will
54
+ be kicked off automatically upon saving a file.
55
+
56
+ === Layers
57
+
58
+ A layer consists of a single piece of metadata about a file. This is
59
+ expressed in terms of the range it applies to in the file, its color,
60
+ and the message associated with it.
61
+
62
+ == Support
63
+
64
+ Backends:
65
+
66
+ * Tests (miniunit)
67
+ * Rspec (planned)
68
+ * Heckle (planned)
69
+ * Rcov (planned)
70
+ * Flog (planned)
71
+ * Performance (planned)
72
+
73
+ Frontends:
74
+
75
+ * ANSI color codes (for shells)
76
+ * Emacs (planned)
77
+ * Vim (planned)
78
+ * Textmate (planned)
79
+
80
+ == Issues
81
+
82
+ * Not really, um, written yet.
83
+
84
+ == License
85
+
86
+ This is free software; you can redistribute it and/or modify it under
87
+ the terms of the GNU General Public License as published by the Free
88
+ Software Foundation; either version 3, or (at your option) any later
89
+ version.
90
+
91
+ This file is distributed in the hope that it will be useful, but
92
+ WITHOUT ANY WARRANTY; without even the implied warranty of
93
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
94
+ General Public License for more details.
95
+
96
+ You should have received a copy of the GNU General Public License
97
+ along with this software; see the file COPYING. If not, write to the
98
+ Free Software Foundation at this address:
99
+
100
+ Free Software Foundation
101
+ 51 Franklin Street, Fifth Floor
102
+ Boston, MA 02110-1301
103
+ USA
@@ -0,0 +1,26 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ require 'spec/rake/spectask'
6
+ require './lib/augment.rb'
7
+
8
+ Hoe.new('augment', Augment::VERSION) do |p|
9
+ p.rubyforge_name = 'augment'
10
+ p.author = 'Phil Hagelberg'
11
+ p.email = 'technomancy@gmail.com'
12
+ p.summary = 'Augment is a system for collecting and displaying code metadata.'
13
+ p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n")
14
+ p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
15
+ p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
16
+ p.extra_deps << ['miniunit', '>= 1.0.1']
17
+ p.extra_deps << ['json', '>= 1.1.1']
18
+ end
19
+
20
+ task :publish_html do
21
+ system("scp -r html/* technomancy@rubyforge.org:/var/www/gforge-projects/augment/")
22
+ end
23
+
24
+ Spec::Rake::SpecTask.new
25
+
26
+ # vim: syntax=Ruby
data/TODO ADDED
@@ -0,0 +1,5 @@
1
+ = Backends
2
+ == test/unit
3
+ * Discover char range from exception
4
+
5
+ * Report rubyforge bug when doing "rubyforge config" on project rinari
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH << File.dirname(__FILE__) + '/../lib'
3
+
4
+ require 'augment'
5
+
6
+ if ARGV.first == '--interactive'
7
+ Augment.interactive
8
+ exit 0
9
+ elsif ARGV.size != 2
10
+ puts "Usage: augment BACKEND/FRONTEND FILE"
11
+ puts " or: augment --interactive"
12
+ puts "Output or store metadata regarding code."
13
+ exit 1
14
+ end
15
+
16
+ Augment.new Augment::BACKENDS[ARGV[0]] || Augment::FRONTENDS[ARGV[0]], ARGV[1]
@@ -0,0 +1,41 @@
1
+ $LOAD_PATH << File.dirname(__FILE__)
2
+ $LOAD_PATH << File.dirname(__FILE__) + '/backends'
3
+ $LOAD_PATH << File.dirname(__FILE__) + '/frontends'
4
+
5
+ require 'rubygems'
6
+
7
+ require 'layer'
8
+ require 'flet'
9
+ require 'backend'
10
+ require 'frontend'
11
+
12
+ class Augment
13
+ VERSION = '1.0.0'
14
+ BACKENDS = {}
15
+ FRONTENDS = {}
16
+
17
+ def initialize(action, file)
18
+ raise "No backend or frontend with that name." if action.nil?
19
+ action.run(file)
20
+ end
21
+
22
+ class << self
23
+ def interactive(backend_names = 'test')
24
+ while true
25
+ begin
26
+ filename = STDIN.gets.chomp
27
+ backend_names.each { |backend| BACKENDS[backend].run(filename) }
28
+ puts "{\"#{filename}\" : #{File.read(augment_path(filename))} }"
29
+ rescue
30
+ puts "Error augmenting #{filename}."
31
+ end
32
+ end
33
+ end
34
+
35
+ def augment_path(original)
36
+ "#{File.dirname(File.expand_path(original))}/.augment/#{File.basename(original)}"
37
+ end
38
+ end
39
+ end
40
+
41
+ Dir.glob(File.dirname(__FILE__) + '/*ends/*rb').each { |b| require b[0 .. -4] }
@@ -0,0 +1,14 @@
1
+ require 'fileutils'
2
+
3
+ class Backend
4
+ class << self
5
+ def write_layers
6
+ @layers.each do |file, layers|
7
+ FileUtils.mkpath(File.dirname(file) + '/.augment')
8
+ File.open(Augment.augment_path(file), 'w') do |f|
9
+ f.puts "[#{layers.map{ |l| l.to_json }.join(", \n")}]"
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,25 @@
1
+ class ColoringBackend < Backend
2
+ COLORS = ['white', 'red', 'green', 'blue', 'black']
3
+
4
+ class << self
5
+ attr_reader :layers
6
+
7
+ def run(file)
8
+ @layers = {}
9
+ text = File.read(file)
10
+
11
+ COLORS.each do |color|
12
+ offset = 0
13
+ while occurance = text.index(color, offset) do
14
+ (@layers[file] ||= []) << Layer.new((occurance ... occurance + color.length),
15
+ color, "Found a #{color}", self)
16
+ offset += (occurance + 1)
17
+ end
18
+ end
19
+
20
+ write_layers
21
+ end
22
+ end
23
+ end
24
+
25
+ Augment::BACKENDS['color'] = ColoringBackend
@@ -0,0 +1,56 @@
1
+ Object.flet(:at_exit => lambda {}) do
2
+ # keep miniunit's at_exit block from running
3
+ gem 'miniunit', ">= 1.0.1"
4
+ require 'test/unit'
5
+ end
6
+
7
+ module Test
8
+ def Unit.puke(*args) # pass on failure info
9
+ TestUnitBackend.record_failure(*args)
10
+ end
11
+
12
+ def Unit.puts(*args); end # muffle test output
13
+ end
14
+
15
+ class TestUnitBackend < Backend
16
+ class << self
17
+ def run(file)
18
+ @file = file
19
+ @layers = {}
20
+
21
+ load(find_test_for(file))
22
+ # this will call record_failure as needed
23
+ with_no_output { Test::Unit.autotest }
24
+ write_layers
25
+ end
26
+
27
+ def record_failure(klass, method, exception)
28
+ # FIXME: errors here could actually occur in impl rather than test file
29
+ (@layers[@file] ||= []) << Layer.from_failure(@file, klass, method, exception)
30
+ end
31
+
32
+ def find_test_for(file)
33
+ # TODO: return test for implementation if possible
34
+ file
35
+ end
36
+
37
+ def with_no_output
38
+ old_stdout = $stdout.clone
39
+ $stdout.reopen(File.new('/dev/null','w'))
40
+ yield
41
+ $stdout.reopen(old_stdout)
42
+ end
43
+ end
44
+ end
45
+
46
+ def Layer.from_failure(file, klass, method, exception)
47
+ color = Test::Assertion === exception ? 'red' : 'yellow'
48
+
49
+ trace = exception.backtrace.detect { |e| e =~ /test_drink/ }
50
+ line = trace.match(/:(\d*):/)[1]
51
+
52
+ range = Layer.line_to_char_range(file, line.to_i)
53
+ Layer.new(range, color, exception.message, TestUnitBackend)
54
+ end
55
+
56
+ Augment::BACKENDS['test'] = TestUnitBackend
@@ -0,0 +1,26 @@
1
+ def Object.flet(bindings, &block)
2
+ old_methods = {}
3
+
4
+ bindings.each do |the_method, body|
5
+ old_methods[the_method] = method(the_method)
6
+ define_method(the_method, body)
7
+ end
8
+
9
+ begin
10
+ block.call
11
+ ensure
12
+ bindings.each do |the_method, body|
13
+ define_method(the_method) { |*args| old_methods[the_method].call(*args) }
14
+ end
15
+ end
16
+ end
17
+
18
+ if $0 == __FILE__
19
+ puts "foo" # should output "foo"
20
+
21
+ Object.flet(:puts => lambda { |str| print "#{str.reverse}\n" }) do
22
+ puts "foo" # should output "oof"
23
+ end
24
+
25
+ puts "foo" # should output "foo"
26
+ end
@@ -0,0 +1,38 @@
1
+ class String
2
+ COLOR_LOOKUP = {'black' => 30,
3
+ 'red' => 31,
4
+ 'green' => 32,
5
+ 'yellow' => 33,
6
+ 'blue' => 34,
7
+ 'magenta' => 35,
8
+ 'cyan' => 36,
9
+ 'white' => 37,
10
+ 'default' => 38 }
11
+
12
+ def colorize(color_code)
13
+ "\e[#{String.lookup_color_code(color_code)}m#{self}\e[0m"
14
+ end
15
+
16
+ def colorize_range(range, color)
17
+ "#{self[0 ... range.begin]}#{self[range].colorize(color)}#{self[range.end .. -1]}"
18
+ end
19
+
20
+ def self.lookup_color_code(color, foreground = true)
21
+ return color if color.is_a? Fixnum
22
+ COLOR_LOOKUP[color.downcase]
23
+ end
24
+ end
25
+
26
+ class AnsiColorFrontend < Frontend
27
+ class << self
28
+ def run(file)
29
+ puts super(file)
30
+ end
31
+
32
+ def process_layer(text, layer)
33
+ text.colorize_range(layer['range'], layer['color'])
34
+ end
35
+ end
36
+ end
37
+
38
+ Augment::FRONTENDS['ansi'] = AnsiColorFrontend
@@ -0,0 +1,148 @@
1
+ ;;; augment.el --- Display metadata about code
2
+
3
+ ;; Copyright (C) 2007 Phil Hagelberg
4
+
5
+ ;; Author: Phil Hagelberg <technomancy@gmail.com>
6
+ ;; Created: 16 Oct 2007
7
+ ;; Version: 0.1
8
+ ;; Keywords: augment testing metadata
9
+
10
+ ;; This file is NOT part of GNU Emacs.
11
+
12
+ ;; This is free software; you can redistribute it and/or modify it under
13
+ ;; the terms of the GNU General Public License as published by the Free
14
+ ;; Software Foundation; either version 3, or (at your option) any later
15
+ ;; version.
16
+
17
+ ;; This file is distributed in the hope that it will be useful, but
18
+ ;; WITHOUT ANY WARRANTY; without even the implied warranty of
19
+ ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20
+ ;; General Public License for more details.
21
+
22
+ ;; You should have received a copy of the GNU General Public License
23
+ ;; along with Emacs; see the file COPYING, or type `C-h C-c'. If not,
24
+ ;; write to the Free Software Foundation at this address:
25
+
26
+ ;; Free Software Foundation
27
+ ;; 51 Franklin Street, Fifth Floor
28
+ ;; Boston, MA 02110-1301
29
+ ;; USA
30
+
31
+ ;;; Commentary:
32
+
33
+ ;; augment.el is a frontend for augment, a system for gathering and
34
+ ;; displaying metadata about code. It's a minor mode for displaying
35
+ ;; data that's been gathered and for initiating new augmentations.
36
+
37
+ ;; Tests are present in spec/emacs-frontend-test.el
38
+
39
+ ;;; Installation:
40
+
41
+ ;; Put this file on your load-path and add "(require 'augment)" to
42
+ ;; your .emacs file.
43
+
44
+ ;;; Todo:
45
+
46
+ ;; * Support overlapping layers
47
+
48
+ ;;; Code:
49
+
50
+ (require 'cl)
51
+ (require 'json) ;; See hober's http://edward.oconnor.cx/2006/03/json.el
52
+
53
+ (defstruct layer begin end color message backend)
54
+
55
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
56
+
57
+ (defun augment-layer-from-plist (plist)
58
+ "Make a layers struct from a plist."
59
+ (make-layer :begin (string-to-number (first (split-string (getf plist :range) "\\.")))
60
+ :end (string-to-number (cadddr (split-string (getf plist :range) "\\.")))
61
+ :color (getf plist :color)
62
+ :message (getf plist :message)))
63
+
64
+ (defun augment-render-layer (layer)
65
+ "Create an overlay for a layer." ;; needs to be reimplemented for xemacs
66
+ (overlay-put (make-overlay (layer-begin layer) (layer-end layer))
67
+ 'face (layer-face layer)))
68
+
69
+ (defun layer-face (layer)
70
+ ;; could do some kind of transformation here for color themes.
71
+ (cons 'background-color (layer-color layer)))
72
+
73
+ (defun augment-file-path (file)
74
+ (concat
75
+ (file-name-directory file) ".augment/"
76
+ (file-name-nondirectory file)))
77
+
78
+ (defun augment-message-at-point (&optional point)
79
+ (interactive)
80
+ ;; find the first layer that the point is between begin and end
81
+ (layer-message (find (or point (point)) layers :test
82
+ (lambda (p l) (and (> p (layer-begin l))
83
+ (< p (layer-end l)))))))
84
+
85
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
86
+
87
+ (define-minor-mode augment-mode
88
+ "Major mode for showing code metadata.
89
+
90
+ \\{augment-mode-map}"
91
+
92
+ :keymap (setq augment-mode-map (make-sparse-keymap))
93
+
94
+ (define-key augment-mode-map (kbd "C-c C-s") 'augment-initiate)
95
+ (define-key augment-mode-map (kbd "C-c C-k") 'augment-clear)
96
+ (define-key augment-mode-map (kbd "C-c C-i") 'augment-message-at-point)
97
+
98
+ (make-local-variable 'layers)
99
+
100
+ (make-local-variable 'after-save-hook)
101
+ (add-hook 'after-save-hook 'augment-initiate)
102
+
103
+ (augment-initiate (buffer-file-name)))
104
+
105
+ (defun augment-initiate (&optional file)
106
+ (interactive)
107
+ (setq layers nil)
108
+ (augment-clear)
109
+ (augment-start-process)
110
+ (process-send-string "augment" (concat (or file (buffer-file-name)) "\n")))
111
+
112
+ (defun augment-start-process ()
113
+ (unless (get-process "augment") ;; only one should be running at a time
114
+ (set-process-filter
115
+ (start-process "augment" "*augment-out*"
116
+ "augment" "--interactive")
117
+ 'augment-filter)))
118
+
119
+ (defun augment-filter (process output)
120
+ (if (string-match "^Error augmenting \\(.*\\)\\." output)
121
+ (error "Error augmenting %s." (match-string 1 output))
122
+ ;; layers need to be cached in local var for messages
123
+ (let* ((json-object-type 'plist)
124
+ (json-array-type 'list)
125
+ (all-layers (json-read-from-string output)))
126
+ (while all-layers
127
+ ;; gotta remove the colon from the plisted filename
128
+ (let ((filename (substring (symbol-name (pop all-layers)) 1 nil))
129
+ (layer-plists (pop all-layers)))
130
+ (with-current-buffer (file-name-nondirectory filename)
131
+ (setq layers (mapcar #'augment-layer-from-plist layer-plists))
132
+ (augment-buffer layers)))))))
133
+
134
+ (defun augment-buffer (layers)
135
+ (dolist (layer layers)
136
+ (augment-render-layer layer)))
137
+
138
+ (defun augment-clear ()
139
+ (interactive)
140
+ (remove-overlays))
141
+
142
+ (defun augment-reset ()
143
+ (interactive)
144
+ (kill-process "augment")
145
+ (augment-clear))
146
+
147
+ (provide 'augment)
148
+ ;;; augment.el ends here