zafu 0.6.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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