hobo 0.5.3 → 0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. data/bin/hobo +18 -4
  2. data/hobo_files/plugin/CHANGES.txt +511 -0
  3. data/hobo_files/plugin/README +8 -3
  4. data/hobo_files/plugin/Rakefile +81 -0
  5. data/hobo_files/plugin/generators/hobo/hobo_generator.rb +4 -4
  6. data/hobo_files/plugin/generators/hobo/templates/guest.rb +1 -1
  7. data/hobo_files/plugin/generators/hobo_front_controller/hobo_front_controller_generator.rb +1 -1
  8. data/hobo_files/plugin/generators/hobo_front_controller/templates/index.dryml +16 -22
  9. data/hobo_files/plugin/generators/hobo_front_controller/templates/login.dryml +4 -6
  10. data/hobo_files/plugin/generators/hobo_front_controller/templates/search.dryml +6 -5
  11. data/hobo_files/plugin/generators/hobo_front_controller/templates/signup.dryml +4 -6
  12. data/hobo_files/plugin/generators/hobo_migration/hobo_migration_generator.rb +237 -0
  13. data/hobo_files/plugin/generators/hobo_migration/templates/migration.rb +9 -0
  14. data/hobo_files/plugin/generators/hobo_model/USAGE +2 -3
  15. data/hobo_files/plugin/generators/hobo_model/hobo_model_generator.rb +1 -14
  16. data/hobo_files/plugin/generators/hobo_model/templates/fixtures.yml +1 -6
  17. data/hobo_files/plugin/generators/hobo_model/templates/model.rb +10 -4
  18. data/hobo_files/plugin/generators/hobo_rapid/hobo_rapid_generator.rb +7 -6
  19. data/hobo_files/plugin/generators/hobo_rapid/templates/hobo_base.css +68 -0
  20. data/hobo_files/plugin/generators/hobo_rapid/templates/hobo_rapid.css +93 -0
  21. data/hobo_files/plugin/generators/hobo_rapid/templates/hobo_rapid.js +11 -6
  22. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/plus.png +0 -0
  23. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/stylesheets/application.css +24 -14
  24. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/views/application.dryml +28 -44
  25. data/hobo_files/plugin/generators/hobo_user_model/USAGE +2 -12
  26. data/hobo_files/plugin/generators/hobo_user_model/hobo_user_model_generator.rb +1 -14
  27. data/hobo_files/plugin/generators/hobo_user_model/templates/fixtures.yml +0 -6
  28. data/hobo_files/plugin/generators/hobo_user_model/templates/model.rb +8 -1
  29. data/hobo_files/plugin/init.rb +6 -2
  30. data/hobo_files/plugin/lib/active_record/has_many_association.rb +23 -12
  31. data/hobo_files/plugin/lib/extensions.rb +134 -40
  32. data/hobo_files/plugin/lib/extensions/test_case.rb +0 -1
  33. data/hobo_files/plugin/lib/hobo.rb +77 -46
  34. data/hobo_files/plugin/lib/hobo/authenticated_user.rb +24 -2
  35. data/hobo_files/plugin/lib/hobo/authentication_support.rb +2 -1
  36. data/hobo_files/plugin/lib/hobo/controller.rb +35 -12
  37. data/hobo_files/plugin/lib/hobo/define_tags.rb +4 -4
  38. data/hobo_files/plugin/lib/hobo/dryml.rb +33 -51
  39. data/hobo_files/plugin/lib/hobo/dryml/dryml_builder.rb +47 -34
  40. data/hobo_files/plugin/lib/hobo/dryml/scoped_variables.rb +37 -0
  41. data/hobo_files/plugin/lib/hobo/dryml/taglib.rb +27 -5
  42. data/hobo_files/plugin/lib/hobo/dryml/template.rb +545 -302
  43. data/hobo_files/plugin/lib/hobo/dryml/template_environment.rb +305 -135
  44. data/hobo_files/plugin/lib/hobo/email_address.rb +5 -0
  45. data/hobo_files/plugin/lib/hobo/field_spec.rb +66 -0
  46. data/hobo_files/plugin/lib/hobo/hobo_helper.rb +325 -0
  47. data/hobo_files/plugin/lib/hobo/html_string.rb +2 -0
  48. data/hobo_files/plugin/lib/hobo/lazy_hash.rb +13 -1
  49. data/hobo_files/plugin/lib/hobo/markdown_string.rb +3 -1
  50. data/hobo_files/plugin/lib/hobo/model.rb +185 -66
  51. data/hobo_files/plugin/lib/hobo/model_controller.rb +56 -49
  52. data/hobo_files/plugin/lib/hobo/password_string.rb +2 -0
  53. data/hobo_files/plugin/lib/hobo/plugins.rb +75 -0
  54. data/hobo_files/plugin/lib/hobo/rapid_helper.rb +98 -0
  55. data/hobo_files/plugin/lib/hobo/static_tags +0 -3
  56. data/hobo_files/plugin/lib/hobo/textile_string.rb +11 -1
  57. data/hobo_files/plugin/lib/hobo/undefined.rb +1 -1
  58. data/hobo_files/plugin/lib/rexml.rb +166 -75
  59. data/hobo_files/plugin/spec/fixtures/users.yml +9 -0
  60. data/hobo_files/plugin/spec/spec.opts +6 -0
  61. data/hobo_files/plugin/spec/spec_helper.rb +28 -0
  62. data/hobo_files/plugin/spec/unit/hobo/dryml/template_spec.rb +650 -0
  63. data/hobo_files/plugin/tags/core.dryml +58 -4
  64. data/hobo_files/plugin/tags/rapid.dryml +289 -135
  65. data/hobo_files/plugin/tags/rapid_document_tags.dryml +49 -0
  66. data/hobo_files/plugin/tags/rapid_editing.dryml +92 -69
  67. data/hobo_files/plugin/tags/rapid_forms.dryml +242 -0
  68. data/hobo_files/plugin/tags/rapid_navigation.dryml +65 -65
  69. data/hobo_files/plugin/tags/rapid_pages.dryml +197 -124
  70. data/hobo_files/plugin/tags/rapid_support.dryml +23 -0
  71. metadata +29 -22
  72. data/hobo_files/plugin/generators/hobo_model/templates/migration.rb +0 -13
  73. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/default_mapping.rb +0 -11
  74. data/hobo_files/plugin/generators/hobo_user_model/templates/migration.rb +0 -15
  75. data/hobo_files/plugin/lib/hobo/HtmlString +0 -3
  76. data/hobo_files/plugin/lib/hobo/controller_helpers.rb +0 -135
  77. data/hobo_files/plugin/lib/hobo/core.rb +0 -475
  78. data/hobo_files/plugin/lib/hobo/rapid.rb +0 -447
  79. data/hobo_files/plugin/test/hobo_dryml_template_test.rb +0 -7
  80. data/hobo_files/plugin/test/hobo_test.rb +0 -7
