crazy_ivan 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
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
+ };