hobo 0.6.1 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/bin/hobo +3 -2
  2. data/hobo_files/plugin/CHANGES.txt +299 -2
  3. data/hobo_files/plugin/Rakefile +12 -10
  4. data/hobo_files/plugin/generators/hobo/templates/guest.rb +1 -13
  5. data/hobo_files/plugin/generators/hobo_migration/hobo_migration_generator.rb +11 -7
  6. data/hobo_files/plugin/generators/hobo_rapid/hobo_rapid_generator.rb +1 -0
  7. data/hobo_files/plugin/generators/hobo_rapid/templates/hobo_rapid.js +1 -1
  8. data/hobo_files/plugin/generators/hobo_rapid/templates/lowpro.js +405 -0
  9. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/views/application.dryml +1 -1
  10. data/hobo_files/plugin/generators/hobo_user_model/templates/model.rb +1 -9
  11. data/hobo_files/plugin/init.rb +5 -0
  12. data/hobo_files/plugin/lib/active_record/has_many_association.rb +1 -1
  13. data/hobo_files/plugin/lib/extensions.rb +26 -5
  14. data/hobo_files/plugin/lib/extensions/test_case.rb +1 -1
  15. data/hobo_files/plugin/lib/hobo.rb +37 -11
  16. data/hobo_files/plugin/lib/hobo/authenticated_user.rb +7 -2
  17. data/hobo_files/plugin/lib/hobo/authentication_support.rb +7 -6
  18. data/hobo_files/plugin/lib/hobo/composite_model.rb +5 -0
  19. data/hobo_files/plugin/lib/hobo/controller.rb +4 -4
  20. data/hobo_files/plugin/lib/hobo/dryml.rb +5 -5
  21. data/hobo_files/plugin/lib/hobo/dryml/part_context.rb +3 -6
  22. data/hobo_files/plugin/lib/hobo/dryml/template.rb +16 -15
  23. data/hobo_files/plugin/lib/hobo/dryml/template_environment.rb +24 -20
  24. data/hobo_files/plugin/lib/hobo/email_address.rb +4 -0
  25. data/hobo_files/plugin/lib/hobo/field_spec.rb +2 -1
  26. data/hobo_files/plugin/lib/hobo/guest.rb +21 -0
  27. data/hobo_files/plugin/lib/hobo/hobo_helper.rb +42 -2
  28. data/hobo_files/plugin/lib/hobo/http_parameters.rb +225 -0
  29. data/hobo_files/plugin/lib/hobo/model.rb +55 -37
  30. data/hobo_files/plugin/lib/hobo/model_controller.rb +151 -151
  31. data/hobo_files/plugin/lib/hobo/model_queries.rb +30 -5
  32. data/hobo_files/plugin/lib/hobo/user_controller.rb +27 -16
  33. data/hobo_files/plugin/lib/hobo/where_fragment.rb +6 -1
  34. data/hobo_files/plugin/tags/rapid.dryml +88 -58
  35. data/hobo_files/plugin/tags/rapid_document_tags.dryml +5 -5
  36. data/hobo_files/plugin/tags/rapid_editing.dryml +3 -3
  37. data/hobo_files/plugin/tags/rapid_forms.dryml +35 -26
  38. data/hobo_files/plugin/tags/rapid_navigation.dryml +13 -12
  39. data/hobo_files/plugin/tags/rapid_pages.dryml +35 -31
  40. data/hobo_files/plugin/tags/rapid_plus.dryml +41 -0
  41. data/hobo_files/plugin/tags/rapid_support.dryml +18 -9
  42. data/hobo_files/plugin/tasks/dump_fixtures.rake +61 -0
  43. metadata +7 -11
  44. data/hobo_files/plugin/spec/fixtures/users.yml +0 -9
  45. data/hobo_files/plugin/spec/spec.opts +0 -6
  46. data/hobo_files/plugin/spec/spec_helper.rb +0 -28
  47. data/hobo_files/plugin/spec/unit/hobo/dryml/template_spec.rb +0 -650
@@ -27,7 +27,24 @@ module Hobo
27
27
  def not_in(association)
28
28
  not_(is_in(association))
29
29
  end
30
+
31
+
32
+ def sql(s)
33
+ WhereFragment.new(s)
34
+ end
35
+
36
+ def block(b)
37
+ b && instance_eval(&b)
38
+ end
39
+
40
+
41
+ def all?(*args)
42
+ args.inject { |m, a| m & a }
43
+ end
30
44
 