@@ -0,0 +1,49 @@
1
+ <def tag="field_list"><table class="field_list"><tagbody/></table></def>
2
+
3
+ <def tag="field_list_item"><tr merge_attrs><tagbody/></tr></def>
4
+
5
+ <def tag="item_label"><th merge_attrs><tagbody/></th></def>
6
+
7
+ <def tag="item_value"><td merge_attrs><tagbody/></td></def>
8
+
9
+
10
+ <def tag="heading">
11
+ <%= content_tag "h#{scope.heading_level || '1'}", tagbody.call, attributes %>
12
+ </def>
13
+
14
+ <def tag="nav">
15
+ <div class="nav" merge_attrs><tagbody/></div>
16
+ </def>
17
+
18
+ <!-- section represents a generic document or application section. -->
19
+ <def tag="section">
20
+ <set body="&tagbody ? tagbody.call : ''"/>
21
+ <div class="section" merge_attrs if="&body"><tagbody/></div>
22
+ </def>
23
+
24
+ <def tag="aside">
25
+ <set body="&tagbody ? tagbody.call : ''"/>
26
+ <div class="aside" merge_attrs if="&body"><tagbody/></div>
27
+ </def>
28
+
29
+ <def tag="header">
30
+ <set body="&tagbody ? tagbody.call : ''"/>
31
+ <div class="header" merge_attrs if="&body"><tagbody/></div>
32
+ </def>
33
+
34
+ <def tag="footer">
35
+ <set body="&tagbody ? tagbody.call : ''"/>
36
+ <div class="footer" merge_attrs if="&body"><tagbody/></div>
37
+ </def>
38
+
39
+ <!-- article represents an independent piece of content of a -->
40
+ <!-- document, such as a blog entry or newspaper article. -->
41
+ <def tag="article">
42
+ <set body="&tagbody ? tagbody.call : ''"/>
43
+ <div class="article" merge_attrs if="&body"><tagbody/></div>
44
+ </def>
45
+
46
+ <!-- temporary tag -->
47
+ <def tag="panel">
48
+ <div class="panel" merge_attrs><tagbody/></div>
49
+ </def>
@@ -1,6 +1,52 @@
1
+ <def tag="editor"><%=
2
+ if !can_edit?
3
+ view(attributes)
4
+ elsif this_type.respond_to?(:macro)
5
+ if this_type.macro == :belongs_to
6
+ belongs_to_editor(attributes)
7
+ else
8
+ has_many_editor(attributes)
9
+ end
10
+ else
11
+ attrs = add_classes(attributes, "#{type_and_field}", "#{type_name}", "editor")
12
+ call_polymorphic_tag("editor", attrs) or
13
+ raise HoboError.new("<editor> not implemented for #{this.class.name}\##{this_field} " +
14
+ "(#{this.inspect}:#{this_type})")
15
+ end
16
+ %></def>
17
+
18
+
19
+ <def tag="has_many_editor">
20
+ <% #TODO: Implement %>
21
+ <a merge_attrs/>
22
+ </def>
23
+
24
+ <def tag="belongs_to_editor"><%= belongs_to_menu_editor(attributes) %></def>
25
+
26
+ <def tag="editor" for="string"><%= in_place_editor "in_place_textfield_bhv", attributes %></def>
27
+
28
+ <def tag="editor" for="text"><%= in_place_editor "in_place_textarea_bhv", attributes %></def>
29
+
30
+ <def tag="editor" for="html"><%= in_place_editor "in_place_html_textarea_bhv", attributes %></def>
31
+
32
+ <def tag="editor" for="datetime"><%= in_place_editor "in_place_textfield_bhv", attributes %></def>
33
+
34
+ <def tag="editor" for="date"><%= in_place_editor "in_place_textfield_bhv", attributes %></def>
35
+
36
+ <def tag="editor" for="integer"><%= in_place_editor "in_place_textfield_bhv", attributes %></def>
37
+
38
+ <def tag="editor" for="float"><%= in_place_editor "in_place_textfield_bhv", attributes %></def>
39
+
40
+ <def tag="editor" for="password"><% raise HoboError, "passwords cannot be edited in place" %></def>
41
+
42
+ <def tag="editor" for="boolean"><boolean_checkbox_editor merge_attrs/></def>
43
+
1
44
 
