crazy_ivan 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Rakefile CHANGED
@@ -20,9 +20,9 @@ begin
20
20
  gem.rubyforge_project = "crazyivan"
21
21
  # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
22
22
  end
23
- Jeweler::RubyforgeTasks.new do |rubyforge|
24
- rubyforge.doc_task = "rdoc"
25
- end
23
+
24
+ Jeweler::GemcutterTasks.new
25
+
26
26
  rescue LoadError
27
27
  puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
28
28
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.0
1
+ 0.3.1
data/crazy_ivan.gemspec CHANGED
@@ -1,15 +1,15 @@
1
1
  # Generated by jeweler
2
- # DO NOT EDIT THIS FILE
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
4
  # -*- encoding: utf-8 -*-
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{crazy_ivan}
8
- s.version = "0.3.0"
8
+ s.version = "0.3.1"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Edward Ocampo-Gooding"]
12
- s.date = %q{2009-09-21}
12
+ s.date = %q{2009-11-29}
13
13
  s.default_executable = %q{crazy_ivan}
14
14
  s.description = %q{Continuous integration should really just be a script that captures the output of running your project update & test commands and presents recent results in a static html page.
15
15
 
@@ -23,7 +23,8 @@ Gem::Specification.new do |s|
23
23
  s.executables = ["crazy_ivan"]
24
24
  s.extra_rdoc_files = [
25
25
  "LICENSE",
26
- "README.rdoc"
26
+ "README.rdoc",
27
+ "TODO"
27
28
  ]
28
29
  s.files = [
29
30
  ".gitignore",
@@ -35,13 +36,20 @@ Gem::Specification.new do |s|
35
36
  "bin/crazy_ivan",
36
37
  "crazy_ivan.gemspec",
37
38
  "lib/crazy_ivan.rb",
38
- "lib/html_asset_crush.rb",
39
- "lib/report_assembler.rb",
40
- "lib/test_runner.rb",
39
+ "lib/crazy_ivan/html_asset_crush.rb",
40
+ "lib/crazy_ivan/report_assembler.rb",
41
+ "lib/crazy_ivan/test_runner.rb",
41
42
  "templates/css/ci.css",
42
43
  "templates/index.html",
44
+ "templates/javascript/builder.js",
45
+ "templates/javascript/controls.js",
46
+ "templates/javascript/date.js",
47
+ "templates/javascript/dragdrop.js",
48
+ "templates/javascript/effects.js",
43
49
  "templates/javascript/json-template.js",
44
50
  "templates/javascript/prototype.js",
51
+ "templates/javascript/scriptaculous.js",
52
+ "templates/javascript/slider.js",
45
53
  "test/crazy_ivan_test.rb",
46
54
  "test/test_helper.rb",
47
55
  "vendor/json-1.1.7/CHANGES",
@@ -180,3 +188,4 @@ Gem::Specification.new do |s|
180
188
  else
181
189
  end
182
190
  end
191
+
@@ -21,14 +21,21 @@ class ReportAssembler
21
21
  end
22
22
  end
23
23
 
24
- def version(string)
25
- string[0..240]
24
+ def filename_from_version(string)
25
+ s = string[0..240]
26
+
27
+ if Dir["#{s}*.json"].size > 0
28
+ s += "-#{Dir["#{s}*.json"].size}"
29
+ end
30
+
31
+ s
26
32
  end
27
33
 
28
34
  def update_project(result)
29
35
  FileUtils.mkdir_p(result.project_name)
30
36
  Dir.chdir(result.project_name) do
31
- File.open("#{version(result.version_output)}.json", 'w+') do |f|
37
+ filename = filename_from_version(result.version_output)
38
+ File.open("#{filename}.json", 'w+') do |f|
32
39
  f.puts({
33
40
  "version" => result.version_output,
34
41
  "timestamp" => result.timestamp,
@@ -39,11 +46,11 @@ class ReportAssembler
39
46
  }.to_json)
40
47
  end
41
48
 
42
- update_recent(result)
49
+ update_recent(result, filename)
43
50
  end
44
51
  end
45
52
 
46
- def update_recent(result)
53
+ def update_recent(result, filename)
47
54
  recent_versions_json = File.open('recent.json', File::RDWR|File::CREAT).read
48
55
 
49
56
  recent_versions = []
@@ -52,19 +59,19 @@ class ReportAssembler
52
59
  recent_versions = JSON.parse(recent_versions_json)["recent_versions"]
53
60
  end
54
61
 
55
- recent_versions << version(result.version_output)
62
+ recent_versions << filename
56
63
  recent_versions.shift if recent_versions.size > MAXIMUM_RECENTS
57
64
 
58
65
  File.open('recent.json', 'w+') do |f|
59
- f.print "{\"recent_versions\": [#{recent_versions.map {|v| "\"#{v}\""}.join(', ')}]}"
66
+ f.print({"recent_versions" => recent_versions}.to_json)
60
67
  end
