ice 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +23 -0
- data/LICENSE +20 -0
- data/README.markdown +84 -0
- data/Rakefile +47 -0
- data/VERSION +1 -0
- data/ice_js/History.md +5 -0
- data/ice_js/Readme.md +29 -0
- data/ice_js/lib/path_helper.js +17 -0
- data/ice_js/spec/commands/example_command.rb +19 -0
- data/ice_js/spec/dom.html +22 -0
- data/ice_js/spec/node.js +10 -0
- data/ice_js/spec/rhino.js +10 -0
- data/ice_js/spec/server.html +18 -0
- data/ice_js/spec/server.rb +4 -0
- data/ice_js/spec/unit/spec.helper.js +0 -0
- data/ice_js/spec/unit/spec.js +40 -0
- data/init.rb +1 -0
- data/lib/ice.rb +54 -0
- data/lib/ice/base_cube.rb +22 -0
- data/lib/ice/cube_association.rb +38 -0
- data/lib/ice/cubeable.rb +17 -0
- data/lib/parser.js +339 -0
- data/spec/base_cube_spec.rb +47 -0
- data/spec/cube_spec.rb +120 -0
- data/spec/ice_spec.rb +94 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +15 -0
- metadata +131 -0
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Nate Kidwell
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
# ice
|
2
|
+
|
3
|
+
This project allows user-submitted templates to be written in the javascript programming language. It is similar to Liquid in terms of safety, but uses javascript to leverage the powers of a language most developers are familiar with.
|
4
|
+
|
5
|
+
It runs the templates through an erb-ish parser and then uses the [TheRubyRacer Gem](http://github.com/cowboyd/therubyracer) to interpet the javascript using Google's V8 javascript engine. Your users can then write ice templates like:
|
6
|
+
|
7
|
+
<table>
|
8
|
+
<tr><th>Name</th><th>Email</th></tr>
|
9
|
+
<% for (i = 0; i < users.length; i++) { %>
|
10
|
+
<tr>
|
11
|
+
<td><%= user.name %></td><td><%= mail_to(user.email) %></td>
|
12
|
+
</tr>
|
13
|
+
<% } %>
|
14
|
+
</table>
|
15
|
+
|
16
|
+
## Why another templating engine when there is Liquid
|
17
|
+
|
18
|
+
Liquid is excellent but has several disadvantages
|
19
|
+
|
20
|
+
* Hard to extend without knowing liquid internals
|
21
|
+
* Introduces yet-another-language, whereas many designers are already familiar with javascript
|
22
|
+
* Doesn't allow template editors to use a rich object model and create their own functions
|
23
|
+
* Doesn't have a rich set of support libraries like javascript brings to the table.
|
24
|
+
|
25
|
+
Note that we're still big fans of liquid. In fact, we call this project "ice" as a tribute (keeping the metaphor alive, we use "Cubes" where they have "Drops").
|
26
|
+
|
27
|
+
Laminate uses the Lua language, which is a slight improvement, but still is unfamiliar to most developers.
|
28
|
+
|
29
|
+
|
30
|
+
## to_ice
|
31
|
+
|
32
|
+
Every object is revealed to the templates via their to_ice method. This helps filter the objects that are passed into the javascript, so people editing the page only have access to a sanitized version of your data.
|
33
|
+
|
34
|
+
Instances of some classes like String and Numeric just return themselves as the result of to_ice. Hashes and Arrays run to_ice recursively on their members.
|
35
|
+
|
36
|
+
## ActiveRecord modifications
|
37
|
+
|
38
|
+
To make life easy, since most complex objects passed to the templates will be subclasses of ActiveRecord::Base, the default to_ice behaviour of ActiveRecord is to pass itself in to a class with the same name, but followed by the word "Cube".
|
39
|
+
|
40
|
+
Therefore calling to_ice on instance of a User class will invoke
|
41
|
+
|
42
|
+
UserCube.new self
|
43
|
+
|
44
|
+
## BaseCube Class
|
45
|
+
|
46
|
+
In order for everything to work easily, you can have your cubes inherit from our Ice::BaseCube class. Your cubes inheriting from it can then determine what additional attributes they want to reveal. For example
|
47
|
+
|
48
|
+
class BookCube < Ice::BaseCube
|
49
|
+
revealing :title, :author_id, :genre_id
|
50
|
+
end
|
51
|
+
|
52
|
+
would provide a cube with access to the title, author_id and genre properties of the underlying ActiveRecord.
|
53
|
+
|
54
|
+
These cubes also have belongs_to and has_many associations, so you can write things like:
|
55
|
+
|
56
|
+
class ArticleCube < Ice::BaseCube
|
57
|
+
has_many :comments, :tags
|
58
|
+
belongs_to :author, :section
|
59
|
+
end
|
60
|
+
|
61
|
+
This brings in association helper functions such as comment_ids, num_comments, has_comments, comments, author_id, and author.
|
62
|
+
|
63
|
+
Note that all revealed functions and associations are also sanitized via to_ice.
|
64
|
+
|
65
|
+
## Note on Patches/Pull Requests
|
66
|
+
|
67
|
+
* Fork the project.
|
68
|
+
* Make your feature addition or bug fix.
|
69
|
+
* Add spec for it. This is important so I don't break it in a future version unintentionally. In fact, try to write your specs in a test-first manner.
|
70
|
+
* Commit, do not mess with rakefile, version, or history.
|
71
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
72
|
+
* Send me a pull request.
|
73
|
+
|
74
|
+
## Todo
|
75
|
+
|
76
|
+
* Allow .ice view files
|
77
|
+
* Add in form builders from clots project
|
78
|
+
* Break form builders and helpers out into separate javascript project that can be included in other frameworks like CakePHP
|
79
|
+
* Allow mappings for other ORMs than ActiveRecord
|
80
|
+
* Haml support
|
81
|
+
|
82
|
+
## Copyright
|
83
|
+
|
84
|
+
Copyright (c) 2010 Nate Kidwell. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "ice"
|
8
|
+
gem.summary = "User templates written in javascript"
|
9
|
+
gem.description = "User templates written in javascript"
|
10
|
+
gem.email = "nate@ludicast.com"
|
11
|
+
gem.homepage = "http://github.com/ludicast/ice"
|
12
|
+
gem.authors = ["Nate Kidwell"]
|
13
|
+
gem.add_development_dependency "rspec", ">= 1.2.9"
|
14
|
+
gem.add_dependency "therubyracer", "0.7.0"
|
15
|
+
gem.add_dependency "active_support"
|
16
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
17
|
+
end
|
18
|
+
Jeweler::GemcutterTasks.new
|
19
|
+
rescue LoadError
|
20
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
21
|
+
end
|
22
|
+
|
23
|
+
require 'spec/rake/spectask'
|
24
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
25
|
+
spec.libs << 'lib' << 'spec'
|
26
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
27
|
+
end
|
28
|
+
|
29
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
30
|
+
spec.libs << 'lib' << 'spec'
|
31
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
32
|
+
spec.rcov = true
|
33
|
+
end
|
34
|
+
|
35
|
+
task :spec => :check_dependencies
|
36
|
+
|
37
|
+
task :default => :spec
|
38
|
+
|
39
|
+
require 'rake/rdoctask'
|
40
|
+
Rake::RDocTask.new do |rdoc|
|
41
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
42
|
+
|
43
|
+
rdoc.rdoc_dir = 'rdoc'
|
44
|
+
rdoc.title = "ice #{version}"
|
45
|
+
rdoc.rdoc_files.include('README*')
|
46
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
47
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/ice_js/History.md
ADDED
data/ice_js/Readme.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
# YourLib
|
3
|
+
|
4
|
+
Description
|
5
|
+
|
6
|
+
## License
|
7
|
+
|
8
|
+
(The MIT License)
|
9
|
+
|
10
|
+
Copyright (c) 2009 Your Name <Your Email>
|
11
|
+
|
12
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
13
|
+
a copy of this software and associated documentation files (the
|
14
|
+
'Software'), to deal in the Software without restriction, including
|
15
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
16
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
17
|
+
permit persons to whom the Software is furnished to do so, subject to
|
18
|
+
the following conditions:
|
19
|
+
|
20
|
+
The above copyright notice and this permission notice shall be
|
21
|
+
included in all copies or substantial portions of the Software.
|
22
|
+
|
23
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
24
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
25
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
26
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
27
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
28
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
29
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1,17 @@
|
|
1
|
+
function view_path(item) {
|
2
|
+
return "/" + item.class_path + "/" + item.id
|
3
|
+
}
|
4
|
+
|
5
|
+
function edit_path(item) {
|
6
|
+
return "/" + item.class_path + "/" + item.id + "/edit"
|
7
|
+
}
|
8
|
+
|
9
|
+
function view_link(item, opts) {
|
10
|
+
label = (opts && opts.label) || "View"
|
11
|
+
return "<a href=\"" + view_path(item) + "\">" + label + "</a>"
|
12
|
+
}
|
13
|
+
|
14
|
+
function edit_link(item, opts) {
|
15
|
+
label = (opts && opts.label) || "Edit"
|
16
|
+
return "<a href=\"" + edit_path(item) + "\">" + label + "</a>"
|
17
|
+
}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
|
2
|
+
# uncomment and call with `$ jspec example `
|
3
|
+
|
4
|
+
# command :example do |c|
|
5
|
+
# c.syntax = 'jspec example [options]'
|
6
|
+
# c.description = 'Just an example command'
|
7
|
+
# c.option '-f', '--foo string', 'Does some foo with <string>'
|
8
|
+
# c.option '-b', '--bar [string]', 'Does some bar with [string]'
|
9
|
+
# c.example 'Do some foo', 'jspec example --foo bar'
|
10
|
+
# c.example 'Do some bar', 'jspec example --bar'
|
11
|
+
# c.when_called do |args, options|
|
12
|
+
# p args
|
13
|
+
# p options.__hash__
|
14
|
+
# # options.foo
|
15
|
+
# # options.bar
|
16
|
+
# # options.__hash__[:foo]
|
17
|
+
# # options.__hash__[:bar]
|
18
|
+
# end
|
19
|
+
# end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<link type="text/css" rel="stylesheet" href="/Users/natekidwell/.rvm/gems/ruby-1.9.1-p378/gems/jspec-4.3.1/lib/jspec.css" />
|
4
|
+
<script src="/Users/natekidwell/.rvm/gems/ruby-1.9.1-p378/gems/jspec-4.3.1/lib/jspec.js"></script>
|
5
|
+
<script src="/Users/natekidwell/.rvm/gems/ruby-1.9.1-p378/gems/jspec-4.3.1/lib/jspec.xhr.js"></script>
|
6
|
+
<script src="../lib/path_helper.js"></script>
|
7
|
+
<script src="unit/spec.helper.js"></script>
|
8
|
+
<script>
|
9
|
+
function runSuites() {
|
10
|
+
JSpec
|
11
|
+
.exec('unit/spec.js')
|
12
|
+
.run({ fixturePath: 'fixtures' })
|
13
|
+
.report()
|
14
|
+
}
|
15
|
+
</script>
|
16
|
+
</head>
|
17
|
+
<body class="jspec" onLoad="runSuites();">
|
18
|
+
<div id="jspec-top"><h2 id="jspec-title">JSpec <em><script>document.write(JSpec.version)</script></em></h2></div>
|
19
|
+
<div id="jspec"></div>
|
20
|
+
<div id="jspec-bottom"></div>
|
21
|
+
</body>
|
22
|
+
</html>
|
data/ice_js/spec/node.js
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
|
2
|
+
require.paths.unshift('spec', '/Users/natekidwell/.rvm/gems/ruby-1.9.1-p378/gems/jspec-4.3.1/lib', 'lib')
|
3
|
+
require('jspec')
|
4
|
+
require('unit/spec.helper')
|
5
|
+
require('yourlib')
|
6
|
+
|
7
|
+
JSpec
|
8
|
+
.exec('spec/unit/spec.js')
|
9
|
+
.run({ reporter: JSpec.reporters.Terminal, fixturePath: 'spec/fixtures', failuresOnly: true })
|
10
|
+
.report()
|
@@ -0,0 +1,10 @@
|
|
1
|
+
|
2
|
+
load('/Users/natekidwell/.rvm/gems/ruby-1.9.1-p378/gems/jspec-4.3.1/lib/jspec.js')
|
3
|
+
load('/Users/natekidwell/.rvm/gems/ruby-1.9.1-p378/gems/jspec-4.3.1/lib/jspec.xhr.js')
|
4
|
+
load('lib/yourlib.js')
|
5
|
+
load('spec/unit/spec.helper.js')
|
6
|
+
|
7
|
+
JSpec
|
8
|
+
.exec('spec/unit/spec.js')
|
9
|
+
.run({ reporter: JSpec.reporters.Terminal, fixturePath: 'spec/fixtures' })
|
10
|
+
.report()
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<script src="/jspec/jspec.js"></script>
|
4
|
+
<script src="/jspec/jspec.xhr.js"></script>
|
5
|
+
<script src="/lib/yourlib.js"></script>
|
6
|
+
<script src="/spec/unit/spec.helper.js"></script>
|
7
|
+
<script>
|
8
|
+
function runSuites() {
|
9
|
+
JSpec
|
10
|
+
.exec('unit/spec.js')
|
11
|
+
.run({ reporter: JSpec.reporters.Server, verbose: true, failuresOnly: true, fixturePath: '/spec/fixtures' })
|
12
|
+
.report()
|
13
|
+
}
|
14
|
+
</script>
|
15
|
+
</head>
|
16
|
+
<body class="jspec" onLoad="runSuites();">
|
17
|
+
</body>
|
18
|
+
</html>
|
File without changes
|
@@ -0,0 +1,40 @@
|
|
1
|
+
|
2
|
+
describe 'path_helper'
|
3
|
+
before_each
|
4
|
+
user = {id:1, class_path:'users'}
|
5
|
+
end
|
6
|
+
|
7
|
+
describe 'for paths'
|
8
|
+
it "should generate view path from item"
|
9
|
+
view_path(user).should.eql "/users/1"
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should generate edit path from item"
|
13
|
+
edit_path(user).should.eql "/users/1/edit"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
describe "for links"
|
19
|
+
describe "without parameters"
|
20
|
+
it "should generate view link from item"
|
21
|
+
view_link(user).should.eql "<a href=\"/users/1\">View</a>"
|
22
|
+
end
|
23
|
+
it "should generate edit link from item"
|
24
|
+
edit_link(user).should.eql "<a href=\"/users/1/edit\">Edit</a>"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "with parameters"
|
29
|
+
it "should generate view link from item"
|
30
|
+
view_link(user, {label:"See Info"}).should.eql "<a href=\"/users/1\">See Info</a>"
|
31
|
+
end
|
32
|
+
it "should generate edit link from item"
|
33
|
+
edit_link(user, {label:"Change Info"}).should.eql "<a href=\"/users/1/edit\">Change Info</a>"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ActiveRecord::Base.send(:include, Ice::Cubeable)
|
data/lib/ice.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'v8'
|
3
|
+
|
4
|
+
class Object
|
5
|
+
def to_ice
|
6
|
+
nil
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
[FalseClass, TrueClass, Numeric, String].each do |class_name|
|
11
|
+
eval "class #{class_name}
|
12
|
+
def to_ice
|
13
|
+
self
|
14
|
+
end
|
15
|
+
end"
|
16
|
+
end
|
17
|
+
|
18
|
+
class Array
|
19
|
+
def to_ice
|
20
|
+
map &:to_ice
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Hash
|
25
|
+
def to_ice
|
26
|
+
res = {}
|
27
|
+
each_pair do |key,value|
|
28
|
+
res[key] = value.to_ice
|
29
|
+
end
|
30
|
+
res
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
|
36
|
+
module Ice
|
37
|
+
def self.convert_template(template_text, vars = {})
|
38
|
+
|
39
|
+
V8::Context.new do |cxt|
|
40
|
+
cxt.load "#{File.dirname(__FILE__)}/parser.js"
|
41
|
+
|
42
|
+
vars.each_pair do |key, value|
|
43
|
+
cxt[key] = value.to_ice
|
44
|
+
end
|
45
|
+
|
46
|
+
cxt['____templateText'] = template_text
|
47
|
+
|
48
|
+
@evaled = cxt.eval "Jst.evaluate(Jst.compile(____templateText), {});"
|
49
|
+
|
50
|
+
end
|
51
|
+
@evaled
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class BaseCube
|
2
|
+
|
3
|
+
def self.revealing(* attributes)
|
4
|
+
attributes.each do |attr|
|
5
|
+
|
6
|
+
define_method attr.to_sym do
|
7
|
+
@source.send(attr).to_ice
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :source
|
13
|
+
|
14
|
+
def to_ice
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(source)
|
19
|
+
@source = source
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Ice
|
2
|
+
module CubeAssociation
|
3
|
+
def belongs_to(*args)
|
4
|
+
args.each do |sym|
|
5
|
+
belongs_to = %{
|
6
|
+
def #{sym}
|
7
|
+
@source.#{sym}.to_ice
|
8
|
+
end
|
9
|
+
def #{sym}_id
|
10
|
+
@source.#{sym}_id
|
11
|
+
end
|
12
|
+
}
|
13
|
+
class_eval belongs_to
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def has_many(*args)
|
18
|
+
args.each do |sym|
|
19
|
+
has_many = %{
|
20
|
+
def #{sym}
|
21
|
+
@source.#{sym}.map(&:to_ice)
|
22
|
+
end
|
23
|
+
def has_#{sym}
|
24
|
+
! @source.#{sym}.empty?
|
25
|
+
end
|
26
|
+
def num_#{sym}
|
27
|
+
@source.#{sym}.count
|
28
|
+
end
|
29
|
+
def #{sym.to_s.singularize}_ids
|
30
|
+
@source.#{sym.to_s.singularize}_ids
|
31
|
+
end
|
32
|
+
}
|
33
|
+
class_eval has_many
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/ice/cubeable.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
module Ice
|
2
|
+
module Cubeable
|
3
|
+
def get_cube_class(class_obj)
|
4
|
+
begin
|
5
|
+
cube_string = class_obj.to_s + "Cube"
|
6
|
+
cube_string.constantize
|
7
|
+
rescue
|
8
|
+
get_cube_class class_obj.superclass
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_ice
|
13
|
+
cube_class = get_cube_class self.class
|
14
|
+
cube_class.new self
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/parser.js
ADDED
@@ -0,0 +1,339 @@
|
|
1
|
+
/*
|
2
|
+
Copyright 2008, mark turansky (www.markturansky.com)
|
3
|
+
Copyright 2010, Andrew Kelley (superjoesoftware.com)
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is furnished
|
10
|
+
to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
The Software shall be used for Good, not Evil.
|
16
|
+
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
23
|
+
THE SOFTWARE.
|
24
|
+
|
25
|
+
(This is the license from www.json.org and I think it's awesome)
|
26
|
+
*/
|
27
|
+
|
28
|
+
String.prototype.endsWith = function endsWith(c) {
|
29
|
+
if (this.charAt(this.length - 1) == c) {
|
30
|
+
return true;
|
31
|
+
} else {
|
32
|
+
return false;
|
33
|
+
}
|
34
|
+
};
|
35
|
+
|
36
|
+
String.prototype.startsWith = function startsWith(c) {
|
37
|
+
if (this.charAt(0) == c) {
|
38
|
+
return true;
|
39
|
+
} else {
|
40
|
+
return false;
|
41
|
+
}
|
42
|
+
};
|
43
|
+
|
44
|
+
String.prototype.replaceAll = function replaceAll(a, b) {
|
45
|
+
var s = this;
|
46
|
+
while (s.indexOf(a) > -1) {
|
47
|
+
s = s.replace(a, b);
|
48
|
+
}
|
49
|
+
return s;
|
50
|
+
};
|
51
|
+
|
52
|
+
var Jst = function () {
|
53
|
+
// private variables:
|
54
|
+
var that; // reference to the public object
|
55
|
+
|
56
|
+
// all write and writeln functions eval'd by Jst
|
57
|
+
// concatenate to this internal variable
|
58
|
+
var html = "";
|
59
|
+
|
60
|
+
// private functions
|
61
|
+
function CharacterStack(str) {
|
62
|
+
var i;
|
63
|
+
|
64
|
+
this.characters = [];
|
65
|
+
this.peek = function peek() {
|
66
|
+
return this.characters[this.characters.length - 1];
|
67
|
+
};
|
68
|
+
this.pop = function pop() {
|
69
|
+
return this.characters.pop();
|
70
|
+
};
|
71
|
+
this.push = function push(c) {
|
72
|
+
this.characters.push(c);
|
73
|
+
};
|
74
|
+
this.hasMore = function hasMore() {
|
75
|
+
if (this.characters.length > 0) {
|
76
|
+
return true;
|
77
|
+
} else {
|
78
|
+
return false;
|
79
|
+
}
|
80
|
+
};
|
81
|
+
|
82
|
+
for (i = str.length; i >= 0; i -= 1) {
|
83
|
+
this.characters.push(str.charAt(i));
|
84
|
+
}
|
85
|
+
}
|
86
|
+
|
87
|
+
function StringWriter() {
|
88
|
+
this.str = "";
|
89
|
+
this.write = function write(s) {
|
90
|
+
this.str += s;
|
91
|
+
};
|
92
|
+
this.toString = function toString() {
|
93
|
+
return this.str;
|
94
|
+
};
|
95
|
+
}
|
96
|
+
|
97
|
+
function parseScriptlet(stack) {
|
98
|
+
var fragment = new StringWriter();
|
99
|
+
var c; // character
|
100
|
+
|
101
|
+
while (stack.hasMore()) {
|
102
|
+
if (stack.peek() == '%') { //possible end delimiter
|
103
|
+
c = stack.pop();
|
104
|
+
if (stack.peek() == '>') { //end delimiter
|
105
|
+
// pop > so that it is not available to main parse loop
|
106
|
+
stack.pop();
|
107
|
+
if (stack.peek() == '\n') {
|
108
|
+
fragment.write(stack.pop());
|
109
|
+
}
|
110
|
+
break;
|
111
|
+
} else {
|
112
|
+
fragment.write(c);
|
113
|
+
}
|
114
|
+
} else {
|
115
|
+
fragment.write(stack.pop());
|
116
|
+
}
|
117
|
+
}
|
118
|
+
return fragment.toString();
|
119
|
+
}
|
120
|
+
|
121
|
+
function isOpeningDelimiter(c) {
|
122
|
+
if (c == "<" || c == "%lt;") {
|
123
|
+
return true;
|
124
|
+
} else {
|
125
|
+
return false;
|
126
|
+
}
|
127
|
+
}
|
128
|
+
|
129
|
+
function isClosingDelimiter(c) {
|
130
|
+
if (c == ">" || c == "%gt;") {
|
131
|
+
return true;
|
132
|
+
} else {
|
133
|
+
return false;
|
134
|
+
}
|
135
|
+
}
|
136
|
+
|
137
|
+
function appendExpressionFragment(writer, fragment, jstWriter) {
|
138
|
+
var i,j;
|
139
|
+
var c;
|
140
|
+
|
141
|
+
// check to be sure quotes are on both ends of a string literal
|
142
|
+
if (fragment.startsWith("\"") && !fragment.endsWith("\"")) {
|
143
|
+
//some scriptlets end with \n, especially if the script ends the file
|
144
|
+
if (fragment.endsWith("\n") && fragment.charAt(fragment.length - 2) == '"') {
|
145
|
+
//we're ok...
|
146
|
+
} else {
|
147
|
+
throw { "message":"'" + fragment + "' is not properly quoted"};
|
148
|
+
}
|
149
|
+
}
|
150
|
+
|
151
|
+
if (!fragment.startsWith("\"") && fragment.endsWith("\"")) {
|
152
|
+
throw { "message":"'" + fragment + "' is not properly quoted"};
|
153
|
+
}
|
154
|
+
|
155
|
+
// print or println?
|
156
|
+
if (fragment.endsWith("\n")) {
|
157
|
+
writer.write(jstWriter + "ln(");
|
158
|
+
//strip the newline
|
159
|
+
fragment = fragment.substring(0, fragment.length - 1);
|
160
|
+
} else {
|
161
|
+
writer.write(jstWriter + "(");
|
162
|
+
}
|
163
|
+
|
164
|
+
if (fragment.startsWith("\"") && fragment.endsWith("\"")) {
|
165
|
+
//strip the quotes
|
166
|
+
fragment = fragment.substring(1, fragment.length - 1);
|
167
|
+
writer.write("\"");
|
168
|
+
for (i = 0; i < fragment.length; i += 1) {
|
169
|
+
c = fragment.charAt(i);
|
170
|
+
if (c == '"') {
|
171
|
+
writer.write("\\");
|
172
|
+
writer.write(c);
|
173
|
+
}
|
174
|
+
}
|
175
|
+
writer.write("\"");
|
176
|
+
} else {
|
177
|
+
for (j = 0; j < fragment.length; j += 1) {
|
178
|
+
writer.write(fragment.charAt(j));
|
179
|
+
}
|
180
|
+
}
|
181
|
+
|
182
|
+
writer.write(");");
|
183
|
+
}
|
184
|
+
|
185
|
+
function appendTextFragment(writer, fragment) {
|
186
|
+
var i;
|
187
|
+
var c;
|
188
|
+
|
189
|
+
if (fragment.endsWith("\n")) {
|
190
|
+
writer.write("writeln(\"");
|
191
|
+
} else {
|
192
|
+
writer.write("write(\"");
|
193
|
+
}
|
194
|
+
|
195
|
+
for (i = 0; i < fragment.length; i += 1) {
|
196
|
+
c = fragment.charAt(i);
|
197
|
+
if (c == '"') {
|
198
|
+
writer.write("\\");
|
199
|
+
}
|
200
|
+
// we took care of the line break with print vs. println
|
201
|
+
if (c != '\n' && c != '\r') {
|
202
|
+
writer.write(c);
|
203
|
+
}
|
204
|
+
}
|
205
|
+
|
206
|
+
writer.write("\");");
|
207
|
+
}
|
208
|
+
|
209
|
+
function parseExpression(stack) {
|
210
|
+
var fragment = new StringWriter();
|
211
|
+
var c;
|
212
|
+
|
213
|
+
while (stack.hasMore()) {
|
214
|
+
if (stack.peek() == '%') { //possible end delimiter
|
215
|
+
c = stack.pop();
|
216
|
+
if (isClosingDelimiter(stack.peek())) { //end delimiter
|
217
|
+
//pop > so that it is not available to main parse loop
|
218
|
+
stack.pop();
|
219
|
+
if (stack.peek() == '\n') {
|
220
|
+
fragment.write(stack.pop());
|
221
|
+
}
|
222
|
+
break;
|
223
|
+
} else {
|
224
|
+
fragment.write("%");
|
225
|
+
}
|
226
|
+
} else {
|
227
|
+
fragment.write(stack.pop());
|
228
|
+
}
|
229
|
+
}
|
230
|
+
|
231
|
+
return fragment.toString();
|
232
|
+
}
|
233
|
+
|
234
|
+
function parseText(stack) {
|
235
|
+
var fragment = new StringWriter();
|
236
|
+
var c,d;
|
237
|
+
|
238
|
+
while (stack.hasMore()) {
|
239
|
+
if (isOpeningDelimiter(stack.peek())) { //possible delimiter
|
240
|
+
c = stack.pop();
|
241
|
+
if (stack.peek() == '%') { // delimiter!
|
242
|
+
// push c onto the stack to be used in main parse loop
|
243
|
+
stack.push(c);
|
244
|
+
break;
|
245
|
+
} else {
|
246
|
+
fragment.write(c);
|
247
|
+
}
|
248
|
+
} else {
|
249
|
+
d = stack.pop();
|
250
|
+
fragment.write(d);
|
251
|
+
if (d == '\n') { //done with this fragment. println it.
|
252
|
+
break;
|
253
|
+
}
|
254
|
+
}
|
255
|
+
}
|
256
|
+
return fragment.toString();
|
257
|
+
}
|
258
|
+
|
259
|
+
function safeWrite(s) {
|
260
|
+
s = s.toString();
|
261
|
+
s = s.replaceAll('&', '&');
|
262
|
+
s = s.replaceAll('"', '"');
|
263
|
+
s = s.replaceAll('<', '<');
|
264
|
+
s = s.replaceAll('>', '>');
|
265
|
+
html += s;
|
266
|
+
}
|
267
|
+
|
268
|
+
function safeWriteln(s) {
|
269
|
+
safeWrite(s + "\n");
|
270
|
+
}
|
271
|
+
|
272
|
+
function write(s) {
|
273
|
+
html += s;
|
274
|
+
}
|
275
|
+
|
276
|
+
function writeln(s) {
|
277
|
+
write(s + "\n");
|
278
|
+
}
|
279
|
+
|
280
|
+
that = {
|
281
|
+
// public methods:
|
282
|
+
// pre-compile a template for quicker rendering. save the return value and
|
283
|
+
// pass it to evaluate.
|
284
|
+
compile: function (src) {
|
285
|
+
var stack = new CharacterStack(src);
|
286
|
+
var writer = new StringWriter();
|
287
|
+
|
288
|
+
var c;
|
289
|
+
var fragment;
|
290
|
+
while (stack.hasMore()) {
|
291
|
+
if (isOpeningDelimiter(stack.peek())) { //possible delimiter
|
292
|
+
c = stack.pop();
|
293
|
+
if (stack.peek() == '%') { //delimiter!
|
294
|
+
c = stack.pop();
|
295
|
+
if (stack.peek() == "=") {
|
296
|
+
// expression, escape all html
|
297
|
+
stack.pop();
|
298
|
+
fragment = parseExpression(stack);
|
299
|
+
appendExpressionFragment(writer, fragment,
|
300
|
+
"safeWrite");
|
301
|
+
} else if (stack.peek() == "+") {
|
302
|
+
// expression, don't escape html
|
303
|
+
stack.pop()
|
304
|
+
fragment = parseExpression(stack);
|
305
|
+
appendExpressionFragment(writer, fragment,
|
306
|
+
"write");
|
307
|
+
} else {
|
308
|
+
fragment = parseScriptlet(stack);
|
309
|
+
writer.write(fragment);
|
310
|
+
}
|
311
|
+
} else { //not a delimiter
|
312
|
+
stack.push(c);
|
313
|
+
fragment = parseText(stack);
|
314
|
+
appendTextFragment(writer, fragment);
|
315
|
+
}
|
316
|
+
} else {
|
317
|
+
fragment = parseText(stack);
|
318
|
+
appendTextFragment(writer, fragment);
|
319
|
+
}
|
320
|
+
}
|
321
|
+
return writer.toString();
|
322
|
+
},
|
323
|
+
|
324
|
+
// evaluate a pre-compiled script. recommended approach
|
325
|
+
evaluate: function (script, args) {
|
326
|
+
with(args) {
|
327
|
+
html = "";
|
328
|
+
eval(script);
|
329
|
+
return html;
|
330
|
+
}
|
331
|
+
},
|
332
|
+
|
333
|
+
// if you're lazy, you can use this
|
334
|
+
evaluateSingleShot: function (src, args) {
|
335
|
+
return this.evaluate(this.compile(src), args);
|
336
|
+
}
|
337
|
+
};
|
338
|
+
return that;
|
339
|
+
}();
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
|
4
|
+
class FooClassCube < BaseCube
|
5
|
+
revealing :first, :second
|
6
|
+
end
|
7
|
+
|
8
|
+
|
9
|
+
class FooClass
|
10
|
+
include Ice::Cubeable
|
11
|
+
|
12
|
+
def first
|
13
|
+
"primero"
|
14
|
+
end
|
15
|
+
|
16
|
+
def second
|
17
|
+
@second ||= SecondClass.new
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class SecondClass
|
22
|
+
def to_ice
|
23
|
+
"segundo"
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
describe "BaseCube" do
|
30
|
+
context "a cubeable class" do
|
31
|
+
it "should automatically to_ice the cube_class" do
|
32
|
+
FooClass.new.to_ice.class.should == FooClassCube
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should retrieve revealed properties" do
|
36
|
+
FooClass.new.to_ice.first.should == "primero"
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should map revealed properties via to_ice" do
|
40
|
+
FooClass.new.to_ice.second.should == "segundo"
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
|
47
|
+
end
|
data/spec/cube_spec.rb
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
class ParentObj
|
4
|
+
def to_ice
|
5
|
+
"parent"
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class TagObj
|
10
|
+
def to_ice
|
11
|
+
@name
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(name)
|
15
|
+
@name = name
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
class ChildModel
|
22
|
+
def parent
|
23
|
+
@parent ||= ParentObj.new
|
24
|
+
end
|
25
|
+
|
26
|
+
def parent_id
|
27
|
+
15
|
28
|
+
end
|
29
|
+
|
30
|
+
def tags
|
31
|
+
@tags ||= [TagObj.new("tag1"), TagObj.new("tag2")]
|
32
|
+
end
|
33
|
+
|
34
|
+
def tag_ids
|
35
|
+
[1, 2]
|
36
|
+
end
|
37
|
+
|
38
|
+
def children
|
39
|
+
[]
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
class BaseCubeWithBelongsTo
|
46
|
+
extend Ice::CubeAssociation
|
47
|
+
|
48
|
+
def initialize
|
49
|
+
@source = ChildModel.new
|
50
|
+
end
|
51
|
+
|
52
|
+
belongs_to :parent
|
53
|
+
end
|
54
|
+
|
55
|
+
class BaseCubeWithHasMany
|
56
|
+
extend Ice::CubeAssociation
|
57
|
+
|
58
|
+
def initialize
|
59
|
+
@source = ChildModel.new
|
60
|
+
end
|
61
|
+
|
62
|
+
has_many :tags
|
63
|
+
has_many :children
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "Cube" do
|
67
|
+
|
68
|
+
context "which has associations" do
|
69
|
+
context "when belongs to an item" do
|
70
|
+
|
71
|
+
it "should delegate object calls to its source object" do
|
72
|
+
cube = BaseCubeWithBelongsTo.new
|
73
|
+
cube.parent.should == "parent"
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should delegate id calls to its source object" do
|
77
|
+
cube = BaseCubeWithBelongsTo.new
|
78
|
+
cube.parent_id.should == 15
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
|
85
|
+
context "when has many of an item" do
|
86
|
+
|
87
|
+
context "for populated collection" do
|
88
|
+
it "should delegate object calls to its source object" do
|
89
|
+
cube = BaseCubeWithHasMany.new
|
90
|
+
cube.tags.should == ["tag1", "tag2"]
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should return true from has" do
|
94
|
+
cube = BaseCubeWithHasMany.new
|
95
|
+
cube.has_tags.should == true
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should return tag count" do
|
99
|
+
cube = BaseCubeWithHasMany.new
|
100
|
+
cube.num_tags.should == 2
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should delegate id calls to its source object" do
|
104
|
+
cube = BaseCubeWithHasMany.new
|
105
|
+
cube.tag_ids.should == [1, 2]
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context "for empty collection" do
|
110
|
+
it "should return false from has" do
|
111
|
+
cube = BaseCubeWithHasMany.new
|
112
|
+
cube.has_children.should == false
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
|
data/spec/ice_spec.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "Ice" do
|
4
|
+
|
5
|
+
|
6
|
+
|
7
|
+
it "converts a javascript template to html" do
|
8
|
+
Ice.convert_template("<%= 'hello world' %>").should == "hello world"
|
9
|
+
end
|
10
|
+
|
11
|
+
it "takes variables as syms" do
|
12
|
+
vars = {:hola => "hello", :mundo => "world" }
|
13
|
+
Ice.convert_template("<%= hola + ' ' + mundo %>", vars).should == "hello world"
|
14
|
+
end
|
15
|
+
|
16
|
+
it "takes variables as string" do
|
17
|
+
vars = {'hola' => "hello", 'mundo' => "world" }
|
18
|
+
Ice.convert_template("<%= hola + ' ' + mundo %>", vars).should == "hello world"
|
19
|
+
end
|
20
|
+
|
21
|
+
it "takes booleans, strings, and numbers as their value" do
|
22
|
+
vars = {'integer' => 1, 'float' => 1.1, 'boolean' => true, 'string' => "String"}
|
23
|
+
Ice.convert_template('<%= integer + " " + float + " " + boolean + " " + string %>',
|
24
|
+
vars).should == "1 1.1 true String"
|
25
|
+
end
|
26
|
+
|
27
|
+
context "to_ice function" do
|
28
|
+
it "should allow identical values for true" do
|
29
|
+
true.to_ice.should == true
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should allow identical values for false" do
|
33
|
+
false.to_ice.should == false
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should allow identical values for integer" do
|
37
|
+
1.to_ice.should == 1
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should allow identical values for float" do
|
41
|
+
1.1.to_ice.should == 1.1
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should allow identical values for string" do
|
45
|
+
"hi".to_ice.should == "hi"
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should default to nil for an object" do
|
49
|
+
Object.new.to_ice.should be_nil
|
50
|
+
end
|
51
|
+
context "for array" do
|
52
|
+
it "should freeze elements of array" do
|
53
|
+
i = []
|
54
|
+
i.should_receive(:to_ice)
|
55
|
+
[i].to_ice
|
56
|
+
end
|
57
|
+
it "should return array" do
|
58
|
+
array = [1, "foo"]
|
59
|
+
array.to_ice.should == [1, "foo"]
|
60
|
+
end
|
61
|
+
it "should pass in array with details" do
|
62
|
+
myarray = ["one", "two", "three"]
|
63
|
+
vars = {"myarray" => myarray }
|
64
|
+
Ice.convert_template(%{<% for (var i = 0; i < myarray.length; i++) { %><p><%= myarray[i] %></p><% } %>},
|
65
|
+
vars).should == "<p>one</p><p>two</p><p>three</p>"
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
context "for hash" do
|
71
|
+
it "should freeze elements of array" do
|
72
|
+
i = []
|
73
|
+
i.should_receive(:to_ice)
|
74
|
+
{:var => i}.to_ice
|
75
|
+
end
|
76
|
+
it "should return hash" do
|
77
|
+
hash = {"foo" => 1}
|
78
|
+
hash.to_ice.should == {"foo" => 1}
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should run to_ice on variables" do
|
83
|
+
message = Object.new
|
84
|
+
def message.to_ice
|
85
|
+
"hello world"
|
86
|
+
end
|
87
|
+
|
88
|
+
vars = {'message' => message }
|
89
|
+
Ice.convert_template("<%= message %>", vars).should == "hello world"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
end
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
+
require 'ice'
|
4
|
+
require 'ice/cubeable'
|
5
|
+
require 'ice/cube_association'
|
6
|
+
require 'ice/base_cube'
|
7
|
+
require 'spec'
|
8
|
+
require 'spec/autorun'
|
9
|
+
require 'active_support'
|
10
|
+
|
11
|
+
|
12
|
+
|
13
|
+
Spec::Runner.configure do |config|
|
14
|
+
|
15
|
+
end
|
metadata
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ice
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Nate Kidwell
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-05-31 00:00:00 -04:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rspec
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 1
|
29
|
+
- 2
|
30
|
+
- 9
|
31
|
+
version: 1.2.9
|
32
|
+
type: :development
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: therubyracer
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 0
|
43
|
+
- 7
|
44
|
+
- 0
|
45
|
+
version: 0.7.0
|
46
|
+
type: :runtime
|
47
|
+
version_requirements: *id002
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: active_support
|
50
|
+
prerelease: false
|
51
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
segments:
|
56
|
+
- 0
|
57
|
+
version: "0"
|
58
|
+
type: :runtime
|
59
|
+
version_requirements: *id003
|
60
|
+
description: User templates written in javascript
|
61
|
+
email: nate@ludicast.com
|
62
|
+
executables: []
|
63
|
+
|
64
|
+
extensions: []
|
65
|
+
|
66
|
+
extra_rdoc_files:
|
67
|
+
- LICENSE
|
68
|
+
- README.markdown
|
69
|
+
files:
|
70
|
+
- .gitignore
|
71
|
+
- LICENSE
|
72
|
+
- README.markdown
|
73
|
+
- Rakefile
|
74
|
+
- VERSION
|
75
|
+
- ice_js/History.md
|
76
|
+
- ice_js/Readme.md
|
77
|
+
- ice_js/lib/path_helper.js
|
78
|
+
- ice_js/spec/commands/example_command.rb
|
79
|
+
- ice_js/spec/dom.html
|
80
|
+
- ice_js/spec/node.js
|
81
|
+
- ice_js/spec/rhino.js
|
82
|
+
- ice_js/spec/server.html
|
83
|
+
- ice_js/spec/server.rb
|
84
|
+
- ice_js/spec/unit/spec.helper.js
|
85
|
+
- ice_js/spec/unit/spec.js
|
86
|
+
- init.rb
|
87
|
+
- lib/ice.rb
|
88
|
+
- lib/ice/base_cube.rb
|
89
|
+
- lib/ice/cube_association.rb
|
90
|
+
- lib/ice/cubeable.rb
|
91
|
+
- lib/parser.js
|
92
|
+
- spec/base_cube_spec.rb
|
93
|
+
- spec/cube_spec.rb
|
94
|
+
- spec/ice_spec.rb
|
95
|
+
- spec/spec.opts
|
96
|
+
- spec/spec_helper.rb
|
97
|
+
has_rdoc: true
|
98
|
+
homepage: http://github.com/ludicast/ice
|
99
|
+
licenses: []
|
100
|
+
|
101
|
+
post_install_message:
|
102
|
+
rdoc_options:
|
103
|
+
- --charset=UTF-8
|
104
|
+
require_paths:
|
105
|
+
- lib
|
106
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
segments:
|
111
|
+
- 0
|
112
|
+
version: "0"
|
113
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
segments:
|
118
|
+
- 0
|
119
|
+
version: "0"
|
120
|
+
requirements: []
|
121
|
+
|
122
|
+
rubyforge_project:
|
123
|
+
rubygems_version: 1.3.6
|
124
|
+
signing_key:
|
125
|
+
specification_version: 3
|
126
|
+
summary: User templates written in javascript
|
127
|
+
test_files:
|
128
|
+
- spec/base_cube_spec.rb
|
129
|
+
- spec/cube_spec.rb
|
130
|
+
- spec/ice_spec.rb
|
131
|
+
- spec/spec_helper.rb
|