eyeballs 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/CHANGELOG +15 -0
  2. data/README.md +293 -0
  3. data/Rakefile +45 -0
  4. data/bin/eyeballs +9 -0
  5. data/config.ru +2 -0
  6. data/dist/jquery-1.4.2.min.js +154 -0
  7. data/dist/jquery.livequery.js +226 -0
  8. data/dist/mustache.js +324 -0
  9. data/eyeballs.gemspec +86 -0
  10. data/eyeballs.js.gemspec +83 -0
  11. data/lib/eyeballs.rb +11 -0
  12. data/lib/eyeballs/app_detector.rb +29 -0
  13. data/lib/eyeballs/app_generator.rb +30 -0
  14. data/lib/eyeballs/cli.rb +14 -0
  15. data/lib/eyeballs/controller_generator.rb +26 -0
  16. data/lib/eyeballs/model_generator.rb +26 -0
  17. data/lib/eyeballs/scaffold_generator.rb +66 -0
  18. data/spec/app_generator_spec.rb +51 -0
  19. data/spec/controller_generator_spec.rb +22 -0
  20. data/spec/model_generator_spec.rb +23 -0
  21. data/spec/rack_app_detector_spec.rb +25 -0
  22. data/spec/scaffold_generator_spec.rb +42 -0
  23. data/spec/spec_helper.rb +42 -0
  24. data/src/jquery.o_O.couchdb.js +107 -0
  25. data/src/jquery.o_O.dom.js +37 -0
  26. data/src/jquery.o_O.js +120 -0
  27. data/src/jquery.o_O.rails.js +68 -0
  28. data/src/o_O.js +286 -0
  29. data/src/o_O.localstorage.js +60 -0
  30. data/templates/app_root/index.html +26 -0
  31. data/templates/controller.js +3 -0
  32. data/templates/model.js +3 -0
  33. data/templates/scaffold_controller.js +75 -0
  34. data/templates/scaffold_edit.html.mustache +13 -0
  35. data/templates/scaffold_index.html +47 -0
  36. data/templates/scaffold_partial.html.mustache +12 -0
  37. data/test/unit/qunit.css +119 -0
  38. data/test/unit/qunit.js +1069 -0
  39. data/test/unit/test_controller.html +137 -0
  40. data/test/unit/test_dom.html +81 -0
  41. data/test/unit/test_dom_with_callbacks.html +121 -0
  42. data/test/unit/test_form.html +42 -0
  43. data/test/unit/test_localstorage.html +79 -0
  44. data/test/unit/test_model.html +136 -0
  45. data/test/unit/test_model_with_callbacks.html +118 -0
  46. data/test/unit/test_rails.html +97 -0
  47. 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
@@ -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
+ }