2
- <def tag="submit" attrs="label">
3
- <input type="submit" value="<%= label %>" xattrs="" class="button_input submit_button"/>
45
+ <def tag="submit" attrs="label, image">
46
+ <input if="&image" type="image" src="&image" merge_attrs class="image_input submit_button"/>
47
+ <else>
48
+ <input type="submit" value="#{label}" merge_attrs class="button_input submit_button"/>
49
+ </else>
4
50
  </def>
5
51
 
6
52
 
@@ -15,35 +61,21 @@
15
61
  id ||= this_field_dom_id + "_completer"
16
62
  url = object_url(completer_model, "completions",
17
63
  { :for => completer_attr },
18
- options.select_hash {|k,v| k.to_s.starts_with? "where_"})
64
+ attributes.select_hash {|k,v| k.to_s.starts_with? "where_"})
19
65
  %>
20
- <input type="text" name="<%= name %>" id="<%= id %>" class="autocomplete_bhv"
21
- autocomplete_url="<%= url %>" value="<%= value %>"
22
- xattrs=""/>
66
+ <input type="text" name="#{name}" id="#{id}" class="autocomplete_bhv"
67
+ autocomplete_url="#{url}" value="#{value}"
68
+ merge_attrs/>
23
69
  <div id="<%= id %>_completions" class="completions_popup" style="display:none"></div>
