sinatra-backbone-2 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.travis.yml +10 -0
- data/Gemfile +8 -0
- data/HISTORY.md +23 -0
- data/NOTES.md +18 -0
- data/README.md +51 -0
- data/Rakefile +34 -0
- data/examples/jstpages/app.rb +21 -0
- data/examples/jstpages/config.ru +3 -0
- data/examples/jstpages/public/app.js +20 -0
- data/examples/jstpages/views/hello.jst.tpl +6 -0
- data/examples/jstpages/views/home.erb +25 -0
- data/examples/restapi/app.rb +40 -0
- data/examples/restapi/config.ru +3 -0
- data/examples/restapi/home.erb +24 -0
- data/examples/restapi/public/app.js +105 -0
- data/lib/sinatra/backbone.rb +10 -0
- data/lib/sinatra/jstpages.rb +261 -0
- data/lib/sinatra/restapi.rb +299 -0
- data/sinatra-backbone.gemspec +20 -0
- data/test/app/views/chrome.jst.tpl +1 -0
- data/test/app/views/editor/edit.jst.jade +2 -0
- data/test/app_test.rb +62 -0
- data/test/arity_test.rb +44 -0
- data/test/emulate_test.rb +32 -0
- data/test/jst_test.rb +26 -0
- data/test/test_helper.rb +20 -0
- data/test/to_json_test.rb +44 -0
- metadata +169 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b298438993e405f71dbfc8505c9a13ad08c2d602
|
4
|
+
data.tar.gz: bf4f06c3d7025a750daf0ed27b12b9bb225103c4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5436faf91a0139869f3d43e1c7cd0e8786529c24a61253e8314766bbc13ec7739da3d289d06d97f56e0092ce012dd1acf589608323b274cf2e2ad9f7ae0a461f
|
7
|
+
data.tar.gz: 2de609aa39dd72b0771f388942f35c5460ff0fb67f29e8a566d7b29cec84da81db1c8a0176739be276d58be991b5a2e4c3c7d5e2c710f3d0e843a6349a8a5289
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/HISTORY.md
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
v0.1.1 - Apr 17, 2013
|
2
|
+
---------------------
|
3
|
+
|
4
|
+
* Allow `.jst` file extensions. (in addition to `.jst.eco` and similar)
|
5
|
+
* Check for validity of models on the server.
|
6
|
+
* Documentation updates.
|
7
|
+
* Make the error message helpful if you forget #to_hash.
|
8
|
+
* Refactor rest_resource to split up into rest_edit, rest_delete and rest_get.
|
9
|
+
* Support multiple args in routes.
|
10
|
+
|
11
|
+
v0.1.0.rc2 - Sep 12, 2011
|
12
|
+
-------------------------
|
13
|
+
|
14
|
+
### Changed:
|
15
|
+
* Check for validity of models on the server, and add an example.
|
16
|
+
* Added examples in the documentation.
|
17
|
+
* Support multiple args in routes.
|
18
|
+
* Added more tests.
|
19
|
+
|
20
|
+
v0.1.0.rc1 - Sept 12, 2011
|
21
|
+
--------------------------
|
22
|
+
|
23
|
+
Initial version.
|
data/NOTES.md
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Development notes
|
2
|
+
=================
|
3
|
+
|
4
|
+
### Multiple Sinatra version testing
|
5
|
+
|
6
|
+
$ rake test!
|
7
|
+
|
8
|
+
Or a certain version:
|
9
|
+
|
10
|
+
$ sinatra=1.3 bundle exec rake test
|
11
|
+
|
12
|
+
### Documentation
|
13
|
+
|
14
|
+
Install [reacco], then:
|
15
|
+
|
16
|
+
$ rake doc:build
|
17
|
+
|
18
|
+
[reacco]: https://github.com/rstacruz/reacco
|
data/README.md
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# Sinatra-backbone
|
2
|
+
#### Neat integration of Backbone.js with Sinatra
|
3
|
+
|
4
|
+
Do you like [Backbone.js][bb]? Do you like [Sinatra][sn]? Did you ever wish you
|
5
|
+
can use them together? Well now you can, with the new Sinatra Backbone!
|
6
|
+
|
7
|
+
[bb]: http://documentcloud.github.com/backbone/
|
8
|
+
[sn]: http://sinatrarb.com
|
9
|
+
|
10
|
+
#### Usage
|
11
|
+
|
12
|
+
This is a Ruby gem.
|
13
|
+
`$ gem install sinatra-backbone --pre`
|
14
|
+
|
15
|
+
Or do you use Bundler?
|
16
|
+
`gem 'sinatra-backbone', :require => 'sinatra/backbone'`
|
17
|
+
|
18
|
+
Contents
|
19
|
+
--------
|
20
|
+
|
21
|
+
__Sinatra-backbone__ is comprised of two Sinatra plugins:
|
22
|
+
|
23
|
+
* `Sinatra::JstPages` – Provides support for JavaScript server templates (JST)
|
24
|
+
for use in Backbone views. See [JstPages example][jstx] for a full example
|
25
|
+
application.
|
26
|
+
|
27
|
+
* `Sinatra::RestAPI` – Provides restful API for your models for use in Backbone
|
28
|
+
models. See [RestAPI example][restx] for a full example application.
|
29
|
+
|
30
|
+
[jstx]: https://github.com/rstacruz/sinatra-backbone/tree/master/examples/jstpages
|
31
|
+
[restx]: https://github.com/rstacruz/sinatra-backbone/tree/master/examples/restapi
|
32
|
+
|
33
|
+
For usage and API reference, please see http://ricostacruz.com/sinatra-backbone. [](#api_reference)
|
34
|
+
|
35
|
+
Acknowledgements
|
36
|
+
----------------
|
37
|
+
|
38
|
+
© 2011, Rico Sta. Cruz. Released under the [MIT
|
39
|
+
License](http://www.opensource.org/licenses/mit-license.php).
|
40
|
+
|
41
|
+
Sinatra-Backbone is authored and maintained by [Rico Sta. Cruz][rsc] with help
|
42
|
+
from its [contributors][c]. It is sponsored by my startup, [Sinefunc, Inc][sf].
|
43
|
+
|
44
|
+
* [My website](http://ricostacruz.com) (ricostacruz.com)
|
45
|
+
* [Sinefunc, Inc.](http://sinefunc.com) (sinefunc.com)
|
46
|
+
* [Github](http://github.com/rstacruz) (@rstacruz)
|
47
|
+
* [Twitter](http://twitter.com/rstacruz) (@rstacruz)
|
48
|
+
|
49
|
+
[rsc]: http://ricostacruz.com
|
50
|
+
[c]: http://github.com/rstacruz/xxxxx
|
51
|
+
[sf]: http://sinefunc.com
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
|
3
|
+
desc "Invokes the test suite in multiple RVM environments"
|
4
|
+
task :'test!' do
|
5
|
+
%w[1.3 1.4 master].each do |version|
|
6
|
+
env = "env sinatra=#{version}"
|
7
|
+
system("rm -f Gemfile.lock; #{env} bundle && #{env} bundle exec rake test") or abort
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
desc "Runs tests"
|
12
|
+
task :test do
|
13
|
+
Dir['test/*_test.rb'].each { |f| load f }
|
14
|
+
end
|
15
|
+
|
16
|
+
task :default => :test
|
17
|
+
|
18
|
+
repo = ENV['GITHUB_REPO'] || 'rstacruz/sinatra-backbone'
|
19
|
+
namespace :doc do
|
20
|
+
desc "Builds documentation"
|
21
|
+
task :build do
|
22
|
+
# github.com/rstacruz/reacco
|
23
|
+
analytics = "--analytics #{ENV['ANALYTICS_ID']}" if ENV['ANALYTICS_ID']
|
24
|
+
system "reacco --literate --toc --api lib --github #{repo} #{analytics}"
|
25
|
+
end
|
26
|
+
|
27
|
+
desc "Uploads documentation"
|
28
|
+
task :deploy => :build do
|
29
|
+
# github.com/rstacruz/git-update-ghpages
|
30
|
+
system "git update-ghpages -i doc #{repo}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
task :doc => :'doc:build'
|
@@ -0,0 +1,21 @@
|
|
1
|
+
$:.unshift File.expand_path('../../../lib', __FILE__)
|
2
|
+
|
3
|
+
require 'sinatra/base'
|
4
|
+
require 'sequel'
|
5
|
+
require 'sinatra/backbone'
|
6
|
+
|
7
|
+
class App < Sinatra::Base
|
8
|
+
enable :raise_errors, :logging
|
9
|
+
enable :show_exceptions if development?
|
10
|
+
|
11
|
+
register Sinatra::JstPages
|
12
|
+
serve_jst '/jst.js'
|
13
|
+
|
14
|
+
set :root, File.expand_path('../', __FILE__)
|
15
|
+
set :views, File.expand_path('../views', __FILE__)
|
16
|
+
set :public, File.expand_path('../public', __FILE__)
|
17
|
+
|
18
|
+
get '/' do
|
19
|
+
erb :home
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
// Here is our Backbone model!
|
2
|
+
$(function() {
|
3
|
+
do_test();
|
4
|
+
});
|
5
|
+
|
6
|
+
function do_test() {
|
7
|
+
echo("<h3>Rendering from template:</h3>");
|
8
|
+
echo(JST['hello']({name: "Julie Kitzinger", age: "33"}));
|
9
|
+
echo("<h3>Success!</h3>");
|
10
|
+
}
|
11
|
+
|
12
|
+
// Helper functions
|
13
|
+
function echo(html) {
|
14
|
+
$("#messages").append(html);
|
15
|
+
};
|
16
|
+
|
17
|
+
function onerror() {
|
18
|
+
echo("<p class='error'>Oops... an error occured.</p>");
|
19
|
+
};
|
20
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="UTF-8" />
|
5
|
+
<title></title>
|
6
|
+
<script src='http://cdnjs.cloudflare.com/ajax/libs/jquery/1.6.2/jquery.min.js'></script>
|
7
|
+
<script src='http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.1.7/underscore-min.js'></script>
|
8
|
+
<script src='http://cdnjs.cloudflare.com/ajax/libs/backbone.js/0.5.3/backbone-min.js'></script>
|
9
|
+
<style>
|
10
|
+
body { font-family: sans-serif; font-size: 13px; line-height: 1.5; background: #d0d0da; }
|
11
|
+
#messages { width: 400px; margin: 20px auto; background: white; padding: 20px; border: solid 10px #c0c0ca; }
|
12
|
+
h3 { border-top: dotted 1px #ccc; padding: 20px 20px 0 20px; margin: 20px -20px 0 -20px; color: #46a; }
|
13
|
+
h3:first-child { border-top: 0; margin-top: 0; padding-top: 0; }
|
14
|
+
dl { overflow: hidden; }
|
15
|
+
dt { float: left; width: 120px; margin-right: 10px; text-align: right; color: #aaa; }
|
16
|
+
h3 { font-family: palatino; font-size: 1.3em; }
|
17
|
+
</style>
|
18
|
+
</head>
|
19
|
+
<body>
|
20
|
+
<div id="messages">
|
21
|
+
</div>
|
22
|
+
<script src='jst.js'></script>
|
23
|
+
<script src='app.js'></script>
|
24
|
+
</body>
|
25
|
+
</html>
|
@@ -0,0 +1,40 @@
|
|
1
|
+
$:.unshift File.expand_path('../../../lib', __FILE__)
|
2
|
+
|
3
|
+
require 'sinatra/base'
|
4
|
+
require 'sequel'
|
5
|
+
require 'sinatra/backbone'
|
6
|
+
|
7
|
+
DB = Sequel.connect("sqlite::memory:")
|
8
|
+
DB.create_table :books do
|
9
|
+
primary_key :id
|
10
|
+
String :title
|
11
|
+
String :author
|
12
|
+
end
|
13
|
+
|
14
|
+
class Book < Sequel::Model
|
15
|
+
def to_hash
|
16
|
+
{ :id => id, :title => title, :author => author }
|
17
|
+
end
|
18
|
+
|
19
|
+
def validate
|
20
|
+
errors.add :author, "can't be empty" if author.to_s.size == 0
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class App < Sinatra::Base
|
25
|
+
enable :raise_errors, :logging
|
26
|
+
enable :show_exceptions if development?
|
27
|
+
|
28
|
+
register Sinatra::RestAPI
|
29
|
+
|
30
|
+
rest_create("/book") { Book.new }
|
31
|
+
rest_resource("/book/:id") { |id| Book[id] }
|
32
|
+
|
33
|
+
set :root, File.expand_path('../', __FILE__)
|
34
|
+
set :views, File.expand_path('../', __FILE__)
|
35
|
+
set :public, File.expand_path('../public', __FILE__)
|
36
|
+
|
37
|
+
get '/' do
|
38
|
+
erb :home
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="UTF-8" />
|
5
|
+
<title></title>
|
6
|
+
<script src='http://cdnjs.cloudflare.com/ajax/libs/jquery/1.6.2/jquery.min.js'></script>
|
7
|
+
<script src='http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.1.7/underscore-min.js'></script>
|
8
|
+
<script src='http://cdnjs.cloudflare.com/ajax/libs/backbone.js/0.5.3/backbone-min.js'></script>
|
9
|
+
<style>
|
10
|
+
body { font-family: sans-serif; font-size: 13px; line-height: 1.5; background: #d0d0da; }
|
11
|
+
#messages { width: 400px; margin: 20px auto; background: white; padding: 20px; border: solid 10px #c0c0ca; }
|
12
|
+
h3 { border-top: dotted 1px #ccc; padding: 20px 20px 0 20px; margin: 20px -20px 0 -20px; color: #46a; }
|
13
|
+
h3:first-child { border-top: 0; margin-top: 0; padding-top: 0; }
|
14
|
+
dl { overflow: hidden; }
|
15
|
+
dt { float: left; width: 120px; margin-right: 10px; text-align: right; color: #aaa; }
|
16
|
+
h3 { font-family: palatino; font-size: 1.3em; }
|
17
|
+
</style>
|
18
|
+
</head>
|
19
|
+
<body>
|
20
|
+
<div id="messages">
|
21
|
+
</div>
|
22
|
+
<script src='app.js'></script>
|
23
|
+
</body>
|
24
|
+
</html>
|
@@ -0,0 +1,105 @@
|
|
1
|
+
// Here is our Backbone model!
|
2
|
+
Book = Backbone.Model.extend({
|
3
|
+
urlRoot: '/book'
|
4
|
+
});
|
5
|
+
|
6
|
+
$(function() {
|
7
|
+
do_create();
|
8
|
+
});
|
9
|
+
|
10
|
+
function do_create() {
|
11
|
+
echo("<h3>Creating a book:</h3>");
|
12
|
+
|
13
|
+
var book = new Book;
|
14
|
+
book.set({ title: "Darkly Dreaming Dexter", author: "Jeff Lindsay" });
|
15
|
+
book.save({}, {
|
16
|
+
error: onerror,
|
17
|
+
success: function() {
|
18
|
+
print_book(book);
|
19
|
+
echo("<h3>Retrieving the same book:</h3>");
|
20
|
+
do_retrieve(book);
|
21
|
+
}
|
22
|
+
});
|
23
|
+
}
|
24
|
+
|
25
|
+
function do_retrieve(_book) {
|
26
|
+
var book = new Book({ id: _book.id });
|
27
|
+
book.fetch({
|
28
|
+
error: onerror,
|
29
|
+
success: function() {
|
30
|
+
print_book(book);
|
31
|
+
do_edit_error(book);
|
32
|
+
}
|
33
|
+
});
|
34
|
+
}
|
35
|
+
|
36
|
+
function do_edit_error(book) {
|
37
|
+
echo("<h3>Editing book with an error:</h3>");
|
38
|
+
console.log("(You should see an HTTP error right about here:)");
|
39
|
+
book.set({ author: '' });
|
40
|
+
book.save({}, {
|
41
|
+
success: onerror,
|
42
|
+
error: function() {
|
43
|
+
console.log("(...yep.)");
|
44
|
+
echo("...yes, it occured.");
|
45
|
+
do_edit(book);
|
46
|
+
}
|
47
|
+
});
|
48
|
+
}
|
49
|
+
|
50
|
+
function do_edit(book) {
|
51
|
+
echo("<h3>Editing book:</h3>");
|
52
|
+
book.set({ author: 'Anne Rice', title: 'The Claiming of Sleeping Beauty' });
|
53
|
+
book.save({}, {
|
54
|
+
error: onerror,
|
55
|
+
success: function() {
|
56
|
+
print_book(book);
|
57
|
+
do_delete(book);
|
58
|
+
}
|
59
|
+
});
|
60
|
+
}
|
61
|
+
|
62
|
+
function do_delete(book) {
|
63
|
+
echo("<h3>Deleting book:</h3>");
|
64
|
+
book.destroy({
|
65
|
+
error: onerror,
|
66
|
+
success: function() {
|
67
|
+
echo("Success.");
|
68
|
+
do_verify_delete(book.id);
|
69
|
+
}
|
70
|
+
});
|
71
|
+
}
|
72
|
+
|
73
|
+
function do_verify_delete(id) {
|
74
|
+
echo("<h3>Checking if book "+id+" still exists:</h3>");
|
75
|
+
console.log("(You should see an HTTP error right about here:)");
|
76
|
+
var book = new Book({ id: id });
|
77
|
+
book.fetch({
|
78
|
+
success: onerror,
|
79
|
+
error: function() {
|
80
|
+
console.log("(...yep.)");
|
81
|
+
echo("No, it doesn't.");
|
82
|
+
do_success();
|
83
|
+
}
|
84
|
+
});
|
85
|
+
}
|
86
|
+
|
87
|
+
function do_success() {
|
88
|
+
echo("<h3>Success!</h3>");
|
89
|
+
}
|
90
|
+
|
91
|
+
function print_book(book) {
|
92
|
+
echo("<dl><dt>Title:</dt><dd>"+book.get('title')+"</dd></dl>");
|
93
|
+
echo("<dl><dt>Author:</dt><dd>"+book.get('author')+"</dd></dl>");
|
94
|
+
echo("<dl><dt>ID:</dt><dd>"+book.get('id')+"</dd></dl>");
|
95
|
+
}
|
96
|
+
|
97
|
+
// Helper functions
|
98
|
+
function echo(html) {
|
99
|
+
$("#messages").append(html);
|
100
|
+
};
|
101
|
+
|
102
|
+
function onerror() {
|
103
|
+
echo("<p class='error'>Oops... an error occured.</p>");
|
104
|
+
};
|
105
|
+
|
@@ -0,0 +1,261 @@
|
|
1
|
+
# ## JstPages [module]
|
2
|
+
# A Sinatra plugin that adds support for JST (JavaScript Server Templates).
|
3
|
+
# See [JstPages example][jstx] for a full example application.
|
4
|
+
#
|
5
|
+
# ### Basic usage
|
6
|
+
# Register the `Sinatra::JstPages` plugin in your application, and use
|
7
|
+
# `serve_jst`. This example serves all JST files found in `/views/**/*.jst.*`
|
8
|
+
# (where `/views` is your views directory as defined in Sinatra's
|
9
|
+
# `settings.views`) as `http://localhost:4567/jst.js`.
|
10
|
+
#
|
11
|
+
# require 'sinatra/jstpages'
|
12
|
+
#
|
13
|
+
# class App < Sinatra::Base
|
14
|
+
# register Sinatra::JstPages
|
15
|
+
# serve_jst '/jst.js'
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# You will need to link to the JST route in your layout. Make a `<script>` tag
|
19
|
+
# where the `src='...'` attribute is the same path you provide to `serve_jst`.
|
20
|
+
#
|
21
|
+
# <script type='text/javascript' src='/jst.js'></script>
|
22
|
+
#
|
23
|
+
# So, if you have a JST view placed in `views/editor/edit.jst.tpl`:
|
24
|
+
#
|
25
|
+
# # views/editor/edit.jst.tpl
|
26
|
+
# <h1>Edit <%= name %></h1>
|
27
|
+
# <form>
|
28
|
+
# <button>Save</button>
|
29
|
+
# </form>
|
30
|
+
#
|
31
|
+
# Now in your browser you may invoke `JST['templatename']`:
|
32
|
+
#
|
33
|
+
# // Renders the editor/edit template
|
34
|
+
# JST['editor/edit']();
|
35
|
+
#
|
36
|
+
# // Renders the editor/edit template with template parameters
|
37
|
+
# JST['editor/edit']({name: 'Item Name'});
|
38
|
+
#
|
39
|
+
# #### Using Sinatra-AssetPack?
|
40
|
+
#
|
41
|
+
# __TIP:__ If you're using the [sinatra-assetpack][sap] gem, just add `/jst.js`
|
42
|
+
# to a package. (If you're not using Sinatra AssetPack yet, you probably
|
43
|
+
# should.)
|
44
|
+
#
|
45
|
+
# [sap]: http://ricostacruz.com/sinatra-assetpack
|
46
|
+
#
|
47
|
+
# ### Supported templates
|
48
|
+
#
|
49
|
+
# __[Jade][jade]__ (`.jst.jade`) -- Jade templates. This requires
|
50
|
+
# [jade.js][jade]. For older browsers, you will also need [json2.js][json2],
|
51
|
+
# and an implementation of [String.prototype.trim][trim].
|
52
|
+
#
|
53
|
+
# # views/editor/edit.jst.jade
|
54
|
+
# h1= "Edit "+name
|
55
|
+
# form
|
56
|
+
# button Save
|
57
|
+
#
|
58
|
+
# __[Underscore templates][under_tpl]__ (`.jst.tpl`) -- Simple templates by
|
59
|
+
# underscore. This requires [underscore.js][under], which Backbone also
|
60
|
+
# requires.
|
61
|
+
#
|
62
|
+
# # views/editor/edit.jst.tpl
|
63
|
+
# <h1>Edit <%= name %></h1>
|
64
|
+
# <form>
|
65
|
+
# <button>Save</button>
|
66
|
+
# </form>
|
67
|
+
#
|
68
|
+
# __[Haml.js][haml]__ (`.jst.haml`) -- A JavaScript implementation of Haml.
|
69
|
+
# Requires [haml.js][haml].
|
70
|
+
#
|
71
|
+
# # views/editor/edit.jst.haml
|
72
|
+
# %h1= "Edit "+name
|
73
|
+
# %form
|
74
|
+
# %button Save
|
75
|
+
#
|
76
|
+
# __[Eco][eco]__ (`.jst.eco`) -- Embedded CoffeeScript templates. Requires
|
77
|
+
# [eco.js][eco] and [coffee-script.js][cs].
|
78
|
+
#
|
79
|
+
# # views/editor/edit.jst.eco
|
80
|
+
# <h1>Edit <%= name %></h1>
|
81
|
+
# <form>
|
82
|
+
# <button>Save</button>
|
83
|
+
# </form>
|
84
|
+
#
|
85
|
+
# You can add support for more templates by subclassing the `Engine` class.
|
86
|
+
#
|
87
|
+
# [jade]: http://github.com/visionmedia/jade
|
88
|
+
# [json2]: https://github.com/douglascrockford/JSON-js
|
89
|
+
# [trim]: http://snippets.dzone.com/posts/show/701
|
90
|
+
# [under_tpl]: http://documentcloud.github.com/underscore/#template
|
91
|
+
# [under]: http://documentcloud.github.com/underscore
|
92
|
+
# [haml]: https://github.com/creationix/haml-js
|
93
|
+
# [eco]: https://github.com/sstephenson/eco
|
94
|
+
# [cs]: http://coffeescript.org
|
95
|
+
# [jstx]: https://github.com/rstacruz/sinatra-backbone/tree/master/examples/jstpages
|
96
|
+
#
|
97
|
+
module Sinatra
|
98
|
+
module JstPages
|
99
|
+
def self.registered(app)
|
100
|
+
app.extend ClassMethods
|
101
|
+
app.helpers Helpers
|
102
|
+
end
|
103
|
+
|
104
|
+
# Returns a hash to determine which engine is mapped onto a given extension.
|
105
|
+
def self.mappings
|
106
|
+
@mappings ||= Hash.new
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.register(ext, engine)
|
110
|
+
mappings[ext] = engine
|
111
|
+
end
|
112
|
+
|
113
|
+
module Helpers
|
114
|
+
# Returns a list of JST files.
|
115
|
+
def jst_files(options = {})
|
116
|
+
# Tuples of [ name, Engine instance ]
|
117
|
+
root = options[:root] || settings.views
|
118
|
+
tuples = Dir.chdir(root) {
|
119
|
+
Dir["**/*.jst*"].map { |fn|
|
120
|
+
name = fn.match(%r{^(.*)\.jst})[1]
|
121
|
+
ext = fn.match(%r{\.([^\.]*)$})[1]
|
122
|
+
engine = JstPages.mappings[ext]
|
123
|
+
|
124
|
+
[ name, engine.new(name, File.join(root, fn)) ] if engine
|
125
|
+
}.compact
|
126
|
+
}
|
127
|
+
|
128
|
+
Hash[*tuples.flatten]
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
module ClassMethods
|
133
|
+
# ### serve_jst(path, [options]) [class method]
|
134
|
+
# Serves JST files in given `path`.
|
135
|
+
#
|
136
|
+
def serve_jst(path, options={})
|
137
|
+
get path do
|
138
|
+
content_type :js
|
139
|
+
jsts = jst_files(options).map do |(name, engine)|
|
140
|
+
engine.compile!
|
141
|
+
end
|
142
|
+
|
143
|
+
%{
|
144
|
+
(function(){
|
145
|
+
var c = {};
|
146
|
+
if (!window.JST) window.JST = {};
|
147
|
+
#{jsts.join("\n ")}
|
148
|
+
})();
|
149
|
+
}.strip.gsub(/^ {12}/, '')
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
|
157
|
+
# ## JstPages::Engine [class]
|
158
|
+
# A template engine.
|
159
|
+
#
|
160
|
+
# #### Adding support for new template engines
|
161
|
+
# You will need to subclass `Engine`, override at least the `function` method,
|
162
|
+
# then use `JstPages.register`.
|
163
|
+
#
|
164
|
+
# This example will register `.jst.my` files to a new engine that uses
|
165
|
+
# `My.compile`.
|
166
|
+
#
|
167
|
+
# module Sinatra::JstPages
|
168
|
+
# class MyEngine < Engine
|
169
|
+
# def compile!() "My.compile(#{contents.inspect})"; end
|
170
|
+
# end
|
171
|
+
#
|
172
|
+
# register 'my', MyEngine
|
173
|
+
# end
|
174
|
+
#
|
175
|
+
module Sinatra::JstPages
|
176
|
+
class Engine
|
177
|
+
# ### name [attribute]
|
178
|
+
# The name of the template.
|
179
|
+
attr_reader :name
|
180
|
+
|
181
|
+
# ### file [attribute]
|
182
|
+
# The string path of the template file.
|
183
|
+
attr_reader :file
|
184
|
+
|
185
|
+
def initialize(name, file)
|
186
|
+
@name = name
|
187
|
+
@file = file
|
188
|
+
end
|
189
|
+
|
190
|
+
# ### contents [method]
|
191
|
+
# Returns the contents of the template file as a string.
|
192
|
+
def contents
|
193
|
+
File.read(@file)
|
194
|
+
end
|
195
|
+
|
196
|
+
def wrap_jst(name)
|
197
|
+
compiled_contents = contents
|
198
|
+
compiled_contents = yield if block_given?
|
199
|
+
|
200
|
+
<<JS.strip.gsub(/^ {12}/, '')
|
201
|
+
JST[#{name.inspect}] = function() {
|
202
|
+
if (!c[#{name.inspect}]) c[#{name.inspect}] = (#{compiled_contents});
|
203
|
+
return c[#{name.inspect}].apply(this, arguments);
|
204
|
+
};
|
205
|
+
JS
|
206
|
+
end
|
207
|
+
|
208
|
+
# ### function [method]
|
209
|
+
# The JavaScript function to invoke on the precompile'd object.
|
210
|
+
#
|
211
|
+
# What this returns should, in JavaScript, return a function that can be
|
212
|
+
# called with an object hash of the params to be passed onto the template.
|
213
|
+
def compile!
|
214
|
+
wrap_jst(name) do
|
215
|
+
"_.template(#{contents.inspect})"
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
class HamlEngine < Engine
|
221
|
+
def compile!
|
222
|
+
wrap_jst(name) do
|
223
|
+
"Haml(#{contents.inspect})";
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
class JadeEngine < Engine
|
229
|
+
def compile!
|
230
|
+
wrap_jst(name) do
|
231
|
+
"require('jade').compile(#{contents.inspect})"
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
class EcoEngine < Engine
|
237
|
+
def compile!
|
238
|
+
wrap_jst(name) do
|
239
|
+
"function() { var a = arguments.slice(); a.unshift(#{contents.inspect}); return eco.compile.apply(eco, a); }"
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
class HamlCoffeeEngine < Engine
|
245
|
+
def compile!
|
246
|
+
HamlCoffeeAssets.config.context = false
|
247
|
+
HamlCoffeeAssets::Compiler.compile(name, contents, true).
|
248
|
+
match(/^ window.JST(.*)\n };$/m)[0].gsub(/^ window.JST/, 'JST')
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
register 'tpl', Engine
|
253
|
+
register 'jst', Engine
|
254
|
+
register 'jade', JadeEngine
|
255
|
+
register 'haml', HamlEngine
|
256
|
+
register 'eco', EcoEngine
|
257
|
+
|
258
|
+
if defined?(HamlCoffeeAssets)
|
259
|
+
register 'hamlc', HamlCoffeeEngine
|
260
|
+
end
|
261
|
+
end
|