61
68
  end
62
69
 
63
70
  def update_projects
64
- projects = @test_results.map {|r| "\"#{r.project_name}\""}
71
+ projects = @test_results.map {|r| "#{r.project_name}"}
65
72
 
66
73
  File.open('projects.json', 'w+') do |f|
67
- f.print "{\"projects\": [#{projects.join(', ')}]}"
74
+ f.print({"projects" => projects}.to_json)
68
75
  end
69
76
  end
70
77
 
data/lib/crazy_ivan.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'fileutils'
2
2
  require 'yaml'
3
- require File.join(File.dirname(__FILE__), 'report_assembler')
4
- require File.join(File.dirname(__FILE__), 'test_runner')
5
- require File.join(File.dirname(__FILE__), 'html_asset_crush')
3
+
4
+ require 'crazy_ivan/report_assembler'
5
+ require 'crazy_ivan/test_runner'
6
+ require 'crazy_ivan/html_asset_crush'
data/templates/css/ci.css CHANGED
@@ -3,14 +3,31 @@ body {
3
3
  padding: 0;
4
4
  background: #fff;
5
5
  color: #333;
6
- font: 100%/1.5 Calibri, "Helvetica Neue", Helvetica, Arial, sans-serif;
6
+ font: 100%/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif;
7
7
  }
8
8
 
9
- td {
10
- vertical-align: top;
11
- font-size: 75%;
12
- }
9
+ h1 { margin: 0;}
10
+
11
+ pre { margin: 0;}
13
12
 
14
13
  .error {
15
14
  color: red;
15
+ }
16
+
17
+ .project h2 { margin: 12px 0 0 0;}
18
+
19
+ .tests { margin: 0 0 0 18px;}
20
+ .tests .test { font-size: 80%; margin-right: 8px;}
21
+ .tests .latest { font-size: 100%;}
22
+
23
+ .results .result { margin: 12px 18px 8px 18px;}
24
+ .results .result .timestamp { margin-right: 12px;}
25
+ .results .result .update { margin: 6px 0 6px 12px }
26
+ .results .result .test { margin: 6px 0 6px 12px }
27
+
28
+ .footer {
29
+ margin: 24px 0 0 0;
30
+ font-size: 60%;
31
+ width: 100%;
32
+ text-align: center;
16
33
  }
data/templates/index.html CHANGED
@@ -7,6 +7,8 @@
7
7
 
8
8
  <link rel="stylesheet" href="css/ci.css" type="text/css" charset="utf-8">
9
9
  <script type="text/javascript" src="javascript/prototype.js"></script>
10
+ <script type="text/javascript" src="javascript/scriptaculous.js"></script>
11
+ <script type="text/javascript" src="javascript/date.js"></script>
10
12
  <script type="text/javascript" src="javascript/json-template.js"></script>
11
13
  <script type="text/javascript">
12
14
  var init = function() {
@@ -68,72 +70,83 @@
68
70
  }
69
71
 
