eyeballs 0.3.2
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/CHANGELOG +15 -0
- data/README.md +293 -0
- data/Rakefile +45 -0
- data/bin/eyeballs +9 -0
- data/config.ru +2 -0
- data/dist/jquery-1.4.2.min.js +154 -0
- data/dist/jquery.livequery.js +226 -0
- data/dist/mustache.js +324 -0
- data/eyeballs.gemspec +86 -0
- data/eyeballs.js.gemspec +83 -0
- data/lib/eyeballs.rb +11 -0
- data/lib/eyeballs/app_detector.rb +29 -0
- data/lib/eyeballs/app_generator.rb +30 -0
- data/lib/eyeballs/cli.rb +14 -0
- data/lib/eyeballs/controller_generator.rb +26 -0
- data/lib/eyeballs/model_generator.rb +26 -0
- data/lib/eyeballs/scaffold_generator.rb +66 -0
- data/spec/app_generator_spec.rb +51 -0
- data/spec/controller_generator_spec.rb +22 -0
- data/spec/model_generator_spec.rb +23 -0
- data/spec/rack_app_detector_spec.rb +25 -0
- data/spec/scaffold_generator_spec.rb +42 -0
- data/spec/spec_helper.rb +42 -0
- data/src/jquery.o_O.couchdb.js +107 -0
- data/src/jquery.o_O.dom.js +37 -0
- data/src/jquery.o_O.js +120 -0
- data/src/jquery.o_O.rails.js +68 -0
- data/src/o_O.js +286 -0
- data/src/o_O.localstorage.js +60 -0
- data/templates/app_root/index.html +26 -0
- data/templates/controller.js +3 -0
- data/templates/model.js +3 -0
- data/templates/scaffold_controller.js +75 -0
- data/templates/scaffold_edit.html.mustache +13 -0
- data/templates/scaffold_index.html +47 -0
- data/templates/scaffold_partial.html.mustache +12 -0
- data/test/unit/qunit.css +119 -0
- data/test/unit/qunit.js +1069 -0
- data/test/unit/test_controller.html +137 -0
- data/test/unit/test_dom.html +81 -0
- data/test/unit/test_dom_with_callbacks.html +121 -0
- data/test/unit/test_form.html +42 -0
- data/test/unit/test_localstorage.html +79 -0
- data/test/unit/test_model.html +136 -0
- data/test/unit/test_model_with_callbacks.html +118 -0
- data/test/unit/test_rails.html +97 -0
- metadata +117 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Eyeballs::ModelGenerator do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
create_test_root
|
7
|
+
FileUtils.cd(test_root)
|
8
|
+
Eyeballs::ModelGenerator.start(["Paul"])
|
9
|
+
end
|
10
|
+
let(:model_file) { file('app', 'models', 'paul.js') }
|
11
|
+
|
12
|
+
it "should create my model" do
|
13
|
+
model_file.should exist
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should create my file" do
|
17
|
+
model_file.read.should include("o_O('Paul', function(paul){")
|
18
|
+
end
|
19
|
+
|
20
|
+
after(:all) do
|
21
|
+
remove_test_root
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Eyeballs::AppGenerator do
|
4
|
+
describe "generate a simple app" do
|
5
|
+
before(:all) do
|
6
|
+
create_test_root
|
7
|
+
FileUtils.mkdir(test_root + '/public')
|
8
|
+
FileUtils.cd(test_root)
|
9
|
+
Eyeballs::AppGenerator.start('test')
|
10
|
+
end
|
11
|
+
let(:app_dir) { file('public', 'javascripts', 'app') }
|
12
|
+
|
13
|
+
it "should create the test root" do
|
14
|
+
File.exists?(test_root).should be_true
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should create the test app in the public javascripts directory" do
|
18
|
+
app_dir.should exist
|
19
|
+
end
|
20
|
+
|
21
|
+
after(:all) do
|
22
|
+
remove_test_root
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Eyeballs::ModelGenerator do
|
4
|
+
before(:all) do
|
5
|
+
create_test_root
|
6
|
+
FileUtils.cd(test_root)
|
7
|
+
Eyeballs::ScaffoldGenerator.start(["Review", "title:string", "content:text"])
|
8
|
+
end
|
9
|
+
let(:model_file) { file('app', 'models', 'review.js') }
|
10
|
+
let(:controller_file) { file('app', 'controllers', 'reviews_controller.js') }
|
11
|
+
let(:html_file) { file('reviews.html') }
|
12
|
+
let(:edit_view_file) { file('app', 'views', 'reviews', 'edit.html.mustache') }
|
13
|
+
let(:partial_view_file) { file('app', 'views', 'reviews', '_review.html.mustache') }
|
14
|
+
|
15
|
+
it "should create my files" do
|
16
|
+
model_file.should exist
|
17
|
+
controller_file.should exist
|
18
|
+
html_file.should exist
|
19
|
+
edit_view_file.should exist
|
20
|
+
partial_view_file.should exist
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should fill out the model file" do
|
24
|
+
model_file.read.should include("o_O('Review', function(review){")
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should fill out the controller file" do
|
28
|
+
controller_file.read.should include("o_O('ReviewsController', {")
|
29
|
+
controller_file.read.should include("Review.all(function(reviews){")
|
30
|
+
controller_file.read.should include("for(var id in reviews)")
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should fill out the html file" do
|
34
|
+
html_file.read.should include("Reviews")
|
35
|
+
html_file.read.should include(%Q[<input type="text" name="title" value="" data-attribute="title">])
|
36
|
+
html_file.read.should include(%Q[<textarea name="content" data-attribute="content"></textarea>])
|
37
|
+
end
|
38
|
+
|
39
|
+
after(:all) do
|
40
|
+
remove_test_root
|
41
|
+
end
|
42
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'eyeballs'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'ruby-debug'
|
4
|
+
Debugger.settings[:autoeval] = true
|
5
|
+
|
6
|
+
def create_test_root
|
7
|
+
FileUtils.mkdir_p(test_root)
|
8
|
+
end
|
9
|
+
|
10
|
+
def remove_test_root
|
11
|
+
FileUtils.rm_r(test_root)
|
12
|
+
end
|
13
|
+
|
14
|
+
def run_command(cmd)
|
15
|
+
system("cd #{test_root}; ../../bin/#{cmd}")
|
16
|
+
end
|
17
|
+
|
18
|
+
def file(*parts)
|
19
|
+
FileChecker.new([test_root] + parts)
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_root
|
23
|
+
File.join(File.dirname(File.expand_path(__FILE__)), 'test_root')
|
24
|
+
end
|
25
|
+
|
26
|
+
class FileChecker
|
27
|
+
def initialize(parts)
|
28
|
+
@file = File.join(parts)
|
29
|
+
end
|
30
|
+
|
31
|
+
def exist?
|
32
|
+
File.exists?(@file)
|
33
|
+
end
|
34
|
+
|
35
|
+
def read
|
36
|
+
File.read(@file)
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_s
|
40
|
+
@file
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
// We're relaxing, baby!
|
2
|
+
o_O.couchdb = {
|
3
|
+
all: function(model, callback){
|
4
|
+
|
5
|
+
var database = o_O.model.adapter.settings.database;
|
6
|
+
|
7
|
+
var ddoc_id = ['_design', model.table_name].join('/')
|
8
|
+
var view_name = model.table_name + '/all'
|
9
|
+
var get_all_documents = function(result){
|
10
|
+
var documents = result.rows;
|
11
|
+
var all_documents = [];
|
12
|
+
for(var i = 0; i < documents.length; i++)
|
13
|
+
{
|
14
|
+
var document = documents[i];
|
15
|
+
document.value.id = document.id
|
16
|
+
all_documents.push(document.value);
|
17
|
+
}
|
18
|
+
if(typeof callback === 'function')
|
19
|
+
{
|
20
|
+
callback(all_documents);
|
21
|
+
}
|
22
|
+
}
|
23
|
+
$.couch.db(database).view(view_name, {
|
24
|
+
success:get_all_documents,
|
25
|
+
error: function(){
|
26
|
+
// create the doc
|
27
|
+
var design_doc = {
|
28
|
+
"_id": ddoc_id,
|
29
|
+
"language": "javascript",
|
30
|
+
"views": {
|
31
|
+
"all": {
|
32
|
+
"map": "function(doc) {\n if(doc.model_name === \
|
33
|
+
'" + model.model_name + "')\n emit(null, doc);\n}"
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
$.couch.db(database).saveDoc(design_doc, {success:function(){
|
38
|
+
$.couch.db(database).view(view_name, {
|
39
|
+
success: get_all_documents,
|
40
|
+
error: function(){
|
41
|
+
if(typeof console === 'object')
|
42
|
+
{
|
43
|
+
console.log("Design Doc couldn't be written");
|
44
|
+
}
|
45
|
+
}
|
46
|
+
})
|
47
|
+
}})
|
48
|
+
}
|
49
|
+
});
|
50
|
+
},
|
51
|
+
destroy: function(object, callback){
|
52
|
+
var database = o_O.model.adapter.settings.database;
|
53
|
+
this.find(object, object.id, function(found){
|
54
|
+
var url = '/' + o_O.model.adapter.settings.database + '/' + object.id;
|
55
|
+
$.ajax({
|
56
|
+
type: 'DELETE',
|
57
|
+
url: '/' + database + '/' + object.id + '?rev=' + found._rev,
|
58
|
+
success: function(data){
|
59
|
+
var response = JSON.parse(data);
|
60
|
+
if(typeof callback === 'function')
|
61
|
+
{
|
62
|
+
callback(response);
|
63
|
+
}
|
64
|
+
}
|
65
|
+
});
|
66
|
+
});
|
67
|
+
},
|
68
|
+
save: function(object, callback)
|
69
|
+
{
|
70
|
+
var database = o_O.model.adapter.settings.database;
|
71
|
+
var object_to_save = {}
|
72
|
+
|
73
|
+
// we only want the attributes, not all extra model data
|
74
|
+
for(var i = 0; i < object.attributes.length; i++)
|
75
|
+
{
|
76
|
+
object_to_save[object.attributes[i]] = object[object.attributes[i]];
|
77
|
+
}
|
78
|
+
object_to_save._id = object.id;
|
79
|
+
object_to_save.model_name = object.model_name;
|
80
|
+
$.couch.db(database).saveDoc(object_to_save, {
|
81
|
+
success:function(response){
|
82
|
+
object_to_save._rev = response.rev;
|
83
|
+
object_to_save._id = response.id;
|
84
|
+
object_to_save.id = response.id;
|
85
|
+
if(typeof callback === 'function')
|
86
|
+
{
|
87
|
+
callback(object_to_save);
|
88
|
+
}
|
89
|
+
}
|
90
|
+
});
|
91
|
+
},
|
92
|
+
table: function(object)
|
93
|
+
{
|
94
|
+
|
95
|
+
},
|
96
|
+
find: function(model, id, callback)
|
97
|
+
{
|
98
|
+
var database = o_O.model.adapter.settings.database;
|
99
|
+
$.couch.db(database).openDoc(id, {success: function(document){
|
100
|
+
document.id = id;
|
101
|
+
if(typeof callback === 'function')
|
102
|
+
{
|
103
|
+
callback(document);
|
104
|
+
}
|
105
|
+
}});
|
106
|
+
}
|
107
|
+
}
|
@@ -0,0 +1,37 @@
|
|
1
|
+
o_O.dom = {
|
2
|
+
save: function(object, callback){
|
3
|
+
if(typeof callback === 'function')
|
4
|
+
{
|
5
|
+
callback(object);
|
6
|
+
}
|
7
|
+
return object;
|
8
|
+
},
|
9
|
+
all: function(model, callback){
|
10
|
+
var output = [];
|
11
|
+
$('[data-model=' + model.model_name + ']').each(function(){
|
12
|
+
output.push(model.initialize(o_O.find_attributes($(this), function(field) { return field.text();})))
|
13
|
+
})
|
14
|
+
if(typeof callback === 'function')
|
15
|
+
{
|
16
|
+
callback(output);
|
17
|
+
}
|
18
|
+
},
|
19
|
+
destroy: function(object, callback){
|
20
|
+
$('[data-model=' + object.model_name +'][data-id= ' + object.id + ']').remove()
|
21
|
+
if(typeof callback === 'function')
|
22
|
+
{
|
23
|
+
callback(object);
|
24
|
+
}
|
25
|
+
},
|
26
|
+
find: function(model, id, callback){
|
27
|
+
var template = $('[data-model=' + model.model_name + '][data-id=' + id + ']');
|
28
|
+
var new_object;
|
29
|
+
if(typeof callback === 'function')
|
30
|
+
{
|
31
|
+
new_object = model.initialize(o_O.find_attributes(template, function(field){return field.text();}));
|
32
|
+
new_object.id = id;
|
33
|
+
callback(new_object);
|
34
|
+
}
|
35
|
+
return new_object;
|
36
|
+
}
|
37
|
+
}
|
data/src/jquery.o_O.js
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
o_O.controller = {
|
2
|
+
initialize: function(controller_name, controller){
|
3
|
+
var action_event = function(object){
|
4
|
+
if(object.attr('data-event'))
|
5
|
+
{
|
6
|
+
return object.attr('data-event');
|
7
|
+
}
|
8
|
+
return (object.is('form')) ? 'submit' : 'click';
|
9
|
+
}
|
10
|
+
|
11
|
+
var controller_name = controller_name.replace('Controller', '').toLowerCase();
|
12
|
+
var controller = controller;
|
13
|
+
|
14
|
+
$(function(){
|
15
|
+
for(var action in controller)
|
16
|
+
{
|
17
|
+
var selector = '[data-controller=' + controller_name + '][data-action=' + action + ']';
|
18
|
+
$(selector).livequery(function(){
|
19
|
+
var element = $(this);
|
20
|
+
$(this).bind(action_event(element), controller[element.attr('data-action')]);
|
21
|
+
if(!($(this).attr('data-default')))
|
22
|
+
{
|
23
|
+
$(this).bind(action_event(element), function(){ return false; });
|
24
|
+
}
|
25
|
+
})
|
26
|
+
}
|
27
|
+
$('[data-bind]').livequery(function(){
|
28
|
+
var binders = $(this).attr('data-bind').match(/[\+]?(\s+)?[^ :]?[: ]?[^ \/]+[ \/]+[^ ;]+[ ;]?/g)
|
29
|
+
if(binders != null && binders.length > 0)
|
30
|
+
{
|
31
|
+
for(i = 0; i < binders.length; i++)
|
32
|
+
{
|
33
|
+
var rule = binders[i];
|
34
|
+
var parts = rule.match(/([\+])?(\s+)?(([^ :]+)([: ]+))?([^ \/]+)[ \/]+([^ ;]+)[ ;]?/)
|
35
|
+
var default_bit = parts[1];
|
36
|
+
var this_action_event = parts[4];
|
37
|
+
if(this_action_event === undefined)
|
38
|
+
{
|
39
|
+
this_action_event = ($(this).is('form')) ? 'submit' : 'click';
|
40
|
+
}
|
41
|
+
var this_controller_name = parts[6];
|
42
|
+
var this_action = parts[7];
|
43
|
+
if(this_controller_name == controller_name)
|
44
|
+
{
|
45
|
+
$(this).bind(this_action_event,controller[this_action]);
|
46
|
+
if(default_bit != '+')
|
47
|
+
{
|
48
|
+
$(this).bind(this_action_event, function(){ return false; });
|
49
|
+
}
|
50
|
+
}
|
51
|
+
}
|
52
|
+
}
|
53
|
+
});
|
54
|
+
});
|
55
|
+
return controller;
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|
59
|
+
o_O.find_attributes = function(template, callback){
|
60
|
+
var object = {};
|
61
|
+
for(i = 0; i<template.find('[data-attribute]').length; i++)
|
62
|
+
{
|
63
|
+
field = $(template.find('[data-attribute]')[i]);
|
64
|
+
object[field.attr('data-attribute')] = callback(field);
|
65
|
+
}
|
66
|
+
return object;
|
67
|
+
}
|
68
|
+
|
69
|
+
o_O.params = function(form){
|
70
|
+
return o_O.find_attributes(form, function(field){
|
71
|
+
if(field.is('[type=radio]'))
|
72
|
+
{
|
73
|
+
return $('[data-attribute=' + field.attr('data-attribute') + ']:checked').val()
|
74
|
+
}
|
75
|
+
else
|
76
|
+
{
|
77
|
+
return field.val();
|
78
|
+
}
|
79
|
+
});
|
80
|
+
}
|
81
|
+
|
82
|
+
o_O.render = function(template, data, options){
|
83
|
+
o_O.get_template(template, data, function(data, template){
|
84
|
+
var rendered = Mustache.to_html(template, data);
|
85
|
+
if(typeof options === 'object')
|
86
|
+
{
|
87
|
+
if(options.append)
|
88
|
+
{
|
89
|
+
$(options.append).append(rendered);
|
90
|
+
}
|
91
|
+
if(options.prepend)
|
92
|
+
{
|
93
|
+
$(options.prepend).prepend(rendered);
|
94
|
+
}
|
95
|
+
}
|
96
|
+
});
|
97
|
+
}
|
98
|
+
|
99
|
+
o_O.get_template = function(template, data, callback){
|
100
|
+
if(o_O.templates[template])
|
101
|
+
{
|
102
|
+
callback(data, o_O.templates[template]);
|
103
|
+
}
|
104
|
+
else
|
105
|
+
{
|
106
|
+
var url;
|
107
|
+
if(o_O.config.template_path)
|
108
|
+
{
|
109
|
+
url = o_O.config.template_path + '/';
|
110
|
+
}
|
111
|
+
else
|
112
|
+
{
|
113
|
+
url = 'app/views/'
|
114
|
+
}
|
115
|
+
$.get(url + template + '.html.mustache', function(response){
|
116
|
+
o_O.templates[template] = response;
|
117
|
+
callback(data, response);
|
118
|
+
});
|
119
|
+
}
|
120
|
+
}
|
@@ -0,0 +1,68 @@
|
|
1
|
+
// REST & Rails, woop!
|
2
|
+
o_O.rails = {
|
3
|
+
all: function(model, callback){
|
4
|
+
$.get('/' + model.table_name, function(response){
|
5
|
+
var documents = JSON.parse(response);
|
6
|
+
if(typeof callback === 'function')
|
7
|
+
{
|
8
|
+
callback(documents);
|
9
|
+
}
|
10
|
+
})
|
11
|
+
},
|
12
|
+
destroy: function(object, callback){
|
13
|
+
object.destroyed = true;
|
14
|
+
$.ajax({
|
15
|
+
type: 'DELETE',
|
16
|
+
url: '/' + object.table_name + '/' + object.id,
|
17
|
+
success: function(){
|
18
|
+
if(typeof callback === 'function')
|
19
|
+
{
|
20
|
+
callback(object);
|
21
|
+
}
|
22
|
+
}
|
23
|
+
})
|
24
|
+
},
|
25
|
+
save: function(object, callback)
|
26
|
+
{
|
27
|
+
var object_to_save = {}
|
28
|
+
for(var i = 0; i < object.attributes.length; i++)
|
29
|
+
{
|
30
|
+
object_to_save[object.attributes[i]] = object[object.attributes[i]];
|
31
|
+
}
|
32
|
+
var respond = function(response){
|
33
|
+
var saved_object = JSON.parse(response);
|
34
|
+
for(var attribute in saved_object)
|
35
|
+
{
|
36
|
+
object_to_save[attribute] = saved_object[attribute];
|
37
|
+
}
|
38
|
+
object_to_save.new_record = false;
|
39
|
+
if(typeof callback === 'function')
|
40
|
+
{
|
41
|
+
callback(object_to_save);
|
42
|
+
}
|
43
|
+
}
|
44
|
+
if(object.new_record)
|
45
|
+
{
|
46
|
+
$.post('/' + object.table_name, object_to_save, respond);
|
47
|
+
}
|
48
|
+
else
|
49
|
+
{
|
50
|
+
$.ajax({
|
51
|
+
type: 'PUT',
|
52
|
+
url: '/' + object.table_name + '/' + object.id,
|
53
|
+
success: respond
|
54
|
+
})
|
55
|
+
}
|
56
|
+
},
|
57
|
+
find: function(model, id, callback)
|
58
|
+
{
|
59
|
+
$.get('/' + model.table_name + '/' + id, function(response){
|
60
|
+
var retrieved_object = JSON.parse(response);
|
61
|
+
if(typeof callback === 'function')
|
62
|
+
{
|
63
|
+
retrieved_object.new_record = false;
|
64
|
+
callback(retrieved_object);
|
65
|
+
}
|
66
|
+
})
|
67
|
+
}
|
68
|
+
}
|