45
+ def any?(*args)
46
+ args.inject { |m, a| m | a }
47
+ end
31
48
 
32
49
  def method_missing(name, *args)
33
50
  check_column = proc do |col|
@@ -46,28 +63,32 @@ module Hobo
46
63
  val = args[0]
47
64
  end
48
65
  return (if val.nil?
49
- WhereFragment.new("#{field} IS NULL")
66
+ WhereFragment.new("#{_query_table}.#{field} IS NULL")
50
67
  else
51
- WhereFragment.new("#{field} = ?", val)
68
+ WhereFragment.new("#{_query_table}.#{field} = ?", val)
52
69
  end)
53
70
  end
54
71
 
55
72
  m, field = *name.to_s.match(/^(.*)_contains$/)
56
73
  if m
57
74
  check_column[field]
58
- return WhereFragment.new("#{field} like ?", "%#{args[0]}%")
75
+ return WhereFragment.new("#{_query_table}.#{field} like ?", "%#{args[0]}%")
59
76
  end
60
77
 
61
78
  m, field = *name.to_s.match(/^(.*)_starts$/)
62
79
  if m
63
80
  check_column[field]
64
- return WhereFragment.new("#{field} like ?", "#{args[0]}%")
81
+ return WhereFragment.new("#{_query_table}.#{field} like ?", "#{args[0]}%")
65
82
  end
66
83
 
67
84
  m, field = *name.to_s.match(/^(.*)_ends$/)
68
85
  if m
69
86
  check_column[field]
70
- return WhereFragment.new("#{field} like ?", "%#{args[0]}")
87
+ return WhereFragment.new("#{_query_table}.#{field} like ?", "%#{args[0]}")
88
+ end
89
+
90
+ if (refl = model.reflections[name.to_sym]) && refl.macro == :belongs_to
91
+ return ModelQueries.new(refl.klass)
71
92
  end
72
93
 
73
94
  return WhereFragment.new(@model.send(name, *args))
@@ -86,6 +107,10 @@ module Hobo
86
107
  Object.instance_method(:instance_variable_get).bind(assoc).call("@finder_sql")
87
108
  end
88
109
  end
110
+
111
+ def _query_table
112
+ model.table_name
113
+ end
89
114
 
90
115
  end
91
116
 
@@ -9,6 +9,7 @@ module Hobo
9
9
 
10
10
  def included(base)
11
11
  base.filter_parameter_logging "password"
12
+ base.skip_before_filter :login_required, :only => [:login]
12
13
  user_models << base.model
13
14
  end
14
15
  end
@@ -20,33 +21,43 @@ module Hobo
20
21
  def logout; hobo_logout; end
21
22
 
22
23
  def hobo_login(options={})
23
- options = options.reverse_merge(:success_notice => "You have logged in.",
24
- :failure_notice => "You did not provide a valid login and password.",
25
- :redirect_to => {:action => "index"})
24
+ @user_model = model
25
+ options = LazyHash.new(options)
26
+ options.reverse_merge!(:success_notice => "You have logged in.",
27
+ :failure_notice => "You did not provide a valid login and password.",
28
+ :redirect_to => {:action => "index"})
26
29
 
27
30
  if request.post?
28
31
  user = model.authenticate(params[:login], params[:password])
29
- if user
32
+ if user.nil?
33
+ flash[:notice] = options[:failure_notice]
34
+ else
35
+ old_user = current_user
30
36
  self.current_user = user
31
- if params[:remember_me] == "1"
32
- current_user.remember_me
33
- create_auth_cookie
37
+
38
+ # If supplied, a block can be used to test if this user is
39
+ # allowed to log in (e.g. the account may be disabled)
40
+ if block_given? && !yield
41
+ # block returned false - cancel this login
42
+ self.current_user = old_user
43
+ else
44
+ if params[:remember_me] == "1"
45
+ current_user.remember_me
46
+ create_auth_cookie
47
+ end
48
+ flash[:notice] ||= options[:success_notice]
49
+ redirect_back_or_default(options[:redirect_to]) unless performed?
34
50
  end
35
- redirect_back_or_default(options[:redirect_to])
36
- flash[:notice] = options[:success_notice]
37
- else
38
- flash[:notice] = options[:failure_notice]
39
- hobo_render
40
51
  end
