zafu 0.6.0 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,13 @@
1
+ == 0.6.1 2010-07-22
2
+
3
+ * Minor enhancements
4
+ * Moved dependent method 'get_attribute_or_eval' from Zena.
5
+ * Fixed bugs in Markup#dup and NodeContext changing.
6
+ * Fixed error reporting on missing template.
7
+ * Added 'pre_wrap' method to Markup.
8
+ * Refactored wrapping to use a single method call.
9
+ * Fixed block in each dom scoping.
10
+
1
11
  == 0.6.0 2010-05-27
2
12
 
3
13
  * 3 major enhancements
data/lib/zafu/info.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module Zafu
2
- VERSION = '0.6.0'
2
+ VERSION = '0.6.1'
3
3
  end
4
4
 
data/lib/zafu/markup.rb CHANGED
@@ -165,6 +165,10 @@ module Zafu
165
165
  end
166
166
  end
167
167
 
168
+ def append_attribute(text_to_append)
169
+ (@append ||= '') << text_to_append
170
+ end
171
+
168
172
  def append_dyn_param(key, value, conditional = false)
169
173
  spacer = conditional ? '' : ' '
170
174
  if prev_value = @params.delete(key)
@@ -189,26 +193,35 @@ module Zafu
189
193
 
190
194
  # Duplicate markup and make sure params and dyn_params are duplicated as well.
191
195
  def dup
192
- markup = Markup.new(@tag)
193
- markup.params = @params.dup
194
- markup.dyn_params = @dyn_params.dup
196
+ markup = super
197
+ markup.instance_variable_set(:@params, @params.dup)
198
+ markup.instance_variable_set(:@dyn_params, @dyn_params.dup)
199
+ markup.instance_variable_set(:@pre_wrap, @pre_wrap.dup) if @pre_wrap
195
200
  markup
196
201
  end
197
202
 
203
+ # Store some text to insert at the beggining of the tag content on wrap. Inserted
204
+ # elements are indexed in a hash but only values are shown.
205
+ def pre_wrap
206
+ @pre_wrap ||= {}
207
+ end
208
+
198
209
  # Wrap the given text with our tag. If 'append' is not empty, append the text
199
210
  # after the tag parameters: <li class='foo'[APPEND HERE]>text</li>.
200
- def wrap(text, *append)
211
+ def wrap(text)
201
212
  return text if @done
213
+
214
+ text = "#{@pre_wrap.values}#{text}" if @pre_wrap
215
+
202
216
  if dyn_params[:id]
203
217
  @tag ||= 'div'
204
218
  end
205
219
 
206
- append ||= []
207
220
  if @tag
208
221
  if text.blank? && EMPTY_TAGS.include?(@tag)
209
- res = "<#{@tag}#{params_to_html}#{append.join('')}/>"
222
+ res = "#{@pre_wrap}<#{@tag}#{params_to_html}#{@append}/>"
210
223
  else
211
- res = "<#{@tag}#{params_to_html}#{append.join('')}>#{text}</#{@tag}>"
224
+ res = "<#{@tag}#{params_to_html}#{@append}>#{text}</#{@tag}>"
212
225
  end
213
226
  else
214
227
  res = text
@@ -36,10 +36,11 @@ module Zafu
36
36
  # Return a new node context that corresponds to the current object when rendered alone (in an ajax response or
37
37
  # from a direct 'show' in a controller). The returned node context has no parent (up is nil).
38
38
  # The convention is to use the class of the current object to build this name.
39
- # You can also use an 'after_class' parameter to move up in the current object's class hierarchy (see #master_class).
39
+ # You can also use an 'after_class' parameter to move up in the current object's class hierarchy to get
40
+ # ivar name (see #master_class).
40
41
  def as_main(after_class = nil)
41
- klass = after_class ? master_class(after_class) : self.klass
42
- NodeContext.new("@#{klass.to_s.underscore}", klass)
42
+ klass = after_class ? master_class(after_class) : Array(self.klass).first
43
+ NodeContext.new("@#{klass.to_s.underscore}", Array(self.klass).first)
43
44
  end
44
45
 
45
46
  # Find the class just afer 'after_class' in the class hierarchy.
@@ -56,12 +57,23 @@ module Zafu
56
57
  end
57
58
 
58
59
  # Generate a unique DOM id for this element based on dom_scopes defined in parent contexts.
59
- def dom_id
60
- @dom_id ||= begin
60
+ def dom_id(opts = {})
61
+ options = {:list => true, :erb => true}.merge(opts)
62
+
63
+ if options[:erb]
64
+ dom = dom_id(options.merge(:erb => false))
65
+ if dom =~ /^#\{([^\{]+)\}$/
66
+ "<%= #{$1} %>"
67
+ elsif dom =~ /#\{/
68
+ "<%= %Q{#{dom}} %>"
69
+ else
70
+ dom
71
+ end
72
+ else
61
73
  if @up
62
- [dom_prefix] + @up.dom_scopes + [make_scope_id]
74
+ [dom_prefix] + @up.dom_scopes + (options[:list] ? [make_scope_id] : [])
63
75
  else
64
- [dom_prefix] + [make_scope_id]
76
+ [dom_prefix] + (options[:list] ? [make_scope_id] : [])
65
77
  end.compact.uniq.join('_')
66
78
  end
67
79
  end
@@ -74,20 +86,20 @@ module Zafu
74
86
 
75
87
  # Mark the current context as being a looping element (each) whose DOM id needs to be propagated to sub-nodes
76
88
  # in order to ensure uniqueness of the dom_id (loops in loops problem).
77
- def dom_scope!
89
+ def propagate_dom_scope!
78
90
  @dom_scope = true
79
91
  end
80
92
 
81
93
  def get(klass)
82
94
  if list_context?
83
- if klass <= self.klass.first
95
+ if self.klass.first <= klass
84
96
  NodeContext.new("#{self.name}.first", self.klass.first)
85
97
  elsif @up
86
98
  @up.get(klass)
87
99
  else
88
100
  nil
89
101
  end
90
- elsif self.klass.ancestors.include?(klass)
102
+ elsif self.klass <= klass
91
103
  return self
92
104
  elsif @up
93
105
  @up.get(klass)
@@ -134,7 +146,7 @@ module Zafu
134
146
 
135
147
  private
136
148
  def make_scope_id
