hobo 0.7.3 → 0.7.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/bin/hobo +1 -1
  2. data/hobo_files/plugin/CHANGES.txt +302 -0
  3. data/hobo_files/plugin/generators/hobo_front_controller/templates/index.dryml +2 -9
  4. data/hobo_files/plugin/generators/hobo_model/templates/model.rb +1 -1
  5. data/hobo_files/plugin/generators/hobo_model_resource/hobo_model_resource_generator.rb +0 -2
  6. data/hobo_files/plugin/generators/hobo_rapid/templates/hobo-rapid.js +76 -46
  7. data/hobo_files/plugin/generators/hobo_rapid/templates/lowpro.js +25 -18
  8. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/clean/public/stylesheets/application.css +29 -11
  9. data/hobo_files/plugin/generators/hobo_user_model/templates/model.rb +2 -2
  10. data/hobo_files/plugin/init.rb +0 -1
  11. data/hobo_files/plugin/lib/active_record/has_many_association.rb +3 -0
  12. data/hobo_files/plugin/lib/hobo.rb +12 -8
  13. data/hobo_files/plugin/lib/hobo/bundle.rb +1 -1
  14. data/hobo_files/plugin/lib/hobo/dryml/dryml_builder.rb +1 -1
  15. data/hobo_files/plugin/lib/hobo/dryml/parser/attribute.rb +41 -0
  16. data/hobo_files/plugin/lib/hobo/dryml/parser/base_parser.rb +253 -0
  17. data/hobo_files/plugin/lib/hobo/dryml/parser/document.rb +26 -0
  18. data/hobo_files/plugin/lib/hobo/dryml/parser/element.rb +27 -0
  19. data/hobo_files/plugin/lib/hobo/dryml/parser/elements.rb +45 -0
  20. data/hobo_files/plugin/lib/hobo/dryml/parser/source.rb +58 -0
  21. data/hobo_files/plugin/lib/hobo/dryml/parser/text.rb +13 -0
  22. data/hobo_files/plugin/lib/hobo/dryml/parser/tree_parser.rb +67 -0
  23. data/hobo_files/plugin/lib/hobo/dryml/scoped_variables.rb +10 -5
  24. data/hobo_files/plugin/lib/hobo/dryml/template.rb +48 -27
  25. data/hobo_files/plugin/lib/hobo/dryml/template_environment.rb +28 -13
  26. data/hobo_files/plugin/lib/hobo/hobo_helper.rb +3 -1
  27. data/hobo_files/plugin/lib/hobo/model.rb +70 -10
  28. data/hobo_files/plugin/lib/hobo/model_controller.rb +49 -34
  29. data/hobo_files/plugin/lib/hobo/model_router.rb +10 -2
  30. data/hobo_files/plugin/lib/hobo/rapid_helper.rb +1 -0
  31. data/hobo_files/plugin/lib/hobo/scopes.rb +15 -0
  32. data/hobo_files/plugin/lib/hobo/scopes/apply_scopes.rb +23 -0
  33. data/hobo_files/plugin/lib/hobo/scopes/association_proxy_extensions.rb +4 -2
  34. data/hobo_files/plugin/lib/hobo/scopes/automatic_scopes.rb +34 -7
  35. data/hobo_files/plugin/lib/hobo/scopes/defined_scope_proxy_extender.rb +3 -1
  36. data/hobo_files/plugin/lib/hobo/scopes/scoped_proxy.rb +1 -5
  37. data/hobo_files/plugin/taglibs/rapid.dryml +33 -24
  38. data/hobo_files/plugin/taglibs/rapid_editing.dryml +6 -5
  39. data/hobo_files/plugin/taglibs/rapid_forms.dryml +37 -31
  40. data/hobo_files/plugin/taglibs/rapid_generics.dryml +68 -27
  41. data/hobo_files/plugin/taglibs/rapid_navigation.dryml +5 -8
  42. data/hobo_files/plugin/taglibs/rapid_pages.dryml +71 -47
  43. data/hobo_files/plugin/taglibs/rapid_plus.dryml +4 -5
  44. data/hobo_files/plugin/taglibs/rapid_support.dryml +11 -4
  45. metadata +23 -6
  46. data/hobo_files/plugin/lib/rexml.rb +0 -443