70
72
  var render = function(template_name, json) {
71
- var template = jsontemplate.Template(" \
72
- {.section projects} \
73
- {.repeated section @} \
74
- <div class='project'> \
75
- <h2>{name}</h2> \
76
- {.section reports} \
77
- <table class='reports'> \
78
- <tr> \
79
- <th class='timestamp'>Timestamp</th> \
80
- <th class='version'>Version</th> \
81
- <th class='update'>Update Result</th> \
82
- <th class='test'>Test Result</th> \
83
- </tr> \
84
- {.repeated section @} \
85
- <tr> \
86
- <td class='timestamp'>{timestamp}</td> \
87
- <td class='version'>{version}</td> \
88
- <td class='update'> \
89
- <pre>{update}</pre> \
90
- {.section update_error} \
91
- <div class='error'><pre>{update_error}<pre></div> \
92
- {.end} \
93
- </td> \
94
- <td class='test'> \
95
- <pre>{test}</pre> \
96
- {.section test_error} \
97
- <a href='#' onclick='expand(this)'>Click to see error</a> \
98
- <div class='error' style='display: hidden'> \
99
- <pre>{test_error}<pre> \
100
- </div> \
101
- {.end} \
102
- </td> \
103
- </tr> \
104
- {.end} \
105
- </table> \
106
- {.or} \
107
- <p>No test reports found. Please run the `ci` executable.</p> \
108
- {.end} \
109
- {.end} \
110
- </div> \
111
- {.or} \
112
- <p>No projects found.</p> \
113
- {.end} \
73
+ var template = jsontemplate.Template(" \
74
+ <h1>Projects</h1> \
75
+ <div class='projects'> \
76
+ {.section projects} \
77
+ {.repeated section @} \
78
+ <div class='project'> \
79
+ <h2>{name}</h2> \
80
+ <div class='tests'> \
81
+ {.section reports} \
82
+ {.repeated section @} \
83
+ <a class='test {.section test_error} error {.end} {.section update_error} error {.end}' href='#'>{timestamp}</a> \
84
+ {.end} \
85
+ {.end} \
86
+ </div> \
87
+ <div class='results'> \
88
+ {.section reports} \
89
+ {.repeated section @} \
90
+ <div class='result'> \
91
+ <div> \
92
+ <span class='timestamp'>{timestamp}</span> \
93
+ <span class='version'>{version}</span> \
94
+ </div> \
95
+ \
96
+ <div class='update'> \
97
+ <pre>{update}</pre> \
98
+ {.section update_error} \
99
+ <pre class='error'>{update_error}</pre> \
100
+ {.end} \
101
+ </div> \
102
+ \
103
+ <div class='test'> \
104
+ <pre>{test}</pre> \
105
+ {.section test_error} \
106
+ <pre class='error'>{test_error}</pre> \
107
+ {.end} \
108
+ </div> \
109
+ </div> \
110
+ {.end} \
111
+ {.or} \
112
+ <p>No test reports found. Please run the `ci` executable.</p> \
113
+ {.end} \
114
+ </div> \
115
+ </div> \
116
+ {.end} \
117
+ {.or} \
118
+ <p>No projects found.</p> \
119
+ {.end} \
120
+ </div> \
114
121
  ");
115
122
 
116
123
  var html = template.expand(json);
117
124
  $("replace").update(html);
118
125
 
119
- // Hide all errors by default
120
- $$('.error').invoke('hide');
121
126
 
122
- $$('.project').each(function(project) {
123
- var firstResultsError = project.down('td.test').down('.error')
124
-
125
- // When the latest test result has an error (i.e. the project is broken)
126
- if(firstResultsError) {
127
- project.down('h2').addClassName('error');
128
-
129
- firstResultsError.show();
130
- firstResultsError.adjacent('a')[0].onclick();
131
- }
127
+ $$('.project .tests').each(function(t) {
128
+ t.down('.test').addClassName('latest')
129
+ });
130
+
131
+ // Reformat the test timestamps to be less enormous
132
+ $$('.project .tests .test').each(function(t) {
133
+ t.update(Date.parse(t.innerHTML).toString("HH:mm"));
134
+ t.observe('click', function(e) {
135
+ console.debug("hallo");
136
+ $$('.results').invoke('show')
137
+ return false;
138
+ });
132
139
  });
140
+
141
+ $$('.results').invoke('hide')
133
142
  }
134
143
  </script>
135
144
  </head>
136
145
  <body onload="init();">
137
146
  <div id="replace"></div>
147
+
148
+ <div class="footer">
149
+ <a href="http://github.com/edward/crazy_ivan">Crazy Ivan on Github</a>
150
+ </div>
138
151
  </body>
139
152
  </html>
@@ -0,0 +1,136 @@
1
+ // script.aculo.us builder.js v1.8.3, Thu Oct 08 11:23:33 +0200 2009
2
+
3
+ // Copyright (c) 2005-2009 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
4
+ //
5
+ // script.aculo.us is freely distributable under the terms of an MIT-style license.
6
+ // For details, see the script.aculo.us web site: http://script.aculo.us/
7
+
8
+ var Builder = {
9
+ NODEMAP: {
10
+ AREA: 'map',
11
+ CAPTION: 'table',
12
+ COL: 'table',
13
+ COLGROUP: 'table',
14
+ LEGEND: 'fieldset',
15
+ OPTGROUP: 'select',
16
+ OPTION: 'select',
17
+ PARAM: 'object',
18
+ TBODY: 'table',
19
+ TD: 'table',
20
+ TFOOT: 'table',
21
+ TH: 'table',
22
+ THEAD: 'table',
23
+ TR: 'table'
24
+ },
25
+ // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken,
26
+ // due to a Firefox bug
27
+ node: function(elementName) {
28
+ elementName = elementName.toUpperCase();
29
+
30
+ // try innerHTML approach
31
+ var parentTag = this.NODEMAP[elementName] || 'div';
32
+ var parentElement = document.createElement(parentTag);
33
+ try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
34
+ parentElement.innerHTML = "<" + elementName + "></" + elementName + ">";
35
+ } catch(e) {}
36
+ var element = parentElement.firstChild || null;
37
+
38
+ // see if browser added wrapping tags
39
+ if(element && (element.tagName.toUpperCase() != elementName))
40
+ element = element.getElementsByTagName(elementName)[0];
41
+
42
+ // fallback to createElement approach
43
+ if(!element) element = document.createElement(elementName);
44
+
45
+ // abort if nothing could be created
46
+ if(!element) return;
47
+
48
+ // attributes (or text)
49
+ if(arguments[1])
50
+ if(this._isStringOrNumber(arguments[1]) ||
51
+ (arguments[1] instanceof Array) ||
52
+ arguments[1].tagName) {
53
+ this._children(element, arguments[1]);
54
+ } else {
55
+ var attrs = this._attributes(arguments[1]);
56
+ if(attrs.length) {
57
+ try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
58
+ parentElement.innerHTML = "<" +elementName + " " +
59
+ attrs + "></" + elementName + ">";
60
+ } catch(e) {}
61
+ element = parentElement.firstChild || null;
62
+ // workaround firefox 1.0.X bug
63
+ if(!element) {
64
+ element = document.createElement(elementName);
65
+ for(attr in arguments[1])
66
+ element[attr == 'class' ? 'className' : attr] = arguments[1][attr];
67
+ }
68
+ if(element.tagName.toUpperCase() != elementName)
69
+ element = parentElement.getElementsByTagName(elementName)[0];
70
+ }
71
+ }
72
+
73
+ // text, or array of children
74
+ if(arguments[2])
75
+ this._children(element, arguments[2]);
76
+
77
+ return $(element);
78
+ },
79
+ _text: function(text) {
80
+ return document.createTextNode(text);
81
+ },
82
+
83
+ ATTR_MAP: {
84
+ 'className': 'class',
85
+ 'htmlFor': 'for'
86
+ },
87
+
88
+ _attributes: function(attributes) {
89
+ var attrs = [];
90
+ for(attribute in attributes)
91
+ attrs.push((attribute in this.ATTR_MAP ? this.ATTR_MAP[attribute] : attribute) +
92
+ '="' + attributes[attribute].toString().escapeHTML().gsub(/"/,'&quot;') + '"');
93
+ return attrs.join(" ");
94
+ },
95
+ _children: function(element, children) {
96
+ if(children.tagName) {
97
+ element.appendChild(children);
98
+ return;
99
+ }
100
+ if(typeof children=='object') { // array can hold nodes and text
101
+ children.flatten().each( function(e) {
102
+ if(typeof e=='object')
103
+ element.appendChild(e);
104
+ else
105
+ if(Builder._isStringOrNumber(e))
106
+ element.appendChild(Builder._text(e));
107
+ });
108
+ } else
109
+ if(Builder._isStringOrNumber(children))
110
+ element.appendChild(Builder._text(children));
111
+ },
112
+ _isStringOrNumber: function(param) {
113
+ return(typeof param=='string' || typeof param=='number');
114
+ },
115
+ build: function(html) {
116
+ var element = this.node('div');
117
+ $(element).update(html.strip());
118
+ return element.down();
119
+ },
120
+ dump: function(scope) {
121
+ if(typeof scope != 'object' && typeof scope != 'function') scope = window; //global scope
122
+
123
+ var tags = ("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY " +
124
+ "BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET " +
125
+ "FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX "+
126
+ "KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P "+
127
+ "PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD "+
128
+ "TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/);
129
+
130
+ tags.each( function(tag){
131
+ scope[tag] = function() {
132
+ return Builder.node.apply(Builder, [tag].concat($A(arguments)));
133
+ };
134
+ });
135
+ }
136
+ };