41
- else
42
- hobo_render
43
52
  end
53
+ hobo_render unless performed?
44
54
  end
45
55
 
46
56
 
47
57
  def hobo_signup(options={})
48
- options = options.reverse_merge(:notice => "Thanks for signing up!",
49
- :redirect_to => {:action => "index"})
58
+ options = LazyHash.new(options)
59
+ options.reverse_merge!(:notice => "Thanks for signing up!",
60
+ :redirect_to => {:action => "index"})
50
61
  if request.post?
51
62
  begin
52
63
  @user = model.new(params[:user])
@@ -7,7 +7,12 @@ module Hobo
7
7
  end
8
8
 
9
9
  def &(rhs)
10
- WhereFragment.new("(#{to_sql}) and (#{rhs.to_sql})")
10
+ # If either is nil, just return the other
11
+ if self.to_sql && rhs.to_sql
12
+ WhereFragment.new("(#{to_sql}) and (#{rhs.to_sql})")
13
+ else
14
+ self.to_sql || rhs.to_sql
15
+ end
11
16
  end
12
17
 
13
18
  def |(rhs)
@@ -6,9 +6,10 @@
6
6
  <include src="rapid_editing"/>
7
7
  <include src="rapid_forms"/>
8
8
  <include src="rapid_navigation"/>
9
+ <include src="rapid_plus"/>
9
10
 
10
11
  <def tag="FieldList" attrs="tag">
11
- <% tag ||= (this.respond_to?(:new_record?) && this.new_record?) ? "input" : "editor" %>
12
+ <% tag ||= scope.in_form ? "input" : "editor" %>
12
13
  <field_list merge_attrs="&attributes - attrs_for(:with_fields)">
13
14
  <with_fields merge_attrs="&attributes & attrs_for(:with_fields)">
14
15
  <field_list_item>
@@ -41,7 +42,7 @@
41
42
  <thead if="&all_parameters[:thead] || fields" param>
42
43
  <tr param="field_heading_row">
43
44
  <with_field_names merge_attrs="&all_attributes & attrs_for(:with_fields)">
44
- <th param="#{this.underscore}_heading"><%= this.titleize %></th>
45
+ <th param="#{scope.field_name}_heading"><%= scope.field_name.titleize %></th>
45
46
  </with_field_names>
46
47
  <th if="&all_parameters[:controls]" class="controls"/>
47
48
  </tr>
@@ -53,7 +54,7 @@
53
54
  hobo_model_id="#{dom_id(this)}">
54
55
  <if test="&fields">
55
56
  <with_fields merge_attrs="&all_attributes & attrs_for(:with_fields)">
56
- <td><call_tag tag="&field_tag"/></td>
57
+ <td param="#{this_field.to_s.sub('?', '').gsub('.', '_')}_view"><call_tag tag="&field_tag"/></td>
57
58
  </with_fields>
58
59
  <td class="controls" param="controls" if="&all_parameters[:controls]">
59
60
  <a param="edit_link">Edit</a>
@@ -127,9 +128,9 @@
127
128
  count
128
129
  elsif this.is_a? Class and this < ActiveRecord::Base
129
130
  this.name.pluralize.titleize
130
- elsif this.respond_to?(:name)
131
+ elsif this.respond_to?(:name) && can_view?(this, :name)
131
132
  view(merge_attrs(attributes, {:field => 'name'}))
132
- elsif this.respond_to?(:title)
133
+ elsif this.respond_to?(:title) && can_view?(this, :title)
133
134
  view(merge_attrs(attributes, {:field => 'title'}))
134
135
  else
135
136
  this.to_s
@@ -155,24 +156,34 @@
155
156
  content = tagbody.call if tagbody
156
157
 
157
158
  if href || attributes[:name]
158
- # Regular link - skip all this stuff
159
+ # Regular link
160
+ content_tag(:a, content, attributes.update(:href => href))
159
161
  else
160
162
  target = to || this
161
163
 
162
- return "(Not Available)" if target.nil?
163
-
164
- if action == "new"
164
+ if target.nil?
165
+ Hobo::Dryml.last_if = false
166
+ "(Not Available)"
167
+ elsif action == "new"
165
168
  # Link to a new object form