@@ -8,6 +8,8 @@ module Hobo
8
8
 
9
9
  include AutomaticScopes
10
10
 
11
+ include ApplyScopes
12
+
11
13
  def method_missing(name, *args, &block)
12
14
  if (scope = named_scope(name))
13
15
  association_proxy_for_scope(name, scope, args)
@@ -69,7 +71,7 @@ module Hobo
69
71
  options[:order] = scope[:order] if scope[:order]
70
72
  options[:include] = scope[:include] if scope[:include]
71
73
 
72
- r = ScopeReflection.new(:has_many, name, options, proxy_owner.class, proxy_reflection.name)
74
+ r = ScopeReflection.new(:has_many, name, options, proxy_owner.class, proxy_reflection.association_name)
73
75
 
74
76
  @reflections ||= {}
75
77
  @reflections[name] = r
@@ -4,7 +4,7 @@ module Hobo
4
4
 
5
5
  class ScopedProxy
6
6
 
7
- #include AutomaticScopes
7
+ include ApplyScopes
8
8
 
9
9
  def initialize(klass, scope)
10
10
  @klass = klass
@@ -22,10 +22,6 @@ module Hobo
22
22
  end
23
23
  end
24
24
 
25
- def all
26
- self.find(:all)
27
- end
28
-
29
25
  def first
30
26
  self.find(:first)
31
27
  end
@@ -31,18 +31,20 @@
31
31
 
32
32
  <def tag="nil-view"><%= scope.nil_view || "(Not Available)" %></def>
33
33
 
34
- <def tag="ul">
34
+ <def tag="ul" attrs="empty">
35
35
  <% if all_parameters.li? # don't use dryml if, because it will mess up <ul/><else> %>
36
- <unless test="&this.empty?">
36
+ <if test="&empty || all_parameters[:head] || all_parameters[:foot] || !this.empty?">
37
37
  <% element "ul", attributes do %>
38
+ <do param="head"/>
38
39
  <repeat>
39
40
  <li param if="&can_view?" class="#{scope.even_odd} #{this_type.name.underscore.dasherize}"
40
41
  merge-attrs="&{'hobo-model-id' => dom_id(this)} if this.respond_to?(:typed_id)">
41
42
  <do param="default"><a/></do>
42
43
  </li>
43
44
  </repeat>
45
+ <do param="foot"/>
44
46
  <% end %>
45
- </unless>
47
+ </if>
46
48
  <% else %>
47
49
  <%= element("ul", attributes, all_parameters.default) %>
48
50
  <% end %>
@@ -162,6 +164,16 @@
162
164
  name = name.downcase if lowercase
163
165
  name
164
166
  %></def>
167
+
168
+
169
+ <def tag="name-for-collection" attrs="singular, lowercase"><%=
170
+ name = (attr = this.try.origin_attribute and attr.to_s) || type_name(:plural => true)
171
+ name = name.titleize
172
+ name = name.singularize if singular
173
+ name = name.downcase if lowercase
174
+ name
175
+ %></def>
176
+
165
177
 
166
178
 
167
179
  <def tag="a" attrs="action, to, params, query-params, href, format, subsite"><%=
@@ -194,6 +206,7 @@
194
206
 
195
207
  add_classes!(attributes, "new-#{new_class_name.underscore}-link")
196
208
  content = "New #{new_class_name.titleize}" if content.blank?
209
+ Hobo::Dryml.last_if = true
197
210
  element(:a, attributes.update(:href => href), content)
198
211
  else
199
212
  Hobo::Dryml.last_if = false
@@ -209,7 +222,8 @@
209
222
  # This target is registered with ModelRouter as not linkable
210
223
  content
211
224
  else
212
- add_classes!(attributes, "#{target.class.name.underscore}-link")
225
+ css_class = target.try.origin_attribute || target.class.name.underscore
226
+ add_classes!(attributes, "#{css_class}-link")
213
227
 
