hobo 1.0.3 → 1.1.0.pre0
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/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">
|