hobo 1.0.3 → 1.1.0.pre0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.txt +0 -66
- data/README +3 -0
- data/Rakefile +7 -7
- data/doctest/model.rdoctest +0 -2
- data/doctest/multi_model_forms.rdoctest +0 -2
- data/doctest/scopes.rdoctest +13 -21
- data/lib/active_record/association_collection.rb +7 -16
- data/lib/hobo.rb +10 -65
- data/lib/hobo/accessible_associations.rb +1 -5
- data/lib/hobo/authentication_support.rb +1 -1
- data/lib/hobo/controller.rb +5 -5
- data/lib/hobo/hobo_helper.rb +0 -84
- data/lib/hobo/lifecycles/lifecycle.rb +37 -31
- data/lib/hobo/lifecycles/transition.rb +1 -2
- data/lib/hobo/model.rb +13 -21
- data/lib/hobo/model_controller.rb +8 -8
- data/lib/hobo/rapid_helper.rb +12 -1
- data/lib/hobo/scopes/automatic_scopes.rb +26 -13
- data/lib/hobo/scopes/named_scope_extensions.rb +16 -28
- data/lib/hobo/user_controller.rb +1 -0
- data/lib/hobo/view_hints.rb +1 -5
- data/rails_generators/hobo/templates/initializer.rb +1 -1
- data/rails_generators/hobo_front_controller/templates/summary.dryml +4 -2
- data/rails_generators/hobo_model/hobo_model_generator.rb +12 -0
- data/rails_generators/hobo_model/templates/model.rb +9 -2
- data/rails_generators/hobo_rapid/templates/hobo-rapid.js +98 -23
- data/rails_generators/hobo_rapid/templates/themes/clean/public/stylesheets/clean.css +1 -1
- data/rails_generators/hobo_rapid/templates/themes/clean/public/stylesheets/rapid-ui.css +3 -1
- data/{dryml_generators → rapid_generators}/rapid/cards.dryml.erb +0 -0
- data/{dryml_generators → rapid_generators}/rapid/forms.dryml.erb +0 -0
- data/{dryml_generators → rapid_generators}/rapid/pages.dryml.erb +1 -1
- data/taglibs/rapid.dryml +2 -0
- data/taglibs/rapid_core.dryml +10 -9
- data/taglibs/rapid_forms.dryml +70 -35
- data/taglibs/rapid_lifecycles.dryml +17 -4
- data/taglibs/rapid_plus.dryml +3 -3
- data/taglibs/rapid_summary.dryml +11 -0
- data/taglibs/rapid_user_pages.dryml +39 -28
- data/tasks/hobo_tasks.rake +1 -1
- metadata +45 -61
- data/hobo.gemspec +0 -226
- data/lib/hobo/dryml.rb +0 -188
- data/lib/hobo/dryml/dryml_builder.rb +0 -140
- data/lib/hobo/dryml/dryml_doc.rb +0 -159
- data/lib/hobo/dryml/dryml_generator.rb +0 -263
- data/lib/hobo/dryml/dryml_support_controller.rb +0 -13
- data/lib/hobo/dryml/parser.rb +0 -3
- data/lib/hobo/dryml/parser/attribute.rb +0 -41
- data/lib/hobo/dryml/parser/base_parser.rb +0 -254
- data/lib/hobo/dryml/parser/document.rb +0 -57
- data/lib/hobo/dryml/parser/element.rb +0 -27
- data/lib/hobo/dryml/parser/elements.rb +0 -45
- data/lib/hobo/dryml/parser/source.rb +0 -58
- data/lib/hobo/dryml/parser/text.rb +0 -13
- data/lib/hobo/dryml/parser/tree_parser.rb +0 -67
- data/lib/hobo/dryml/part_context.rb +0 -137
- data/lib/hobo/dryml/scoped_variables.rb +0 -42
- data/lib/hobo/dryml/tag_parameters.rb +0 -36
- data/lib/hobo/dryml/taglib.rb +0 -123
- data/lib/hobo/dryml/template.rb +0 -1019
- data/lib/hobo/dryml/template_environment.rb +0 -613
- data/lib/hobo/dryml/template_handler.rb +0 -187
- data/lib/hobo/static_tags +0 -98
- data/taglibs/core.dryml +0 -104
data/lib/hobo/user_controller.rb
CHANGED
data/lib/hobo/view_hints.rb
CHANGED
@@ -3,11 +3,7 @@ module Hobo
|
|
3
3
|
class ViewHints
|
4
4
|
|
5
5
|
def self.enable
|
6
|
-
|
7
|
-
ActiveSupport::Dependencies.autoload_paths |= [ "#{RAILS_ROOT}/app/viewhints" ]
|
8
|
-
else
|
9
|
-
ActiveSupport::Dependencies.load_paths |= [ "#{RAILS_ROOT}/app/viewhints" ]
|
10
|
-
end
|
6
|
+
ActiveSupport::Dependencies.load_paths |= ["#{RAILS_ROOT}/app/viewhints"]
|
11
7
|
end
|
12
8
|
|
13
9
|
def self.setter(name, default=nil, &block)
|
@@ -1,2 +1,2 @@
|
|
1
1
|
Hobo::ModelRouter.reload_routes_on_every_request = true
|
2
|
-
#
|
2
|
+
# Dryml.precompile_taglibs if File.basename($0) != "rake" && Rails.env.production?
|
@@ -67,11 +67,12 @@
|
|
67
67
|
|
68
68
|
<h2>Models</h2>
|
69
69
|
<table class="app-summary">
|
70
|
-
<tr><th>Class</th><th>Table</th></tr>
|
70
|
+
<tr><th>Class</th><th>Table</th><th></th></tr>
|
71
71
|
<with-models>
|
72
72
|
<tr>
|
73
73
|
<td><model-name/></td>
|
74
74
|
<td><model-table-name/></td>
|
75
|
+
<td><model-table-comment/></td>
|
75
76
|
</tr>
|
76
77
|
</with-models>
|
77
78
|
</table>
|
@@ -80,10 +81,11 @@
|
|
80
81
|
<h3 if="&this.try.table_name"><model-name /></h3>
|
81
82
|
<table class="app-summary">
|
82
83
|
<with-model-columns>
|
83
|
-
<tr if="&first_item?"><th>Column</th><th>Type</th></tr>
|
84
|
+
<tr if="&first_item?"><th>Column</th><th>Type</th><th></th></tr>
|
84
85
|
<tr>
|
85
86
|
<td><model-column-name/></td>
|
86
87
|
<td><model-column-type/></td>
|
88
|
+
<td><model-column-comment/></td>
|
87
89
|
</tr>
|
88
90
|
</with-model-columns>
|
89
91
|
</table>
|
@@ -27,5 +27,17 @@ class HoboModelGenerator < Rails::Generator::NamedBase
|
|
27
27
|
def max_attribute_length
|
28
28
|
attributes.*.name.*.length.max
|
29
29
|
end
|
30
|
+
|
31
|
+
def field_attributes
|
32
|
+
attributes.reject { |a| a.name == "bt" || a.name == "hm" }
|
33
|
+
end
|
34
|
+
|
35
|
+
def hms
|
36
|
+
attributes.select { |a| a.name == "hm" }.*.type
|
37
|
+
end
|
38
|
+
|
39
|
+
def bts
|
40
|
+
attributes.select { |a| a.name == "bt" }.*.type
|
41
|
+
end
|
30
42
|
|
31
43
|
end
|
@@ -3,13 +3,20 @@ class <%= class_name %> < ActiveRecord::Base
|
|
3
3
|
hobo_model # Don't put anything above this
|
4
4
|
|
5
5
|
fields do
|
6
|
-
<% for attribute in
|
6
|
+
<% for attribute in field_attributes -%>
|
7
7
|
<%= "%-#{max_attribute_length}s" % attribute.name %> :<%= attribute.type %>
|
8
8
|
<% end -%>
|
9
9
|
timestamps
|
10
10
|
end
|
11
11
|
|
12
|
-
|
12
|
+
<% for bt in bts -%>
|
13
|
+
belongs_to :<%= bt %>
|
14
|
+
<% end -%>
|
15
|
+
<%= "\n" unless bts.empty? -%>
|
16
|
+
<% for hm in hms -%>
|
17
|
+
has_many :<%= hm %>, :dependent => :destroy
|
18
|
+
<% end -%>
|
19
|
+
<%= "\n" unless hms.empty? -%>
|
13
20
|
# --- Permissions --- #
|
14
21
|
|
15
22
|
def create_permitted?
|
@@ -408,8 +408,8 @@ var Hobo = {
|
|
408
408
|
|
409
409
|
|
410
410
|
updateElement: function(id, content) {
|
411
|
-
// TODO: Do we need this method?
|
412
411
|
Element.update(id, content)
|
412
|
+
Element.fire($(id), "rapid:partupdated")
|
413
413
|
},
|
414
414
|
|
415
415
|
getStyle: function(el, styleProp) {
|
@@ -560,7 +560,7 @@ HoboBehavior = Class.create({
|
|
560
560
|
})
|
561
561
|
|
562
562
|
|
563
|
-
|
563
|
+
HoboInputMany = {
|
564
564
|
|
565
565
|
events: {
|
566
566
|
"> li > div.buttons": {
|
@@ -569,9 +569,9 @@ new HoboBehavior("ul.input-many", {
|
|
569
569
|
}
|
570
570
|
},
|
571
571
|
|
572
|
-
initialize: function(
|
572
|
+
initialize: function(ev) {
|
573
573
|
/* the second clause should be sufficient, but it isn't in IE7. See bug 603 */
|
574
|
-
|
574
|
+
Element.select(ev.target, ".input-many-template input:hidden, .input-many-template select:hidden, .input-many-template textarea:hidden, .input-many-template button:hidden").each(function(input) {
|
575
575
|
if(!input.disabled) {
|
576
576
|
input.disabled = true;
|
577
577
|
input.addClassName("input_many_template_input");
|
@@ -579,10 +579,33 @@ new HoboBehavior("ul.input-many", {
|
|
579
579
|
});
|
580
580
|
|
581
581
|
// disable all elements inside our template, and mark them so we can find them later.
|
582
|
-
|
582
|
+
Element.select(ev.target, ".input-many-template input:enabled, .input-many-template select:enabled, .input-many-template textarea:enabled, .input-many-template button:enabled").each(function(input) {
|
583
583
|
input.disabled = true;
|
584
584
|
input.addClassName("input_many_template_input");
|
585
585
|
});
|
586
|
+
|
587
|
+
Element.select(ev.target, ".sortable-input-many").each(function(el) {
|
588
|
+
HoboInputMany.createSortable.call(el);
|
589
|
+
});
|
590
|
+
|
591
|
+
/* need to reinitialize after every change */
|
592
|
+
Event.addBehavior({".sortable-input-many:rapid:change": function(ev) {
|
593
|
+
HoboInputMany.createSortable.call(this);
|
594
|
+
}});
|
595
|
+
|
596
|
+
document.observe("rapid:partupdated", HoboInputMany.initialize);
|
597
|
+
},
|
598
|
+
|
599
|
+
createSortable: function() {
|
600
|
+
Sortable.create(this.id, {
|
601
|
+
constraint: 'vertical',
|
602
|
+
handle: 'ordering-handle',
|
603
|
+
overlap: 'vertical',
|
604
|
+
scroll: 'window',
|
605
|
+
onUpdate: function(list) {
|
606
|
+
HoboInputMany.fixIndices.call(list);
|
607
|
+
}
|
608
|
+
});
|
586
609
|
},
|
587
610
|
|
588
611
|
// given this==the input-many, returns a lambda that updates the name & id for an element
|
@@ -619,23 +642,56 @@ new HoboBehavior("ul.input-many", {
|
|
619
642
|
|
620
643
|
// given this==an input-many item, get the submit index
|
621
644
|
getIndex: function() {
|
622
|
-
return Number(this.id.match(
|
645
|
+
return Number(this.id.match(/_([-0-9]+)$/)[1]);
|
623
646
|
},
|
624
647
|
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
648
|
+
/* For some reason, select() and down() and all those useful functions aren't working for us. Roll our own replacement.
|
649
|
+
|
650
|
+
this: element to recurse on.
|
651
|
+
klass: class to filter on
|
652
|
+
f: function to invoke
|
653
|
+
*/
|
654
|
+
recurse_elements_with_class: function(klass,f ) {
|
655
|
+
if(klass==null || this.hasClassName(klass)) {
|
656
|
+
f(this);
|
630
657
|
}
|
631
|
-
|
658
|
+
this.childElements().each(function(el2) {HoboInputMany.recurse_elements_with_class.call(el2, klass, f);});
|
632
659
|
},
|
633
660
|
|
661
|
+
/* fixes the indices on an input-many so they're in order. */
|
662
|
+
fixIndices: function() {
|
663
|
+
var lis = this.immediateDescendants();
|
664
|
+
var minimum = parseInt(Hobo.getClassData(this, 'minimum'));
|
665
|
+
/* first two lis are hidden/disabled on an input-many */
|
666
|
+
for(var i=0; i<lis.length-2; i++) {
|
667
|
+
var il=i+2;
|
668
|
+
if(i!=HoboInputMany.getIndex.call(lis[il])) {
|
669
|
+
var updater = HoboInputMany.getNameUpdater.call(this, i);
|
670
|
+
HoboInputMany.recurse_elements_with_class.call(lis[il], null, function(el) {
|
671
|
+
updater.call(el);
|
672
|
+
});
|
673
|
+
var position=lis[il].childWithClass("sortable-position");
|
674
|
+
if(position) position.value=i+1;
|
675
|
+
if(i==minimum-1 && il==lis.length-1) {
|
676
|
+
lis[il].childWithClass("buttons").childWithClass("remove-item").addClassName("hidden");
|
677
|
+
} else {
|
678
|
+
lis[il].childWithClass("buttons").childWithClass("remove-item").removeClassName("hidden");
|
679
|
+
}
|
680
|
+
if(il==lis.length-1) {
|
681
|
+
lis[il].childWithClass("buttons").childWithClass("add-item").removeClassName("hidden");
|
682
|
+
} else {
|
683
|
+
lis[il].childWithClass("buttons").childWithClass("add-item").addClassName("hidden");
|
684
|
+
}
|
685
|
+
}
|
686
|
+
}
|
687
|
+
},
|
688
|
+
|
689
|
+
|
634
690
|
addOne: function(ev, el) {
|
635
691
|
Event.stop(ev);
|
636
692
|
var ul = el.up('ul.input-many'), li = el.up('li.input-many-li');
|
637
693
|
|
638
|
-
if(li.id.search(
|
694
|
+
if(li.id.search(/_-1$/ && ul.immediateDescendants().length>2)>=0) {
|
639
695
|
/* if(console) console.log("IE7 messed up again (bug 605)"); */
|
640
696
|
return;
|
641
697
|
}
|
@@ -658,7 +714,7 @@ new HoboBehavior("ul.input-many", {
|
|
658
714
|
reenable_inputs(clone);
|
659
715
|
|
660
716
|
// update id & name
|
661
|
-
|
717
|
+
HoboInputMany.recurse_elements_with_class.call(clone, null, function(el) {
|
662
718
|
name_updater.call(el);
|
663
719
|
});
|
664
720
|
|
@@ -691,7 +747,7 @@ new HoboBehavior("ul.input-many", {
|
|
691
747
|
var ul = el.up('ul.input-many'), li = el.up('li.input-many-li')
|
692
748
|
var minimum = parseInt(Hobo.getClassData(ul, 'minimum'));
|
693
749
|
|
694
|
-
if(li.id.search(
|
750
|
+
if(li.id.search(/_-1$/)>=0) {
|
695
751
|
/* if(console) console.log("IE7 messed up again (bug 605)"); */
|
696
752
|
return;
|
697
753
|
}
|
@@ -703,7 +759,7 @@ new HoboBehavior("ul.input-many", {
|
|
703
759
|
var n=li.next();
|
704
760
|
for(; n; i+=1, n=n.next()) {
|
705
761
|
var name_updater = this.getNameUpdater.call(ul, i);
|
706
|
-
|
762
|
+
HoboInputMany.recurse_elements_with_class.call(n, null, function(el) {name_updater.call(el);});
|
707
763
|
}
|
708
764
|
|
709
765
|
// adjust +/- buttons on the button element as appropriate
|
@@ -714,7 +770,7 @@ new HoboBehavior("ul.input-many", {
|
|
714
770
|
|
715
771
|
if(last.hasClassName("empty")) {
|
716
772
|
last.removeClassName("hidden");
|
717
|
-
|
773
|
+
HoboInputMany.recurse_elements_with_class.call(last, "empty-input", function(el) {el.disabled=false;});
|
718
774
|
} else {
|
719
775
|
// if we've reached the minimum, we don't want to add the '-' button
|
720
776
|
if(ul.childElements().length-3 <= minimum||0) {
|
@@ -734,7 +790,9 @@ new HoboBehavior("ul.input-many", {
|
|
734
790
|
|
735
791
|
|
736
792
|
|
737
|
-
}
|
793
|
+
}
|
794
|
+
|
795
|
+
new HoboBehavior("ul.input-many", HoboInputMany);
|
738
796
|
|
739
797
|
|
740
798
|
SelectManyInput = Behavior.create({
|
@@ -811,9 +869,10 @@ NameManyInput = Object.extend(SelectManyInput, {
|
|
811
869
|
}
|
812
870
|
})
|
813
871
|
|
814
|
-
|
872
|
+
|
815
873
|
AutocompleteBehavior = Behavior.create({
|
816
874
|
initialize : function() {
|
875
|
+
this.minChars = parseInt(Hobo.getClassData(this.element, "min-chars"));
|
817
876
|
var match = this.element.className.match(/complete-on::([\S]+)/)
|
818
877
|
var target = match[1].split('::')
|
819
878
|
var typedId = target[0]
|
@@ -822,11 +881,27 @@ AutocompleteBehavior = Behavior.create({
|
|
822
881
|
var spec = Hobo.parseModelSpec(typedId)
|
823
882
|
var url = urlBase + "/" + Hobo.pluralise(spec.name) + "/complete_" + completer
|
824
883
|
var parameters = spec.id ? "id=" + spec.id : ""
|
825
|
-
new Ajax.Autocompleter(this.element,
|
826
|
-
|
827
|
-
|
828
|
-
|
884
|
+
this.autocompleter = new Ajax.Autocompleter(this.element,
|
885
|
+
this.element.next('.completions-popup'),
|
886
|
+
url,
|
887
|
+
{paramName:'query', method:'get', parameters: parameters, minChars: this.minChars,
|
888
|
+
afterUpdateElement: this.afterUpdateElement});
|
889
|
+
},
|
890
|
+
|
891
|
+
onfocus: function() {
|
892
|
+
if(this.element.hasClassName("nil-value")) {
|
893
|
+
this.element.value = '';
|
894
|
+
this.element.removeClassName("nil-value");
|
895
|
+
}
|
896
|
+
if(this.minChars==0) {
|
897
|
+
this.autocompleter.activate();
|
898
|
+
}
|
899
|
+
},
|
900
|
+
|
901
|
+
afterUpdateElement: function(input, li) {
|
902
|
+
input.fire("rapid:autocomplete-assigned");
|
829
903
|
}
|
904
|
+
|
830
905
|
})
|
831
906
|
|
832
907
|
|
@@ -57,7 +57,7 @@ pre, code {
|
|
57
57
|
font-family: "Courier New", Courier, monospace;
|
58
58
|
}
|
59
59
|
|
60
|
-
input.text, input.string, input.email-address, input.password, input.search, input.integer, input.float, textarea {
|
60
|
+
input.text, input.string, input.email-address, input.password, input.search, input.integer, input.float, input.autocompleter, input.decimal, textarea {
|
61
61
|
border-top:1px solid #7c7c7c;
|
62
62
|
border-left:1px solid #c3c3c3;
|
63
63
|
border-right:1px solid #c3c3c3;
|
File without changes
|
File without changes
|
data/taglibs/rapid.dryml
CHANGED
@@ -6,7 +6,9 @@ The Rapid tag library makes web development go fast. The Rapid tag library is yo
|
|
6
6
|
|
7
7
|
-->
|
8
8
|
|
9
|
+
<include module="Hobo::HoboHelper"/>
|
9
10
|
<include module="Hobo::RapidHelper"/>
|
11
|
+
<include module="Hobo::Translations"/>
|
10
12
|
|
11
13
|
<include src="rapid_core"/>
|
12
14
|
<include src="rapid_support"/>
|
data/taglibs/rapid_core.dryml
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
<!-- Core Rapid tags and tags that don't belong anywhere else. -->
|
1
2
|
|
2
3
|
<!-- Renders a table with one row per field, where each row contains a `<th>` with the field name, and a `<td>` with (by default)
|
3
4
|
a `<view>` of the field.
|
@@ -35,11 +36,11 @@
|
|
35
36
|
input_attrs = {:no_edit => no_edit} if tag == "input"
|
36
37
|
-%>
|
37
38
|
<labelled-item unless="&tag == 'input' && no_edit == 'skip' && !can_edit?">
|
38
|
-
<item-label param="#{scope.field_name.to_s.sub('?', '').
|
39
|
+
<item-label param="#{scope.field_name.to_s.sub('?', '').gsub('.', '-')}-label" unless="&field_name.blank?">
|
39
40
|
<do param="label"><%= field_name %></do>
|
40
41
|
</item-label>
|
41
|
-
<item-value param="#{scope.field_name.to_s.sub('?', '').
|
42
|
-
<do param="view"><call-tag tag="&tag" param="#{scope.field_name.to_s.sub('?', '').
|
42
|
+
<item-value param="#{scope.field_name.to_s.sub('?', '').gsub('.', '-')}-view" colspan="&2 if field_name.blank?">
|
43
|
+
<do param="view"><call-tag tag="&tag" param="#{scope.field_name.to_s.sub('?', '').gsub('.', '-')}-tag" merge-attrs="&input_attrs"/></do>
|
43
44
|
<div param="input-help" if="&tag.to_sym == :input && !this_field_help.blank?"><%= this_field_help %></div>
|
44
45
|
</item-value>
|
45
46
|
</labelled-item>
|
@@ -107,7 +108,7 @@ This will use `<input/>` as the tag in each table cell instead of `<view/>`
|
|
107
108
|
<thead if="&all_parameters[:thead] || fields" param>
|
108
109
|
<tr param="field-heading-row">
|
109
110
|
<with-field-names merge-attrs="&all_attributes & attrs_for(:with_fields)">
|
110
|
-
<th param="#{scope.field_name}-heading"><%= this.member_class.try.view_hints.try.field_name(scope.field_name) if scope %></th>
|
111
|
+
<th param="#{scope.field_name}-heading"><%= (this.member_class.try.view_hints.try.field_name(scope.field_name) || scope.field_name.titleize) if scope %></th>
|
111
112
|
</with-field-names>
|
112
113
|
<th if="&all_parameters[:controls]" class="controls"/>
|
113
114
|
</tr>
|
@@ -335,7 +336,7 @@ Or a new page if the context is a class:
|
|
335
336
|
target = to || this
|
336
337
|
|
337
338
|
if target.nil?
|
338
|
-
|
339
|
+
Dryml.last_if = false
|
339
340
|
nil_view
|
340
341
|
elsif action == "new"
|
341
342
|
# Link to a new object form
|
@@ -352,10 +353,10 @@ Or a new page if the context is a class:
|
|
352
353
|
|
353
354
|
add_classes!(attributes, "new-#{new_class_name.underscore}-link")
|
354
355
|
content = "New #{new_class_name.titleize}" if content.blank?
|
355
|
-
|
356
|
+
Dryml.last_if = true
|
356
357
|
element(:a, attributes.update(:href => href), content)
|
357
358
|
else
|
358
|
-
|
359
|
+
Dryml.last_if = false
|
359
360
|
""
|
360
361
|
end
|
361
362
|
else
|
@@ -456,7 +457,7 @@ Assuming the context is a blog post...
|
|
456
457
|
end
|
457
458
|
end
|
458
459
|
end
|
459
|
-
|
460
|
+
Dryml.last_if = !res.blank?
|
460
461
|
res
|
461
462
|
%></def>
|
462
463
|
|
@@ -535,7 +536,7 @@ The label can be customised using the `label` attribute, e.g.
|
|
535
536
|
|
536
537
|
label = label.downcase if lowercase
|
537
538
|
|
538
|
-
|
539
|
+
Dryml.last_if = c > 0 if if_any
|
539
540
|
if if_any && c == 0
|
540
541
|
""
|
541
542
|
else
|
data/taglibs/rapid_forms.dryml
CHANGED
@@ -51,8 +51,7 @@ executed at various points in the ajax request cycle:
|
|
51
51
|
hiddens = case fields
|
52
52
|
when '*', nil
|
53
53
|
# TODO: Need a better (i.e. extensible) way to eleminate certain fields
|
54
|
-
|
55
|
-
this.class.column_names - [this.class.inheritance_column, 'created_at', 'updated_at']
|
54
|
+
this.class.column_names - ['type', 'created_at', 'updated_at']
|
56
55
|
else
|
57
56
|
comma_split(fields)
|
58
57
|
end
|
@@ -103,6 +102,8 @@ AJAX based submission can be enabled by simply adding an `update` attribute. e.g
|
|
103
102
|
<div part="comments"><collection:comments/></div>
|
104
103
|
<form with="&Comment.new" update="comments"/>
|
105
104
|
|
105
|
+
`<form>` support all of the standard ajax attributes.
|
106
|
+
|
106
107
|
### Additional Notes
|
107
108
|
|
108
109
|
- Hobo automatically inserts an `auth_token` hidden field if forgery protection is enabled
|
@@ -111,19 +112,6 @@ AJAX based submission can be enabled by simply adding an `update` attribute. e.g
|
|
111
112
|
validation error occurs.
|
112
113
|
|
113
114
|
- `<form>` supports all of the standrd ajax attributes - (see the main taglib docs for Rapid Forms)
|
114
|
-
|
115
|
-
- `<form>` resets `last_if` if it does not have permission to display the form. The `<else>` clause may be used to display alternate content. For example:
|
116
|
-
|
117
|
-
<form>...</form>
|
118
|
-
<else>You do not have permission to edit this form</else>
|
119
|
-
|
120
|
-
or on a standard generated page using a default form:
|
121
|
-
|
122
|
-
<some-page>
|
123
|
-
<after-form:>
|
124
|
-
<else>You do not have permission to edit this form</else>
|
125
|
-
</after-form:>
|
126
|
-
</some-page>
|
127
115
|
|
128
116
|
### Attributes
|
129
117
|
|
@@ -186,7 +174,7 @@ The standard form tag does not have any parameters, nor does it have any default
|
|
186
174
|
if action.nil? && (html_attrs[:action].nil? ||
|
187
175
|
(lifecycle.nil? && new_record && !this.creatable_by?(current_user)) ||
|
188
176
|
(lifecycle.nil? && !new_record && !can_edit?))
|
189
|
-
|
177
|
+
Dryml.last_if = false
|
190
178
|
""
|
191
179
|
else
|
192
180
|
if method == "put"
|
@@ -223,7 +211,7 @@ The standard form tag does not have any parameters, nor does it have any default
|
|
223
211
|
page_path = if (request.post? || request.put?) && params[:page_path]
|
224
212
|
params[:page_path]
|
225
213
|
else
|
226
|
-
view_name.sub(
|
214
|
+
view_name.sub(Dryml::EMPTY_PAGE, params[:action] || '')
|
227
215
|
end
|
228
216
|
page_path_hidden = hidden_field_tag("page_path", page_path)
|
229
217
|
end
|
@@ -240,7 +228,7 @@ The standard form tag does not have any parameters, nor does it have any default
|
|
240
228
|
end
|
241
229
|
end
|
242
230
|
|
243
|
-
|
231
|
+
Dryml.last_if = true
|
244
232
|
element("form", html_attrs, body)
|
245
233
|
end
|
246
234
|
%></def>
|
@@ -350,12 +338,11 @@ edit collections a `Category` model in your application:
|
|
350
338
|
<def tag="collection-input" for="ActiveRecord::Base"><select-many merge/></def>
|
351
339
|
|
352
340
|
|
353
|
-
<!-- A `<textarea>` input
|
341
|
+
<!-- A `<textarea>` input -->
|
354
342
|
<def tag="input" for="text" attrs="name">
|
355
|
-
<%= text_area_tag(name,
|
343
|
+
<%= text_area_tag(name, this, attributes) %>
|
356
344
|
</def>
|
357
345
|
|
358
|
-
|
359
346
|
<!-- A checkbox plus a hidden-field. The hidden field trick comes from Rails - it means that when the checkbox is not checked, the parameter name is still submitted, with a '0' value (the value is '1' when the checkbox is checked) -->
|
360
347
|
<def tag="input" for="boolean" attrs="name">
|
361
348
|
<%= unless attributes[:disabled]
|
@@ -570,7 +557,7 @@ All the standard ajax attributes *except the callbacks* are supported (see the m
|
|
570
557
|
<def tag="delete-button" attrs="label, update, in-place, image, confirm, fade, subsite"><%=
|
571
558
|
in_place = false if in_place.nil? && this == @this && request.method == :get
|
572
559
|
url = object_url(this, :method => :delete, :subsite => subsite)
|
573
|
-
if (
|
560
|
+
if (Dryml.last_if = url && can_delete?)
|
574
561
|
attributes = attributes.merge(if image
|
575
562
|
{ :type => "image", :src => "#{base_url}/images/#{image}" }
|
576
563
|
else
|
@@ -730,14 +717,26 @@ We're using an object as the complete-target rather than a class. This allows
|
|
730
717
|
|
731
718
|
There's another example of `<name-one>` use in the [recipes](http://cookbook.hobocentral.net/recipes/36-using-a-name-one-one-a).
|
732
719
|
|
720
|
+
### Attributes:
|
721
|
+
|
722
|
+
- `complete-target`, `completer`: see above
|
723
|
+
- `min-chars`: The minimum number of characters that must be entered in the input field before an Ajax request is made.
|
724
|
+
- `nil-value`: If there is no current value, this text will appear greyed out inside the control, and will disappear on focus.
|
725
|
+
|
726
|
+
### Note:
|
727
|
+
|
728
|
+
If you wish to set `min-chars` to 0, you will require this [patch to controls.js](http://github.com/bryanlarsen/scriptaculous/commit/3915b7b). 'controls.js' was added to your project via the rails generator, not via Hobo.
|
729
|
+
|
733
730
|
-->
|
734
|
-
<def tag="name-one" attrs="complete-target, completer"><%
|
731
|
+
<def tag="name-one" attrs="complete-target, completer, min-chars, nil-value"><%
|
735
732
|
complete_target ||= this_field_reflection.klass
|
736
733
|
completer ||= (complete_target.is_a?(Class) ? complete_target : complete_target.class).name_attribute
|
734
|
+
min_chars ||= 1
|
735
|
+
value = name(:no_wrapper => true, :if_present => true)
|
737
736
|
-%>
|
738
737
|
<input type="text" name="#{param_name_for_this}"
|
739
|
-
class="autocompleter #{type_and_field.dasherize} #{css_data :complete_on, typed_id(complete_target), completer}"
|
740
|
-
value="
|
738
|
+
class="autocompleter #{type_and_field._?.dasherize} #{css_data :complete_on, typed_id(complete_target), completer} #{css_data :min_chars, min_chars} #{'nil-value' if value==''}"
|
739
|
+
value="#{value=='' ? nil_value : value}"
|
741
740
|
merge-attrs/>
|
742
741
|
<div class="completions-popup" style="display:none"></div>
|
743
742
|
</def>
|
@@ -976,8 +975,11 @@ Example javascript:
|
|
976
975
|
}
|
977
976
|
});
|
978
977
|
|
978
|
+
Note: if your javascript does not work, please ensure that you have
|
979
|
+
the Hobo version of lowpro.js.
|
980
|
+
|
979
981
|
-->
|
980
|
-
<def tag="input-many" attrs="minimum, fields, skip, template" polymorphic >
|
982
|
+
<def tag="input-many" attrs="minimum, fields, skip, more-skip, template" polymorphic >
|
981
983
|
<%
|
982
984
|
# helper function to create id's on buttons to facilitate testing
|
983
985
|
def underize(s)
|
@@ -987,10 +989,11 @@ end
|
|
987
989
|
<set empty="&this.empty?"/>
|
988
990
|
<% template ||= this.try.new_candidate || this.member_class.new %>
|
989
991
|
<% minimum ||= 0 ; minimum = minimum.to_i %>
|
990
|
-
<% skip ||= this.proxy_reflection.klass.reflect_on_all_associations.detect {|p| p.primary_key_name==this.proxy_reflection.primary_key_name}.try.name.to_s if this.respond_to? :proxy_reflection %>
|
991
|
-
|
992
|
+
<% skip ||= this.proxy_reflection.klass.reflect_on_all_associations.detect {|p| p.primary_key_name==this.proxy_reflection.primary_key_name}.try.name.to_s if this.respond_to? :proxy_reflection %>
|
993
|
+
<% skip += ",#{more_skip}" if more_skip -%>
|
994
|
+
<ul class="input-many #{this_field.dasherize} #{css_data :input_many_prefix, param_name_for_this} #{css_data(:minimum, minimum)}" merge-attrs>
|
992
995
|
<fake-field-context fake-field="-1" context="&template">
|
993
|
-
<li class="input-many-li input-many-template" id="#{param_name_for_this}">
|
996
|
+
<li class="input-many-li input-many-template" id="#{underize param_name_for_this}">
|
994
997
|
<div class="input-many-item" param="default">
|
995
998
|
<field-list param merge-attrs="fields" skip="&skip" />
|
996
999
|
</div>
|
@@ -1000,13 +1003,13 @@ end
|
|
1000
1003
|
</div>
|
1001
1004
|
</li>
|
1002
1005
|
</fake-field-context>
|
1003
|
-
<li class="input-many-li empty #{'hidden' unless this.empty? and minimum==0}" id="#{param_name_for_this}
|
1006
|
+
<li class="input-many-li empty #{'hidden' unless this.empty? and minimum==0}" id="#{underize param_name_for_this}_-1_empty">
|
1004
1007
|
<!-- HACK way to signal an empty collection to the controller -->
|
1005
|
-
<input type="hidden" class="empty-input" id="#{param_name_for_this}" name="#{param_name_for_this}" value="" disabled="&(!this.empty? || minimum>0)" />
|
1008
|
+
<input type="hidden" class="empty-input" id="#{underize param_name_for_this}" name="#{param_name_for_this}" value="" disabled="&(!this.empty? || minimum>0)" />
|
1006
1009
|
<fake-field-context fake-field="-1" context="&template">
|
1007
1010
|
<div param="empty-message">
|
1008
|
-
<ht key="#{this.class.
|
1009
|
-
No <%= this.class.
|
1011
|
+
<ht key="#{this.class.class_name.tableize}.collection.empty_message">
|
1012
|
+
No <%= this.class.class_name.titleize.downcase.pluralize %>.
|
1010
1013
|
</ht>
|
1011
1014
|
</div>
|
1012
1015
|
<div class="buttons">
|
@@ -1016,7 +1019,7 @@ end
|
|
1016
1019
|
</fake-field-context>
|
1017
1020
|
</li>
|
1018
1021
|
<fake-field-context fake-field="0" context="&template">
|
1019
|
-
<li class="input-many-li" if="&(this_parent.empty? && minimum>0)" id="#{param_name_for_this}">
|
1022
|
+
<li class="input-many-li" if="&(this_parent.empty? && minimum>0)" id="#{underize param_name_for_this}">
|
1020
1023
|
<div class="input-many-item" param="default">
|
1021
1024
|
<field-list param merge-attrs="fields" skip="&skip" />
|
1022
1025
|
</div>
|
@@ -1026,7 +1029,7 @@ end
|
|
1026
1029
|
</div>
|
1027
1030
|
</li>
|
1028
1031
|
</fake-field-context>
|
1029
|
-
<li repeat class="input-many-li #{'record-with-errors' unless this.errors.empty?}" id="#{param_name_for_this}">
|
1032
|
+
<li repeat class="input-many-li #{'record-with-errors' unless this.errors.empty?}" id="#{underize param_name_for_this}">
|
1030
1033
|
<error-messages without-heading class="sub-record"/>
|
1031
1034
|
<hidden-id-field/>
|
1032
1035
|
<div class="input-many-item" param="default">
|
@@ -1055,6 +1058,38 @@ end
|
|
1055
1058
|
</ul>
|
1056
1059
|
</def>
|
1057
1060
|
|
1061
|
+
<!-- An enhanced version of [`<input-many>`](/api_tag_defs/input-many) that supports drag-and-drop re-ordering.
|
1062
|
+
|
1063
|
+
Each item in the collection has a `<div class="ordering-handle" param="handle">` added, which can be used to drag the item up and down.
|
1064
|
+
|
1065
|
+
If the items in the collection contain an [`acts_as_list`](http://ar.rubyonrails.org/classes/ActiveRecord/Acts/List/ClassMethods.html) declaration, it is updated.
|
1066
|
+
|
1067
|
+
The specified sort order may be maintained even without `acts_as_list`. The items will be passed to the controller in the correct order, so the order may be persisted there.
|
1068
|
+
|
1069
|
+
### Attributes
|
1070
|
+
|
1071
|
+
- `id`: Due to a limitation in script.aculo.us, an id is required. If you do not supply one, one will be generated.
|
1072
|
+
- `position-column`: The position column may be specified via `acts_as_list`, via a `position_column` method on your model or via this attribute.
|
1073
|
+
- others: all other attributes are passed through to `<input-many>`
|
1074
|
+
|
1075
|
+
-->
|
1076
|
+
|
1077
|
+
<def tag="sortable-input-many" attrs="id, position-column, template">
|
1078
|
+
<% this_id = this_parent.id || rand(100000) -%>
|
1079
|
+
<% id ||= "sortable-input-many-#{this_parent.class.name.underscore.dasherize}-#{this_id}-#{this_field_reflection.name}" -%>
|
1080
|
+
<% template ||= this.try.new_candidate || this.member_class.new %>
|
1081
|
+
<% position_column ||= template.try.position_column -%>
|
1082
|
+
<input-many merge id="&id" class="sortable-input-many" template="&template" more-skip="&position_column">
|
1083
|
+
<default: replace>
|
1084
|
+
<div class="ordering-handle" param="handle" if="&can_edit?">↑<br/>↓</div>
|
1085
|
+
<if test="&position_column">
|
1086
|
+
<input class="sortable-position" type="hidden" value="&this.send(position_column)" name="#{param_name_for_this}[#{position_column}]" />
|
1087
|
+
</if>
|
1088
|
+
<default restore/>
|
1089
|
+
</default:>
|
1090
|
+
</input-many>
|
1091
|
+
</def>
|
1092
|
+
|
1058
1093
|
<!-- Renders the common "or (Cancel)" for a form. Attributes are merged into the link (`<a>Cancel</a>`), making it easy to customise the destination of the cancel link. By default it will link to `this` or `this.class`.
|
1059
1094
|
-->
|
1060
1095
|
<def tag="or-cancel">
|