137
- "<%= #{@name}.zip %>"
149
+ "\#{#{@name}.zip}"
138
150
  end
139
151
  end
140
152
  end
data/lib/zafu/parser.rb CHANGED
@@ -9,12 +9,23 @@ module Zafu
9
9
  end
10
10
 
11
11
  class Parser
12
- TEXT_CALLBACKS = %w{before_parse after_parse before_wrap wrap after_wrap}
13
- PROCESS_CALLBACKS = %w{before_process expander process_unknown after_process}
12
+ # If you wonder what the difference is between 'after_wrap' and 'after_process' here it is:
13
+ # 'after_wrap' is called by the 'wrap' method from within the method handler, 'after_process' is called
14
+ # at the very end. Example:
15
+ #
16
+ # <% if var = Node.all -%> | <---
17
+ # <li>...</li> <--- content for after_wrap | <--- content for after_process
18
+ # <% end -%> | <---
19
+ #
20
+
21
+
22
+ TEXT_CALLBACKS = %w{before_parse after_parse before_wrap wrap after_wrap after_process}
23
+ PROCESS_CALLBACKS = %w{before_process expander process_unknown}
14
24
  CALLBACKS = TEXT_CALLBACKS + PROCESS_CALLBACKS
15
25
 
16
26
  @@callbacks = {}
17
- attr_accessor :text, :method, :pass, :options, :blocks, :ids, :defined_ids, :parent, :errors
27
+ attr_accessor :text, :name, :method, :pass, :options, :blocks, :ids, :defined_ids, :parent, :errors
28
+
18
29
  # Method parameters "<r:show attr='name'/>" (params contains {'attr' => 'name'}).
19
30
  attr_accessor :params
20
31
 
@@ -22,6 +33,7 @@ module Zafu
22
33
  def new_with_url(path, opts={})
23
34
  helper = opts[:helper] || Zafu::MockHelper.new
24
35
  text, fullpath, base_path = self.get_template_text(path, helper)
36
+ return parser_error("template '#{path}' not found", 'include') unless text
25
37
  self.new(text, :helper => helper, :base_path => base_path, :included_history => [fullpath], :root => path)
26
38
  end
27
39
 
@@ -34,7 +46,11 @@ module Zafu
34
46
  end
35
47
 
36
48
  def parser_error(message, method)
37
- "<span class='parser_error'><span class='method'>#{method}: <span class='message'>#{message}</span></span>"
49
+ "<span class='parser_error'><span class='method'>#{erb_safe method}</span> <span class='message'>#{erb_safe message}</span></span>"
50
+ end
51
+
52
+ def erb_safe(text)
53
+ text.gsub('<%', '&lt;%').gsub('%>', '%&gt;')
38
54
  end
39
55
 
40
56
  CALLBACKS.each do |clbk|
@@ -85,6 +101,17 @@ module Zafu
85
101
  }
86
102
  end
87
103
 
104
+ alias wrap_callbacks wrap
105
+
106
+ def wrap(text)
107
+ after_wrap(
108
+ wrap_callbacks(
109
+ before_wrap(text) + @out_post
110
+ )
111
+ # @text contains unparsed data (white space)
112
+ ) + @text
113
+ end
114
+
88
115
  # This method is called at the very beginning of the processing chain and is
89
116
  # used to store state to make 'process' reintrant...
90
117
  def save_state
@@ -104,9 +131,17 @@ module Zafu
104
131
  end
105
132
  end
106
133
 
107
- def parser_error(message, method = @method)
108
- @errors << "<span class='parser_error'><span class='method'>#{method}</span> <span class='message'>#{message}</span></span>"
109
- nil
134
+ def parser_error(message, method = @method, halt = true)
135
+ if halt
136
+ self.class.parser_error(message, method)
137
+ else
138
+ @errors << self.class.parser_error(message, method)
139
+ nil
140
+ end
141
+ end
142
+
143
+ def parser_continue(message, method = @method)
144
+ parser_error(message, method, false)
110
145
  end
111
146
 
112
147
  def process_unknown
@@ -194,7 +229,7 @@ module Zafu
194
229
  def process(context={})
195
230
  return '' if @method == 'ignore' || @method.blank?
196
231
 
197
- @saved = save_state
232
+ saved = save_state
198
233
 
199
234
  if @name
200
235
  # we pass the name as 'context' in the children tags
@@ -209,18 +244,12 @@ module Zafu
209
244
 
210
245
  @pass = {} # used to pass information to the parent (is this used ?)
211
246
 
212
- res = expander || default_expander
213
-
214
- res = before_wrap(res)
215
- res = wrap(res + @out_post)
247
+ res = wrap(expander || default_expander)
216
248
 
217
- # @text contains unparsed data (empty space)
218
- res = after_wrap(res) + @text
219
-
220
- after_process
249
+ res = after_process(res)
221
250
 
222
251
  # restore state
223
- restore_state(@saved)
252
+ restore_state(saved)
224
253
 
225
254
  res
226
255
  end
@@ -287,7 +316,7 @@ module Zafu
287
316
  end
288
317
 
289
318
  def include_template
290
- return parser_error("missing 'template' attribute", 'include') unless @params[:template]
319
+ return parser_error("missing 'template' attribute") unless @params[:template]
291
320
  if @options[:part] && @options[:part] == @params[:part]
292
321
  # fetching only a part, do not open this element (same as original caller) as it is useless and will make us loop the loop.
293
322
  @method = 'ignore'
@@ -301,7 +330,7 @@ module Zafu
301
330
 
302
331
  included_text, absolute_url, base_path = self.class.get_template_text(@params[:template], @options[:helper], @options[:base_path])
303
332
 
304
- if included_text
333
+ if absolute_url
305
334
  absolute_url += "::#{@params[:part].gsub('/','_')}" if @params[:part]
306
335
  absolute_url += "??#{@options[:part].gsub('/','_')}" if @options[:part]
307
336
  if @options[:included_history].include?(absolute_url)
@@ -310,7 +339,9 @@ module Zafu
310
339
  included_history = @options[:included_history] + [absolute_url]
311
340
  end
312
341
  else
313
- return "<span class='parser_error'>[include] template '#{@params[:template]}' not found</span>"
342
+ # Error: included_text contains the error meessage
343
+ @blocks = [included_text]
344
+ return
314
345
  end
315
346
 