214
228
  href.sub!(/\?|$/, ".#{format}\\0") unless format.blank?
215
229
 
@@ -227,18 +241,18 @@
227
241
 
228
242
  res = if this.nil? && if_blank.nil?
229
243
  this_type.is_a?(Class) && this_type <= String ? "" : nil_view
230
- elsif refl = this_field_reflection
231
- if refl.macro == :belongs_to
232
- belongs_to_view(attributes)
233
- elsif refl.macro == :has_many
234
- has_many_view(attributes)
235
- end
244
+ elsif (refl = this_field_reflection) && refl.macro == :has_many
245
+ has_many_view(attributes)
236
246
  else
237
247
 
238
248
  view_tag = find_polymorphic_tag("view")
239
249
 
240
250
  if view_tag == "view" # i.e. it didn't find a type specific tag
241
- raise HoboError, "Cannot view: #{this.inspect} (field is #{this_field}, type is #{this.class})"
251
+ if this.respond_to?(:to_html)
252
+ this.to_html
253
+ else
254
+ raise HoboError, "Cannot view: #{this.inspect} (field is #{this_field}, type is #{this.class})"
255
+ end
242
256
  else
243
257
  attrs = add_classes(attributes, "view", type_and_field)
244
258
  id = dom_id
@@ -282,20 +296,12 @@
282
296
 
283
297
  <def tag="view" for="Numeric" attrs="format"><%= format ? format % this : this.to_s %></def>
284
298
 
285
- <def tag="view" for="Hobo::Text"><%= h(this).gsub("\n", "<br/>") %></def>
286
-
287
- <def tag="view" for="html"><%= this %></def>
288
-
289
- <def tag="view" for="markdown"><%= this.to_html %></def>
290
-
291
- <def tag="view" for="textile"><%= this.to_html %></def>
292
-
293
- <def tag="view" for="password">[password withheld]</def>
294
-
295
299
  <def tag="view" for="string"><%= h(this).gsub("\n", "<br/>") %></def>
296
300
 
297
301
  <def tag="view" for="boolean"><%= this ? 'Yes' : 'No' %></def>
298
302
 
303
+ <def tag="view" for="ActiveRecord::Base"><a/></def>
304
+
299
305
 
300
306
  <def tag="count" attrs="label, prefix, if-any, lowercase"><%=
301
307
  raise Exception.new("asked for count of a string") if this.is_a?(String)
@@ -304,9 +310,11 @@
304
310
 
305
311
  label ||= if this.is_a?(Class)
306
312
  this.name
313
+ elsif (attr = this.try.origin_attribute)
314
+ attr.to_s.singularize
307
315
  else
308
- (this.try.association_name || this.try.member_class._?.name)
309
- end.to_s.singularize.titleize
316
+ this.member_class.name
317
+ end.titleize
310
318
 
311
319
  label = label.downcase if lowercase
312
320
 
@@ -378,7 +386,7 @@ in the future - use at your own risk. -->
378
386
  </def>
379
387
 
380
388
 
381
- <def tag="you" attrs="have, are, do, titleize"><if test="&this == current_user"><%= "#{titleize ? 'Y' : 'y'}ou #{'have' if have}#{'are' if are}#{'do' if do_}" %></if><else><do param="default"><name/> <%= "#{'has' if have}#{'is' if are}#{'does' if do_}" %></do></else></def>
389
+ <def tag="you" attrs="have, are, do, titleize"><if test="&this == current_user"><%= "#{titleize ? 'Y' : 'y'}ou#{' have' if have}#{' are' if are}#{' do' if do_}" %></if><else><do param="default"><name/><%= "#{' has' if have}#{' is' if are}#{' does' if do_}" %></do></else></def>
382
390
 
383
391
  <def tag="You"><you merge titleize/></def>
384
392
 
@@ -417,6 +425,7 @@ in the future - use at your own risk. -->
417
425
 
418
426
 
419
427
  <def tag="filter-menu" attrs="param-name, options, no-filter">
428
+ <% no_filter ||= "All" %>
420
429
  <form action="&request.request_uri" method="get" class="filter-menu">