24
70
  </def>
25
71
 
26
72
 
27
- <def tag="belongs_to_menu_field"><%
28
- raise HoboError.new("Not allowed to edit") unless can_edit_this?
29
- #Todo: switch to autocompleter for id_name when too many records, and id_name supported
30
- select_options = this_type.klass.find(:all).select {|x| can_view?(x)}.map { |x|
31
- [ display_name(:obj => x, :no_span => true), x.id ]
32
- }
33
- select_options.insert(0, ["(No #{this_type.name.to_s.titleize})", ""]) if this.nil?
34
- %>
35
- <select name="<%= param_name_for_this(true) %>">
36
- <%= options_for_select(select_options.sort, this ? this.id : "") %>
37
- </select>
38
- </def>
39
-
40
-
41
73
  <def tag="belongs_to_menu_editor"><%
42
- raise HoboError.new("Not allowed to edit") unless can_edit_this?
74
+ raise HoboError.new("Not allowed to edit") unless can_edit?
43
75
  link_id = "#{this_field_dom_id}_editor" %>
44
- <span id="#link_id" part_id="rapid_belongs_to_edit">
76
+ <span id="#{link_id}" part="rapid_belongs_to_edit">
45
77
  <% select_options = this_type.klass.find(:all).select {|x| can_view?(x)}.map {|x|
46
- [ display_name(:obj => x, :no_span => true), x.id ]
78
+ [ name(:with => x, :no_wrapper => true), x.id ]
47
79
  }
48
80
  select_options.insert(0, ["(No #{this_type.name.to_s.titleize})", ""]) if this.nil?
49
81
  link_id = "#{this_field_dom_id}_editor"
@@ -54,32 +86,17 @@
54
86
  this_type.primary_key_name => Hobo.raw_js('this.value')
55
87
  } })
56
88
  %>
57
- <select onchange="<%= f %>" id="#link_id">
89
+ <select onchange="<%= f %>">
58
90
  <%= options_for_select(select_options.sort, this ? this.id : "") %>
59
91
  </select>
60
- <if_this><object_link>View</object_link></if_this>
92
+ <a if="&this">View</a>
61
93
  </span>
62
94
  </def>
63
95
 
64
96
 
65
- <def tag="belongs_to_autocompleting_field">
66
- <% refl = this_type
67
- completer_model ||= refl.klass
68
- completer_attr ||= refl.klass.id_name_column
69
- id ||= this_field_dom_id + "_completer"
70
- where_options = options.select_hash {|k,v| k.to_s.starts_with? "where_"}
71
- url = object_url(completer_model, :completions, { :for => completer_attr }.update(where_options))
72
- %>
73
-
74
- <input type="text" id="<%= id %>" class="autocomplete_bhv" autocomplete_url="<%= url %>"
75
- name="<%= param_name_for_this %>" xattrs=""/>
76
- <div id="<%= id %>_completions" class="completions_popup" style="display:none"></div>
77
- </def>
78
-
79
-
80
97
  <def tag="belongs_to_autocompleting_editor" attrs="update">
81
98
  <if_can_edit><%
82
- return object_link unless can_edit_this?
99
+ return object_link unless can_edit?
83
100
 
84
101
  id ||= this_field_dom_id + "_completer"
