zafu 0.5.0

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.
@@ -0,0 +1,145 @@
1
+ require 'rubyless'
2
+
3
+ module Zafu
4
+ module Process
5
+ module RubyLess
6
+ include ::RubyLess::SafeClass
7
+ # Actual method resolution. The lookup first starts in the current helper. If nothing is found there, it
8
+ # searches inside a 'helpers' module and finally looks into the current node_context.
9
+ # If nothing is found at this stage, we prepend the method with the current node and start over again.
10
+ def safe_method_type(signature)
11
+ get_method_type(signature, false)
12
+ end
13
+
14
+ # Resolve unknown methods by using RubyLess in the current compilation context (the
15
+ # translate method in RubyLess will call 'safe_method_type' in this module).
16
+ def r_unknown
17
+ rubyless_render(@method, @params)
18
+ rescue ::RubyLess::NoMethodError => err
19
+ parser_error("#{err.error_message} <span class='type'>#{err.method_with_arguments}</span>", err.receiver_with_class)
20
+ rescue ::RubyLess::Error => err
21
+ parser_error(err.message)
22
+ end
23
+
24
+ # Print documentation on the current node type.
25
+ def r_m
26
+ if @params[:helper] == 'true'
27
+ klass = helper.class
28
+ else
29
+ klass = node.klass
30
+ end
31
+
32
+ out "<div class='rubyless-m'><h3>Documentation for <b>#{klass}</b></h3>"
33
+ out "<ul>"
34
+ ::RubyLess::SafeClass.safe_methods_for(klass).each do |signature, opts|
35
+ opts = opts.dup
36
+ opts.delete(:method)
37
+ if opts.keys == [:class]
38
+ opts = opts[:class]
39
+ end
40
+ out "<li>#{signature.inspect} => #{opts.inspect}</li>"
41
+ end
42
+ out "</ul></div>"
43
+ end
44
+
45
+ # TEMPORARY METHOD DURING HACKING...
46
+ def r_erb
47
+ "<pre><%= @erb.gsub('<','&lt;').gsub('>','&gt;') %></pre>"
48
+ end
49
+
50
+ def rubyless_render(method, params)
51
+ rubyless_expand(::RubyLess.translate(method_with_arguments(method, params), self))
52
+ end
53
+
54
+ def rubyless_attr(val)
55
+ ::RubyLess.translate_string(val, self)
56
+ end
57
+
58
+ private
59
+ def get_method_type(signature, added_options = false)
60
+ if type = node_context_from_signature(signature)
61
+ # Resolve @page, @node
62
+ type
63
+ elsif type = safe_method_from(helper, signature)
64
+ # Resolve template helper methods
65
+ type
66
+ elsif helper.respond_to?(:helpers) && type = safe_method_from(helper.helpers, signature)
67
+ # Resolve by looking at the included helpers
68
+ type
69
+ elsif node && node.klass.kind_of?(Class) && type = safe_method_from(node.klass, signature)
70
+ # Resolve node context methods (xxx.foo, xxx.bar)
71
+ type.merge(:method => "#{node.name}.#{type[:method]}")
72
+ elsif node && !added_options
73
+ # Try prepending current node before arguments: link("foo") becomse link(var1, "foo")
74
+ signature_with_node = signature.dup
75
+ signature_with_node.insert(1, node.klass)
76
+ if type = get_method_type(signature_with_node, added_options = true)
77
+ type = type.merge(:prepend_args => ::RubyLess::TypedString.new(node.name, :class => node.klass))
78
+ type
79
+ else
80
+ nil
81
+ end
82
+ else
83
+ nil
84
+ end
85
+ end
86
+
87
+ def method_with_arguments(method, params)
88
+ hash_arguments = {}
89
+ arguments = []
90
+ keys = params.keys.map {|k| k.to_s}
91
+ keys.sort.each do |k|
92
+ if k.to_s =~ /\A_/
93
+ arguments << params[k.to_sym]
94
+ else
95
+ hash_arguments[k.to_s] = params[k.to_sym]
96
+ end
97
+ end
98
+
99
+ arguments += [hash_arguments] if hash_arguments != {}
100
+ if arguments != [] && method[-1..-1] =~ /\w/
101
+ "#{method}(#{arguments.inspect[1..-2]})"
102
+ else
103
+ method
104
+ end
105
+ end
106
+
107
+ def rubyless_expand(res)
108
+ if res.klass == String && @blocks.map {|b| b.kind_of?(String) ? nil : b.method}.compact.empty?
109
+ out "<%= #{res} %>"
110
+ elsif res.could_be_nil?
111
+ out "<% if #{var} = #{res} -%>"
112
+ out @markup.wrap(expand_with_node(var, res.klass))
113
+ out "<% end -%>"
114
+ else
115
+ out "<% #{var} = #{res} -%>"
116
+ out @markup.wrap(expand_with_node(var, res.klass))
117
+ end
118
+ end
119
+
120
+ # This is used to resolve '@node' as NodeContext with class Node, '@page' as first NodeContext
121
+ # of type Page, etc.
122
+ def node_context_from_signature(signature)
123
+ return nil unless signature.size == 1
124
+ ivar = signature.first
125
+ return nil unless ivar[0..0] == '@'
126
+ begin
127
+ klass = Module.const_get(ivar[1..-1].capitalize)
128
+ context = node(klass)
129
+ rescue NameError
130
+ return nil
131
+ end
132
+ {:class => context.klass, :method => context.name}
133
+ end
134
+
135
+ def safe_method_from(context, signature)
136
+ if context.respond_to?(:safe_method_type)
137
+ context.safe_method_type(signature)
138
+ else
139
+ ::RubyLess::SafeClass.safe_method_type_for(context, signature)
140
+ end
141
+ end
142
+
143
+ end # RubyLess
144
+ end # Process
145
+ end # Zafu
@@ -0,0 +1,25 @@
1
+ require 'zafu/compiler'
2
+
3
+ module Zafu
4
+ class Template
5
+ def initialize(template, src_helper = nil, compiler = Zafu::Compiler)
6
+ if template.kind_of?(String)
7
+ @ast = compiler.new(template)
8
+ else
9
+ @ast = compiler.new_with_url(template.path, :helper => src_helper)
10
+ end
11
+ end
12
+
13
+ def to_erb(context = {})
14
+ @ast.to_erb(context)
15
+ end
16
+
17
+ def to_ruby(context = {})
18
+ src = ::ERB.new("<% __in_erb_template=true %>#{to_erb(context)}", nil, '-').src
19
+
20
+ # Ruby 1.9 prepends an encoding to the source. However this is
21
+ # useless because you can only set an encoding on the first line
22
+ RUBY_VERSION >= '1.9' ? src.sub(/\A#coding:.*\n/, '') : src
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,19 @@
1
+ require 'zafu/template'
2
+
3
+ module Zafu
4
+ module TestHelper
5
+ include ::RubyLess::SafeClass
6
+
7
+ def zafu_erb(source, src_helper = self, compiler = Zafu::Compiler)
8
+ Zafu::Template.new(source, src_helper, compiler).to_erb(compilation_context)
9
+ end
10
+
11
+ def zafu_render(source, src_helper = self, compiler = Zafu::Compiler)
12
+ eval Zafu::Template.new(source, src_helper, compiler).to_ruby(compilation_context)
13
+ end
14
+
15
+ def compilation_context
16
+ {:node => @node_context, :helper => self}
17
+ end
18
+ end
19
+ end
data/lib/zafu.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'zafu/all'
2
+
3
+ if defined?(ActionView)
4
+ require 'zafu/handler'
5
+ ActionView::Template.register_template_handler(:zafu, Zafu::Handler)
6
+ ActionView::Template.register_template_handler(:html, Zafu::Handler)
7
+ end
data/rails/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'zafu' #File.join(File.dirname(__FILE__), '..', 'lib', 'zafu')
data/script/console ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # File: script/console
3
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
4
+
5
+ libs = " -r irb/completion"
6
+ # Perhaps use a console_lib to store any extra methods I may want available in the cosole
7
+ # libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
8
+ libs << " -r #{File.dirname(__FILE__) + '/../lib/zafu.rb'}"
9
+ puts "Loading zafu gem"
10
+ exec "#{irb} #{libs} --simple-prompt"
data/script/destroy ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
data/script/generate ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)
@@ -0,0 +1,232 @@
1
+ require 'test_helper'
2
+
3
+ class String
4
+ def blank?
5
+ self == ''
6
+ end
7
+ end
8
+
9
+ class NodeContextTest < Test::Unit::TestCase
10
+ include RubyLess::SafeClass
11
+ safe_method :day => {:class => String, :method => %q{Time.now.strftime('%A')}}
12
+ Markup = Zafu::Markup
13
+
14
+ context 'Parsing parameters' do
15
+ should 'retrieve values escaped with single quotes' do
16
+ h = {:class => 'worker', :style => 'tired'}
17
+ assert_equal h, Markup.parse_params("class='worker' style='tired'")
18
+ end
19
+
20
+ should 'retrieve values escaped with double quotes' do
21
+ h = {:class => 'worker', :style => 'tired'}
22
+ assert_equal h, Markup.parse_params('class="worker" style="tired"')
23
+ end
24
+
25
+ should 'retrieve values escaped with mixed quotes' do
26
+ h = {:class => 'worker', :style => 'tired'}
27
+ assert_equal h, Markup.parse_params('class=\'worker\' style="tired"')
28
+ end
29
+
30
+ should 'properly handle escaped single quotes' do
31
+ h = {:class => "that's nice", :style => 'tired'}
32
+ assert_equal h, Markup.parse_params("class='that\\\'s nice' style='tired'")
33
+ end
34
+
35
+ should 'properly handle escaped double quotes' do
36
+ h = {:class => '30"', :style => 'tired'}
37
+ assert_equal h, Markup.parse_params('class="30\\"" style="tired"')
38
+ end
39
+ end
40
+
41
+ context 'Setting parameters' do
42
+ setup do
43
+ @markup = Markup.new('p')
44
+ end
45
+
46
+ should 'parse params if the parameters are provided as a string' do
47
+ @markup.params = "class='shiny' id='slogan'"
48
+ h = {:class => 'shiny', :id => 'slogan'}
49
+ assert_equal h, @markup.params
50
+ end
51
+
52
+ should 'set params if the parameters are provided as a hash' do
53
+ @markup.params = {:class => 'shiny', :style => 'good'}
54
+ h = {:class => 'shiny', :style => 'good'}
55
+ assert_equal h, @markup.params
56
+ end
57
+ end
58
+
59
+ context 'Stealing html params' do
60
+ setup do
61
+ @markup = Markup.new('p')
62
+ end
63
+
64
+ should 'remove common html params' do
65
+ base = {:class => 'blue', :name => 'sprout', :id => 'front_cover', :style => 'padding:5px;', :attr => 'title'}
66
+ @markup.steal_html_params_from(base)
67
+ new_base = {:name => 'sprout', :attr => 'title'}
68
+ markup_params = {:class => 'blue', :id => 'front_cover', :style => 'padding:5px;'}
69
+ assert_equal new_base, base
70
+ assert_equal markup_params, @markup.params
71
+ end
72
+ end
73
+
74
+ context 'Defining the dom id' do
75
+ setup do
76
+ @markup = Markup.new('p')
77
+ @markup.params[:id] = 'foobar'
78
+ @markup.dyn_params[:id] = 'foobar'
79
+ @markup.set_id('<%= @node.zip %>')
80
+ end
81
+
82
+ should 'remove any predifined id' do
83
+ assert_nil @markup.params[:id]
84
+ end
85
+
86
+ should 'write id in the dynamic params' do
87
+ assert_equal '<%= @node.zip %>', @markup.dyn_params[:id]
88
+ end
89
+ end
90
+
91
+ context 'Setting a dynamic param' do
92
+ setup do
93
+ @markup = Markup.new('p')
94
+ @markup.params[:foo] = 'one'
95
+ @markup.set_dyn_params(:foo => '<%= @node.two %>')
96
+ end
97
+
98
+ should 'clear a static param with same key' do
99
+ assert_nil @markup.params[:foo]
100
+ assert_equal '<%= @node.two %>', @markup.dyn_params[:foo]
101
+ end
102
+ end
103
+
104
+ context 'Setting a static param' do
105
+ setup do
106
+ @markup = Markup.new('p')
107
+ @markup.dyn_params[:foo] = '<%= @node.two %>'
108
+ @markup.set_params(:foo => 'one')
109
+ end
110
+
111
+ should 'clear a dynamic param with same key' do
112
+ assert_nil @markup.dyn_params[:foo]
113
+ assert_equal 'one', @markup.params[:foo]
114
+ end
115
+ end
116
+
117
+
118
+ context 'Appending a static param' do
119
+ context 'on a static param' do
120
+ setup do
121
+ @markup = Markup.new('p')
122
+ @markup.set_params(:class => 'simple')
123
+ end
124
+
125
+ should 'append param in the static params' do
126
+ @markup.append_param(:class, 'mind')
127
+ assert_equal 'simple mind', @markup.params[:class]
128
+ end
129
+ end
130
+
131
+ context 'on a dynamic param' do
132
+ setup do
133
+ @markup = Markup.new('p')
134
+ @markup.set_dyn_params(:class => '<%= @foo %>')
135
+ end
136
+
137
+ should 'append param in the dynamic params' do
138
+ @markup.append_param(:class, 'bar')
139
+ assert_equal '<%= @foo %> bar', @markup.dyn_params[:class]
140
+ end
141
+ end
142
+
143
+ context 'on an empty param' do
144
+ setup do
145
+ @markup = Markup.new('p')
146
+ end
147
+
148
+ should 'set param in the static params' do
149
+ @markup.append_param(:class, 'bar')
150
+ assert_equal 'bar', @markup.params[:class]
151
+ end
152
+ end
153
+ end
154
+
155
+ context 'Appending a dynamic param' do
156
+ context 'on a static param' do
157
+ setup do
158
+ @markup = Markup.new('p')
159
+ @markup.set_params(:class => 'simple')
160
+ end
161
+
162
+ should 'copy the static param in the dynamic params' do
163
+ @markup.append_dyn_param(:class, '<%= @mind %>')
164
+ assert_equal 'simple <%= @mind %>', @markup.dyn_params[:class]
165
+ end
166
+ end
167
+
168
+ context 'on a dynamic param' do
169
+ setup do
170
+ @markup = Markup.new('p')
171
+ @markup.set_dyn_params(:class => '<%= @foo %>')
172
+ end
173
+
174
+ should 'append param in the dynamic params' do
175
+ @markup.append_dyn_param(:class, '<%= @bar %>')
176
+ assert_equal '<%= @foo %> <%= @bar %>', @markup.dyn_params[:class]
177
+ end
178
+ end
179
+
180
+ context 'on an empty param' do
181
+ setup do
182
+ @markup = Markup.new('p')
183
+ end
184
+
185
+ should 'set param in the dynamic params' do
186
+ @markup.append_dyn_param(:class, '<%= @bar %>')
187
+ assert_equal '<%= @bar %>', @markup.dyn_params[:class]
188
+ end
189
+ end
190
+ end
191
+
192
+ context 'Wrapping some text' do
193
+ setup do
194
+ @text = 'Alice: It would be so nice if something made sense for a change.'
195
+ @markup = Markup.new('p')
196
+ @markup.params = {:class => 'quote', :style => 'padding:3px; border:1px solid red;'}
197
+ end
198
+
199
+ should 'add the markup tag around the text' do
200
+ assert_equal "<p class='quote' style='padding:3px; border:1px solid red;'>#{@text}</p>", @markup.wrap(@text)
201
+ end
202
+
203
+ should 'not wrap twice if called twice' do
204
+ assert_equal "<p class='quote' style='padding:3px; border:1px solid red;'>#{@text}</p>", @markup.wrap(@markup.wrap(@text))
205
+ end
206
+ end
207
+
208
+ context 'Compiling params' do
209
+ setup do
210
+ @markup = Markup.new('p')
211
+ @markup.params = %q{class='one #{day}' id='foobar' name='#{day}'}
212
+ end
213
+
214
+ context 'with compile_params' do
215
+ setup do
216
+ @markup.compile_params(self)
217
+ end
218
+
219
+ should 'translate dynamic params into ERB by using RubyLess' do
220
+ assert_equal %q{<%= "one #{Time.now.strftime('%A')}" %>}, @markup.dyn_params[:class]
221
+ end
222
+
223
+ should 'translate without string on single dynamic content' do
224
+ assert_equal %q{<%= Time.now.strftime('%A') %>}, @markup.dyn_params[:name]
225
+ end
226
+ end
227
+ end
228
+ end
229
+
230
+
231
+
232
+
@@ -0,0 +1,19 @@
1
+ module Mock
2
+ module Params
3
+ def self.included(base)
4
+ base.before_process :filter_params
5
+ end
6
+
7
+ def filter_params
8
+ if klass = @params.delete(:class)
9
+ if klass =~ /#\{/
10
+ res = RubyLess.translate("\"#{klass}\"", self)
11
+ @markup.append_dyn_param(:class, "<%= #{res} %>")
12
+ else
13
+ @markup.append_param(:class, klass)
14
+ end
15
+ end
16
+ true
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,190 @@
1
+ require 'test_helper'
2
+
3
+ class NodeContextTest < Test::Unit::TestCase
4
+ class Page;end
5
+ class SubPage < Page; end
6
+ class Comment;end
7
+ NodeContext = Zafu::NodeContext
8
+
9
+ context 'In a blank context' do
10
+ setup do
11
+ @context = NodeContext.new('@node', Page)
12
+ end
13
+
14
+ should 'return the current name' do
15
+ assert_equal '@node', @context.name
16
+ end
17
+
18
+ should 'return the current class' do
19
+ assert_equal Page, @context.klass
20
+ end
21
+
22
+ should 'return true on will_be with the same class' do
23
+ assert @context.will_be?(Page)
24
+ end
25
+
26
+ should 'return false on will_be with a sub class' do
27
+ assert !@context.will_be?(SubPage)
28
+ end
29
+
30
+ should 'return false on will_be with a different class' do
31
+ assert !@context.will_be?(String)
32
+ end
33
+
34
+ should 'return self on a get for the same class' do
35
+ assert_equal @context.object_id, @context.get(Page).object_id
36
+ end
37
+
38
+ should 'return nil on a get for another class' do
39
+ assert_nil @context.get(Comment)
40
+ end
41
+
42
+ context 'with a sub-class' do
43
+ setup do
44
+ @context = NodeContext.new('@node', SubPage)
45
+ end
46
+
47
+ should 'return true on will_be with the same class' do
48
+ assert @context.will_be?(SubPage)
49
+ end
50
+
51
+ should 'return true on will_be with a super class' do
52
+ assert @context.will_be?(Page)
53
+ end
54
+
55
+ should 'return false on will_be with a different class' do
56
+ assert !@context.will_be?(String)
57
+ end
58
+ end
59
+ end
60
+
61
+ context 'In a sub-context' do
62
+ setup do
63
+ @parent = NodeContext.new('@node', Page)
64
+ @context = @parent.move_to('comment1', Comment)
65
+ end
66
+
67
+ should 'return the current name' do
68
+ assert_equal 'comment1', @context.name
69
+ end
70
+
71
+ should 'return the current class' do
72
+ assert_equal Comment, @context.klass
73
+ end
74
+
75
+ should 'return self on a get for the same class' do
76
+ assert_equal @context.object_id, @context.get(Comment).object_id
77
+ end
78
+
79
+ should 'return the parent on a get for the class of the parent' do
80
+ assert_equal @parent.object_id, @context.get(Page).object_id
81
+ end
82
+ end
83
+
84
+ context 'In a deeply nested context' do
85
+ setup do
86
+ @grandgrandma = NodeContext.new('@comment', Comment)
87
+ @grandma = @grandgrandma.move_to('page', Page)
88
+ @mother = @grandma.move_to('comment1', Comment)
89
+ @context = @mother.move_to('var1', String)
90
+ end
91
+
92
+ should 'return the current name' do
93
+ assert_equal 'var1', @context.name
94
+ end
95
+
96
+ should 'return the current class' do
97
+ assert_equal String, @context.klass
98
+ end
99
+
100
+ should 'return the first ancestor matching class on get' do
101
+ assert_equal @mother.object_id, @context.get(Comment).object_id
102
+ end
103
+
104
+ should 'return nil if no ancestor matches class on get' do
105
+ assert_nil @context.get(Fixnum)
106
+ end
107
+ end
108
+
109
+ context 'In a sub-classes context' do
110
+ setup do
111
+ @context = NodeContext.new('super', SubPage)
112
+ end
113
+
114
+ should 'find the current context required class is an ancestor' do
115
+ assert_equal @context.object_id, @context.get(Page).object_id
116
+ end
117
+ end
118
+
119
+ context 'In a list context' do
120
+ setup do
121
+ @context = NodeContext.new('list', [Page])
122
+ end
123
+
124
+ should 'find the context and resolve with first' do
125
+ assert context = @context.get(Page)
126
+ assert_equal 'list.first', context.name
127
+ assert_equal Page, context.klass
128
+ end
129
+ end
130
+
131
+ context 'Generating a dom id' do
132
+ context 'in a blank context' do
133
+ setup do
134
+ @context = NodeContext.new('@foo', Page)
135
+ end
136
+
137
+ should 'return the node name in DOM id' do
138
+ assert_equal '<%= @foo.zip %>', @context.dom_id
139
+ end
140
+ end
141
+
142
+ context 'in a hierarchy of contexts' do
143
+ setup do
144
+ @a = NodeContext.new('@node', Page)
145
+ @b = NodeContext.new('var1', [Page], @a)
146
+ @c = NodeContext.new('var2', Page, @b)
147
+ @context = NodeContext.new('var3', Page, @c)
148
+ end
149
+
150
+ context 'with parents as dom_scopes' do
151
+ setup do
152
+ @b.dom_scope!
153
+ @c.dom_scope!
154
+ end
155
+
156
+ should 'use dom_scopes' do
157
+ assert_equal '<%= var1.zip %>_<%= var2.zip %>_<%= var3.zip %>', @context.dom_id
158
+ end
159
+ end
160
+
161
+ context 'with ancestors and self as dom_scopes' do
162
+ setup do
163
+ @a.dom_scope!
164
+ @context.dom_scope!
165
+ end
166
+
167
+ should 'not use self twice' do
168
+ assert_equal '<%= @node.zip %>_<%= var3.zip %>', @context.dom_id
169
+ end
170
+ end
171
+
172
+ context 'with a parent defining a dom_prefix' do
173
+ setup do
174
+ @b.dom_prefix = 'cart'
175
+ end
176
+
177
+ should 'use dom_prefix' do
178
+ assert_equal 'cart_<%= var3.zip %>', @context.dom_id
179
+ end
180
+ end
181
+
182
+ end
183
+ end
184
+ end
185
+
186
+
187
+
188
+
189
+
190
+