166
-
167
- new_class_name = if target.respond_to?(:proxy_reflection)
168
- target.proxy_reflection.klass.name
169
- else
170
- target.name
171
- end
172
-
173
- href = object_url(target, "new")
174
- add_classes!(attributes, "new_#{new_class_name.underscore}_link")
175
- content ||= "New #{new_class_name.titleize}"
169
+ new_record = target.new
170
+ new_record.set_creator(current_user)
171
+ if can_create?(new_record)
172
+
173
+ new_class_name = if target.respond_to?(:proxy_reflection)
174
+ target.proxy_reflection.klass.name
175
+ else
176
+ target.name
177
+ end
178
+
179
+ href = object_url(target, "new")
180
+ add_classes!(attributes, "new_#{new_class_name.underscore}_link")
181
+ content ||= "New #{new_class_name.titleize}"
182
+ content_tag(:a, content, attributes.update(:href => href))
183
+ else
184
+ Hobo::Dryml.last_if = false
185
+ ""
186
+ end
176
187
  else
177
188
  # Link to an existing object
178
189
 
@@ -185,52 +196,58 @@
185
196
 
186
197
  # Set default link text if none given
187
198
  content ||= name
199
+ content_tag(:a, content, attributes.update(:href => href))
188
200
  end
189
201
  end
190
- content_tag :a, content, attributes.update(:href => href)
191
202
  %></def>
192
203
 
204
+
193
205
 
194
- <def tag="view" attrs="inline, block, if_blank, no_wrapper"><%=
206
+ <def tag="view" attrs="inline, block, if_blank, no_wrapper, truncate"><%=
195
207
  raise HoboError, "view of non-viewable field '#{this_field}' of #{this_parent.typed_id rescue this_parent}" unless
196
208
  can_view?
197
209
 
198
- if this == nil
199
- this_type.is_a?(Class) && this_type <= String ? "" : "(Not Available)"
200
- elsif this_type.respond_to?(:macro)
201
- if this_type.macro == :belongs_to
202
- belongs_to_view(attributes)
203
- elsif this_type.macro == :has_many
204
- has_many_view(attributes)
205
- end
206
- else
207
- attrs = add_classes(attributes, "view", type_id, type_and_field)
208
- attrs[:hobo_model_id] = this_field_dom_id if this_parent && this_parent.respond_to?(:typed_id)
209
-
210
- view_tag = find_polymorphic_tag("view")
211
-
212
- if view_tag == "view" # i.e. it didn't find a type specific tag
213
- raise HoboError, "Cannot view: #{this.inspect} (field is #{this_field}, type is #{this.class})"
214
- else
215
- view_attrs = attrs_for(view_tag)
216
- the_view = send(view_tag, attrs & view_attrs)
217
-
218
- the_view = if_blank if if_blank && the_view.blank?
219
-
220
- if no_wrapper
221
- the_view
222
- else
223
- wrapper = if inline
224
- :span
225
- elsif block || (this.class.const_defined?(:COLUMN_TYPE) && this.class::COLUMN_TYPE == :text)
226
- :div
227
- else
228
- :span
229
- end
230
- content_tag(wrapper, the_view, attrs - view_attrs)
231
- end
232
- end
233
- end
210
+ res = if this.nil? && if_blank.nil?
211
+ this_type.is_a?(Class) && this_type <= String ? "" : "(Not Available)"
212
+ elsif this_type.respond_to?(:macro)
213
+ if this_type.macro == :belongs_to
214
+ belongs_to_view(attributes)
215
+ elsif this_type.macro == :has_many
216
+ has_many_view(attributes)
217
+ end
218
+ else
219
+ attrs = add_classes(attributes, "view", type_id, type_and_field)
220
+ attrs[:hobo_model_id] = this_field_dom_id if this_parent && this_parent.respond_to?(:typed_id)
221
+
222
+ view_tag = find_polymorphic_tag("view")
223
+
224
+ if view_tag == "view" # i.e. it didn't find a type specific tag
225
+ raise HoboError, "Cannot view: #{this.inspect} (field is #{this_field}, type is #{this.class})"
226
+ else
227
+ view_attrs = attrs_for(view_tag)
228
+ the_view = send(view_tag, attrs & view_attrs)
229
+
230
+ the_view = if_blank if if_blank && the_view.blank?
231
+
232
+ truncate = 30 if truncate == true
233
+ the_view = self.truncate(the_view, truncate.to_i) if truncate
234
+
235
+ if no_wrapper
236
+ the_view
237
+ else
238
+ wrapper = if inline
239
+ :span
240
+ elsif block || (this.class.const_defined?(:COLUMN_TYPE) && this.class::COLUMN_TYPE == :text)
241
+ :div
242
+ else
243
+ :span
244
+ end
245
+ content_tag(wrapper, the_view, attrs - view_attrs)
246
+ end
247
+ end
248
+ end
249
+ Hobo::Dryml.last_if = !res.blank?
250
+ res
234
251
  %></def>