421
430
  <hidden-field name="filter-parameter" value="&param_name"/>
422
431
  <select-menu name="&param_name" options="&options" selected="&params[param_name.gsub('-', '_')]" first-option="&no_filter" merge-params/>
@@ -43,7 +43,7 @@
43
43
 
44
44
  <def tag="editor" for="big_integer"><%= in_place_editor "in_place_textfield_bhv", attributes %></def>
45
45
 
46
- <def tag="editor" for="Hobo::EnumString">
46
+ <def tag="editor" for="HoboFields::EnumString">
47
47
  <string-select-editor values="&this_type.values" merge/>
48
48
  </def>
49
49
 
@@ -67,13 +67,14 @@
67
67
  </def>
68
68
 
69
69
 
70
- <def tag="belongs-to-menu-editor"><%
70
+ <def tag="belongs-to-menu-editor" attrs="sort"><%
71
71
  raise HoboError.new("Not allowed to edit") unless can_edit?
72
72
  link_id = "#{dom_id}_editor" %>
73
- <span id="#{link_id}" part="rapid-belongs-to-edit">
73
+ <span id="#{link_id}" part="rapid-belongs-to-edit" part-locals="sort">
74
74
  <% select_options = this_field_reflection.klass.find(:all).select {|x| can_view?(x)}.map {|x|
75
75
  [ name(:with => x, :no_wrapper => true), x.id ]
76
76
  }
77
+ select_options = select_options.sort if sort
77
78
  select_options.insert(0, ["(No #{this_type.name.to_s.titleize})", ""]) if this.nil?
78
79
  link_id = "#{dom_id}_editor"
79
80
  f = ajax_updater(object_url(this_parent),
@@ -84,8 +85,8 @@
84
85
  } })
85
86
  %>
86
87
  <select onchange="#{f}">
87
- <%= options_for_select(select_options.sort, this ? this.id : "") %>
88
- </select>&nbsp;<a if="&this">View</a>
88
+ <%= options_for_select(select_options, this ? this.id : "") %>
89
+ </select>&nbsp;<a if="&this && linkable?">View</a>
89
90
  </span>
90
91
  </def>
91
92
 
@@ -58,7 +58,7 @@
58
58
 
59
59
  hiddens = hidden_fields(:fields => hidden_fields, :skip => field_names) if new_record
60
60
 
61
- auth_token = if request_forgery_protection_token.nil?
61
+ auth_token = if method.nil? || method == 'get' || request_forgery_protection_token.nil?
62
62
  ''
63
63
  else
