ice 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,18 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "rails", "3.0.7"
4
+ gem 'rake', '0.8.7'
5
+ gem "capybara", ">= 0.4.0"
6
+ gem "sqlite3"
7
+
8
+ gem "rspec-rails", ">= 2.0.0.beta"
9
+
10
+ gem "therubyracer"
11
+ gem "informal"
12
+
13
+ gem "jasmine"
14
+
15
+ gem "eco"
16
+ # To use debugger (ruby-debug for Ruby 1.8.7+, ruby-debug19 for Ruby 1.9.2+)
17
+ # gem 'ruby-debug'
18
+ # gem 'ruby-debug19'
@@ -1,4 +1,4 @@
1
- Copyright (c) 2010 Nate Kidwell
1
+ Copyright 2011 YOURNAME
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.markdown CHANGED
@@ -1,54 +1,43 @@
1
- # Ice Ice Baby!
1
+ #Ice Ice Baby!!!
2
2
 
3
- The Ice project allows user-created templates to be written in the javascript programming language. Thanks to the [therubyracer](http://github.com/cowboyd/therubyracer) they are then interpreted using Google's V8 javascript engine.
3
+ The Ice system for CoffeeScript/Javascript templating allows people to serve Javascript templates thanks to [The Ruby Racer](http://github.com/cowboyd/therubyracer), a gem letting you use Google's V8 Javascript engine. These templates are then compiled and served to the browser.
4
4
 
5
- Ice is similar to Liquid in terms of safety, but uses javascript to leverage the powers of a language most developers are familiar with. Ice runs the templates through an erb-ish parser (written by [Mark Turansky](http://blog.markturansky.com/BetterJavascriptTemplates.html)).
5
+ One of the key advantages of this approach is that the templates execute in their own sandbox. This is the approach taken by [Liquid](http://github.com/tobi/liquid) and some of the other template systems.
6
6
 
7
- Your users can then write Ice templates like:
7
+ The template parser we currently use is Eco (written by [Sam Stephenson](https://github.com/sstephenson/eco)). This allows you to use Coffeescript with HTML in an ERB-ish fashion.
8
+
9
+ You can then write Eco templates like:
8
10
 
9
11
  <table>
10
12
  <tr><th>Name</th><th>Email</th></tr>
11
- <% for (i = 0; i < users.length; i++) { %>
13
+ <% for user in @users %>
12
14
  <tr>
13
- <td><%= user.name %></td><td><%= mail_to(user.email) %></td>
15
+ <td><%= user.name %></td><td><%= @mailTo(user.email) %></td>
14
16
  </tr>
15
- <% } %>
17
+ <% end %>
16
18
  </table>
17
19
 
18
- These templates can be run from the appropriate views directory, provided they have a .ice extension. Also, the templates may be compiled on demand with the method:
19
-
20
- Ice.convert_template(template_text, vars = {})
21
-
22
- ## Why another templating engine when there is Liquid?
23
-
24
- [Liquid](http://github.com/tobi/liquid) is excellent but it showing its age in a few ways:
20
+ Eco-formatted files may also exist in your filesystem, provided they have a .eco extension. Also, the templates may be compiled on demand with the method:
25
21
 
26
- * Hard to extend without knowing Liquid internals
27
- * Introduces yet-another-language, whereas many designers/developers are already familiar with javascript
28
- * Doesn't allow template creators to use a rich object model and easily create their own functions
29
- * Doesn't have a rich set of support libraries like what javascript brings to the table
30
-
31
- Note that we're still big fans of Liquid. In fact, we call this project "Ice" as a tribute (extending the metaphor, we use "Cubes" where they have "Drops").
32
-
33
- In addition, our ice_view.rb file is almost directly ripped out of the liquid project.
22
+ Ice::EcoTemplate.convert(template_text, vars = {})
34
23
 
35
24
  ## Installation
36
25
 
37
- Ice is curently being developed only for Rails 3 (we have a Rails 2 branch as well). Simply add to your Gemfile
26
+ Ice is currently being developed only for Rails 3. Simply add to your Gemfile
38
27
 
39
28
  gem 'ice'
40
29
 
41
30
  ## to_ice
42
31
 
43
- Every object is revealed to the templates via its to_ice method. This helps filter the objects that are passed into the javascript, so people editing the template only have access to a sanitized version of the data that you want them to format.
32
+ Every object is revealed to the templates via its to_ice method. This helps sanitize the objects that are passed into Ice, so people editing the template only have access to a limited subset of the data.
44
33
 
45
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.
46
35
 
47
- If you want an object to map to a different representation, simply define a to_ice object that returns whatever object you want to represent it within the javascript template. These objects are referred to as "Cubes", and are equivalent to "Drops" for those used to the Liquid template.
36
+ If you want an object to map to a different representation, simply define a to_ice object that returns whatever object you want to represent it within the eco template. These objects are referred to as "Cubes", and are equivalent to "Drops" for those used to the Liquid template.
48
37
 
49
- ## ActiveRecord and to_ice
38
+ ## ActiveModel and to_ice
50
39
 
51
- 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".
40
+ To make life easy, since most complex objects passed to the templates will be classes including ActiveModel::Serializable, the default to_ice behaviour of these classes is to pass itself in to a class with the same name, but followed by the word "Cube".
52
41
 
53
42
  Therefore calling to_ice on instance of a User class will invoke
54
43
 
@@ -56,7 +45,7 @@ Therefore calling to_ice on instance of a User class will invoke
56
45
 
57
46
  ## BaseCube Class
58
47
 
59
- 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
48
+ 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
60
49
 
61
50
  class BookCube < Ice::BaseCube
62
51
  revealing :title, :author_id, :genre_id
@@ -66,7 +55,7 @@ In order for everything to work easily, you can have your cubes inherit from our
66
55
  end
67
56
  end
68
57
 
69
- would provide a cube with access to the title, author_id and genre properties of the underlying ActiveRecord. In addition, it exposes a reviewer_names function that uses the @source instance variable to get at the record which is being filtered.
58
+ would provide a cube with access to the title, author_id and genre properties of the underlying ActiveModel. In addition, it exposes a reviewer_names function that uses the @source instance variable to get at the record which is being filtered. Note that if no call to `revealing` occurs, the cube generates a mapping for the `@source` object's serializable `attributes`.
70
59
 
71
60
  These cubes also have simple belongs_to and has_many associations, so you can write things like:
72
61
 
@@ -79,15 +68,18 @@ This generates association helper functions such as comment_ids, num_comments, h
79
68
 
80
69
  Note that the results of all associations and revealed functions are also sanitized via to_ice.
81
70
 
71
+ ## Partials
72
+
73
+ Partials may now be written in Eco, and included in Erb (and other) templates.
74
+
82
75
  ## NavBar
83
76
 
84
- To make it easier to generate links, we added a NavBar class to the javascript helpers. THis class has an open and close method, as well as a link_to mehod which either takes a url, or a url and a link label.
77
+ To make it easier to generate links, we added a `@navBar` helper.
85
78
 
86
- <% var nav = new NavBar() %>
87
- <%= nav.open() %>
88
- <%= nav.link_to("Bar", "/foo") %>
89
- <%= nav.link_to("http://ludicast.com") %>
90
- <%= nav.close() %>
79
+ <%- @navBar (bar) => %>
80
+ <%- bar.linkTo("Bar", "/foo") %>
81
+ <%- bar.linkTo("http://ludicast.com") %>
82
+ <% end %>
91
83
 
92
84
  This then generates the following html
93
85
 
@@ -96,59 +88,37 @@ This then generates the following html
96
88
  <li><a href="http://ludicast.com">http://ludicast.com</a></li>
97
89
  </ul>
98
90
 
99
- You'll notice that the resulting html code is shorter than the generator code, making this look inefficient. However the NavBar also takes options so if the NavBar above was instantiated with:
91
+ The `@navBar` helper also takes options so if the above was instead instantiated with:
100
92
 
101
- <% var nav = new NavBar({nav_open:"<div>", nav_close:"</div>",link_wrapper:function(link){
102
- return "<span>" + link + "</span>"
93
+ <% @opts = nav_prefix:'<div>', nav_postfix: '</div>', link_prefix: '<span>', link_postfix: '</span>' %>
94
+ <%- @navBar @opts, (bar)=> %>
103
95
 
104
- it would automatically generate
96
+ it would generate
105
97
 
106
98
  <div>
107
99
  <span><a href="/foo">Bar</a></span>
108
100
  <span><a href="http://ludicast.com">http://ludicast.com</a></span>
109
101
  </div>
110
102
 
111
- Also, if you want to make a site- or page-wide change, all you need to do is add these options to the NavBar class like
103
+ Also, if you want to make a site-wide change to the default NavBar settings, all you need to do is add these options to the NavBarConfig class (in Ruby) like
112
104
 
113
- NavBar.default_options = {nav_open:"<div>", nav_close:"</div>",link_wrapper:function(link){
114
- return "<span>" + link + "</span>"
115
- }}
105
+ NavBarConfig[:nav_prefix] = "<div>"
106
+ NavBarConfig[:nav_postfix] = "</div>"
107
+ NavBarConfig[:link_prefix] = "<span>"
108
+ NavBarConfig[:link_postfix] = "</span>"
116
109
 
117
- Then all links will generate with these options, unless overridden in the NavBar's constructor. If the NavBar has a separator property added, it will add that separator between links. If a link is not shown (due to access restrictions or whatever in the link_wrapper function) the separator obviously will not appear for that link. So the code
118
-
119
- bar.separator = "---"
120
- bar.link_wrapper = function (link) {
121
- if (link.match(/aa/)) {
122
- return ""
123
- } else {
124
- return link
125
- }
126
- }
127
-
128
- would cause
129
-
130
- bar.open() + bar.link_to("ff") + bar.link_to("aa") + bar.link_to("gg") + bar.close()
131
-
132
- to render as:
133
-
134
- links.should.eql "<div><a href=\"ff\">ff</a>----<a href=\"gg\">gg</a></div>"
110
+ Then all links will generate with these options, unless overridden in the values passed it to `@navBar`.
135
111
 
136
112
  ## Routes
137
113
 
138
- Keeping with our tradition of stealing from other projects, we took the code from [RouteJs middleware](http://coderack.org/users/kossnocorp/middlewares/88-routesjs) to expose to our templates all your routes. This is a big convenience and lets you put in your templates things like:
139
-
140
- <% var nav = new NavBar() %>
141
- <%= nav.open() %>
142
- <%= nav.link_to("List Pizzas", pizzas_path() ) %>
143
- <%= nav.link_to("Modify Pizza", edit_pizza_path({id: pizza.id})) %>
144
- <%= nav.close() %>
114
+ Assuming that all your cubes are models that you are exposing to your app, we add to your eco templates routing helpers for every class inheriting from BaseCube. Therefore, if you have a cube class named `NoteDrop`, you will have the following helper methods available:
145
115
 
146
- which is converted into
116
+ @newNotePath
117
+ @notesPath
118
+ @notePath(@note)
119
+ @editNotePath(@note)
147
120
 
148
- <ul class="linkBar">
149
- <li><a href="/pizzas">List Pizzas</a></li>
150
- <li><a href="/pizzas/2/edit">Modify Pizza</a></li>
151
- </ul>
121
+ which are converted to the appropriate paths.
152
122
 
153
123
  Note that some people might claim that it is insecure to expose your resources like this, but that probably should be dealt with on a case-by-case basis.
154
124
 
@@ -162,11 +132,13 @@ Note that some people might claim that it is insecure to expose your resources l
162
132
 
163
133
  ## Todo
164
134
 
135
+ * Allow Coffeescript (or Javascript) helpers to be read from an additional file.
165
136
  * Add in form builders (from clots project)
166
- * Break form builders and helpers out into separate javascript project that can be included in other frameworks like CakePHP
167
- * Allow mappings for other ORMs than ActiveRecord
168
- * Haml support (really just deciding what extension those files would use :))
137
+ * Haml support
138
+ * Use [Moneta](http://github.com/wycats/moneta) for caching autogenerated javascript files.
139
+ * Allowing Ice to render Rails partials
140
+ * Allowing Ice to serve as Rails layout files.
169
141
 
170
142
  ## Copyright
171
143
 
172
- Copyright (c) 2010 Nate Kidwell. See LICENSE for details.
144
+ MIT Licence. See MIT-LICENSE file for details.
data/Rakefile CHANGED
@@ -1,47 +1,34 @@
1
+ # encoding: UTF-8
1
2
  require 'rubygems'
2
- require 'rake'
3
-
4
3
  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.5"
15
- gem.add_dependency "rails", "3.0.0"
16
- # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
17
- end
18
- Jeweler::GemcutterTasks.new
4
+ require 'bundler/setup'
19
5
  rescue LoadError
20
- puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
6
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
21
7
  end
22
8
 
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
9
+ require 'rake'
10
+ require 'rake/rdoctask'
28
11
 
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
12
+ require 'rspec/core'
13
+ require 'rspec/core/rake_task'
34
14
 
35
- task :spec => :check_dependencies
15
+ RSpec::Core::RakeTask.new(:spec)
36
16
 
37
17
  task :default => :spec
38
18
 
39
- require 'rake/rdoctask'
40
- Rake::RDocTask.new do |rdoc|
41
- version = File.exist?('VERSION') ? File.read('VERSION') : ""
42
-
19
+ Rake::RDocTask.new(:rdoc) do |rdoc|
43
20
  rdoc.rdoc_dir = 'rdoc'
44
- rdoc.title = "ice #{version}"
45
- rdoc.rdoc_files.include('README*')
21
+ rdoc.title = 'Ice'
22
+ rdoc.options << '--line-numbers' << '--inline-source'
23
+ rdoc.rdoc_files.include('README.rdoc')
46
24
  rdoc.rdoc_files.include('lib/**/*.rb')
47
25
  end
26
+
27
+ begin
28
+ require 'jasmine'
29
+ load 'jasmine/tasks/jasmine.rake'
30
+ rescue LoadError
31
+ task :jasmine do
32
+ abort "Jasmine is not available. In order to run jasmine, you must: (sudo) gem install jasmine"
33
+ end
34
+ end
data/lib/ice.rb CHANGED
@@ -2,10 +2,14 @@ require 'ice'
2
2
  require 'ice/cubeable'
3
3
  require 'ice/cube_association'
4
4
  require 'ice/base_cube'
5
- require 'ice/base'
6
5
 
6
+ require 'ice/railtie'
7
+ require 'ice/cube_helpers'
7
8
  require 'rails'
8
9
 
9
- module Ice
10
- require 'ice/railtie' if defined?(Rails)
11
- end
10
+ require 'ice/eco_template/handler'
11
+
12
+ NavBarConfig = {}
13
+
14
+
15
+ ActionView::Template.register_template_handler :eco, Ice::EcoTemplate::Handler
data/lib/ice/base_cube.rb CHANGED
@@ -2,9 +2,13 @@ module Ice
2
2
  class BaseCube
3
3
  extend Ice::CubeAssociation
4
4
 
5
- def self.revealing(* attributes)
6
- attributes.each do |attr|
5
+ def self.revealing(* attributes)
6
+ unless @attribute_names
7
+ @attribute_names = []
8
+ end
9
+ @attribute_names.concat(attributes)
7
10
 
11
+ attributes.each do |attr|
8
12
  define_method attr.to_sym do
9
13
  @source.send(attr).to_ice
10
14
  end
@@ -17,8 +21,34 @@ module Ice
17
21
  self
18
22
  end
19
23
 
24
+ def to_hash
25
+ if @attribute_names
26
+ hash = {:id => @source.id}
27
+ @attribute_names && @attribute_names.each do |name|
28
+ hash[name] = @source.send(name)
29
+ end
30
+ hash
31
+ else
32
+ @hash = @source.serializable_hash
33
+ end
34
+ end
35
+
36
+ def id
37
+ @source.id
38
+ end
39
+
20
40
  def initialize(source)
21
41
  @source = source
42
+ unless @attribute_names
43
+ to_hash.each_key do |key|
44
+ unless self.respond_to? key.to_sym
45
+ self.class.send :define_method, key.to_sym do
46
+ @source.send(key.to_sym)
47
+ end
48
+ end
49
+ end
50
+ end
22
51
  end
52
+
23
53
  end
24
54
  end
@@ -0,0 +1,29 @@
1
+ class Object
2
+ def to_ice
3
+ nil
4
+ end
5
+ end
6
+
7
+ [FalseClass, TrueClass, Numeric, String].each do |cls|
8
+ cls.class_eval do
9
+ def to_ice
10
+ self
11
+ end
12
+ end
13
+ end
14
+
15
+ class Array
16
+ def to_ice
17
+ map &:to_ice
18
+ end
19
+ end
20
+
21
+ class Hash
22
+ def to_ice
23
+ res = {}
24
+ each_pair do |key,value|
25
+ res[key] = value.to_ice
26
+ end
27
+ res
28
+ end
29
+ end
data/lib/ice/cubeable.rb CHANGED
@@ -1,17 +1,17 @@
1
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
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
10
9
  end
10
+ end
11
11
 
12
- def to_ice
13
- cube_class = get_cube_class self.class
14
- cube_class.new self
15
- end
12
+ def to_ice
13
+ cube_class = get_cube_class self.class
14
+ cube_class.new self
16
15
  end
16
+ end
17
17
  end
@@ -0,0 +1,17 @@
1
+ require "ice/eco_template/context"
2
+ require 'eco'
3
+ require 'v8'
4
+
5
+ module Ice
6
+ module EcoTemplate
7
+ def self.convert_template(template_text, vars = {})
8
+ env = Context.new vars
9
+ context = V8::Context.new
10
+ context.eval(Eco::Source.combined_contents)
11
+
12
+ template = context["eco"]["compile"].call(template_text)
13
+ # Render the template
14
+ template.call(env)
15
+ end
16
+ end
17
+ end