hobo 0.5.3 → 0.6

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.
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>