235
252
 
236
253
 
@@ -238,7 +255,7 @@
238
255
 
239
256
  <def tag="has_many_view"><%= this.empty? ? "(none)" : map_this { a }.join(", ") %></def>
240
257
 
241
- <def tag="view" for="Date"><%= this.to_s(:long) %></def>
258
+ <def tag="view" for="Date" attrs="format"><%= format ? this.strftime(format) : this.to_s(:long) %></def>
242
259
 
243
260
  <def tag="view" for="Time" attrs="format"><%= format ? this.strftime(format) : this.to_s(:long) %></def>
244
261
 
@@ -344,4 +361,17 @@ in the future - use at your own risk. -->
344
361
  </with>
345
362
  </def>
346
363
 
364
+ <def tag="restricted_page" attrs="require_login,error_message"><%
365
+ allowed = if require_login == false
366
+ true
367
+ elsif require_login == true
368
+ logged_in?
369
+ else
370
+ models = comma_split(require_login).map &it.titleize.constantize
371
+ current_user.is_a?(*models)
372
+ end
373
+ raise Hobo::ModelController::UserPermissionError.new(models), (error_message || "Permission Denied") unless allowed
374
+ -%><tagbody/></def>
375
+
347
376
 
377
+ <def tag="you_have"><if test="&this == current_user">You have</if><else><name/> has</else></def>
@@ -18,29 +18,29 @@
18
18
  <!-- section represents a generic document or application section. -->
19
19
  <def tag="section">
20
20
  <set body="&tagbody ? tagbody.call : ''"/>
21
- <div class="section" merge_attrs if="&body"><tagbody/></div>
21
+ <div class="section" merge_attrs if="&body"><%= body %></div>
22
22
  </def>
23
23
 
24
24
  <def tag="aside">
25
25
  <set body="&tagbody ? tagbody.call : ''"/>
26
- <div class="aside" merge_attrs if="&body"><tagbody/></div>
26
+ <div class="aside" merge_attrs if="&body"><%= body %></div>
27
27
  </def>
28
28
 
29
29
  <def tag="header">
30
30
  <set body="&tagbody ? tagbody.call : ''"/>
31
- <div class="header" merge_attrs if="&body"><tagbody/></div>
31
+ <div class="header" merge_attrs if="&body"><%= body %></div>
32
32
  </def>
33
33
 
34
34
  <def tag="footer">
35
35
  <set body="&tagbody ? tagbody.call : ''"/>
36
- <div class="footer" merge_attrs if="&body"><tagbody/></div>
36
+ <div class="footer" merge_attrs if="&body"><%= body %></div>
37
37
  </def>
38
38
 
39
39
  <!-- article represents an independent piece of content of a -->
40
40
  <!-- document, such as a blog entry or newspaper article. -->
41
41
  <def tag="article">
42
42
  <set body="&tagbody ? tagbody.call : ''"/>
43
- <div class="article" merge_attrs if="&body"><tagbody/></div>
43
+ <div class="article" merge_attrs if="&body"><%= body %></div>
44
44
  </def>
45
45
 
46
46
  <!-- temporary tag -->
@@ -1,4 +1,4 @@
1
- <def tag="editor"><%=
1
+ <def tag="editor" ><%=
2
2
  if !can_edit?
3
3
  view(attributes)
4
4
  elsif this_type.respond_to?(:macro)
@@ -11,10 +11,10 @@
11
11
  attrs = add_classes(attributes, "#{type_and_field}", "#{type_id}", "editor")
12
12
  call_polymorphic_tag("editor", attrs) or
13
13
  raise HoboError.new("<editor> not implemented for #{this.class.name}\##{this_field} " +
14
- "(#{this.inspect}:#{this_type})")
14
+ "(#{this.inspect}:#{this_type})")
15
15
  end