85
102
  f = ajax_updater(object_url(this_parent),
@@ -93,12 +110,12 @@
93
110
  completer_attr ||= refl.klass.id_name_column
94
111
  url = object_url(completer_model, "completions",
95
112
  { :for => completer_attr },
96
- options.select_hash {|k,v| k.to_s.starts_with? "where_"})
113
+ attributes.select_hash {|k,v| k.to_s.starts_with? "where_"})
97
114
  %>
98
- <form onsubmit="<%= f %>; $('<%= id %>').blur(); return false">
99
- <input type="text" class="autocomplete_bhv autosubmit" id="<%= id %>" autocomplete_url="<%= url %>"
100
- value="<%= this && this.id_name %>" xattrs="" />
101
- <div id="<%= id %>_completions" class="completions_popup" style="display:none"></div>
115
+ <form onsubmit="#{f}; $('#{id}').blur(); return false">
116
+ <input type="text" class="autocomplete_bhv autosubmit" id="#{id}" autocomplete_url="#{url}"
117
+ value="#{this && this.id_name}" merge_attrs />
118
+ <div id="#{id}_completions" class="completions_popup" style="display:none"></div>
102
119
  </form>
103
120
  </if_can_edit>
104
121
  <else>
@@ -107,32 +124,43 @@
107
124
  </def>
108
125
 
109
126
 
110
- <def tag="boolean_checkbox_editor" attrs="update"><%
111
- raise HoboError.new("Not allowed to edit") unless can_edit_this?
127
+ <def tag="string_select_editor" attrs="update, values"><%
128
+ raise HoboError.new("Not allowed to edit") unless can_edit?
129
+
130
+ values = comma_split(values)
112
131
  f = ajax_updater(object_url(this_parent),
113
132
  "Change #{this_field.to_s.titleize}", update,
114
133
  :method => "put",
115
134
  :params => { this_parent.class.name.underscore => {
116
- this_field => Hobo.raw_js('this.checked')
135
+ this_field => Hobo.raw_js('this.value')
117
136
  } })
118
- options = add_classes(options, editor_class)
137
+ html_attributes = add_classes(attributes, editor_class)
119
138
  %>
120
- <input type="checkbox" value="1" onchange="<%= f %>"
121
- xattrs="# this ? options.merge(:checked => 'checked') : options" />
139
+ <select onchange="#{f}" merge_attrs="&html_attributes">
140
+ <%= options_for_select(values, this) %>
141
+ </select>
122
142
  </def>
123
143
 
124
144
 
125
- <def tag="sti_type_field">
126
- <select name="<%= param_name_for(form_this, form_field_path + ['type']) %>">
127
- <%= options_for_select(this.class.send(:subclasses).omap{[name.titleize, name]}, this.class.name) %>
128
- </select>
145
+ <def tag="boolean_checkbox_editor" attrs="update"><%
146
+ raise HoboError.new("Not allowed to edit") unless can_edit?
147
+ f = ajax_updater(object_url(this_parent),
148
+ "Change #{this_field.to_s.titleize}", update,
149
+ :method => "put",
150
+ :params => { this_parent.class.name.underscore => {
151
+ this_field => Hobo.raw_js('this.checked')
152
+ } })
153
+ attributes = add_classes(attributes, editor_class)
154
+ %>
155
+ <input type="checkbox" value="1" onclick="#{f}"
156
+ merge_attrs="& this ? attributes.merge(:checked => 'checked') : attributes" />
129
157
  </def>
130
158
 
131
-
159
+
132
160
  <def tag="sti_type_editor" attrs="update">
133
161
  <% base_class = this.class
134
162
  base_class = base_class.superclass while base_class.superclass != ActiveRecord::Base
