hobo 0.9.102 → 0.9.103
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.txt +45 -1
- data/Rakefile +2 -0
- data/bin/hobo +0 -0
- data/lib/hobo.rb +1 -1
- data/lib/hobo/accessible_associations.rb +6 -3
- data/lib/hobo/lifecycles.rb +0 -1
- data/lib/hobo/permissions.rb +4 -4
- data/lib/hobo/view_hints.rb +1 -1
- data/rails_generators/hobo_rapid/templates/hobo-rapid.js +150 -76
- data/rails_generators/hobo_rapid/templates/themes/clean/public/stylesheets/clean.css +1 -2
- data/taglibs/rapid_core.dryml +9 -1
- data/taglibs/rapid_forms.dryml +72 -18
- metadata +6 -7
data/CHANGES.txt
CHANGED
@@ -14,7 +14,51 @@ likely to cause conflicts, so it is highly recommended that you have
|
|
14
14
|
your code backed up and in a change control system such as git or
|
15
15
|
subversion.
|
16
16
|
|
17
|
-
=== Hobo 0.9.
|
17
|
+
=== Hobo 0.9.103 (AKA 1.0.RC2) ===
|
18
|
+
|
19
|
+
|
20
|
+
### Warning
|
21
|
+
|
22
|
+
If you are on Rails 2.3.5 and are running Hobo as a plugin,
|
23
|
+
please check out bug
|
24
|
+
[#574](https://hobo.lighthouseapp.com/projects/8324/tickets/574-rails-235-b0rks-our-rake-tasks-running-on-edge-hobo)
|
25
|
+
for a workaround you need to apply to your Rakefile.
|
26
|
+
|
27
|
+
### Bugs
|
28
|
+
|
29
|
+
This release fixes a couple of serious bugs:
|
30
|
+
[565](https://hobo.lighthouseapp.com/projects/8324-hobo/tickets/565)
|
31
|
+
and
|
32
|
+
[567](https://hobo.lighthouseapp.com/projects/8324-hobo/tickets/567).
|
33
|
+
|
34
|
+
### Input-Many & has-many :through
|
35
|
+
|
36
|
+
The `<input-many>` tag in Rapid has been replaced with a version
|
37
|
+
ported from the `<hjq-input-many>` tag in Hobo-JQuery. This brings
|
38
|
+
the following enhancements:
|
39
|
+
|
40
|
+
- it supports 0 length associations
|
41
|
+
- input-many's may be nested inside of other input-many's
|
42
|
+
- it allows the (+) and (-) buttons to be customized
|
43
|
+
- it provides a default for the `item` parameter
|
44
|
+
- it copies from a template rather than cloning the current item and clearing it
|
45
|
+
- the template may be overridden
|
46
|
+
- id's of textareas and selects and other non-input's are adjusted properly
|
47
|
+
- classdata for inner elements updated
|
48
|
+
|
49
|
+
The new `<input-many>` tag differs from `<hjq-input-many>` in that:
|
50
|
+
|
51
|
+
- it's written in prototype.js rather than in jquery
|
52
|
+
- it doesn't have the delayed initialization feature
|
53
|
+
- the name of the main parameter is `default` rather than `item`
|
54
|
+
- hjq-input-many allows you to provide javascript callbacks.
|
55
|
+
input-many fires rapid:add, rapid:change and rapid:remove events
|
56
|
+
that can be hooked.
|
57
|
+
|
58
|
+
You will have to ensure that your hobo-rapid.js and clean.css files
|
59
|
+
are updated in your application.
|
60
|
+
|
61
|
+
=== Hobo 0.9.101/0.9.102 (AKA 1.0.BROWN_PAPER_BAG) ===
|
18
62
|
|
19
63
|
Yes, that was embarrassing. How the test suite failed to catch that
|
20
64
|
one is mind blowing.
|
data/Rakefile
CHANGED
@@ -55,6 +55,8 @@ Jeweler::Tasks.new do |gemspec|
|
|
55
55
|
gemspec.summary = "The web app builder for Rails"
|
56
56
|
gemspec.homepage = "http://hobocentral.net/"
|
57
57
|
gemspec.authors = ["Tom Locke"]
|
58
|
+
gemspec.executables = ['hobo']
|
59
|
+
gemspec.default_executable = 'hobo'
|
58
60
|
gemspec.rubyforge_project = "hobo"
|
59
61
|
gemspec.add_dependency("rails", [">= 2.2.2"])
|
60
62
|
gemspec.add_dependency("will_paginate", [">= 2.3.11"])
|
data/bin/hobo
CHANGED
File without changes
|
data/lib/hobo.rb
CHANGED
@@ -46,10 +46,13 @@ module Hobo
|
|
46
46
|
# work around
|
47
47
|
# https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/3510-has_many-build-does-not-set-reverse-reflection
|
48
48
|
# https://hobo.lighthouseapp.com/projects/8324/tickets/447-validation-problems-with-has_many-accessible-true
|
49
|
-
|
50
|
-
|
49
|
+
reverse = owner.class.reverse_reflection(association_name)
|
50
|
+
if reverse && reverse.macro==:belongs_to
|
51
|
+
method = "#{reverse.name}=".to_sym
|
52
|
+
record.send(method, owner) if record.respond_to? method
|
53
|
+
end
|
51
54
|
else
|
52
|
-
owner.include_in_save(association_name, record)
|
55
|
+
owner.include_in_save(association_name, record) unless owner.class.reflections[association_name].options[:through]
|
53
56
|
end
|
54
57
|
else
|
55
58
|
# It's already a record
|
data/lib/hobo/lifecycles.rb
CHANGED
@@ -45,7 +45,6 @@ module Hobo
|
|
45
45
|
index_options = { :name => options[:index] } unless options[:index] == true
|
46
46
|
index(options[:state_field], index_options || {})
|
47
47
|
end
|
48
|
-
never_show options[:state_field]
|
49
48
|
attr_protected options[:state_field]
|
50
49
|
|
51
50
|
unless options[:key_timestamp_field] == false
|
data/lib/hobo/permissions.rb
CHANGED
@@ -100,12 +100,12 @@ module Hobo
|
|
100
100
|
has_one_without_hobo_permission_check(association_id, options, &extension)
|
101
101
|
reflection = reflections[association_id]
|
102
102
|
if reflection.options[:dependent]==:destroy
|
103
|
-
#overriding dynamic method created in ActiveRecord::Associations#
|
103
|
+
#overriding dynamic method created in ActiveRecord::Associations#configure_dependency_for_has_one
|
104
104
|
method_name = "has_one_dependent_destroy_for_#{reflection.name}".to_sym
|
105
105
|
define_method(method_name) do
|
106
106
|
association = send(reflection.name)
|
107
107
|
unless association.nil?
|
108
|
-
association.is_a?(Hobo::Model) ? association.user_destroy(
|
108
|
+
association.is_a?(Hobo::Model) ? association.user_destroy(acting_user) : association.destroy
|
109
109
|
end
|
110
110
|
end
|
111
111
|
end
|
@@ -115,12 +115,12 @@ module Hobo
|
|
115
115
|
belongs_to_without_hobo_permission_check(association_id, options, &extension)
|
116
116
|
reflection = reflections[association_id]
|
117
117
|
if reflection.options[:dependent]==:destroy
|
118
|
-
#overriding dynamic method created in ActiveRecord::Associations#
|
118
|
+
#overriding dynamic method created in ActiveRecord::Associations#configure_dependency_for_belongs_to
|
119
119
|
method_name = "belongs_to_dependent_destroy_for_#{reflection.name}".to_sym
|
120
120
|
define_method(method_name) do
|
121
121
|
association = send(reflection.name)
|
122
122
|
unless association.nil?
|
123
|
-
association.is_a?(Hobo::Model) ? association.user_destroy(
|
123
|
+
association.is_a?(Hobo::Model) ? association.user_destroy(acting_user) : association.destroy
|
124
124
|
end
|
125
125
|
end
|
126
126
|
end
|
data/lib/hobo/view_hints.rb
CHANGED
@@ -36,7 +36,7 @@ module Hobo
|
|
36
36
|
child_model = model.reflections[args.first].klass
|
37
37
|
if child_model.view_hints.parent.nil? and !child_model.view_hints.parent_defined
|
38
38
|
parent = model.reverse_reflection(args.first)
|
39
|
-
child_model.view_hints.parent(parent.name, :undefined => true)
|
39
|
+
child_model.view_hints.parent(parent.name, :undefined => true) if parent
|
40
40
|
end
|
41
41
|
args
|
42
42
|
end
|
@@ -291,7 +291,7 @@ var Hobo = {
|
|
291
291
|
|
292
292
|
removeButton: function(el, url, updates, options) {
|
293
293
|
if (options.fade == null) { options.fade = true; }
|
294
|
-
if (options.confirm == null) { options.
|
294
|
+
if (options.confirm == null) { options.confirm = "Are you sure?"; }
|
295
295
|
|
296
296
|
if (options.confirm == false || confirm(options.confirm)) {
|
297
297
|
var objEl = Hobo.objectElementFor(el)
|
@@ -481,6 +481,14 @@ Element.findContaining = function(el, tag) {
|
|
481
481
|
return null;
|
482
482
|
}
|
483
483
|
|
484
|
+
Element.prototype.childWithClass = function(klass) {
|
485
|
+
var ret=null;
|
486
|
+
this.childElements().each(function(el2) {
|
487
|
+
if(ret==null && el2.hasClassName(klass)) ret=el2;
|
488
|
+
});
|
489
|
+
return ret;
|
490
|
+
}
|
491
|
+
|
484
492
|
// Add an afterEnterEditMode hook to in-place-editor
|
485
493
|
origEnterEditMode = Ajax.InPlaceEditor.prototype.enterEditMode
|
486
494
|
Ajax.InPlaceEditor.prototype.enterEditMode = function(evt) {
|
@@ -519,6 +527,9 @@ HoboBehavior = Class.create({
|
|
519
527
|
this.mainSelector = mainSelector
|
520
528
|
this.features = features
|
521
529
|
this.addEvents(mainSelector, features.events)
|
530
|
+
if (features.initialize) {
|
531
|
+
document.observe("dom:loaded", features.initialize);
|
532
|
+
}
|
522
533
|
},
|
523
534
|
|
524
535
|
addEvents: function(parentSelector, events) {
|
@@ -557,91 +568,154 @@ new HoboBehavior("ul.input-many", {
|
|
557
568
|
".remove-item:click": 'removeOne'
|
558
569
|
}
|
559
570
|
},
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
var
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
ul.fire("rapid:change", { element: newItem })
|
571
|
+
|
572
|
+
initialize: function(ul) {
|
573
|
+
// disable all elements inside our template, and mark them so we can find them later.
|
574
|
+
$$(".input-many-template input:enabled, .input-many-template select:enabled, .input-many-template textarea:enabled, .input-many-template button:enabled").each(function(input) {
|
575
|
+
input.disabled = true;
|
576
|
+
input.addClassName("input_many_template_input");
|
577
|
+
});
|
578
|
+
},
|
579
|
+
|
580
|
+
// given this==the input-many, returns a lambda that updates the name & id for an element
|
581
|
+
getNameUpdater: function(new_index) {
|
582
|
+
var name_prefix = Hobo.getClassData(this, 'input-many-prefix');
|
583
|
+
var id_prefix = name_prefix.replace(/\[/g, "_").replace(/\]/g, "");
|
584
|
+
var name_re = RegExp("^" + RegExp.escape(name_prefix)+ "\[\-?[0-9]+\]");
|
585
|
+
var name_sub = name_prefix + '[' + new_index.toString() + ']';
|
586
|
+
var id_re = RegExp("^" + RegExp.escape(id_prefix)+ "_\-?[0-9]+");
|
587
|
+
var id_sub = id_prefix + '_' + new_index.toString();
|
588
|
+
var class_re = RegExp(RegExp.escape(name_prefix)+ "\[\-?[0-9]+\]");
|
589
|
+
var class_sub = name_sub;
|
580
590
|
|
581
|
-
|
591
|
+
return function() {
|
592
|
+
if(this.name) {
|
593
|
+
this.name = this.name.replace(name_re, name_sub);
|
594
|
+
}
|
595
|
+
if (id_prefix==this.id.slice(0, id_prefix.length)) {
|
596
|
+
this.id = this.id.replace(id_re, id_sub);
|
597
|
+
} else {
|
598
|
+
// silly rails. text_area_tag and text_field_tag use different conventions for the id.
|
599
|
+
if(name_prefix==this.id.slice(0, name_prefix.length)) {
|
600
|
+
this.id = this.id.replace(name_re, name_sub);
|
601
|
+
} /* else {
|
602
|
+
hjq.util.log("hjq.input_many.update_id: id_prefix "+id_prefix+" didn't match input "+this.id);
|
603
|
+
} */
|
604
|
+
}
|
605
|
+
if (class_re.test(this.className)) {
|
606
|
+
this.className = this.className.replace(class_re, class_sub);
|
607
|
+
}
|
608
|
+
return this;
|
609
|
+
};
|
582
610
|
},
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
self.updateButtons()
|
595
|
-
self.updateInputNames()
|
596
|
-
} });
|
611
|
+
|
612
|
+
// given this==an input-many item, get the submit index
|
613
|
+
getIndex: function() {
|
614
|
+
return Number(this.id.match(/\[([0-9])+\]$/)[1]);
|
615
|
+
},
|
616
|
+
|
617
|
+
/* For some reason, select() and down() and all those useful functions aren't working for us. Roll our own replacement. */
|
618
|
+
recurse_elements_with_class: function(el, klass, f) {
|
619
|
+
var that=this;
|
620
|
+
if(klass==null || el.hasClassName(klass)) {
|
621
|
+
f(el);
|
597
622
|
}
|
598
|
-
|
599
|
-
ul.fire("rapid:change")
|
623
|
+
el.childElements().each(function(el2) {that.recurse_elements_with_class.call(that, el2, klass, f);});
|
600
624
|
},
|
601
625
|
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
626
|
+
addOne: function(ev, el) {
|
627
|
+
Event.stop(ev);
|
628
|
+
var ul = el.up('ul.input-many'), li = el.up('li.input-many-li');
|
629
|
+
|
630
|
+
var template = ul.down("li.input-many-template");
|
631
|
+
var clone = $(template.cloneNode(true));
|
632
|
+
clone.removeClassName("input-many-template");
|
633
|
+
// length-2 because ignore the template li and the empty li
|
634
|
+
var name_updater = this.getNameUpdater.call(ul, ul.childElements().length-2);
|
635
|
+
|
636
|
+
function reenable_inputs(el) {
|
637
|
+
if(el.hasClassName("input_many_template_input")) {
|
638
|
+
el.disabled = false;
|
639
|
+
el.removeClassName("input_many_template_input");
|
610
640
|
}
|
611
|
-
|
641
|
+
el.childElements().each(function(el2) {
|
642
|
+
if(!el2.hasClassName("input-many-template")) reenable_inputs(el2);
|
643
|
+
});
|
644
|
+
}
|
645
|
+
reenable_inputs(clone);
|
646
|
+
|
647
|
+
// update id & name
|
648
|
+
this.recurse_elements_with_class.call(this, clone, null, function(el) {
|
649
|
+
name_updater.call(el);
|
650
|
+
});
|
651
|
+
|
652
|
+
// do the add with anim
|
653
|
+
clone.setStyle("display", "none")
|
654
|
+
li.insert({after: clone});
|
655
|
+
new Effect.BlindDown(clone, {duration: 0.3})
|
656
|
+
|
657
|
+
// visibility
|
658
|
+
if(li.hasClassName("empty")) {
|
659
|
+
li.addClassName("hidden");
|
660
|
+
li.childWithClass("empty-input").disabled = true;
|
661
|
+
} else {
|
662
|
+
// now that we've added an element after us, we should only have a '-' button
|
663
|
+
li.childWithClass("buttons").childWithClass("remove-item").removeClassName("hidden");
|
664
|
+
li.childWithClass("buttons").childWithClass("add-item").addClassName("hidden");
|
665
|
+
}
|
666
|
+
|
667
|
+
Event.addBehavior.reload();
|
668
|
+
|
669
|
+
ul.fire("rapid:add", { element: clone })
|
670
|
+
ul.fire("rapid:change", { element: clone })
|
671
|
+
|
672
|
+
return;
|
612
673
|
},
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
var
|
617
|
-
|
618
|
-
var
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
674
|
+
|
675
|
+
removeOne: function(ev, el) {
|
676
|
+
Event.stop(ev);
|
677
|
+
var that = this;
|
678
|
+
var ul = el.up('ul.input-many'), li = el.up('li.input-many-li')
|
679
|
+
var minimum = parseInt(Hobo.getClassData(ul, 'minimum'));
|
680
|
+
|
681
|
+
ul.fire("rapid:remove", { element: li })
|
682
|
+
|
683
|
+
// rename everybody from me onwards
|
684
|
+
var i=this.getIndex.call(li)
|
685
|
+
var n=li.next();
|
686
|
+
for(; n; i+=1, n=n.next()) {
|
687
|
+
var name_updater = this.getNameUpdater.call(ul, i);
|
688
|
+
this.recurse_elements_with_class.call(this, n, null, function(el) {name_updater.call(el);});
|
689
|
+
}
|
690
|
+
|
691
|
+
// adjust +/- buttons on the button element as appropriate
|
692
|
+
var last=ul.childElements()[ul.childElements().length-1];
|
693
|
+
if(last==li) {
|
694
|
+
last = last.previous();
|
624
695
|
}
|
625
|
-
|
626
|
-
|
696
|
+
|
697
|
+
if(last.hasClassName("empty")) {
|
698
|
+
last.removeClassName("hidden");
|
699
|
+
this.recurse_elements_with_class.call(this, last, "empty-input", function(el) {el.disabled=false;});
|
700
|
+
} else {
|
701
|
+
// if we've reached the minimum, we don't want to add the '-' button
|
702
|
+
if(ul.childElements().length-3 <= minimum||0) {
|
703
|
+
last.childWithClass("buttons").childWithClass("remove-item").addClassName("hidden");
|
704
|
+
} else {
|
705
|
+
last.childWithClass("buttons").childWithClass("remove-item").removeClassName("hidden");
|
706
|
+
}
|
707
|
+
last.childWithClass("buttons").childWithClass("add-item").removeClassName("hidden");
|
627
708
|
}
|
628
|
-
|
709
|
+
|
710
|
+
new Effect.BlindUp(li, { duration: 0.3, afterFinish: function (ef) {
|
711
|
+
li.remove()
|
712
|
+
} });
|
713
|
+
|
714
|
+
ul.fire("rapid:change")
|
629
715
|
},
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
this.element.selectChildren('li').each(function(li, index) {
|
635
|
-
li.select('*[name]').each(function(control) {
|
636
|
-
if(control.name) {
|
637
|
-
var changeId = control.id == control.name;
|
638
|
-
control.name = control.name.sub(new RegExp("^" + RegExp.escape(prefix) + "\[[0-9]+\]"), prefix + '[' + index +']');
|
639
|
-
if (changeId) control.id = control.name;
|
640
|
-
}
|
641
|
-
})
|
642
|
-
})
|
643
|
-
}
|
644
|
-
|
716
|
+
|
717
|
+
|
718
|
+
|
645
719
|
})
|
646
720
|
|
647
721
|
|
@@ -111,8 +111,6 @@ form .actions input { margin: 0; }
|
|
111
111
|
color: white;
|
112
112
|
}
|
113
113
|
|
114
|
-
.article {margin: 20px 0; border-top: 1px dotted #ccc;}
|
115
|
-
|
116
114
|
.field-list th {width: 120px; white-space: nowrap;}
|
117
115
|
.field-list td {width: auto;}
|
118
116
|
.field-list .input-help { color: #888;}
|
@@ -317,6 +315,7 @@ ul.input-all {list-style-type: none;}
|
|
317
315
|
ul.input-many > li { overflow:hidden; zoom:1;}
|
318
316
|
ul.input-many .input-many-item {float:left;}
|
319
317
|
ul.input-many div.buttons {float:left; margin-left:10px;}
|
318
|
+
li.input-many-template { display:none; }
|
320
319
|
|
321
320
|
ul.check-many { list-style-type: none; margin-left: 0px;}
|
322
321
|
ul.check-many li input { vertical-align: -20%;}
|
data/taglibs/rapid_core.dryml
CHANGED
@@ -19,6 +19,14 @@
|
|
19
19
|
- skip: render nothing at all. This will omit the entire row (including the label)
|
20
20
|
- ignore: render the input normally. That is, don't even perform the edit check.
|
21
21
|
|
22
|
+
### Example
|
23
|
+
|
24
|
+
<field-list fields="first-name, last-name, city">
|
25
|
+
<first-name-label:>Given Name</first-name-label:>
|
26
|
+
<last-name-label:>Family Name</last-name-label:>
|
27
|
+
<city-view:><name-one/></city-view:>
|
28
|
+
</field-list>
|
29
|
+
|
22
30
|
-->
|
23
31
|
<def tag="field-list" attrs="tag, no-edit">
|
24
32
|
<% tag ||= scope.in_form ? "input" : "view"; no_edit ||= "skip" %>
|
@@ -606,7 +614,7 @@ The context should be a user object. If `this == current_user` the "you" form is
|
|
606
614
|
<set user="&Hobo::User.default_user_model"/>
|
607
615
|
<select-menu if="&user && RAILS_ENV == 'development'"
|
608
616
|
first-option="Guest" options="&user.all(:limit => 30).*.login"
|
609
|
-
onchange="location.href = '/
|
617
|
+
onchange="location.href = '#{dev_support_path}/set_current_user?login=' + this.options[this.selectedIndex].value"
|
610
618
|
selected="#{current_user.login}"
|
611
619
|
class="dev-user-changer"/>
|
612
620
|
</def>
|
data/taglibs/rapid_forms.dryml
CHANGED
@@ -874,7 +874,7 @@ Use the `uri` option to specify a redirect location:
|
|
874
874
|
|
875
875
|
This tag is very different from tags like `<select-many>` and `<check-many>` in that:
|
876
876
|
|
877
|
-
- Those tags are used to *
|
877
|
+
- Those tags are used to *choose existing records* to include in the association, while `<input-many>` is used to actually create or edit the records in the association.
|
878
878
|
|
879
879
|
### Example
|
880
880
|
|
@@ -890,36 +890,90 @@ The body of the tag will be repeated for each of the current records in the coll
|
|
890
890
|
|
891
891
|
- skip: Passed through to the `<field-list>`. If not specified, it defaults to the parent association.
|
892
892
|
|
893
|
+
### Example
|
894
|
+
|
895
|
+
Say you are creating a new `Category` in your online shop, and you want to create some initial products *in the same form*, you can add the following to your form:
|
896
|
+
|
897
|
+
<hjq-input-many:products fields="name, price" />
|
898
|
+
|
899
|
+
You'll often want to provide the `item` parameter:
|
900
|
+
|
901
|
+
<hjq-input-many:products><item:><field-list fields="name, price" /></item:></hjq-input-many>
|
902
|
+
|
903
|
+
A fully worked up example of nested hjq-input-many's may be found in [agility/jquery-test](http://github.com/tablatom/agility/blob/jquery-test/app/views/projects/nested_has_many_test.dryml)
|
904
|
+
|
905
|
+
### Attributes
|
906
|
+
|
907
|
+
- `minimum`: the minimum number of items in the collection. Currently only '0' and '1' are supported values. The default is '0'.
|
908
|
+
|
909
|
+
- `fields`, `skip`: passed down to the `field-list` tag in the default `item`.
|
910
|
+
|
911
|
+
- `template`: the default values for new items. Normally this functionality is better provided by Model.new, but it's here if you need it.
|
912
|
+
|
893
913
|
-->
|
894
|
-
<def tag="input-many" attrs="fields,skip" polymorphic>
|
914
|
+
<def tag="input-many" attrs="minimum, fields, skip, template, add-hook, remove-hook" polymorphic >
|
915
|
+
<%
|
916
|
+
# helper function to create id's on buttons to facilitate testing
|
917
|
+
def underize(s)
|
918
|
+
s.gsub(/\[/,"_").gsub(/\]/,"")
|
919
|
+
end
|
920
|
+
%>
|
895
921
|
<set empty="&this.empty?"/>
|
922
|
+
<% template ||= this.try.new_candidate || this.member_class.new %>
|
923
|
+
<% minimum ||= 0 ; minimum = minimum.to_i %>
|
896
924
|
<% 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 %>
|
897
|
-
<ul class="input-many #{this_field.dasherize} #{css_data :input_many_prefix, param_name_for_this}">
|
898
|
-
<
|
925
|
+
<ul class="input-many #{this_field.dasherize} #{css_data :input_many_prefix, param_name_for_this} #{css_data(:minimum, minimum)} #{css_data(:add_hook, add_hook) if add_hook} #{css_data(:remove_hook, remove_hook) if remove_hook}">
|
926
|
+
<fake-field-context fake-field="-1" context="&template">
|
927
|
+
<li class="input-many-li input-many-template" id="#{param_name_for_this}">
|
928
|
+
<div class="input-many-item" param="default">
|
929
|
+
<field-list param merge-attrs="fields" skip="&skip" />
|
930
|
+
</div>
|
931
|
+
<div class="buttons">
|
932
|
+
<button param="remove-item" id="#{underize param_name_for_this}_remove">-</button>
|
933
|
+
<button param="add-item" id="#{underize param_name_for_this}_add">+</button>
|
934
|
+
</div>
|
935
|
+
</li>
|
936
|
+
</fake-field-context>
|
937
|
+
<li class="input-many-li empty #{'hidden' unless this.empty? and minimum==0}" id="#{param_name_for_this}[-1]_empty">
|
938
|
+
<!-- HACK way to signal an empty collection to the controller -->
|
939
|
+
<input type="hidden" class="empty-input" id="#{param_name_for_this}" name="#{param_name_for_this}" value="" disabled="&(!this.empty? || minimum>0)" />
|
940
|
+
<fake-field-context fake-field="-1" context="&template">
|
941
|
+
<div param="empty-message">
|
942
|
+
<ht key="#{this.class.class_name.tableize}.collection.empty_message">
|
943
|
+
No <%= this.class.class_name.titleize.downcase.pluralize %>.
|
944
|
+
</ht>
|
945
|
+
</div>
|
946
|
+
<div class="buttons">
|
947
|
+
<button param="remove-item" class="hidden" id="#{underize param_name_for_this}_remove">-</button>
|
948
|
+
<button param="add-item" id="#{underize param_name_for_this}_add">+</button>
|
949
|
+
</div>
|
950
|
+
</fake-field-context>
|
951
|
+
</li>
|
952
|
+
<fake-field-context fake-field="0" context="&template">
|
953
|
+
<li class="input-many-li" if="&(this_parent.empty? && minimum>0)" id="#{param_name_for_this}">
|
954
|
+
<div class="input-many-item" param="default">
|
955
|
+
<field-list param merge-attrs="fields" skip="&skip" />
|
956
|
+
</div>
|
957
|
+
<div class="buttons">
|
958
|
+
<button param="remove-item" class="hidden" id="#{underize param_name_for_this}_remove">-</button>
|
959
|
+
<button param="add-item" id="#{underize param_name_for_this}_add">+</button>
|
960
|
+
</div>
|
961
|
+
</li>
|
962
|
+
</fake-field-context>
|
963
|
+
<li repeat class="input-many-li #{'record-with-errors' unless this.errors.empty?}" id="#{param_name_for_this}">
|
899
964
|
<error-messages without-heading class="sub-record"/>
|
900
965
|
<hidden-id-field/>
|
901
966
|
<div class="input-many-item" param="default">
|
902
|
-
<field-list merge-attrs="fields" skip="&skip"/>
|
967
|
+
<field-list param merge-attrs="fields" skip="&skip" />
|
903
968
|
</div>
|
904
969
|
<div class="buttons">
|
905
|
-
<button
|
906
|
-
<button
|
907
|
-
</div>
|
908
|
-
</li>
|
909
|
-
<li if="&empty">
|
910
|
-
<fake-field-context fake-field="0" context="&this.try.new_candidate || this.member_class.new">
|
911
|
-
<div class="input-many-item" param="default">
|
912
|
-
<field-list merge-attrs="fields" skip="&skip"/>
|
913
|
-
</div>
|
914
|
-
</fake-field-context>
|
915
|
-
<div class="buttons">
|
916
|
-
<button type="button" class="add-item" merge-attrs="disabled" param="add-item">+</button>
|
970
|
+
<button param="remove-item" class="#{'hidden' if this_parent.length<=minimum}" id="#{underize param_name_for_this}_remove">-</button>
|
971
|
+
<button param="add-item" class="#{'hidden' if not last_item?}" id="#{underize param_name_for_this}_add">+</button>
|
917
972
|
</div>
|
918
973
|
</li>
|
919
974
|
</ul>
|
920
975
|
</def>
|
921
976
|
|
922
|
-
|
923
977
|
<!-- Renders a sub-section of a form with fields for every record in a `has_many` association. This is similar to `<input-many>` except there is no ability to add and remove items (i.e. no (+) and (-) buttons).
|
924
978
|
-->
|
925
979
|
<def tag="input-all">
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hobo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.103
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tom Locke
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-12-
|
12
|
+
date: 2009-12-10 00:00:00 -05:00
|
13
13
|
default_executable: hobo
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -40,7 +40,7 @@ dependencies:
|
|
40
40
|
requirements:
|
41
41
|
- - "="
|
42
42
|
- !ruby/object:Gem::Version
|
43
|
-
version: 0.9.
|
43
|
+
version: 0.9.103
|
44
44
|
version:
|
45
45
|
- !ruby/object:Gem::Dependency
|
46
46
|
name: hobofields
|
@@ -50,7 +50,7 @@ dependencies:
|
|
50
50
|
requirements:
|
51
51
|
- - "="
|
52
52
|
- !ruby/object:Gem::Version
|
53
|
-
version: 0.9.
|
53
|
+
version: 0.9.103
|
54
54
|
version:
|
55
55
|
description:
|
56
56
|
email: tom@tomlocke.com
|
@@ -58,9 +58,8 @@ executables:
|
|
58
58
|
- hobo
|
59
59
|
extensions: []
|
60
60
|
|
61
|
-
extra_rdoc_files:
|
62
|
-
|
63
|
-
- README
|
61
|
+
extra_rdoc_files: []
|
62
|
+
|
64
63
|
files:
|
65
64
|
- CHANGES.txt
|
66
65
|
- LICENSE.txt
|