hobo 0.6.1 → 0.6.2

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