sinatra-backbone-2 0.1.1
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.
- 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
|