316
347
  res = self.class.new(included_text, :helper => @options[:helper], :base_path => base_path, :included_history => included_history, :part => @params[:part], :parent => self) # we set :part to avoid loop failure when doing self inclusion
@@ -558,6 +589,7 @@ module Zafu
558
589
  end
559
590
 
560
591
  def expand_with(acontext={})
592
+
561
593
  blocks = acontext.delete(:blocks) || @blocks
562
594
  res = ""
563
595
 
@@ -580,10 +612,10 @@ module Zafu
580
612
 
581
613
  blocks.each do |b|
582
614
  if b.kind_of?(String)
583
- if (!only || only.include?(:string)) && (!ignore || !ignore.include?(:string))
615
+ if (!only || (only.kind_of?(Array) && only.include?(:string))) && (!ignore || !ignore.include?(:string))
584
616
  res << b
585
617
  end
586
- elsif (!only || only.include?(b.method)) && (!ignore || !ignore.include?(b.method))
618
+ elsif (!only || (only.kind_of?(Array) && only.include?(b.method)) || only =~ b.method) && (!ignore || !ignore.include?(b.method))
587
619
  res << b.process(new_context.dup)
588
620
  if pass = b.pass
589
621
  if pass[:part]
@@ -10,6 +10,9 @@ module Zafu
10
10
  def expand_with_finder(finder)
11
11
  return super unless finder[:class].kind_of?(Array)
12
12
 
13
+ # reset scope
14
+ @context[:saved_template] = nil
15
+
13
16
  # Get the block responsible for rendering each elements in the list
14
17
  each_block = descendant('each')
15
18
  add_block = descendant('add')
@@ -35,14 +38,14 @@ module Zafu
35
38
  set_dom_prefix
36
39
 
37
40
  # New node context.
38
- open_node_context(finder, :node => self.node.move_to(var, finder[:class])) do #, :need_link_id => form_block.need_link_id) do
41
+ open_node_context(finder, :node => self.node.move_to(var, finder[:class])) do
39
42
  # Pagination count and other contextual variables exist here.
40
43
 
41
44
  # INLINE ==========