135
- f = ajax_updater("#{urlb}/#{controller_for base_class}/#{this.id}",
163
+ f = ajax_updater("#{base_url}/#{controller_for base_class}/#{this.id}",
136
164
  "Change #{this_field.to_s.titleize}", update,
137
165
  :method => "put",
138
166
  :params => { base_class.name.underscore => {
@@ -140,8 +168,8 @@
140
168
  } })
141
169
  %>
142
170
 
143
- <select onchange="<%= f %>">
144
- <if q="#tagbody">
171
+ <select onchange="#{f}">
172
+ <if test="&tagbody">
145
173
  <tagbody/>
146
174
  </if>
147
175
  <else>
@@ -152,17 +180,12 @@
152
180
 
153
181
 
154
182
  <def tag="integer_select_editor" attrs="min, max, update, nil_option, message">
155
- <select class="number_editor_bhv" hobo_update="<%= update %>"
156
- hobo_model_id="<%= this_field_dom_id %>"
157
- xattrs="#message ? options.merge(:hobo_message => message) : options">
158
- <if q="#this.nil?"><option value=""><%= nil_option || "Choose a value" %></option></if>
183
+ <select class="number_editor_bhv" hobo_update="#{update}"
184
+ hobo_model_id="#{this_field_dom_id}"
185
+ merge_attrs="&message ? attributes.merge(:hobo_message => message) : attributes">
186
+ <if test="&this.nil?"><option value=""><%= nil_option || "Choose a value" %></option></if>
159
187
  <%= options_for_select((min.to_i..max.to_i).to_a, this) %>
160
188
  </select>
161
189
  </def>
162
190
 
163
191
 
164
- <def tag="select_field">
165
- <select name="<%= param_name_for_this %>">
166
- <tagbody/>
167
- </select>
168
- </def>
@@ -0,0 +1,242 @@
1
+ <def tag="hidden_fields" attrs="fields, skip"><%=
2
+ hiddens = case fields
3
+ when '*', nil
4
+ this.class.column_names - ['type', 'created_at', 'updated_at']
5
+ else
6
+ comma_split(fields)
7
+ end
8
+ hiddens -= comma_split(skip)
9
+ pname = this.class.name.underscore
10
+ hidden_tags = hiddens.map do |h|
11
+ val = this.send(h)
12
+ name = "#{pname}[#{h}]"
13
+ hidden_field_tag(name, val.to_s) if val
14
+ end.join("\n")
15
+ %></def>
16
+
17
+
18
+ <def tag="form" attrs="message, update, hidden_fields, action, method, web_method"><%=
19
+ ajax_attrs, html_attrs = attributes.partition_hash(Hobo::RapidHelper::AJAX_ATTRS)
20
+
21
+ html_attrs[:action] = action || object_url(this, web_method)
22
+
23
+ new_record = this.respond_to?(:new_record?) && this.new_record?
24
+
25
+ method = if method.nil?
26
+ (action || web_method || new_record) ? "post" : "put"
27
+ else
28
+ method.downcase
29
+ end
30
+ if method == "put"
31
+ http_method_hidden = hidden_field_tag("_method", "PUT")
32
+ html_attrs[:method] = "post"
33
+ else
34
+ html_attrs[:method] = method
35
+ end
36
+
37
+ if update || !ajax_attrs.empty?
38
+ # add an onsubmit to convert to an ajax form if `update` is given
39
+ function = ajax_updater(:post_form, message, update, ajax_attrs)
40
+ html_attrs[:onsubmit] = [html_attrs[:onsubmit], "#{function}; return false;"].compact.join("; ")
41
+ end
42
+
43
+ body, field_names = with_form_context { tagbody.call }
44
+
45
+ hiddens = hidden_fields(:fields => hidden_fields, :skip => field_names) if new_record
46
+
47
+ body = [http_method_hidden, hiddens, body].join
48
+
49
+ if web_method
50
+ add_classes!(html_attrs, "#{type_name}_#{web_method}_form")
51
+ else
52
+ add_classes!(html_attrs, "#{'new_' if new_record}#{type_name}")
53
+ end
54
+
55
+ content_tag("form", body, html_attrs)
56
+ %></def>
57
+
58
+
59
+ <def tag="input"><%=
60
+ if attributes[:type]
61
+ tag :input, attributes
62
+ elsif !can_edit?
63
+ view
64
+ else
65
+ if this_type.respond_to?(:macro)
66
+ if this_type.macro == :belongs_to
67
+ belongs_to_input(attributes)
68
+ elsif this_type.macro == :has_many
69
+ has_many_input(attributes)
70
+ end
71
+ else
72
+ attrs = {}.update(attributes)
73
+ attrs[:name] ||= param_name_for_this
74
+ the_input = call_polymorphic_tag('input', attrs) or
75
+ raise HoboError, ("No input tag for #{this_field}:#{this_type} (this=#{this.inspect})")
76
+ if this_parent.errors[this_field]
77
+ "<div class='field_with_errors'>#{the_input}</div>"
78
+ else
79
+ the_input
80
+ end
81
+ end
82
+ end
83
+ %></def>
84
+
85
+ <def tag="input" for="text" attrs="name">
86
+ <%= text_area_tag(name, this, attributes) %>
87
+ </def>
88
+
89
+ <def tag="input" for="boolean" attrs="name">
90
+ <%= check_box_tag(name, '1', this, attributes) + hidden_field_tag(name, '0') %>
91
+ </def>
92
+
93
+ <def tag="input" for="password" attrs="name">
94
+ <%= password_field_tag(name, this) %>
95
+ </def>
96
+
97
+ <def tag="input" for="html" attrs="name">
98
+ <%= text_area_tag(name, this, add_classes(attributes, :tiny_mce)) %>
99
+ </def>
100
+
101
+ <def tag="input" for="date">
102
+ <%= select_date(this || Time.now, :prefix => param_name_for_this) %>
103
+ </def>
104
+
105
+ <def tag="input" for="datetime">
106
+ <%= select_datetime(this || Time.now, :prefix => param_name_for_this) %>
107
+ </def>
108
+
109
+ <def tag="input" for="integer" attrs="name">
110
+ <%= text_field_tag(name, this, attributes) %>
111
+ </def>
112
+
113
+ <def tag="input" for="float" attrs="name">
114
+ <%= text_field_tag(name, this, attributes) %>
115
+ </def>
116
+
117
+ <def tag="input" for="string" attrs="name">
118
+ <%= text_field_tag(name, this, attributes) %>
119
+ </def>
120
+
121
+ <def tag="belongs_to_input">
122
+ <%= belongs_to_menu_input(attributes) %>
123
+ </def>
124
+
125
+ <!--- Buttons --->
126
+
127
+ <def tag="remote_method_button" attrs="method, update, result_update, params, label, message"><%=
128
+ ajax_attributes, html_attributes = attributes.partition_hash(Hobo::RapidHelper::AJAX_ATTRS)
129
+
130
+ message ||= method.titleize
131
+ func = ajax_updater(object_url(this) + "/#{method}", message, update,
132
+ ajax_attributes.merge(:params => params, :result_update => result_update))
133
+ html_attributes.update(:type =>'button', :onclick => "var e = this; " + func, :value => label)
134
+ tag(:input, add_classes(html_attributes, "button_input remote_method_button #{method}_button"))
135
+ %></def>
136
+
137
+
138
+ <def tag="update_button" attrs="label, message, update, fields, params"><%=
139
+ raise HoboError.new("no update specified") unless update
140
+ message ||= label
141
+ func = ajax_updater(object_url(this), message, update,
142
+ :params => { this.class.name.underscore => fields }.merge(params || {}),
143
+ :method => :put)
144
+ tag :input, add_classes(attributes.merge(:type =>'button', :onclick => func, :value => label),
145
+ "button_input update_button update_#{this.class.name.underscore}_button") %>
146
+ </def>
147
+
148
+
149
+ <def tag="delete_button" attrs="label, message, update, in_place, image, confirm, fade"><%=
150
+ if can_delete?
151
+ attributes = attributes.merge(if image
152
+ { :type => "image", :src => "#{base_url}/images/#{image}" }
153
+ else
154
+ { :type => "button" }
155
+ end)
156
+ label ||= "Remove"
157
+ confirm ||= "Are you sure?"
158
+
159
+ add_classes!(attributes,
160
+ image ? "image_button_input" : "button_input",
161
+ "delete_button delete_#{this.class.name.underscore}_button")
162
+ url = object_url(this, "destroy")
163
+ if in_place == false
164
+ attributes[:confirm] = confirm
165
+ button_to(label, url, attributes)
166
+ else
167
+ fade = true if fade.nil?
168
+ attributes[:value] = label
169
+ attributes[:onclick] = "Hobo.removeButton(this, '#{url}', #{js_updates(update)}, #{fade ? 'true' : 'false'})"
170
+ tag(:input, attributes)
171
+ end
172
+ end
173
+ %></def>
174
+
175
+
176
+ <def tag="create_button" attrs="model, update, label, message, fields"><%=
177
+ raise HoboError.new("no update specified") unless update
178
+
179
+ fields ||= {}
180
+ class_or_assoc = if model
181
+ model.is_a?(String) ? model.constantize : model
182
+ elsif Hobo.simple_has_many_association?(this)
183
+ fields[this.proxy_reflection.primary_key_name] = this.proxy_owner.id
184
+ this
185
+ else
186
+ raise HoboError.new("invalid context for <create_button>")
187
+ end
188
+ new = class_or_assoc.new(fields)
189
+ new.set_creator(current_user)
190
+ if can_create?(new)
191
+ label ||= "New #{new.class.name.titleize}"
192
+ message ||= label
193
+ class_name = new.class.name.underscore
194
+ func = ajax_updater(object_url(new.class), message, update,
195
+ ({:params => { class_name => fields }} unless fields.empty?))
196
+ tag :input, add_classes(attributes.merge(:type =>'button', :onclick => func, :value => label),
197
+ "button_input create_button create_#{class_name}_button")
198
+ end
199
+ %></def>
200
+
201
+
202
+ <def tag="belongs_to_menu_input" attrs="include_none"><%
203
+ raise HoboError.new("Not allowed to edit") unless can_edit?
204
+ #Todo: switch to autocompleter for id_name when too many records, and id_name supported
205
+ select_options = this_type.klass.find(:all).select {|x| can_view?(x)}.map { |x|
206
+ [ name(:with => x, :no_wrapper => true), x.id ]
207
+ }
208
+ select_options.insert(0, ["(No #{this_type.name.to_s.titleize})", ""]) if this.nil? || include_none
209
+ %>
210
+ <select name="<%= param_name_for_this(true) %>">
211
+ <%= options_for_select(select_options.sort, this ? this.id : "") %>
212
+ </select>
213
+ </def>
214
+
215
+
216
+ <def tag="belongs_to_autocompleting_input">
217
+ <% refl = this_type
218
+ completer_model ||= refl.klass
219
+ completer_attr ||= refl.klass.id_name_column
220
+ id ||= this_field_dom_id + "_completer"
221
+ where_attributes = attributes.select_hash {|k,v| k.to_s.starts_with? "where_"}
222
+ url = object_url(completer_model, :completions, { :for => completer_attr }.update(where_attributes))
223
+ %>
224
+
225
+ <input type="text" id="#{id}" class="autocomplete_bhv" autocomplete_url="#{url}"
226
+ name="#{param_name_for_this}" merge_attrs/>
227
+ <div id="#{id}_completions" class="completions_popup" style="display:none"></div>
228
+ </def>
229
+
230
+
231
+ <def tag="sti_type_input">
232
+ <select name="<%= param_name_for(form_this, form_field_path + ['type']) %>">
233
+ <%= options_for_select(this.class.send(:subclasses).omap{[name.titleize, name]}, this.class.name) %>
234
+ </select>
235
+ </def>
236
+
237
+
238
+ <def tag="select_input">
239
+ <select name="#{param_name_for_this}">
240
+ <tagbody/>
241
+ </select>
242
+ </def>