16
16
  %></def>
17
-
17
+
18
18
 
19
19
  <def tag="has_many_editor">
20
20
  <% #TODO: Implement %>
@@ -1,17 +1,17 @@
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")
1
+ <def tag="hidden_fields" attrs="fields, skip, for_query_string"><%=
2
+ pairs = if for_query_string
3
+ query_params.to_a
4
+ else
5
+ hiddens = case fields
6
+ when '*', nil
7
+ this.class.column_names - ['type', 'created_at', 'updated_at']
8
+ else
9
+ comma_split(fields)
10
+ end
11
+ pname = this.class.name.underscore
12
+ hiddens.map { |h| [ "#{pname}[#{h}]", this.send(h) ] }
13
+ end
14
+ pairs.map {|n, v| hidden_field_tag(n, v.to_s) if v && n.not_in?(skip)}.compact.join("\n")
15
15
  %></def>
16
16
 
17
17
 
@@ -40,7 +40,10 @@
40
40
  html_attrs[:onsubmit] = [html_attrs[:onsubmit], "#{function}; return false;"].compact.join("; ")
41
41
  end
42
42
 
43
- body, field_names = with_form_context { tagbody.call }
43
+ body, field_names = scope.new_scope do
44
+ scope[:in_form] = true
45
+ with_form_context { tagbody.call }
46
+ end
44
47
 
45
48
  hiddens = hidden_fields(:fields => hidden_fields, :skip => field_names) if new_record
46
49
 
@@ -69,7 +72,7 @@
69
72
  has_many_input(attributes)
70
73
  end
71
74
  else
72
- attrs = add_classes(attributes, "#{type_and_field}", "#{type_id}")
75
+ attrs = add_classes(attributes, type_and_field, type_id)
73
76
  attrs[:name] ||= param_name_for_this
74
77
  the_input = call_polymorphic_tag('input', attrs) or
75
78
  raise HoboError, ("No input tag for #{this_field}:#{this_type} (this=#{this.inspect})")
@@ -98,12 +101,14 @@
98
101
  <%= text_area_tag(name, this, add_classes(attributes, :tiny_mce)) %>
99
102
  </def>
100
103
 
101
- <def tag="input" for="date">
102
- <%= select_date(this || Time.now, :prefix => param_name_for_this) %>
104
+ <def tag="input" for="date" attrs="order">
105
+ <% order = order.nil? ? [:year, :month, :day] : comma_split(order).every(:to_sym) -%>
106
+ <%= select_date(this || Time.now, attributes.merge(:prefix => param_name_for_this, :order => order)) %>
103
107
  </def>
104
108
 
105
- <def tag="input" for="datetime">
106
- <%= select_datetime(this || Time.now, :prefix => param_name_for_this) %>
109
+ <def tag="input" for="datetime" attrs="order">
110
+ <% order = order.nil? ? [:year, :month, :day, :hour, :minute] : comma_split(order).every(:to_sym) -%>
111
+ <%= select_datetime(this || Time.now, attributes.merge(:prefix => param_name_for_this, :order => order)) %>
107
112
  </def>
108
113
 
109
114
  <def tag="input" for="integer" attrs="name">
@@ -199,16 +204,20 @@
199
204
  %></def>
200
205
 
201
206
 
202
- <def tag="belongs_to_menu_input" attrs="include_none"><%
207
+ <def tag="belongs_to_menu_input" attrs="include_none, none_message, options"><%
203
208
  raise HoboError.new("Not allowed to edit") unless can_edit?
209
+
210
+ none_message ||= "(No #{this_type.name.to_s.titleize})"
211
+ options ||= this_type.klass.find(:all).select {|x| can_view?(x)}
204
212
  #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|
213
+ select_options = options.map { |x|
206
214
  [ 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
215
+ }.sort
216
+ select_options.insert(0, [none_message, ""]) if this.nil? || include_none
217
+ attributes = add_classes(attributes, "input", type_and_field)
209
218
  %>
210
- <select name="#{param_name_for_this(true)}">
211
- <%= options_for_select(select_options.sort, this ? this.id : "") %>
219
+ <select name="#{param_name_for_this(true)}" merge_attrs>
220
+ <%= options_for_select(select_options, this ? this.id : "") %>
212
221
  </select>
213
222
  </def>
214
223