42
- # 'r_add' needs the form when rendering. Send with :form.
43
- out @markup.wrap(
45
+ out wrap(
44
46
  expand_with(
45
47
  :in_if => false,
48
+ # 'r_add' needs the form when rendering. Send with :form.
46
49
  :form => form_block,
47
50
  :publish_after_save => publish_after_save,
48
51
  # Do not render the form block directly: let [add] do this.
@@ -53,32 +56,32 @@ module Zafu
53
56
 
54
57
  # Render 'else' clauses
55
58
  @markup.done = false
56
- out @markup.wrap(
59
+ out wrap(
57
60
  expand_with(
58
61
  :in_if => true,
59
62
  :only => ['elsif', 'else']
60
63
  )
61
64
  )
62
- end
63
- out "<% end -%>"
64
65
 
65
- # 2. Save 'each' template
66
- store_block(each_block) #, :klass => klass) # do we need klass here ?
66
+ # 2. Save 'each' template
67
+ store_block(each_block, :node => self.node.move_to(var, finder[:class])) #, :klass => klass) # do we need klass here ?
67
68
 
68
- # 3. Save 'form' template
69
- cont = {
70
- :saved_template => form_url(node),
71
- :klass => klass,
72
- :make_form => each_block == form_block,
73
- :publish_after_save => publish_after_save,
74
- }
69
+ # 3. Save 'form' template
70
+ cont = {
71
+ :saved_template => form_url(node.dom_prefix),
72
+ :klass => klass,
73
+ :make_form => each_block == form_block,
74
+ :publish_after_save => publish_after_save,
75
+ }
75
76
 
76
- store_block(form_block, cont)
77
+ store_block(form_block, cont)
78
+ end
79
+ out "<% end -%>"
77
80
  else
78
81
  super
79
82
  end
80
83
 
81
- # out @markup.wrap(expand_with(:node => node.move_to(var, finder[:class]), :in_if => true))
84
+ # out wrap(expand_with(:node => node.move_to(var, finder[:class]), :in_if => true))
82
85
 
83
86
 
84
87
  #query = opts[:query]
@@ -114,13 +117,21 @@ module Zafu
114
117
 
115
118
  # Store a context as a sub-template that can be used in ajax calls
116
119
  def r_block
120
+ if parent.method == 'each' && @method == parent.single_child_method
121
+ # Block stored in 'each', do nothing
122
+ # What happens when this is used as remote target ?
123
+ return expand_with
124
+ end
125
+
117
126
  # Since we are using ajax, we will need this object to have an ID set.
118
127
  set_dom_prefix
119
128
 
129
+ @markup.done = false
130
+
120
131
  if @context[:block] == self
121
132
  # Storing template (called from within store_block)
122
- # Set id with the template's node context (<%= @node.zip %>)
123
- @markup.set_id(node.dom_id)
133
+ # Set id with the template's node context (<%= @node.zip %>).
134
+ @markup.set_id(node.dom_id(:list => false))
124
135
  expand_with
125
136
  else
126
137
  # 1. store template
@@ -128,14 +139,43 @@ module Zafu
128
139
  store_block(self)
129
140
 
130
141
  # 2. render
131
- # Set id with the current node context (<%= var1.zip %>)
132
- @markup.set_id(node.dom_id)
142
+ # Set id with the current node context (<%= var1.zip %>).
143
+ @markup.set_id(node.dom_id(:list => false))
133
144
  out expand_with
134
145
  end
135
146
  end
136
147
 
148
+
149
+ def r_edit
150
+ # ajax
151
+ if @context[:form_cancel]
152
+ # cancel button
153
+ @context[:form_cancel]
154
+ else
155
+ # edit button
156
+
157
+ # TODO: show 'reply' instead of 'edit' in comments if visitor != author
158
+ each_block = ancestor('each')
159
+
160
+ link = wrap(make_link(:default_text => _('edit'), :update => each_block, :action => 'edit'))
161
+
162
+ out "<% if #{node}.can_write? -%>#{link}<% end -%>"
163
+ end
164
+
165
+ #if @context[:template_url]
166
+ #else
167
+ # # FIXME: we could link to some html page to edit the item.
168
+ # ""
169
+ #end
170
+ end
171
+
172
+ def r_cancel
173
+ (@context[:form_options] || {})[:form_cancel]
174
+ end
175
+
137
176
  def r_add
138
177
  return parser_error("Should not be called from within 'each'") if parent.method == 'each'
178
+ return parser_error("Should not be called outside list context") unless node.list_context?
139
179
  return '' if @context[:make_form]
140
180
 
141
181
  if node.will_be?(Comment)
@@ -165,7 +205,7 @@ module Zafu
165
205
  end
166
206
 
167
207
  # Expand 'add' block
168
- out @markup.wrap("#{expand_with(:onclick=>"[\"#{node.dom_prefix}_add\", \"#{node.dom_prefix}_form\"].each(Element.toggle);#{focus}return false;")}")
208
+ out wrap("#{expand_with(:onclick=>"[\"#{node.dom_prefix}_add\", \"#{node.dom_prefix}_form\"].each(Element.toggle);#{focus}return false;")}")
169
209
 
170
210
  # New object to render form.
171
211
  # FIXME: use 'klass' param in r_add or r_form instead of current list content.
@@ -185,10 +225,10 @@ module Zafu
185
225
  # Expand (inline) 'form' block
186
226
  out expand_block(form_block,
187
227
  # Needed in form to be able to return the result
188
- :template_url => template_url(node),
228
+ :template_url => template_url(node.dom_prefix),
189
229
  # ??
190
230
  :in_add => true,
191
- # ??
231
+ # Used to get parameters like 'publish' or 'klass'
192
232
  :add => self,
193
233
  # Transform 'each' block into a form
194
234
  :make_form => form_block.method == 'each',
@@ -199,7 +239,7 @@ module Zafu
199
239
  else
200
240
  # no ajax
201
241
  @markup.append_param(:class, 'btn_add') if @markup.tag
202
- out @markup.wrap(expand_with)
242
+ out wrap(expand_with)
203
243
  end
204
244
  out "<% end -%>"
205
245
  end
@@ -217,30 +257,49 @@ module Zafu
217
257
  @markup.set_id(options[:id]) if options[:id]
218
258
  @markup.set_param(:style, options[:style]) if options[:style]
219
259
 
220
- out @markup.wrap(expand_with)
260
+ out wrap(expand_with)
221
261
  else
222
262
  super
223
263
  end
224
264
  end
225
265
 
226
- # Return true if we need to insert the dom id for this element. This method is overwritten in Ajax.
266
+ # Block visibility of descendance with 'do_list'.
267
+ def public_descendants
268
+ all = super
269
+ if ['context', 'each', 'block'].include?(self.method)
270
+ # do not propagate 'form',etc up
271
+ all.reject do |k,v|
272
+ ['form','unlink'].include?(k)
273
+ end
274
+ elsif ['if', 'case'].include?(self.method)
275
+ all.reject do |k,v|
276
+ ['else', 'elsif', 'when'].include?(k)
277
+ end
278
+ else
279
+ all
280
+ end
281
+ end
282
+
283
+ # Return true if we need to insert the dom id for this element.
227
284
  def need_dom_id?
228
- @context[:form]
285
+ @context[:form] || descendant('unlink') || descendant('drop')
229
286
  end
230
287
 
231
288
  # Set a unique DOM prefix to build unique ids in the page.
232
- def set_dom_prefix
289
+ def set_dom_prefix(node = self.node)
233
290
  @name ||= unique_name
291
+ raise if @name == 'list3'
292
+ # TODO: should rebuild descendants list in parents...
234
293
  node.dom_prefix = @name
235
294
  end
236
295
 
237
296
  # Unique template_url, ending with dom_id
238
- def template_url(node)
239
- "#{root.options[:root]}/#{node.dom_prefix}"
297
+ def template_url(dom_prefix = node.dom_prefix)
298
+ "#{root.options[:root]}/#{dom_prefix}"
240
299
  end
241
300
 
242
- def form_url(node)
243
- template_url(node) + '_form'
301
+ def form_url(dom_prefix = node.dom_prefix)
302
+ template_url(dom_prefix) + '_form'
244
303
  end
245
304
 
246
305
  # Return a different name on each call
@@ -263,27 +322,39 @@ module Zafu
263
322
  end
264
323
  end
265
324
 
325
+ private
266
326
 
327
+ # Find a block to update on the page
328
+ def find_target(name)
329
+ # Hack for drop until descendants is rebuilt on set_dom_prefix
330
+ return self if name == self.name
331
+
332
+ root.descendants('block').each do |block|
333
+ return block if block.name == name
334
+ end
335
+
336
+ out parser_error("could not find a block named '#{name}'")
337
+ return nil
338
+ end
267
339
 
268
- private
269
340
  def store_block(block, cont = {})
270
- cont = @context.merge(cont)
341
+ cont = context_without_vars.merge(cont)
271
342
 
272
343
  # Create new node context
273
344
  node = cont[:node].as_main(ActiveRecord::Base)
274
345
  node.dom_prefix = @name
275
346
 
276
- cont[:template_url] = template_url(node)
347
+ cont[:template_url] = template_url(node.dom_prefix)
277
348
  cont[:node] = node
278
349
  cont[:block] = block
279
350
  cont[:saved_template] ||= cont[:template_url]
280
- @context.each do |k, v|
281
- if k.kind_of?(String)
282
- cont[k] = nil
283
- end
284
- end
285
351
 
286
- template = expand_block(block, cont)
352
+ template = nil
353
+
354
+ # We overwrite all context: no merge.
355
+ with_context(cont, false) do
356
+ template = expand_block(block)
357
+ end
287
358
 
288
359
  out helper.save_erb_to_url(template, cont[:saved_template])
289
360
  end
@@ -3,8 +3,8 @@ module Zafu
3
3
  # This module manages conditional rendering (if, else, elsif, case, when).
4
4
  module Conditional
5
5
  def r_if(cond = nil)
6
- cond ||= get_attribute_or_eval(false)
7
- return parser_error("condition error") unless cond
6
+ cond ||= get_condition
7
+ return unless cond
8
8
  expand_if(cond)
9
9
  end
10
10
 
@@ -22,8 +22,8 @@ module Zafu
22
22
 
23
23
  def r_elsif(cond = nil)
24
24
  return '' unless @context[:in_if]
25
- cond ||= get_attribute_or_eval(false)
26
- return parser_error("condition error") unless cond
25
+ cond ||= get_condition
26
+ return unless cond
27
27
 
28
28
  res = expand_with(:in_if => false, :markup => nil)
29
29
 
@@ -32,25 +32,50 @@ module Zafu
32
32
  @markup.tag ||= markup.tag
33
33
  @markup.steal_html_params_from(@params)
34
34
  markup.params.each do |k, v|
35
- next if @markup.param[k]
35
+ next if @markup.params[k]
36
36
  @markup.set_param(k, v)
37
37
  end
38
- out "<% elsif #{cond} -%>#{@markup.wrap(res)}" # do not propagate
38
+
39
+ markup.dyn_params.each do |k, v|
40
+ next if @markup.params[k] || @markup.dyn_params[k]
41
+ @markup.set_dyn_param(k, v)
42
+ end
43
+
44
+ out "<% elsif #{cond} -%>#{wrap(res)}" # do not propagate
39
45
  else
40
46
  @markup.done = true # never wrap else/elsif clause
41
47
  out "<% elsif #{cond} -%>#{res}" # do not propagate
42
48
  end
43
49
  end
44
50
 
45
- # Expand blocks with conditional enabled (else, elsif, etc).
46
- def expand_if(condition, new_node_context = self.node)
51
+ # Expand blocks with conditional processing enabled (else, elsif, etc).
52
+ #
53
+ # ==== Parameters
54
+ #
55
+ # * +condition+ - ruby condition for the conditional execution.
56
+ # * +new_node_context+ - (optional) new node context to enter if the clause succeeds.
57
+ # * +alt_markup+ - (optional) alternative markup to use for the 'else', 'elsif' clauses.
58
+ def expand_if(condition, new_node_context = self.node, alt_markup = @markup)
47
59
  res = ""
48
60
  res << "<% if #{condition} -%>"
49
- res << @markup.wrap(expand_with(:node => new_node_context))
50
- res << expand_with(:in_if => true, :only => %w{else elsif when}, :markup => @markup)
61
+
62
+ with_context(:node => new_node_context) do
63
+ res << wrap(expand_with)
64
+ end
65
+
66
+ res << expand_with(:in_if => true, :only => /^[A-Z]|else|elsif|when/, :markup => alt_markup)
51
67
  res << "<% end -%>"
52
68
  res
53
69
  end
70
+
71
+ private
72
+ def get_condition
73
+ if in_tag = @params[:in]
74
+ ancestor(in_tag) ? 'true' : 'false'
75
+ else
76
+ get_attribute_or_eval(false)
77
+ end
78
+ end
54
79
  end # Context
55
80
  end # Process
56
81
  end # Zafu
@@ -5,7 +5,7 @@ module Zafu
5
5
  # context, the "node" context holds information on the type of "this" (first responder).
6
6
  module Context
7
7
  def r_each
8
- if node.klass.kind_of?(Array)
8
+ if node.list_context?
9
9
  if @params[:alt_class] || @params[:join]
10
10
  out "<% #{var}_max_index = #{node}.size - 1 -%>" if @params[:alt_reverse]
11
11
  out "<% #{node}.each_with_index do |#{var},#{var}_index| -%>"
@@ -31,10 +31,19 @@ module Zafu
31
31
  out "<% #{node}.each do |#{var}| -%>"
32
32
  end
33
33
 
34
+
34
35
  with_context(:node => node.move_to(var, node.klass.first)) do
36
+ # The id set here should be used as prefix for sub-nodes to ensure uniqueness of generated DOM ids
37
+ node.propagate_dom_scope!
38
+
35
39
  steal_and_eval_html_params_for(@markup, @params)
36
- @markup.set_id(node.dom_id) if need_dom_id?
37
- out @markup.wrap(expand_with)
40
+
41
+ if need_dom_id?
42
+ set_dom_prefix
43
+ @markup.set_id(node.dom_id)
44
+ end
45
+
46
+ out wrap(expand_with)
38
47
  end
39
48
  out "<% end -%>"
40
49
  else
@@ -63,28 +72,37 @@ module Zafu
63
72
 
64
73
  # Store some contextual value / variable inside a named group. This should be
65
74
  # used to avoid key clashes between different types of elements to store.
66
- def set_context_var(group, key, obj)
67
- @context["#{group}::#{key}"] = obj
75
+ def set_context_var(group, key, obj, context = @context)
76
+ context["#{group}::#{key}"] = obj
68
77
  end
69
78
 
70
79
  # Retrieve a value from a given contextual group. The value must have been
71
80
  # previously set with 'set_context_var' somewhere in the hierarchy.
72
- def get_context_var(group, key)
73
- @context["#{group}::#{key}"]
81
+ def get_context_var(group, key, context = @context)
82
+ context["#{group}::#{key}"]
83
+ end
84
+
85
+ # Return a new context without contextual variables.
86
+ def context_without_vars
87
+ context = @context.dup
88
+ context.keys.each do |k|
89
+ context.delete(k) if k.kind_of?(String)
90
+ end
91
+ context
74
92
  end
75
93
 
76
94
  # Expand blocks in a new context.
77
95
  # This method is partly overwriten in Ajax
78
96
  def expand_with_finder(finder)
79
97
  if finder[:nil]
80
- open_node_context(finder) do
98
+ open_node_context(finder, :form => nil) do # do not propagate :form
81
99
  expand_if("#{var} = #{finder[:method]}", node.move_to(var, finder[:class]))
82
100
  end
83
101
  else
84
102
  res = ''
85
103
  res << "<% #{var} = #{finder[:method]} -%>"
86
- open_node_context(finder, :node => node.move_to(var, finder[:class])) do
87
- res << @markup.wrap(expand_with)
104
+ open_node_context(finder, :node => node.move_to(var, finder[:class]), :form => nil) do
105
+ res << wrap(expand_with)
88
106
  end
89
107
  res
90
108
  end
@@ -110,19 +128,36 @@ module Zafu
110
128
  {}
111
129
  end
112
130
 
113
- # Declare a variable that can be used later in the compilation. This method
114
- # returns the variable name to use.
115
- def set_var(var_list, var_name)
116
- var_name = var_name.to_sym
117
- out parser_error("'#{var_name}' already defined.") if @context[var_name] || var_list[var_name]
118
- var_list[var_name] = "_z#{var_name}"
131
+ # Get a variable name and store the name in context variables for the given group.
132
+ #
133
+ # ==== Parameters
134
+ #
135
+ # * +group_name+ - name of the variable context group
136
+ # * +wanted_name+ - wanted variable name (used as key to get real var back later with #get_context_var)
137
+ # * +context+ - (optional) can be used if we do not want to store the variable definition in the current context
138
+ #
139
+ def get_var_name(group_name, wanted_name, context = @context)
140
+ secure_name = wanted_name.gsub(/[^a-zA-Z0-9]/,'')
141
+ name = "_z#{secure_name}"
142
+ i = 0
143
+ while get_context_var('var', name, context)
144
+ i += 1
145
+ name = "_z#{secure_name}#{i}"
146
+ end
147
+ set_context_var('var', name, true)
148
+ set_context_var(group_name, wanted_name, name)
149
+ name
119
150
  end
120
151
 
121
152
  # Change context for a given scope.
122
- def with_context(cont)
153
+ def with_context(cont, merge = true)
123
154
  raise "Block missing" unless block_given?
124
155
  cont_bak = @context.dup
125
- @context.merge!(cont)
156
+ if merge
157
+ @context.merge!(cont)
158
+ else
159
+ @context = cont
160
+ end
126
161
  res = yield
127
162
  @context = cont_bak
128
163
  res
@@ -13,7 +13,7 @@ module Zafu
13
13
 
14
14
  if descendant('form_tag')
15
15
  # We have a specific place to insert our form
16
- out expand_with(:form_options => options)
16
+ out expand_with(:form_options => options, :form => self)
17
17
  else
18
18
  r_form_tag(options)
19
19
  end
@@ -26,14 +26,16 @@ module Zafu
26
26
  @params.each do |k, v|
27
27
  markup.set_param(k, v)
28
28
  end
29
- out markup.wrap(expand_with)
30
29
  else
31
30
  # <form> inside <r:form>
32
31
  form_tag(options) do |opts|
33
32
  # Render error messages tag
34
33
  form_error_messages(opts[:form_helper])
35
34
 
36
- # Render hidden fields
35
+ # Render form elements
36
+ out expand_with(opts)
37
+
38
+ # Render hidden fields (these must go after normal elements so that focusFirstElement works)
37
39
  hidden_fields = form_hidden_fields(options)
38
40
  out "<div class='hidden'>"
39
41
  hidden_fields.each do |k,v|
@@ -47,9 +49,6 @@ module Zafu
47
49
  end
48
50
  out '</div>'
49
51
 
50
- # Render form elements
51
- out expand_with(opts)
52
-
53
52
  # What is this ?
54
53
  #@blocks = opts[:blocks_bak] if opts[:blocks_bak]
55
54
  end
@@ -70,7 +69,10 @@ module Zafu
70
69
  # Return id, style, form and cancel parts of the form.
71
70
  def form_options
72
71
  opts = {}
73
- opts[:klass] = node.master_class(ActiveRecord::Base)
72
+
73
+ # Do we need this ?
74
+ # opts[:klass] = node.master_class(ActiveRecord::Base).to_s
75
+
74
76
  if @context[:in_add]
75
77
  opts[:id] = "#{node.dom_prefix}_form"
76
78
  opts[:style] = 'display:none;'
@@ -99,8 +101,9 @@ module Zafu
99
101
  # Render the 'form' tag and set expansion context.
100
102
  def form_tag(options)
101
103
  opts = options.dup
104
+ form = @context[:form]
102
105
 
103
- if descendant('cancel') || descendant('edit')
106
+ if form && (form.descendant('cancel') || form.descendant('edit'))
104
107
  # Pass 'form_cancel' content to expand_with (already in options).
105
108
  else
106
109
  # Insert cancel before form
@@ -107,7 +107,8 @@ module Zafu
107
107
 
108
108
  src = @params.delete(key)
109
109
  if src && src[0..6] != 'http://'
110
- @markup.params[key] = helper.send(:template_url_for_asset, :src => src, :base_path => @options[:base_path], :type => type)
110
+ new_value = helper.send(:template_url_for_asset, :src => src, :base_path => @options[:base_path], :type => type)
111
+ @markup.params[key] = new_value.blank? ? src : new_value
111
112
  end
112
113
 
113
114
  @markup.steal_html_params_from(@params)
@@ -12,7 +12,7 @@ module Zafu
12
12
  def do_method(sym)
13
13
  super
14
14
  rescue RubyLess::Error => err
15
- self.class.parser_error(err.message, @method)
15
+ parser_error(err.message)
16
16
  end
17
17
  end
18
18
  end
@@ -27,21 +27,21 @@ module Zafu
27
27
 
28
28
  # Resolve unknown methods by using RubyLess in the current compilation context (the
29
29
  # translate method in RubyLess will call 'safe_method_type' in this module).
30
- def rubyless_eval
30
+ def rubyless_eval(params = @params)
31
31
  if @method =~ /^[A-Z]\w+$/
32
32
  return rubyless_class_scope(@method)
33
33
  end
34
34
 
35
35
  if code = @method[/^\#\{(.+)\}$/, 1]
36
- @params[:eval] = $1
36
+ params[:eval] = $1
37
37
  r_show
38
38
  else
39
- rubyless_render(@method, @params)
39
+ rubyless_render(@method, params)
40
40
  end
41
41
  rescue RubyLess::NoMethodError => err
42
- parser_error("#{err.error_message} <span class='type'>#{err.method_with_arguments}</span> (#{node.class_name} context)")
42
+ parser_continue("#{err.error_message} <span class='type'>#{err.method_with_arguments}</span> for #{err.receiver_with_class}")
43
43
  rescue RubyLess::Error => err
44
- parser_error(err.message)
44
+ parser_continue(err.message)
45
45
  end
46
46
 
47
47
  # Print documentation on the current node type.
@@ -98,9 +98,40 @@ module Zafu
98
98
  end
99
99
  end
100
100
 
101
+ def get_attribute_or_eval(use_string_block = true)
102
+ if attribute = @params[:attr] || @params[:date]
103
+ code = "this.#{attribute}"
104
+ elsif code = @params[:eval] || @params[:test]
105
+ elsif text = @params[:text]
106
+ code = "%Q{#{text}}"
107
+ elsif use_string_block && @blocks.size == 1 && @blocks.first.kind_of?(String)
108
+ return RubyLess::TypedString.new(@blocks.first.inspect, :class => String, :literal => @blocks.first)
109
+ else
110
+ return parser_continue("Missing attribute/eval parameter")
111
+ end
112
+
113
+ RubyLess.translate(code, self)
114
+ rescue RubyLess::Error => err
115
+ return parser_continue(err.message, code)
116
+ end
117
+
101
118
  private
119
+ # Extract arguments from params
120
+ def extract_from_params(*keys)
121
+ res = []
122
+
123
+ keys.each do |key|
124
+ next unless value = @params[key.to_sym]
125
+ res << ":#{key} => #{RubyLess.translate_string(value, self)}"
126
+ end
127
+
128
+ res.empty? ? nil : res
129
+ end
130
+
102
131
  # block_owner should be set to true when we are resolving <r:xxx>...</r:xxx> or <div do='xxx'>...</div>
103
132
  def get_method_type(signature, added_options = false)
133
+ raise "#{node.klass.class}" unless node.klass.kind_of?(Array) || node.klass.kind_of?(Class)
134
+
104
135
  if type = node_context_from_signature(signature)
105
136
  # Resolve self, @page, @node
106
137
  type
@@ -213,15 +244,21 @@ module Zafu
213
244
  def node_context_from_signature(signature)
214
245
  return nil unless signature.size == 1
215
246
  ivar = signature.first
216
- return {:class => node.klass, :method => node.name} if ivar == 'this'
217
- return nil unless ivar[0..0] == '@'
218
- begin
219
- klass = Module.const_get(ivar[1..-1].capitalize)
220
- context = node(klass)
221
- rescue NameError
222
- return nil
247
+ if ivar == 'this'
248
+ if node.list_context?
249
+ raise RubyLess::Error.new("Cannot use 'this' in list_context.")
250
+ else
251
+ {:class => node.klass, :method => node.name}
252
+ end
253
+ elsif ivar[0..0] == '@' && klass = get_class(ivar[1..-1].capitalize)
254
+ if node = self.node(klass)
255
+ {:class => node.klass, :method => node.name}
256
+ else
257
+ nil
258
+ end
259
+ else
260
+ nil
223
261
  end
224
- {:class => context.klass, :method => context.name}
225
262
  end
226
263
 
227
264
  # Find stored variables back. Stored elements are set with set_xxx='something to eval'.
data/test/markup_test.rb CHANGED
@@ -236,7 +236,7 @@ class MarkupTest < Test::Unit::TestCase
236
236
  @markup.append_dyn_param(:class, '<%= @bar %>')
237
237
  assert_equal '<%= @foo %> <%= @bar %>', @markup.dyn_params[:class]
238
238
  end
239
-
239
+
240
240
  should 'append param without spacer if conditional' do
241
241
  @markup.append_dyn_param(:class, '<%= @bar %>', true)
242
242
  assert_equal '<%= @foo %><%= @bar %>', @markup.dyn_params[:class]
@@ -278,7 +278,7 @@ class MarkupTest < Test::Unit::TestCase
278
278
  @markup.prepend_dyn_param(:class, '<%= @to %>')
279
279
  assert_equal '<%= @to %> <%= @fu %>', @markup.dyn_params[:class]
280
280
  end
281
-
281
+
282
282
  should 'prepend param without spacer if conditional' do
283
283
  @markup.prepend_dyn_param(:class, '<%= @to %>', true)
284
284
  assert_equal '<%= @to %><%= @fu %>', @markup.dyn_params[:class]
@@ -309,6 +309,11 @@ class MarkupTest < Test::Unit::TestCase
309
309
  assert_equal "<p class='quote' style='padding:3px; border:1px solid red;'>#{@text}</p>", @markup.wrap(@text)
310
310
  end
311
311
 
312
+ should 'add the appended params inside the tag' do
313
+ @markup.append_attribute("<%= anything %>")
314
+ assert_equal "<p class='quote' style='padding:3px; border:1px solid red;'<%= anything %>>#{@text}</p>", @markup.wrap(@text)
315
+ end
316
+
312
317
  should 'not wrap twice if called twice' do
313
318
  assert_equal "<p class='quote' style='padding:3px; border:1px solid red;'>#{@text}</p>", @markup.wrap(@markup.wrap(@text))
314
319
  end
@@ -318,6 +323,11 @@ class MarkupTest < Test::Unit::TestCase
318
323
  @markup.set_params(:baz => 'buzz')
319
324
  assert_equal "<p class='quote' style='padding:3px; border:1px solid red;' baz='buzz' foo='<%= @bar %>'>foo</p>", @markup.wrap('foo')
320
325
  end
326
+
327
+ should 'insert pre_wrap content' do
328
+ @markup.pre_wrap[:foo] = 'FOO'
329
+ assert_equal %q{<p class='quote' style='padding:3px; border:1px solid red;'>FOOcontent</p>}, @markup.wrap('content')
330
+ end
321
331
  end
322
332
 
323
333
  context 'Compiling params' do
@@ -345,14 +355,41 @@ class MarkupTest < Test::Unit::TestCase
345
355
  context 'and changing params' do
346
356
  setup do
347
357
  @markup = Markup.new('p')
348
- @markup.params[:class] = 'one'
358
+ @markup.params[:class] = 'one'
349
359
  @duplicate = @markup.dup
350
360
  end
351
361
 
352
- should 'not propagate changes to original' do
362
+ should 'not propagate params changes to original' do
353
363
  @duplicate.params[:class] = 'two'
354
364
  assert_equal "<p class='one'>one</p>", @markup.wrap('one')
355
365
  end
366
+
367
+ should 'not propagate params changes to duplicate' do
368
+ @markup.params[:class] = 'two'
369
+ assert_equal "<p class='one'>one</p>", @duplicate.wrap('one')
370
+ end
371
+
372
+ should 'not propagate appended params to duplicate' do
373
+ @markup.append_param(:class, 'drop')
374
+ assert_equal "<p class='one'>one</p>", @duplicate.wrap('one')
375
+ end
376
+
377
+ should 'not propagate dyn_params changes to original' do
378
+ @markup.append_dyn_param(:class, 'two')
379
+ assert_equal "<p class='one'>one</p>", @duplicate.wrap('one')
380
+ end
381
+
382
+ should 'not propagate dyn_params changes to duplicate' do
383
+ @duplicate.append_dyn_param(:class, 'two')
384
+ assert_equal "<p class='one'>one</p>", @markup.wrap('one')
385
+ end
386
+
387
+ should 'not propagate pre_wrap changes to duplicate' do
388
+ @markup.pre_wrap[:drop] = 'no bombs'
389
+ @duplicate = @markup.dup
390
+ @duplicate.pre_wrap[:drop] = 'ego'
391
+ assert_equal "<p class='one'>no bombs</p>", @markup.wrap('')
392
+ end
356
393
  end
357
394
 
358
395
  context 'and wrapping' do
@@ -366,7 +403,7 @@ class MarkupTest < Test::Unit::TestCase
366
403
  assert_equal '<p>one</p>', @duplicate.wrap('one')
367
404
  end
368
405
  end
369
- end
406
+ end # Duplicating a markup
370
407
  end
371
408
 
372
409
 
@@ -90,15 +90,30 @@ class NodeContextTest < Test::Unit::TestCase
90
90
  assert_equal subject.klass, subject.as_main.klass
91
91
  end
92
92
 
93
- should 'return an ancestor when using after_class argument' do
93
+ should 'return current class when using after_class argument' do
94
94
  subject = NodeContext.new('@node', SubSubPage)
95
- assert_equal SubPage, subject.as_main(Page).klass
95
+ assert_equal SubSubPage, subject.as_main(Page).klass
96
96
  end
97
97
 
98
98
  should 'rebuild name from ancestor when using after_class argument' do
99
99
  subject = NodeContext.new('@node', SubSubPage)
100
100
  assert_equal '@sub_page', subject.as_main(Page).name
101
101
  end
102
+
103
+ context 'in a list context' do
104
+ subject do
105
+ NodeContext.new('list', [SubSubPage])
106
+ end
107
+
108
+ should 'return current class when using after_class argument' do
109
+ assert_equal SubSubPage, subject.as_main(Page).klass
110
+ end
111
+
112
+ should 'rebuild name from ancestor when using after_class argument' do
113
+ assert_equal '@sub_page', subject.as_main(Page).name
114
+ end
115
+ end # in a list context
116
+
102
117
  end
103
118
 
104
119
  should 'return subclass on class_name' do
@@ -180,14 +195,14 @@ class NodeContextTest < Test::Unit::TestCase
180
195
  NodeContext.new('super', SubPage)
181
196
  end
182
197
 
183
- should 'find the current context required class is an ancestor' do
198
+ should 'find the required class in ancestors' do
184
199
  assert_equal subject.object_id, subject.get(Page).object_id
185
200
  end
186
201
  end
187
202
 
188
203
  context 'In a list context' do
189
204
  setup do
190
- @grandma = NodeContext.new('@page', Page)
205
+ @grandma = NodeContext.new('@page', SubPage)
191
206
  @mother = @grandma.move_to('@comment', Comment)
192
207
  end
193
208
 
@@ -223,6 +238,10 @@ class NodeContextTest < Test::Unit::TestCase
223
238
  should 'return the node name in DOM id' do
224
239
  assert_equal '<%= @foo.zip %>', subject.dom_id
225
240
  end
241
+
242
+ should 'scope without erb on erb false' do
243
+ assert_equal '#{@foo.zip}', subject.dom_id(:erb => false)
244
+ end
226
245
  end
227
246
 
228
247
  context 'in a hierarchy of contexts' do
@@ -238,23 +257,23 @@ class NodeContextTest < Test::Unit::TestCase
238
257
 
239
258
  context 'with parents as dom_scopes' do
240
259
  setup do
241
- @b.dom_scope!
242
- @c.dom_scope!
260
+ @b.propagate_dom_scope!
261
+ @c.propagate_dom_scope!
243
262
  end
244
263
 
245
264
  should 'use dom_scopes' do
246
- assert_equal '<%= var1.zip %>_<%= var2.zip %>_<%= var3.zip %>', subject.dom_id
265
+ assert_equal '<%= %Q{#{var1.zip}_#{var2.zip}_#{var3.zip}} %>', subject.dom_id
247
266
  end
248
267
  end
249
268
 
250
269
  context 'with ancestors and self as dom_scopes' do
251
270
  setup do
252
- @a.dom_scope!
253
- subject.dom_scope!
271
+ @a.propagate_dom_scope!
272
+ subject.propagate_dom_scope!
254
273
  end
255
274
 
256
275
  should 'not use self twice' do
257
- assert_equal '<%= @node.zip %>_<%= var3.zip %>', subject.dom_id
276
+ assert_equal '<%= %Q{#{@node.zip}_#{var3.zip}} %>', subject.dom_id
258
277
  end
259
278
  end
260
279
 
@@ -264,7 +283,7 @@ class NodeContextTest < Test::Unit::TestCase
264
283
  end
265
284
 
266
285
  should 'use dom_prefix' do
267
- assert_equal 'cart_<%= var3.zip %>', subject.dom_id
286
+ assert_equal '<%= %Q{cart_#{var3.zip}} %>', subject.dom_id
268
287
  end
269
288
  end
270
289
 
data/test/zafu/meta.yml CHANGED
@@ -5,4 +5,8 @@ some_template:
5
5
  include_with_part:
6
6
  # part 'a' is moved around
7
7
  src: "<r:include template='/some/template'><r:with part='a'/><r:with part='b'>new b:<r:include template='/some/template' part='a'/></r:with></r:include>"
8
- tem: "<div id='b'>new b:<div id='a'>a</div></div>"
8
+ tem: "<div id='b'>new b:<div id='a'>a</div></div>"
9
+
10
+ missing_template:
11
+ src: "<r:include template='Foo'/>"
12
+ tem: "/template 'Foo' not found/"
data/zafu.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{zafu}
8
- s.version = "0.6.0"
8
+ s.version = "0.6.1"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Gaspard Bucher"]
12
- s.date = %q{2010-05-27}
12
+ s.date = %q{2010-07-22}
13
13
  s.description = %q{Provides a powerful templating language based on xhtml for rails}
14
14
  s.email = %q{gaspard@teti.ch}
15
15
  s.extra_rdoc_files = [
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 6
8
- - 0
9
- version: 0.6.0
8
+ - 1
9
+ version: 0.6.1
10
10
  platform: ruby
11
11
  authors:
12
12
  - Gaspard Bucher
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-05-27 00:00:00 +02:00
17
+ date: 2010-07-22 00:00:00 +02:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency