danwrong-evil 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +1 -0
- data/Manifest +11 -1
- data/README.textile +48 -10
- data/Rakefile +2 -1
- data/assets/evil-lib.js +306 -7
- data/assets/evil.css +9 -1
- data/assets/evil.js +22 -1
- data/evil.gemspec +5 -5
- data/lib/evil/application.rb +17 -1
- data/lib/evil/helpers.rb +1 -1
- data/lib/evil/models/template.rb +1 -1
- data/lib/evil/plugin.rb +4 -0
- data/lib/evil/plugin/base.rb +2 -2
- data/lib/evil/plugin/block_tag.rb +48 -0
- data/lib/evil/plugin/configuration.rb +4 -8
- data/lib/evil/plugin/filesystem.rb +1 -1
- data/lib/evil/plugin/singleton_tag.rb +18 -0
- data/lib/evil/plugin/tag.rb +18 -52
- data/test/application/frontend_test.rb +52 -0
- data/test/{app/evil_test.rb → application/openid_test.rb} +5 -23
- data/test/application/plugin_config_test.rb +73 -0
- data/test/application/template_test.rb +119 -0
- data/test/test_helper.rb +26 -1
- data/test/units/models/template_test.rb +16 -0
- data/test/units/models/whitelist_test.rb +31 -0
- data/test/units/plugin/base_test.rb +27 -2
- data/test/units/plugin/configuration_test.rb +60 -0
- data/test/units/plugin/environment_test.rb +13 -0
- data/test/units/plugin/filesystem_test.rb +22 -0
- data/test/units/plugin/tag_test.rb +36 -7
- data/views/index.haml +2 -2
- metadata +24 -4
data/CHANGELOG
CHANGED
data/Manifest
CHANGED
@@ -15,9 +15,11 @@ lib/evil/models/whitelist.rb
|
|
15
15
|
lib/evil/models.rb
|
16
16
|
lib/evil/open_id.rb
|
17
17
|
lib/evil/plugin/base.rb
|
18
|
+
lib/evil/plugin/block_tag.rb
|
18
19
|
lib/evil/plugin/configuration.rb
|
19
20
|
lib/evil/plugin/environment.rb
|
20
21
|
lib/evil/plugin/filesystem.rb
|
22
|
+
lib/evil/plugin/singleton_tag.rb
|
21
23
|
lib/evil/plugin/tag.rb
|
22
24
|
lib/evil/plugin.rb
|
23
25
|
lib/evil/setup/db_tool.rb
|
@@ -28,10 +30,18 @@ LICENSE
|
|
28
30
|
Manifest
|
29
31
|
Rakefile
|
30
32
|
README.textile
|
31
|
-
test/
|
33
|
+
test/application/frontend_test.rb
|
34
|
+
test/application/openid_test.rb
|
35
|
+
test/application/plugin_config_test.rb
|
36
|
+
test/application/template_test.rb
|
32
37
|
test/dev_env.rb
|
33
38
|
test/test_helper.rb
|
39
|
+
test/units/models/template_test.rb
|
40
|
+
test/units/models/whitelist_test.rb
|
34
41
|
test/units/plugin/base_test.rb
|
42
|
+
test/units/plugin/configuration_test.rb
|
43
|
+
test/units/plugin/environment_test.rb
|
44
|
+
test/units/plugin/filesystem_test.rb
|
35
45
|
test/units/plugin/tag_test.rb
|
36
46
|
views/_banner.haml
|
37
47
|
views/_errors.haml
|
data/README.textile
CHANGED
@@ -2,19 +2,57 @@ h1. EVIL
|
|
2
2
|
|
3
3
|
Author: Dan Webb (dan@danwebb.net)
|
4
4
|
|
5
|
-
Evil is
|
5
|
+
Evil is a tool for very easily putting together sites that take advantage of data from around the web. Grab your blog entries from an ATOM or RSS feed, your recent links from Delicious, your projects from Github or whatever. If it's got an API you can use pull it in to Evil and twist it together with other data and make a site. Evil does all the frustrating stuff like caching template processing and handling HTTP requests to APIs.
|
6
6
|
|
7
|
-
|
7
|
+
NB. It's 'working' at the moment and has decent test coverage but it's still very bare bones so it's probably not worth using until it approaches 1.0.
|
8
8
|
|
9
|
-
|
9
|
+
Here's an overview of how to use it:
|
10
10
|
|
11
|
-
|
12
|
-
evil whitelist {your_openid}</pre></code>
|
11
|
+
1. Install the gem
|
13
12
|
|
14
|
-
|
13
|
+
@sudo gem install danwrong-evil@
|
15
14
|
|
16
|
-
|
15
|
+
2. Generate a site skeleton
|
17
16
|
|
18
|
-
|
19
|
-
|
20
|
-
|
17
|
+
@evil init {path}@
|
18
|
+
|
19
|
+
This will give you the files and dirs that you need for your site including a public directory to add static files to if you need them.
|
20
|
+
|
21
|
+
3. Whitelist your OpenID
|
22
|
+
|
23
|
+
<code><pre>cd {path}
|
24
|
+
evil whitelist {your OpenID URL}</pre></code>
|
25
|
+
|
26
|
+
You can use wildcards in here if you want so if you wanted to let in all subdomains of danwebb.net you could do *.danwebb.net.
|
27
|
+
|
28
|
+
4. Upload the whole directory to your server and point passenger at it. Something like this in your Apache config should do it:
|
29
|
+
|
30
|
+
<code><pre><VirtualHost *:80>
|
31
|
+
ServerName www.example.com
|
32
|
+
DocumentRoot "/var/www/example/public"
|
33
|
+
</VirtualHost></pre></code>
|
34
|
+
|
35
|
+
5. Go to the admin site and start writing templates. It will be at /admin. Just log in with your OpenID.
|
36
|
+
|
37
|
+
6. Mess around. Create templates using liquid syntax, give them routes using the Sinatra/Rails syntax eg. /mythings/:id.
|
38
|
+
|
39
|
+
That's all for now. More detail later.
|
40
|
+
|
41
|
+
h2. Acknowledgements
|
42
|
+
|
43
|
+
Evil is built, as all good software is, on the efforts of lots of brilliant projects:
|
44
|
+
|
45
|
+
* Sinatra
|
46
|
+
* Rack::Cache
|
47
|
+
* Liquid
|
48
|
+
* HTTParty
|
49
|
+
* jQuery
|
50
|
+
* HAML
|
51
|
+
|
52
|
+
So thanks to all the authors.
|
53
|
+
|
54
|
+
h2. In the works...
|
55
|
+
|
56
|
+
There will be a suite of built in plugins covering all of the popular services and protocols (Flickr, Twitter, ATOM/RSS, Github, Upcoming, Delicious, YQL, Last.fm + loads more). Also, I'll be adding documentation to write your own plugins and easy handling of OAuth. This is just the very start.
|
57
|
+
|
58
|
+
Feedback welcome.
|
data/Rakefile
CHANGED
@@ -13,7 +13,8 @@ Echoe.new("evil") do |p|
|
|
13
13
|
|
14
14
|
p.development_dependencies = ['shoulda']
|
15
15
|
p.retain_gemspec = true
|
16
|
-
p.ignore_pattern = ['test/example/**/*']
|
16
|
+
p.ignore_pattern = ['test/example/**/*', 'test/evil.db']
|
17
|
+
p.rcov_options = "--exclude 'gems/*'"
|
17
18
|
end
|
18
19
|
|
19
20
|
desc 'Move assets from text/example to assets'
|
data/assets/evil-lib.js
CHANGED
@@ -213,10 +213,309 @@
|
|
213
213
|
}
|
214
214
|
});
|
215
215
|
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
216
|
+
})(jQuery);
|
217
|
+
|
218
|
+
jQuery.tableDnD = {
|
219
|
+
/** Keep hold of the current table being dragged */
|
220
|
+
currentTable : null,
|
221
|
+
/** Keep hold of the current drag object if any */
|
222
|
+
dragObject: null,
|
223
|
+
/** The current mouse offset */
|
224
|
+
mouseOffset: null,
|
225
|
+
/** Remember the old value of Y so that we don't do too much processing */
|
226
|
+
oldY: 0,
|
227
|
+
|
228
|
+
/** Actually build the structure */
|
229
|
+
build: function(options) {
|
230
|
+
// Set up the defaults if any
|
231
|
+
|
232
|
+
this.each(function() {
|
233
|
+
// This is bound to each matching table, set up the defaults and override with user options
|
234
|
+
this.tableDnDConfig = jQuery.extend({
|
235
|
+
onDragStyle: null,
|
236
|
+
onDropStyle: null,
|
237
|
+
// Add in the default class for whileDragging
|
238
|
+
onDragClass: "tDnD_whileDrag",
|
239
|
+
onDrop: null,
|
240
|
+
onDragStart: null,
|
241
|
+
scrollAmount: 5,
|
242
|
+
serializeRegexp: /[^\-]*$/, // The regular expression to use to trim row IDs
|
243
|
+
serializeParamName: null, // If you want to specify another parameter name instead of the table ID
|
244
|
+
dragHandle: null // If you give the name of a class here, then only Cells with this class will be draggable
|
245
|
+
}, options || {});
|
246
|
+
// Now make the rows draggable
|
247
|
+
jQuery.tableDnD.makeDraggable(this);
|
248
|
+
});
|
249
|
+
|
250
|
+
// Now we need to capture the mouse up and mouse move event
|
251
|
+
// We can use bind so that we don't interfere with other event handlers
|
252
|
+
jQuery(document)
|
253
|
+
.bind('mousemove', jQuery.tableDnD.mousemove)
|
254
|
+
.bind('mouseup', jQuery.tableDnD.mouseup);
|
255
|
+
|
256
|
+
// Don't break the chain
|
257
|
+
return this;
|
258
|
+
},
|
259
|
+
|
260
|
+
/** This function makes all the rows on the table draggable apart from those marked as "NoDrag" */
|
261
|
+
makeDraggable: function(table) {
|
262
|
+
var config = table.tableDnDConfig;
|
263
|
+
if (table.tableDnDConfig.dragHandle) {
|
264
|
+
// We only need to add the event to the specified cells
|
265
|
+
var cells = jQuery("td."+table.tableDnDConfig.dragHandle, table);
|
266
|
+
cells.each(function() {
|
267
|
+
// The cell is bound to "this"
|
268
|
+
jQuery(this).mousedown(function(ev) {
|
269
|
+
jQuery.tableDnD.dragObject = this.parentNode;
|
270
|
+
jQuery.tableDnD.currentTable = table;
|
271
|
+
jQuery.tableDnD.mouseOffset = jQuery.tableDnD.getMouseOffset(this, ev);
|
272
|
+
if (config.onDragStart) {
|
273
|
+
// Call the onDrop method if there is one
|
274
|
+
config.onDragStart(table, this);
|
275
|
+
}
|
276
|
+
return false;
|
277
|
+
});
|
278
|
+
})
|
279
|
+
} else {
|
280
|
+
// For backwards compatibility, we add the event to the whole row
|
281
|
+
var rows = jQuery("tr", table); // get all the rows as a wrapped set
|
282
|
+
rows.each(function() {
|
283
|
+
// Iterate through each row, the row is bound to "this"
|
284
|
+
var row = jQuery(this);
|
285
|
+
if (! row.hasClass("nodrag")) {
|
286
|
+
row.mousedown(function(ev) {
|
287
|
+
if (ev.target.tagName == "TD") {
|
288
|
+
jQuery.tableDnD.dragObject = this;
|
289
|
+
jQuery.tableDnD.currentTable = table;
|
290
|
+
jQuery.tableDnD.mouseOffset = jQuery.tableDnD.getMouseOffset(this, ev);
|
291
|
+
if (config.onDragStart) {
|
292
|
+
// Call the onDrop method if there is one
|
293
|
+
config.onDragStart(table, this);
|
294
|
+
}
|
295
|
+
return false;
|
296
|
+
}
|
297
|
+
}).css("cursor", "move"); // Store the tableDnD object
|
298
|
+
}
|
299
|
+
});
|
300
|
+
}
|
301
|
+
},
|
302
|
+
|
303
|
+
updateTables: function() {
|
304
|
+
this.each(function() {
|
305
|
+
// this is now bound to each matching table
|
306
|
+
if (this.tableDnDConfig) {
|
307
|
+
jQuery.tableDnD.makeDraggable(this);
|
308
|
+
}
|
309
|
+
})
|
310
|
+
},
|
311
|
+
|
312
|
+
/** Get the mouse coordinates from the event (allowing for browser differences) */
|
313
|
+
mouseCoords: function(ev){
|
314
|
+
if(ev.pageX || ev.pageY){
|
315
|
+
return {x:ev.pageX, y:ev.pageY};
|
316
|
+
}
|
317
|
+
return {
|
318
|
+
x:ev.clientX + document.body.scrollLeft - document.body.clientLeft,
|
319
|
+
y:ev.clientY + document.body.scrollTop - document.body.clientTop
|
320
|
+
};
|
321
|
+
},
|
322
|
+
|
323
|
+
/** Given a target element and a mouse event, get the mouse offset from that element.
|
324
|
+
To do this we need the element's position and the mouse position */
|
325
|
+
getMouseOffset: function(target, ev) {
|
326
|
+
ev = ev || window.event;
|
327
|
+
|
328
|
+
var docPos = this.getPosition(target);
|
329
|
+
var mousePos = this.mouseCoords(ev);
|
330
|
+
return {x:mousePos.x - docPos.x, y:mousePos.y - docPos.y};
|
331
|
+
},
|
332
|
+
|
333
|
+
/** Get the position of an element by going up the DOM tree and adding up all the offsets */
|
334
|
+
getPosition: function(e){
|
335
|
+
var left = 0;
|
336
|
+
var top = 0;
|
337
|
+
/** Safari fix -- thanks to Luis Chato for this! */
|
338
|
+
if (e.offsetHeight == 0) {
|
339
|
+
/** Safari 2 doesn't correctly grab the offsetTop of a table row
|
340
|
+
this is detailed here:
|
341
|
+
http://jacob.peargrove.com/blog/2006/technical/table-row-offsettop-bug-in-safari/
|
342
|
+
the solution is likewise noted there, grab the offset of a table cell in the row - the firstChild.
|
343
|
+
note that firefox will return a text node as a first child, so designing a more thorough
|
344
|
+
solution may need to take that into account, for now this seems to work in firefox, safari, ie */
|
345
|
+
e = e.firstChild; // a table cell
|
346
|
+
}
|
347
|
+
|
348
|
+
while (e.offsetParent){
|
349
|
+
left += e.offsetLeft;
|
350
|
+
top += e.offsetTop;
|
351
|
+
e = e.offsetParent;
|
352
|
+
}
|
353
|
+
|
354
|
+
left += e.offsetLeft;
|
355
|
+
top += e.offsetTop;
|
356
|
+
|
357
|
+
return {x:left, y:top};
|
358
|
+
},
|
359
|
+
|
360
|
+
mousemove: function(ev) {
|
361
|
+
if (jQuery.tableDnD.dragObject == null) {
|
362
|
+
return;
|
363
|
+
}
|
364
|
+
|
365
|
+
var dragObj = jQuery(jQuery.tableDnD.dragObject);
|
366
|
+
var config = jQuery.tableDnD.currentTable.tableDnDConfig;
|
367
|
+
var mousePos = jQuery.tableDnD.mouseCoords(ev);
|
368
|
+
var y = mousePos.y - jQuery.tableDnD.mouseOffset.y;
|
369
|
+
//auto scroll the window
|
370
|
+
var yOffset = window.pageYOffset;
|
371
|
+
if (document.all) {
|
372
|
+
// Windows version
|
373
|
+
//yOffset=document.body.scrollTop;
|
374
|
+
if (typeof document.compatMode != 'undefined' &&
|
375
|
+
document.compatMode != 'BackCompat') {
|
376
|
+
yOffset = document.documentElement.scrollTop;
|
377
|
+
}
|
378
|
+
else if (typeof document.body != 'undefined') {
|
379
|
+
yOffset=document.body.scrollTop;
|
380
|
+
}
|
381
|
+
|
382
|
+
}
|
383
|
+
|
384
|
+
if (mousePos.y-yOffset < config.scrollAmount) {
|
385
|
+
window.scrollBy(0, -config.scrollAmount);
|
386
|
+
} else {
|
387
|
+
var windowHeight = window.innerHeight ? window.innerHeight
|
388
|
+
: document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight;
|
389
|
+
if (windowHeight-(mousePos.y-yOffset) < config.scrollAmount) {
|
390
|
+
window.scrollBy(0, config.scrollAmount);
|
391
|
+
}
|
392
|
+
}
|
393
|
+
|
394
|
+
|
395
|
+
if (y != jQuery.tableDnD.oldY) {
|
396
|
+
// work out if we're going up or down...
|
397
|
+
var movingDown = y > jQuery.tableDnD.oldY;
|
398
|
+
// update the old value
|
399
|
+
jQuery.tableDnD.oldY = y;
|
400
|
+
// update the style to show we're dragging
|
401
|
+
if (config.onDragClass) {
|
402
|
+
dragObj.addClass(config.onDragClass);
|
403
|
+
} else {
|
404
|
+
dragObj.css(config.onDragStyle);
|
405
|
+
}
|
406
|
+
// If we're over a row then move the dragged row to there so that the user sees the
|
407
|
+
// effect dynamically
|
408
|
+
var currentRow = jQuery.tableDnD.findDropTargetRow(dragObj, y);
|
409
|
+
if (currentRow) {
|
410
|
+
// TODO worry about what happens when there are multiple TBODIES
|
411
|
+
if (movingDown && jQuery.tableDnD.dragObject != currentRow) {
|
412
|
+
jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject, currentRow.nextSibling);
|
413
|
+
} else if (! movingDown && jQuery.tableDnD.dragObject != currentRow) {
|
414
|
+
jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject, currentRow);
|
415
|
+
}
|
416
|
+
}
|
417
|
+
}
|
418
|
+
|
419
|
+
return false;
|
420
|
+
},
|
421
|
+
|
422
|
+
/** We're only worried about the y position really, because we can only move rows up and down */
|
423
|
+
findDropTargetRow: function(draggedRow, y) {
|
424
|
+
var rows = jQuery.tableDnD.currentTable.rows;
|
425
|
+
for (var i=0; i<rows.length; i++) {
|
426
|
+
var row = rows[i];
|
427
|
+
var rowY = this.getPosition(row).y;
|
428
|
+
var rowHeight = parseInt(row.offsetHeight)/2;
|
429
|
+
if (row.offsetHeight == 0) {
|
430
|
+
rowY = this.getPosition(row.firstChild).y;
|
431
|
+
rowHeight = parseInt(row.firstChild.offsetHeight)/2;
|
432
|
+
}
|
433
|
+
// Because we always have to insert before, we need to offset the height a bit
|
434
|
+
if ((y > rowY - rowHeight) && (y < (rowY + rowHeight))) {
|
435
|
+
// that's the row we're over
|
436
|
+
// If it's the same as the current row, ignore it
|
437
|
+
if (row == draggedRow) {return null;}
|
438
|
+
var config = jQuery.tableDnD.currentTable.tableDnDConfig;
|
439
|
+
if (config.onAllowDrop) {
|
440
|
+
if (config.onAllowDrop(draggedRow, row)) {
|
441
|
+
return row;
|
442
|
+
} else {
|
443
|
+
return null;
|
444
|
+
}
|
445
|
+
} else {
|
446
|
+
// If a row has nodrop class, then don't allow dropping (inspired by John Tarr and Famic)
|
447
|
+
var nodrop = jQuery(row).hasClass("nodrop");
|
448
|
+
if (! nodrop) {
|
449
|
+
return row;
|
450
|
+
} else {
|
451
|
+
return null;
|
452
|
+
}
|
453
|
+
}
|
454
|
+
return row;
|
455
|
+
}
|
456
|
+
}
|
457
|
+
return null;
|
458
|
+
},
|
459
|
+
|
460
|
+
mouseup: function(e) {
|
461
|
+
if (jQuery.tableDnD.currentTable && jQuery.tableDnD.dragObject) {
|
462
|
+
var droppedRow = jQuery.tableDnD.dragObject;
|
463
|
+
var config = jQuery.tableDnD.currentTable.tableDnDConfig;
|
464
|
+
// If we have a dragObject, then we need to release it,
|
465
|
+
// The row will already have been moved to the right place so we just reset stuff
|
466
|
+
if (config.onDragClass) {
|
467
|
+
jQuery(droppedRow).removeClass(config.onDragClass);
|
468
|
+
} else {
|
469
|
+
jQuery(droppedRow).css(config.onDropStyle);
|
470
|
+
}
|
471
|
+
jQuery.tableDnD.dragObject = null;
|
472
|
+
if (config.onDrop) {
|
473
|
+
// Call the onDrop method if there is one
|
474
|
+
config.onDrop(jQuery.tableDnD.currentTable, droppedRow);
|
475
|
+
}
|
476
|
+
jQuery.tableDnD.currentTable = null; // let go of the table too
|
477
|
+
}
|
478
|
+
},
|
479
|
+
|
480
|
+
serialize: function() {
|
481
|
+
if (jQuery.tableDnD.currentTable) {
|
482
|
+
return jQuery.tableDnD.serializeTable(jQuery.tableDnD.currentTable);
|
483
|
+
} else {
|
484
|
+
return "Error: No Table id set, you need to set an id on your table and every row";
|
485
|
+
}
|
486
|
+
},
|
487
|
+
|
488
|
+
serializeTable: function(table) {
|
489
|
+
var result = "";
|
490
|
+
var tableId = table.id;
|
491
|
+
var rows = table.rows;
|
492
|
+
for (var i=0; i<rows.length; i++) {
|
493
|
+
if (result.length > 0) result += "&";
|
494
|
+
var rowId = rows[i].id;
|
495
|
+
if (rowId && rowId && table.tableDnDConfig && table.tableDnDConfig.serializeRegexp) {
|
496
|
+
rowId = rowId.match(table.tableDnDConfig.serializeRegexp)[0];
|
497
|
+
}
|
498
|
+
|
499
|
+
result += tableId + '[]=' + rowId;
|
500
|
+
}
|
501
|
+
return result;
|
502
|
+
},
|
503
|
+
|
504
|
+
serializeTables: function() {
|
505
|
+
var result = "";
|
506
|
+
this.each(function() {
|
507
|
+
// this is now bound to each matching table
|
508
|
+
result += jQuery.tableDnD.serializeTable(this);
|
509
|
+
});
|
510
|
+
return result;
|
511
|
+
}
|
512
|
+
|
513
|
+
}
|
514
|
+
|
515
|
+
jQuery.fn.extend(
|
516
|
+
{
|
517
|
+
tableDnD : jQuery.tableDnD.build,
|
518
|
+
tableDnDUpdate : jQuery.tableDnD.updateTables,
|
519
|
+
tableDnDSerialize: jQuery.tableDnD.serializeTables
|
520
|
+
}
|
521
|
+
);
|
data/assets/evil.css
CHANGED
@@ -180,6 +180,10 @@ input.submit {
|
|
180
180
|
margin: 0 2% 2em 0;
|
181
181
|
}
|
182
182
|
|
183
|
+
#overview table#templates tr.dragging {
|
184
|
+
background: #444;
|
185
|
+
}
|
186
|
+
|
183
187
|
#content form {
|
184
188
|
width: 50%;
|
185
189
|
margin: 0 auto;
|
@@ -193,12 +197,16 @@ form p {
|
|
193
197
|
margin: 1em 0;
|
194
198
|
}
|
195
199
|
|
196
|
-
form input.text, form textarea {
|
200
|
+
form input.text, form textarea, form input.short {
|
197
201
|
width: 100%;
|
198
202
|
font-family: Verdana, Helvetica, Arial, sans-serif;
|
199
203
|
font-size: 1em;
|
200
204
|
}
|
201
205
|
|
206
|
+
form input.short {
|
207
|
+
width: 30%;
|
208
|
+
}
|
209
|
+
|
202
210
|
form div.errors {
|
203
211
|
border: 1px solid red;
|
204
212
|
font-size: 0.8em;
|