hobo 1.3.0.RC2 → 1.3.0.RC3

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.3.0.RC2
1
+ 1.3.0.RC3
@@ -34,6 +34,7 @@ var Hobo = {
34
34
  var params = Hobo.fieldSetParam(el, val)
35
35
  var p = el.getAttribute("hobo-ajax-params")
36
36
  if (p) params = params + "&" + p
37
+ params = params + '&_method=PUT'
37
38
 
38
39
  var opts = Object.merge(options || {}, { params: params, message: el.getAttribute("hobo-ajax-message")})
39
40
  Hobo.ajaxRequest(Hobo.putUrl(el), updates, opts)
@@ -257,8 +258,9 @@ var Hobo = {
257
258
 
258
259
 
259
260
  putUrl: function(el) {
261
+ /* we used to append _method=put here, but it doesn't work in Rails 3 */
260
262
  var spec = Hobo.modelSpecForElement(el)
261
- return urlBase + "/" + Hobo.pluralise(spec.name) + "/" + spec.id + "?_method=PUT"
263
+ return urlBase + "/" + Hobo.pluralise(spec.name) + "/" + spec.id
262
264
  },
263
265
 
264
266
 
@@ -14,6 +14,7 @@ body {color: #193440; background: url(../images/300-ACD3E6-fff.png) repeat-x #ff
14
14
  .section.content {background: #FCFFF5;}
15
15
  .button {color: white; background: #5B8BA0;}
16
16
  .button:hover {background-color: #193440;}
17
+ .button[disabled] { background-color: #CCCCCC; }
17
18
  .add-to-collection {background: #E6E7DE;}
18
19
  .aside { background: #E5E5E5;}
19
20
 
@@ -92,7 +92,7 @@ module Generators
92
92
  end
93
93
 
94
94
  def user_routes
95
- return [] unless controller < ::Hobo::Controller::User
95
+ return [] unless controller < ::Hobo::Controller::UserBase
96
96
  prefix = records == "users" ? "" : "#{record}_"
97
97
  [
98
98
  link("match '#{prefix}login(.:format)' => '#{records}#login', :as => '#{record}_login'", 'login'),
data/lib/hobo.rb CHANGED
@@ -6,7 +6,7 @@ require 'hobo/extensions/enumerable'
6
6
  require 'hobo/extensions/array'
7
7
 
8
8
  ActiveSupport::Dependencies.autoload_paths |= [File.dirname(__FILE__)]
9
-
9
+ ActiveSupport::Dependencies.autoload_once_paths |= [File.dirname(__FILE__)]
10
10
 
11
11
  module Hobo
12
12
 
@@ -129,13 +129,15 @@ module Hobo
129
129
  def auto_actions(*args)
130
130
  options = args.extract_options!
131
131
 
132
- @auto_actions = case args.first
132
+ @auto_actions = args.map do |arg|
133
+ case arg
133
134
  when :all then available_auto_actions
134
- when :write_only then available_auto_write_actions + args.rest
135
- when :read_only then available_auto_read_actions + args.rest
136
- when :lifecycle then available_auto_lifecycle_actions + args.rest
137
- else args
138
- end
135
+ when :write_only then available_auto_write_actions
136
+ when :read_only then available_auto_read_actions
137
+ when :lifecycle then available_auto_lifecycle_actions
138
+ else arg
139
+ end
140
+ end.flatten.uniq
139
141
 
140
142
  except = Array(options[:except])
141
143
  except_actions = except.map do |arg|
@@ -143,9 +145,9 @@ module Hobo
143
145
  when :lifecycle then available_auto_lifecycle_actions
144
146
  else arg
145
147
  end
146
- end
148
+ end.flatten.uniq
147
149
 
148
- @auto_actions -= except_actions.flatten
150
+ @auto_actions -= except_actions
149
151
 
150
152
  def_auto_actions
151
153
  end
@@ -1,6 +1,6 @@
1
1
  module Hobo
2
2
  module Controller
3
- module User
3
+ module UserBase
4
4
 
5
5
  class << self
6
6
  def included(base)
@@ -12,7 +12,7 @@ module Hobo
12
12
  alias_method_chain :def_auto_actions, :user_actions
13
13
  end
14
14
 
15
- skip_before_filter :login_required, :only => [:login, :signup, :forgot_password, :reset_password, :do_reset_password,
15
+ skip_before_filter :login_required, :only => [:login, :signup, :do_signup, :forgot_password, :reset_password, :do_reset_password,
16
16
  :accept_invitation, :do_accept_invitation]
17
17
 
18
18
  include_taglib "rapid_user_pages", :plugin => "hobo"
@@ -2,7 +2,7 @@ ActionController::Base.class_eval do
2
2
 
3
3
  def self.hobo_user_controller
4
4
  include Hobo::Controller::Model
5
- include Hobo::Controller::User
5
+ include Hobo::Controller::UserBase
6
6
  end
7
7
 
8
8
  def self.hobo_model_controller
@@ -28,12 +28,17 @@ module ActiveRecord
28
28
  # DO NOT call super here - AssociationProxy's version loads the collection, and that's bad.
29
29
  # TODO: this really belongs in Rails; migrate it there ASAP
30
30
  def respond_to?(*args)
31
- proxy_respond_to?(*args) || Array.new.respond_to?(*args)
31
+ return super if has_one_collection?
32
+ proxy_respond_to?(*args) || [].respond_to?(*args)
32
33
  end
33
34
 
34
- # TODO: send this patch into Rails. There's no reason to load the collection just to find out it acts like an array.
35
35
  def is_a?(klass)
36
- [].is_a?(klass)
36
+ if has_one_collection?
37
+ load_target
38
+ @target.is_a?(klass)
39
+ else
40
+ [].is_a?(klass)
41
+ end
37
42
  end
38
43
 
39
44
  def member_class
@@ -52,6 +57,10 @@ module ActiveRecord
52
57
  end
53
58
  end
54
59
 
60
+ def has_one_collection?
61
+ proxy_reflection.macro == :has_one
62
+ end
63
+
55
64
  end
56
65
  end
57
66
  end
@@ -9,7 +9,7 @@ module ActiveRecord
9
9
  begin
10
10
  klass_without_create_polymorphic_class
11
11
  rescue NameError => e
12
- Object.class_eval "class #{e.missing_name} < ActiveRecord::Base; set_table_name '#{active_record.name.tableize}'; end"
12
+ Object.class_eval "class #{e.missing_name} < ActiveRecord::Base; set_table_name '#{active_record.name.tableize}'; def self.hobo_shim?; true; end; end"
13
13
  e.missing_name.constantize
14
14
  end
15
15
  else
data/lib/hobo/helper.rb CHANGED
@@ -179,6 +179,10 @@ module Hobo
179
179
  object.respond_to?(:typed_id) ? "model::#{typed_id(object, attribute).to_s.dasherize}" : ""
180
180
  end
181
181
 
182
+ def update_elements_class(updates)
183
+ 'update::'+comma_split(updates).join(':') unless updates.blank?
184
+ end
185
+
182
186
  def can_create?(object=this)
183
187
  if object.is_a?(Class) and object < ActiveRecord::Base
184
188
  object = object.new
@@ -248,7 +252,8 @@ module Hobo
248
252
  # TODO: Man does this need a big cleanup!
249
253
 
250
254
  if args.empty?
251
- if this_parent && this_field
255
+ # if we're repeating over an array, this_field ends up with the current index. Is this useful to anybody?
256
+ if this_parent && this_field && !this_field.is_a?(Integer)
252
257
  object = this_parent
253
258
  field = this_field
254
259
  else
@@ -42,7 +42,7 @@ en:
42
42
  raise Hobo::I18nError, %(wrong model name: "#{model_name}" (extracted from translation key: "#{key}"). You might want to use the translate/t tag/method instead.)
43
43
  end
44
44
  options[:default].unshift("hobo.#{keys.join(".")}".to_sym)
45
- options[:model] = model_class.model_name.human(:count=>options[:count]||1)
45
+ options[:model] = model_class.model_name.human(:count=>(options[:count] || 1).to_i)
46
46
  translate key.to_sym, options
47
47
  end
48
48
  alias_method :ht, :hobo_translate
data/lib/hobo/model.rb CHANGED
@@ -33,17 +33,6 @@ module Hobo
33
33
  alias_method_chain :attr_accessor, :creator_metadata
34
34
 
35
35
  alias_method_chain :has_one, :new_method
36
-
37
- # eval avoids the ruby 1.9.2 "super from singleton method ..." error
38
- eval %(
39
- def inherited(klass)
40
- super
41
- fields(false) do
42
- Hobo.register_model(klass)
43
- field(klass.inheritance_column, :string)
44
- end
45
- end
46
- )
47
36
  end
48
37
 
49
38
  base.fields(false) # force hobo_fields to load
@@ -172,6 +161,18 @@ module Hobo
172
161
  send(:login_attribute=, name.to_sym, validate) if options.delete(:login) && respond_to?(:login_attribute=)
173
162
  end
174
163
 
164
+ # eval avoids the ruby 1.9.2 "super from singleton method ..." error
165
+ eval %(
166
+ def inherited(klass)
167
+ super
168
+ Hobo::Model.register_model(klass)
169
+ # TODO: figure out when this is needed, as Hobofields already does this
170
+ fields(false) do
171
+ field(klass.inheritance_column, :string)
172
+ end
173
+ end
174
+ )
175
+
175
176
  private
176
177
 
177
178
  def attrib_names
@@ -188,22 +189,27 @@ module Hobo
188
189
  def belongs_to_with_test_methods(name, options={}, &block)
189
190
  belongs_to_without_test_methods(name, options, &block)
190
191
  refl = reflections[name]
192
+ id_method = refl.options[:primary_key] || refl.klass.primary_key
191
193
  if options[:polymorphic]
192
194
  # TODO: the class lookup in _is? below is incomplete; a polymorphic association to an STI base class
193
195
  # will fail to match an object of a derived type
194
196
  # (ie X belongs_to Y (polymorphic), Z is a subclass of Y; @x.y_is?(some_z) will never pass)
195
197
  class_eval %{
196
198
  def #{name}_is?(target)
197
- target.class.name == self.#{refl.options[:foreign_type]} && target.id == self.#{refl.primary_key_name}
199
+ target.class.name == self.#{refl.options[:foreign_type]} && target.#{id_method} == self.#{refl.primary_key_name}
198
200
  end
199
201
  def #{name}_changed?
200
202
  #{refl.primary_key_name}_changed? || #{refl.options[:foreign_type]}_changed?
201
203
  end
202
204
  }
203
205
  else
206
+ id_method = refl.options[:primary_key] || refl.klass.primary_key
204
207
  class_eval %{
205
208
  def #{name}_is?(target)
206
- target.class <= ::#{refl.klass.name} && target.id == self.#{refl.primary_key_name}
209
+ our_id = self.#{refl.primary_key_name}
210
+ # if our_id is nil, only return true if target is nil
211
+ return target.nil? unless our_id
212
+ target.class <= ::#{refl.klass.name} && target.#{id_method} == our_id
207
213
  end
208
214
  def #{name}_changed?
209
215
  #{refl.primary_key_name}_changed?
@@ -464,7 +470,11 @@ module Hobo
464
470
  if parts.include?(0)
465
471
  nil
466
472
  else
467
- Date.new(*parts)
473
+ begin
474
+ Date.new(*parts)
475
+ rescue ArgumentError => ex
476
+ Time.time_with_datetime_fallback(ActiveRecord::Base.default_timezone, *parts).to_date
477
+ end
468
478
  end
469
479
  else
470
480
  value
@@ -1,5 +1,6 @@
1
1
  <% each_controller do -%>
2
2
  <%
3
+ next unless @controller < Hobo::Controller::Model
3
4
  form_fields = standard_fields :belongs_to, :has_many
4
5
 
5
6
  cancel_to_show_page = linkable?(:show)
@@ -12,7 +12,7 @@
12
12
 
13
13
 
14
14
  <% each_controller do -%>
15
-
15
+ <% next unless @controller < Hobo::Controller::Model %>
16
16
  <!-- ====== <%= model.name %> Pages ====== -->
17
17
  <%
18
18
  new_link = linkable?(:new)
@@ -124,11 +124,10 @@ end
124
124
 
125
125
  unless model.view_hints.secondary_children.empty?
126
126
  aside_collections = model.view_hints.secondary_children.map { |c| [model.reflections[c], model.reverse_reflection(c)] }
127
- aside_collections.reject! { |refl, reverse| reverse.nil? }
128
127
  end
129
128
  -%>
130
129
  <def tag="show-page" for="<%= model.name %>">
131
- <page merge title="#{ht '<%=model_key %>.show.title', :default=>['<%=sq_escape model_name %>'] }">
130
+ <page merge title="#{ht '<%=model_key %>.show.title', :default=>['<%=sq_escape model_name %>'], :name => name(:no_wrapper => true) }">
132
131
 
133
132
  <body: class="show-page <%= model_class %>" param/>
134
133
 
@@ -142,8 +141,8 @@ end
142
141
  <a:<%= back_link %> param="parent-link">&laquo; <ht key="<%= model_key %>.actions.back_to_parent" parent="<%= back_link_human_name %>" name="&this">Back to <name/></ht></a:<%= back_link %>>
143
142
  <% end -%>
144
143
  <h2 param="heading">
145
- <ht key="<%= model_key %>.show.heading" name="&this.respond_to?(:name) ? this.name : ''">
146
- <name/>
144
+ <ht key="<%= model_key %>.show.heading" name="#{name(:no_wrapper => true)}">
145
+ <%= model_name %> <name/>
147
146
  </ht>
148
147
  </h2>
149
148
  <% if boolean_fields -%>
@@ -160,7 +159,7 @@ end
160
159
  <% if edit_link -%>
161
160
 
162
161
  <a action="edit" if="&can_edit?" param="edit-link">
163
- <ht key="<%= model_key %>.actions.edit" name="&this.respond_to?(:name) ? this.name : ''">
162
+ <ht key="<%= model_key %>.actions.edit" name="#{name(:no_wrapper => true)}">
164
163
  Edit <%= model_name %>
165
164
  </ht>
166
165
  </a>
@@ -169,7 +168,7 @@ end
169
168
 
170
169
  <section param="content-body">
171
170
  <% if main_content -%>
172
- <view:<%= main_content %> param="description"/>
171
+ <view:<%= main_content %> param="description" />
173
172
  <% end -%>
174
173
  <% if show_fields.any? -%>
175
174
  <field-list fields="<%= show_fields * ', ' %>" param/>
@@ -216,7 +215,7 @@ end
216
215
 
217
216
  <aside param>
218
217
  <% for refl, reverse_refl in aside_collections -%>
219
- <% if linkable?(refl.klass, :"index_for_#{reverse_refl.name}") -%>
218
+ <% if reverse_refl && linkable?(refl.klass, :"index_for_#{reverse_refl.name}") -%>
220
219
  <collection-preview:<%= refl.name %>.recent param="<%= refl.name %>-preview">
221
220
  <heading-content:><human-collection-name with="&this.origin" collection="<%= refl.name %>" your/></heading-content:>
222
221
  </collection-preview:<%= refl.name %>.recent>
@@ -244,14 +243,14 @@ end
244
243
  name_attribute = model.name_attribute
245
244
  -%>
246
245
  <def tag="edit-page" for="<%= model.name %>">
247
- <page merge title="#{ht '<%= model_key %>.edit.title', :default=>['Edit <%= sq_escape model_name %>'] }">
246
+ <page merge title="#{ht '<%= model_key %>.edit.title', :default=>['Edit <%= sq_escape model_name %>'], :name => name(:no_wrapper => true) }">
248
247
 
249
248
  <body: class="edit-page <%= model_class %>" param/>
250
249
 
251
250
  <content:>
252
251
  <section param="content-header">
253
252
  <h2 param="heading">
254
- <ht key="<%= model_key %>.edit.heading" name="&this.respond_to?(:name) ? this.name : ''">
253
+ <ht key="<%= model_key %>.edit.heading" name="#{name(:no_wrapper => true)}">
255
254
  Edit <%= model_name %>
256
255
  </ht>
257
256
  </h2>
@@ -120,15 +120,16 @@ module Hobo
120
120
  edit_text = this._?.to_s
121
121
  attributes.update(:hobo_edit_text => edit_text) unless edit_text.nil?
122
122
 
123
- update = attributes.delete(:update)
124
- attributes[:hobo_update] = update if update
123
+ if update = attributes.delete(:update)
124
+ attributes = add_classes(attributes, update_elements_class(update))
125
+ end
125
126
 
126
127
  view(attributes)
127
128
  end
128
129
 
129
130
 
130
131
 
131
- AJAX_CALLBACKS = [ :before, :success, :failure, :complete ]
132
+ AJAX_CALLBACKS = [ :success, :failure, :complete ]
132
133
 
133
134
  AJAX_ATTRS = AJAX_CALLBACKS + [ :update, :type, :method,
134
135
  :script, :form, :params, :confirm, :message,
@@ -93,7 +93,15 @@ This will use `<input/>` as the tag in each table cell instead of `<view/>`
93
93
  <table fields="name, created_at, description">
94
94
  <controls:/>
95
95
  </table>
96
- The controls can be further customised using the "edit-link" and "delete-button" parameters or by providing completely new content for the control column, e.g.
96
+
97
+ The controls can be further customised using the "edit-link" and "delete-button" parameters:
98
+
99
+ <table fields="name, created_at, description">
100
+ <controls:/>
101
+ <delete-button: label="Nuke Me"/>
102
+ </table>
103
+
104
+ or by providing completely new content for the control column:
97
105
 
98
106
  <table fields="name, created_at, description">
99
107
  <controls:>my controls!</controls:>
@@ -681,8 +689,8 @@ The "titleize" attribute is deprecated: use "capitalize" instead.
681
689
  </if>
682
690
  <else>
683
691
  <do param="default">
684
- <%= s = t("tags.you.other_user.#{k || attributes.keys.first || 'default'}", :name=>this.name,
685
- :default=>"#{this.name} #{'has' if attributes[:have]}#{'is' if attributes[:are]}#{'does' if attributes[:do]}")
692
+ <%= s = t("tags.you.other_user.#{k || attributes.keys.first || 'default'}", :name=>name(:no_wrapper => true),
693
+ :default=>"#{name(:no_wrapper => true)} #{'has' if attributes[:have]}#{'is' if attributes[:are]}#{'does' if attributes[:do]}")
686
694
  (titleize||capitalize) ? s.sub(/^./){|c| c.upcase} : s %>
687
695
  </do>
688
696
  </else>
@@ -750,7 +758,7 @@ Notes
750
758
  attrs = {}
751
759
  attributes.each_pair{|k,v| attrs[k.to_sym] = v}
752
760
  d = "#{your_default} #{all_parameters.default}"
753
- options = {:default=>[d], :count=>(attrs[:count]||1), :name=>this.name}
761
+ options = {:default=>[d], :count=>(attrs[:count]||1), :name=>name(:no_wrapper => true)}
754
762
  your_key = key.split('.').last
755
763
  unless key.eql?(your_key) || attrs.has_key?(your_key.to_sym)
756
764
  options[your_key.to_sym] = t(key, :count=>options[:count], :default=>your_key.titleize)
@@ -81,25 +81,45 @@ This tag does not sanitize HTML; this is provided by HtmlString before saving to
81
81
 
82
82
  ### Attributes
83
83
 
84
- - include-none: Should the menu include a "none" option (true/false). Defaults: false, or true if the association is nil at render-time.
85
-
86
- - blank-message: The text for the "none" option. Default: "(No Product)" (or whatever the model name is)
87
-
88
- - sort: Sort the options (true/false)? Default: false
89
-
90
- - update: one or more DOM ID's (comma separated string or an array) to be updated as part of the ajax call.
84
+ - `include-none` - whether to include a 'none' option (i.e. set the foreign key to null). If this value is not supplied, the default is "true" if the current value is nil; otherwise the default is "false". One implication of this is that the default may change when the form is re-rendered due to a validation failure. Setting this value explicitly is recommended.
85
+ - `blank-message` - the message for the 'none' option. Defaults to "(No `<model-name>`)", e.g. "(No Product)"
86
+ - `options` - an array of records to include in the menu. Defaults to the all the records in the target table that match any `:conditions` declared on the `belongs_to` (subject to `limit`)
87
+ - `sort` - whether to sort the array of options. Defaults to no sorting.
88
+ - `limit` - if `options` is not specified, this limits the number of records. Default: 100
89
+ - `text_method` - The method to call on each record to get the text for the option. Multiple methods are supported ie "institution.name"
90
+ - `update` - one or more DOM ID's (comma separated string or an array) to be updated as part of the ajax call.
91
91
 
92
92
  NOTE: yes that's *DOM ID's* not part-names. A common source of confusion because by default the part name and DOM ID are the same.
93
93
 
94
94
  -->
95
- <def tag="select-one-editor" attrs="include-none, blank-message, sort, update, options"><%
95
+ <def tag="select-one-editor" attrs="include-none, blank-message, options, sort, limit, text-method, update"><%
96
96
  raise Hobo::Error.new("Not allowed to edit") unless can_edit?
97
97
  blank_message ||= ht("#{this_type.name.to_s.underscore}.select_one_editor.blank_message", :count => 0, :default => "(No #{this_type.name.to_s.titleize})")
98
- options ||= this_field_reflection.klass.all.select {|x| can_view?(x)}.map {|x|
99
- [ name(:with => x, :no_wrapper => true), x.id ]
100
- }
101
- options = options.sort if sort
102
- options.insert(0, [blank_message, ""]) if this.nil? || include_none
98
+ limit ||= 100
99
+
100
+ if options.blank? || !options.first.respond_to?(:first)
101
+ options ||= begin
102
+ conditions = ActiveRecord::Associations::BelongsToAssociation.new(this_parent, this_field_reflection).send(:conditions)
103
+ order = this_field_reflection.klass.default_order
104
+ this_field_reflection.klass.all(:conditions => conditions, :limit => limit, :order => order).select {|x| can_view?(x)}
105
+ end
106
+
107
+ id_method = this_field_reflection.options[:primary_key] || this_field_reflection.klass.primary_key
108
+ if text_method.nil?
109
+ select_options = options.map { |x| [name(:with => x, :no_wrapper => true), x.send(id_method)] }
110
+ else
111
+ select_options = options.map do |x|
112
+ [ text_method.split(".").inject(x) { |v, method| v.send(method) },
113
+ x.send(id_method) ]
114
+ end
115
+ end
116
+ else
117
+ # handle the old style, where options could be passed an options_for_select style array
118
+ select_options = options
119
+ end
120
+ select_options = select_options.sort if sort
121
+ select_options.insert(0, [blank_message, ""]) if include_none || (this.nil? && include_none != false)
122
+
103
123
  f = ajax_updater(object_url(this_parent, :method => :put),
104
124
  update,
105
125
  :method => "put",
@@ -108,7 +128,7 @@ This tag does not sanitize HTML; this is provided by HtmlString before saving to
108
128
  } })
109
129
  %>
110
130
  <select onchange="#{f}" merge-attrs>
111
- <%= options_for_select(options, this ? this.id : "") %>
131
+ <%= options_for_select(select_options, this ? this.send(id_method) : "") %>
112
132
  </select>
113
133
  </def>
114
134
 
@@ -216,7 +236,7 @@ This tag does not sanitize HTML; this is provided by HtmlString before saving to
216
236
  -->
217
237
  <def tag="integer-select-editor" attrs="options, min, max, update, nil-option, message">
218
238
  <% options ||= (min.to_i..max.to_i).to_a %>
219
- <select class="integer editor #{'update:' + comma_split(update).join(':') unless update.blank?} #{model_id_class(this_parent, this_field)}"
239
+ <select class="integer editor #{update_elements_class(update)} #{model_id_class(this_parent, this_field)}"
220
240
  merge-attrs="&message ? attributes.merge(:hobo_message => message) : attributes">
221
241
  <if test="&this.nil?"><option value=""><%= nil_option || "Choose a value" %></option></if>
222
242
  <%= options_for_select(options.*.to_s, this.to_s) %>
@@ -32,8 +32,6 @@ Several of the tags in this taglib support the following set of ajax attributes:
32
32
  The following attributes are also supported by all the ajax tags. Set them to fragments of javascript to have that script
33
33
  executed at various points in the ajax request cycle:
34
34
 
35
- - before: script to run before the request
36
-
37
35
  - success: script to run on successful completion of the request
38
36
 
39
37
  - failure: script to run on a request failure
@@ -635,7 +633,8 @@ All of the standard ajax attributes are supported (see the main taglib documenti
635
633
  class_or_assoc = if model
636
634
  model.is_a?(String) ? model.constantize : model
637
635
  elsif Hobo.simple_has_many_association?(this)
638
- fields[this_field_reflection.primary_key_name] = this.proxy_owner.id
636
+ id_method = this_field_reflection.options[:primary_key] || this_field_reflection.klass.primary_key
637
+ fields[this_field_reflection.primary_key_name] = this.proxy_owner.send(id_method)
639
638
  this
640
639
  else
641
640
  raise Hobo::Error.new("invalid context for <create-button>")
@@ -644,11 +643,17 @@ All of the standard ajax attributes are supported (see the main taglib documenti
644
643
  new.set_creator(current_user)
645
644
  if can_create?(new)
646
645
  label ||= ht("#{new.class.to_s.underscore}.actions.new", :default=>"New #{new.class.model_name.human}")
647
- ajax_attributes = { :message => message }
648
646
  class_name = new.class.name.underscore
649
- ajax_attributes[:params] = { class_name => fields } unless fields.empty?
647
+ params ||= params || {}
648
+ params = params.merge(class_name => fields) unless fields.blank?
649
+
650
+ ajax_attributes, html_attributes = attributes.partition_hash(Hobo::Rapid::Helper::AJAX_ATTRS)
651
+
652
+ ajax_attributes.reverse_merge!(:message => message, :params => params)
650
653
  func = ajax_updater(object_url(new.class, :method => :post), update, ajax_attributes)
651
- element :input, add_classes(attributes.merge(:type =>'button', :onclick => func, :value => label),
654
+ html_attributes.reverse_merge!(:type =>'button', :onclick => func, :value => label)
655
+
656
+ element :input, add_classes(html_attributes,
652
657
  "button create-button create-#{class_name}-button"), nil, true, true
653
658
  end
654
659
  %></def>
@@ -663,6 +668,7 @@ This is the default input that Rapid uses for `belongs_to` associations. The men
663
668
  - `include-none` - whether to include a 'none' option (i.e. set the foreign key to null). If this value is not supplied, the default is "true" if the current value is nil; otherwise the default is "false". One implication of this is that the default may change when the form is re-rendered due to a validation failure. Setting this value explicitly is recommended.
664
669
  - `blank-message` - the message for the 'none' option. Defaults to "(No `<model-name>`)", e.g. "(No Product)"
665
670
  - `options` - an array of records to include in the menu. Defaults to the all the records in the target table that match any `:conditions` declared on the `belongs_to` (subject to `limit`)
671
+ - `sort` - whether to sort the array of options. Defaults to no sorting.
666
672
  - `limit` - if `options` is not specified, this limits the number of records. Default: 100
667
673
  - `text_method` - The method to call on each record to get the text for the option. Multiple methods are supported ie "institution.name"
668
674
 
@@ -682,12 +688,13 @@ For situations where there are too many target records to practically include in
682
688
  this_field_reflection.klass.all(:conditions => conditions, :limit => limit, :order => order).select {|x| can_view?(x)}
683
689
  end
684
690
 
691
+ id_method = this_field_reflection.options[:primary_key] || this_field_reflection.klass.primary_key
685
692
  if text_method.nil?
686
- select_options = options.map { |x| [x.to_s, x.id] }
693
+ select_options = options.map { |x| [name(:with => x, :no_wrapper => true), x.send(id_method)] }
687
694
  else
688
695
  select_options = options.map do |x|
689
696
  [ text_method.split(".").inject(x) { |v, method| v.send(method) },
690
- x.id ]
697
+ x.send(id_method) ]
691
698
  end
692
699
  end
693
700
  select_options = select_options.sort if sort
@@ -695,7 +702,7 @@ For situations where there are too many target records to practically include in
695
702
  attributes = add_classes(attributes, "input", "belongs_to", type_and_field)
696
703
  -%>
697
704
  <select name="#{param_name_for_this(true)}" merge-attrs="&attributes.except :name">
698
- <%= options_for_select(select_options, this ? this.id : "") %>
705
+ <%= options_for_select(select_options, this ? this.send(id_method) : "") %>
699
706
  </select>
700
707
  </def>
701
708
 
@@ -766,11 +773,13 @@ If you wish to set `min-chars` to 0, you will require this [patch to controls.js
766
773
  min_chars ||= 1
767
774
  value = name(:no_wrapper => true, :if_present => true)
768
775
  -%>
769
- <input type="text" name="#{param_name_for_this}"
770
- class="autocompleter #{type_and_field._?.dasherize} #{css_data :complete_on, typed_id(complete_target), completer} #{css_data :min_chars, min_chars} #{'nil-value' if value==''}"
771
- value="#{value=='' ? nil_value : value}"
772
- merge-attrs/>
773
- <div class="completions-popup" style="display:none"></div>
776
+ <wrap tag="span" class="field-with-errors" when="&!this_parent.errors[this_field].empty?">
777
+ <input type="text" name="#{param_name_for_this}"
778
+ class="autocompleter #{type_and_field._?.dasherize} #{css_data :complete_on, typed_id(complete_target), completer} #{css_data :min_chars, min_chars} #{'nil-value' if value==''}"
779
+ value="#{value=='' ? nil_value : value}"
780
+ merge-attrs/>
781
+ <div class="completions-popup" style="display:none"></div>
782
+ </wrap>
774
783
  </def>
775
784
 
776
785
 
@@ -877,7 +886,7 @@ Use the `stay-here` attribute to remain on the current page:
877
886
  ...
878
887
  </form>
879
888
 
880
- Use the `go-back` option to return to the previous page:
889
+ Use the `go-back` option to return to the page in `session[:previous_uri]`:
881
890
 
882
891
  <form>
883
892
  <after-submit go-back/>
@@ -919,7 +928,7 @@ Use the `uri` option to specify a redirect location:
919
928
  <select merge-attrs param="default">
920
929
  <% selected=this if selected.nil? %>
921
930
  <option value="#{first_value}" unless="&first_option.nil?"><first-option/></option>
922
- <do param="options"><% options_for_select(options, selected) %></do>
931
+ <do param="options"><%= options_for_select(options, selected) %></do>
923
932
  </select>
924
933
  </def>
925
934
 
@@ -944,8 +953,8 @@ Use the `uri` option to specify a redirect location:
944
953
  <input type="hidden" name="#{param_name}[]" value=""/><% # ensure all items are removed when nothing checked
945
954
  %>
946
955
  <li repeat="&options" param>
947
- <input type="checkbox" name="#{param_name}[]" value="@#{this.id}" checked="&this.in?(collection)" disabled="&disabled"/>
948
- <name param/>
956
+ <input id="#{dom_id(this, :check_many)}" type="checkbox" name="#{param_name}[]" value="@#{this.id}" checked="&this.in?(collection)" disabled="&disabled"/>
957
+ <label for="#{dom_id(this, :check_many)}"><name param/></label>
949
958
  </li>
950
959
  </ul>
951
960
  </def>
@@ -98,7 +98,7 @@ With the `your` attribute and in the special case the context is a Hobo::Model::
98
98
  attrs = {}
99
99
  all_attributes.each_pair{|k,v| attrs[k.to_sym] = v} -%>
100
100
  <% if your && belong_class < Hobo::Model::UserBase && this.kind_of?(Hobo::Model::UserBase) && I18n.locale == :en -%>
101
- <your key="&collection" merge-attrs="&{:name=>this.name}.merge(attrs)" capitalize>
101
+ <your key="&collection" merge-attrs="&{:name=>name(:no_wrapper => true)}.merge(attrs)" capitalize>
102
102
  <%= belong_class.human_attribute_name(collection.to_sym, attrs) %>
103
103
  </your>
104
104
  <% else -%>
@@ -13,7 +13,7 @@ An [worked example](/tutorials/agility#improve_the_project_page_with_a_searchabl
13
13
  <div class="table-plus" merge-attrs="&attributes - attrs_for(:with_fields) - attrs_for(:table)">
14
14
  <div class="header" param="header">
15
15
  <div class="search">
16
- <form param="search-form" method="get" action="">
16
+ <form param="search-form" method="get" action="" with="&nil">
17
17
  <hidden-fields for-query-string skip="page, search"/>
18
18
  <span><t key="hobo.table_plus.search">Search</t></span>
19
19
  <input class="search" type="search" name="search" value="&params[:search]"/>
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: hobo
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease: 6
5
- version: 1.3.0.RC2
5
+ version: 1.3.0.RC3
6
6
  platform: ruby
7
7
  authors:
8
8
  - Tom Locke
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-08-15 00:00:00 -04:00
13
+ date: 2011-10-25 00:00:00 -04:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -21,7 +21,7 @@ dependencies:
21
21
  requirements:
22
22
  - - "="
23
23
  - !ruby/object:Gem::Version
24
- version: 1.3.0.RC2
24
+ version: 1.3.0.RC3
25
25
  type: :runtime
26
26
  version_requirements: *id001
27
27
  - !ruby/object:Gem::Dependency
@@ -32,7 +32,7 @@ dependencies:
32
32
  requirements:
33
33
  - - "="
34
34
  - !ruby/object:Gem::Version
35
- version: 1.3.0.RC2
35
+ version: 1.3.0.RC3
36
36
  type: :runtime
37
37
  version_requirements: *id002
38
38
  - !ruby/object:Gem::Dependency
@@ -43,7 +43,7 @@ dependencies:
43
43
  requirements:
44
44
  - - "="
45
45
  - !ruby/object:Gem::Version
46
- version: 1.3.0.RC2
46
+ version: 1.3.0.RC3
47
47
  type: :runtime
48
48
  version_requirements: *id003
49
49
  - !ruby/object:Gem::Dependency
@@ -228,7 +228,7 @@ files:
228
228
  - lib/hobo/controller.rb
229
229
  - lib/hobo/controller/authentication_support.rb
230
230
  - lib/hobo/controller/model.rb
231
- - lib/hobo/controller/user.rb
231
+ - lib/hobo/controller/user_base.rb
232
232
  - lib/hobo/engine.rb
233
233
  - lib/hobo/extensions/action_controller/hobo_methods.rb
234
234
  - lib/hobo/extensions/action_mailer/helper.rb
@@ -341,7 +341,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
341
341
  requirements: []
342
342
 
343
343
  rubyforge_project: hobo
344
- rubygems_version: 1.5.0
344
+ rubygems_version: 1.6.2
345
345
  signing_key:
346
346
  specification_version: 3
347
347
  summary: The web app builder for Rails