64
64
  element(:input, :type => "hidden",
@@ -107,10 +107,10 @@
107
107
  attrs = add_classes(attributes, type_id, type_and_field)
108
108
  the_input = if (refl = this_field_reflection)
109
109
  if refl.macro == :belongs_to
110
- belongs_to_input(attrs)
110
+ select_one(attrs)
111
111
  elsif refl.macro == :has_many
112
112
  if refl.options[:through]
113
- has_many_through_input(attrs)
113
+ select_many(attrs)
114
114
  else
115
115
  has_many_input(attrs)
116
116
  end
@@ -145,13 +145,21 @@
145
145
  <%= text_area_tag(name, this, add_classes(attributes, :tiny_mce)) %>
146
146
  </def>
147
147
 
148
+ <def tag="input" for="markdown" attrs="name">
149
+ <%= text_area_tag(name, this, attributes) %>
150
+ </def>
151
+
152
+ <def tag="input" for="textile" attrs="name">
153
+ <%= text_area_tag(name, this, attributes) %>
154
+ </def>
155
+
148
156
  <def tag="input" for="date" attrs="order">
149
157
  <% order = order.nil? ? [:year, :month, :day] : comma_split(order).*.to_sym -%>
150
158
  <%= select_date(this || Time.now, attributes.merge(:prefix => param_name_for_this, :order => order)) %>
151
159
  </def>
152
160
 
153
161
  <def tag="input" for="datetime" attrs="order">
154
- <% order = order.nil? ? [:year, :month, :day, :hour, :minute] : comma_split(order).*.to_sym -%>
162
+ <% order = order.nil? ? [:year, :month, :day ] : comma_split(order).*.to_sym -%>
155
163
  <%= select_datetime(this || Time.now, attributes.merge(:prefix => param_name_for_this, :order => order)) %>
156
164
  </def>
157
165
 
@@ -179,16 +187,8 @@
179
187
  </select>
180
188
  </def>
181
189
 
182
- <def tag="input" for="percentage" attrs="name">
183
- <%= text_field_tag(name, this, attributes.merge(:size => '3', :maxlength => '3')) %>%
184
- </def>
185
-
186
190
 
187
- <def tag="belongs-to-input">
188
- <%= belongs_to_menu_input(attributes) %>
189
- </def>
190
-
191
- <!--- Buttons --->
191
+ <!-- Buttons -->
192
192
 
193
193
  <def tag="remote-method-button" attrs="method, update, label"><%=
194
194
  ajax_attributes, html_attributes = attributes.partition_hash(Hobo::RapidHelper::AJAX_ATTRS)
@@ -219,7 +219,7 @@
219
219
 
220
220
 
221
221
  <def tag="delete-button" attrs="label, update, in-place, image, confirm, fade, subsite"><%=
222
- in_place = false if in_place.nil? && this == @this
222
+ in_place = false if in_place.nil? && this == @this && !request.xhr?
223
223
  url = object_url(this, :method => :delete, :subsite => subsite)
224
224
  if (Hobo::Dryml.last_if = url && can_delete?)
225
225
  attributes = attributes.merge(if image
@@ -240,6 +240,7 @@
240
240
  button_to(label, url, attributes)
241
241
  else
242
242
  fade = true if fade.nil?
243
+ scope.collection_contains_delete_button = true if fade
243
244
  attributes[:value] = label
244
245
  attributes[:onclick] = "Hobo.removeButton(this, '#{url}', #{js_updates(update)}, {fade:#{fade}, confirm: #{confirm.inspect}})"
245
246
  element(:input, attributes)
@@ -277,16 +278,17 @@
277
278
  %></def>
278
279
 
279
280
 
280
- <def tag="belongs-to-menu-input" attrs="include-none, blank-message, options"><%
281
+ <def tag="select-one" attrs="include-none, blank-message, options, sort"><%
281
282
  raise HoboError.new("Not allowed to edit") unless can_edit?
282
283
 
283
284
  blank_message ||= "(No #{this_type.name.to_s.titleize})"
284
285
  conditions = ActiveRecord::Associations::BelongsToAssociation.new(this, this_field_reflection).conditions
285
- options ||= this_field_reflection.klass.find(:all, :conditions => conditions).select {|x| can_view?(x)}
286
+ options ||= this_field_reflection.klass.all(:conditions => conditions).select {|x| can_view?(x)}
286
287
  #Todo: switch to autocompleter for id_name when too many records, and id_name supported
287
288
  select_options = options.map { |x|
288
289
  [ name(:with => x, :no_wrapper => true), x.id ]
289
- }.sort
290
+ }
291
+ select_options = select_options.sort if sort
290
292
  select_options.insert(0, [blank_message, ""]) if include_none || (this.nil? && include_none != false)
291
293
  attributes = add_classes(attributes, "input", "belongs_to", type_and_field)
292
294
  %>
@@ -335,29 +337,33 @@
335
337
  </def>
336
338
 
337
339
 
338
- <def tag="name-array-input" attrs="targets, remove-label">
340
+ <def tag="select-many" attrs="options, targets, remove-label, prompt">
339
341
  <%
340
- all ||= this.member_class.find(:all)
342
+ prompt ||= "Add a #{this_field.titleize.singularize}"
343
+ options ||= this.member_class.all
341
344
  values = this
342
345
  %>
343
- <div class="input has-many-through" merge-attrs>
346
+ <div class="input select-many" merge-attrs>
344
347
  <div style="display:none" class="item-proto">
345
- <div class="item">
348
+ <div class="item" param="proto-item">
346
349
  <span></span>
347
- <input type="hidden" name="#{param_name_for_this}[]" />
348
- <input type="button" class="remove-item" value="#{remove_label || 'Remove'}"/>
350
+ <input type="hidden" name="#{param_name_for_this}[]" param="proto-hidden"/>
351
+ <input type="button" class="remove-item" value="#{remove_label || 'Remove'}" param="proto-remove-button"/>
349
352
  </div>
350
353
  </div>
351
354
  <div class="items">
352
- <div class="item" repeat>
353
- <span><%= this %></span>
354
- <input type="hidden" name="#{param_name_for_this}[]" value="#{this}"/>
355
- <input type="button" class="remove-item" value="#{remove_label || 'Remove'}"/>
356
- </div>
355
+ <set param-name="&param_name_for_this"/>
356
+ <repeat>
357
+ <div class="item" param="item">
358
+ <span><%= this %></span>
359
+ <input type="hidden" name="#{param_name}[]" value="#{this}" param="hidden"/>
360
+ <input type="button" class="remove-item" value="#{remove_label || 'Remove'}" param="remove-button"/>
361
+ </div>
362
+ </repeat>
357
363
  </div>
358
364
  <select>
359
- <option value="">Add a <%= this_field.titleize.singularize %></option>
360
- <option repeat="&all.sort_by {|x| name(:no_wrapper => true, :with => x).downcase}"
365
+ <option value=""><prompt/></option>
366
+ <option repeat="&options.sort_by {|x| name(:no_wrapper => true, :with => x).downcase}"
361
367
  merge-attrs="&{:style => 'display:none'} if this.in?(values)"><name/></option>
362
368
  </select>
363
369
  </div>
@@ -377,6 +383,6 @@
377
383
  <def tag="select-menu" attrs="options, selected, first-option, first-value">
378
384
  <select merge-attrs param="default">
379
385
  <option value="#{first_value}" unless="&first_option.nil?"><first-option/></option>
380
- <do param="options"><%= options_for_select(options, selected) %></do>
386
+ <do param="options"><% options_for_select(options.*.to_s, selected) %></do>
381
387
  </select>
382
388
  </def>
@@ -1,8 +1,8 @@
1
1
  <def tag="creation-details">
2
- <span class="creation-details">
3
- <view field="&this.class.creator_attribute" class="creator" if="&this.class.creator_attribute" />
4
- <view:created_at class="created-at" if="&this.respond_to?(:created_at)"/>
5
- </span>
2
+ <div class="creation-details">
3
+ <view field="&this.class.creator_attribute" class="creator" if="&this.class.creator_attribute" param="by"/>
4
+ <view:created_at class="created-at" if="&this.respond_to?(:created_at)" param="at"/>
5
+ </div>
6
6
  </def>
7
7
 
8
8
  <def tag="primary-content">
@@ -17,9 +17,15 @@
17
17
 
18
18
  <def tag="base-card">
19
19
  <if test="&can_view?">
20
- <div class="card #{linkable? ? 'linkable' : 'content'} #{type_name :dasherize => true}" merge-attrs>
21
- <h3 param="title"><a if="&linkable?"/></h3>
22
- <div class="content" param="content" if="&!linkable?">
20
+ <set has-heading="&this.class.name_attribute || all_parameters[:heading]"/>
21
+ <set has-link="&has_heading && linkable?"/>
22
+ <div class="card #{has_link ? 'linkable' : 'content'} #{'with-owner' if this.class.creator_attribute} #{type_name :dasherize => true}"
23
+ merge-attrs>
24
+ <a action="edit" class="edit" if="&!has_link && linkable?(:edit)" param="edit-link">Edit</a>
25
+ <delete-button label="X" unless="&linkable?(:edit)" param/>
26
+
27
+ <h3 param="heading" if="&has_heading"><a><name/></a></h3>
28
+ <div class="content" param="content" unless="&has_link">
23
29
  <primary-content/>
24
30
  </div>
25
31
  <creation-details param/>
@@ -29,7 +35,6 @@
29
35
  <span class="dependents"><count field="&primary_collection"/></span>
30
36
  </if>
31
37
  </do>
32
- <delete-button label="X" unless="&linkable?(this, :edit)" param/>
33
38
  </div>
34
39
  </if>
35
40
  </def>
@@ -47,28 +52,64 @@
47
52
  </def>
48
53
 
49
54
  <def tag="collection">
50
- <%= poly = call_polymorphic_tag('collection', attributes, parameters) %>
51
- <ul class="collection" merge-attrs unless="&poly">
52
- <li:><card param/></li:>
55
+ <set-scoped collection-contains-delete-button="&false">
56
+ <%= poly = call_polymorphic_tag('collection', attributes, parameters) %>
57
+ <base-collection if="&poly.nil?" merge/>
58
+ <p class="empty-collection-message"
59
+ if="&this.empty? || scope.collection_contains_delete_button"
60
+ style="#{'display:none' if !this.empty?}"
61
+ param="empty-message">
62
+ There are no <name-for-collection lowercase/>
63
+ </p>
64
+ </set-scoped>
65
+ </def>
66
+
67
+
68
+ <def tag="base-collection" attrs="sortable, sortable-options"><%
69
+ sortable = (sortable != false and
70
+ first = this.first and
71
+ first.respond_to?(:position_column) and
72
+ reorder_url = object_url(this.member_class, :reorder, :method => :post) and
73
+ can_edit?(first, first.position_column))
74
+ if sortable
75
+ singular_name = first.class.name.underscore
76
+ attributes[:id] ||= "#{singular_name}_ordering"
77
+ end
78
+ %>
79
+ <ul class="collection #{this.origin_attribute.to_s.gsub('_', '-')}" merge-attrs>
80
+ <li: id="&%(#{singular_name}_#{this.id}) if sortable" param>
81
+ <div class="ordering-handle" param="handle" if="&sortable">&uarr;<br/>&darr;</div>
82
+ <card param/>
83
+ </li:>
53
84
  </ul>
54
- <p class="empty-collection-message" if="&this.empty?" param="empty-message">
55
- <% label = if (refl = this.try.proxy_reflection)
56
- refl.association_name.to_s.humanize.downcase
57
- elsif (klass = this.try.member_class)
58
- klass.name.humanize.downcase.pluralize
59
- else
60
- type_name(:plural => true).pluralize.humanize.downcase.to_s
61
- end %>
62
- There are no <%= label.pluralize %>
63
- </p>
85
+ <%= if sortable && Hobo::Dryml.last_if
86
+ opts = { :url => reorder_url,
87
+ :constraint => :vertical,
88
+ :overlap => :vertical,
89
+ :scroll => :window,
90
+ :handle => 'ordering-handle',
91
+ :complete => [visual_effect(:highlight, attributes[:id])]
92
+ }
93
+ opts.reverse_merge!(sortable_options) if sortable_options
94
+ sortable_element attributes[:id], opts
95
+ end
96
+ %>
64
97
  </def>
65
98
 
99
+
66
100
  <def tag="collection-preview" attrs="limit">
67
101
  <% limit ||= 6 %>
68
- <div class="collection-preview" merge-attrs>
69
- <h2><do param="heading"><this-field.titleize/></do> <span param="show-all-link" if="&linkable?">(<a>show all</a>)</span></h2>
70
- <collection with="&this.limit(limit)" merge-params>
71
- <card: class="small"/>
72
- </collection>
73
- </div>
102
+ <do with="&this.is_a?(Class) ? this.limit(limit).all : this.limit(limit)">
103
+ <set collection-name="&(this.try.origin_attribute || this.member_class.name.pluralize).to_s"/>
104
+ <div class="collection-preview" merge-attrs>
105
+ <h2>
106
+ <do param="heading"><collection-name.titleize/></do>
107
+ <span param="show-all-link" if="&this.any? && linkable?">(<a>show all</a>)</span>
108
+ </h2>
109
+ <collection merge-params>
110
+ <card: class="small"/>
111
+ </collection>
112
+ </div>
113
+ </do>
74
114
  </def>
115
+